using System;
using System.Collections.Generic;
using ZeroLevel.DocumentObjectModel;
using ZeroLevel.DocumentObjectModel.Flow;
namespace DOM.Services
{
///
/// Allows to build the content of document.
///
public class ContentBuilder
{
private readonly Document _parent;
private readonly FlowContent _content;
private readonly Stack _containers;
public ContentBuilder(Document document)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
_parent = document;
_content = new FlowContent();
_containers = new Stack();
}
#region Helpers
#region Map
private readonly Dictionary> _includingMap =
new Dictionary>
{
{
ContentElementType.Audio, new HashSet
{
ContentElementType.Audioplayer,
ContentElementType.List,
ContentElementType.Paragraph,
ContentElementType.Section,
ContentElementType.Row
}
},
{
ContentElementType.Audioplayer, new HashSet
{
ContentElementType.Paragraph,
ContentElementType.Section
}
},
{
ContentElementType.Form, new HashSet
{
ContentElementType.List,
ContentElementType.Paragraph,
ContentElementType.Section,
ContentElementType.Row
}
},
{
ContentElementType.Gallery, new HashSet
{
ContentElementType.Paragraph,
ContentElementType.Section
}
},
{
ContentElementType.Image, new HashSet
{
ContentElementType.Gallery,
ContentElementType.List,
ContentElementType.Paragraph,
ContentElementType.Section,
ContentElementType.Row
}
},
{
ContentElementType.Link, new HashSet
{
ContentElementType.List,
ContentElementType.Paragraph,
ContentElementType.Section,
ContentElementType.Row
}
},
{
ContentElementType.List, new HashSet
{
ContentElementType.List,
ContentElementType.Paragraph,
ContentElementType.Section,
ContentElementType.Row
}
},
{
ContentElementType.Paragraph, new HashSet
{
ContentElementType.Section
}
},
{
ContentElementType.Quote, new HashSet
{
ContentElementType.List,
ContentElementType.Paragraph,
ContentElementType.Section,
ContentElementType.Row
}
},
{
ContentElementType.Row, new HashSet
{
ContentElementType.Table
}
},
{
ContentElementType.Section, new HashSet()
},
{
ContentElementType.Table, new HashSet
{
ContentElementType.Paragraph,
ContentElementType.Section
}
},
{
ContentElementType.Text, new HashSet
{
ContentElementType.Audioplayer,
ContentElementType.Videoplayer,
ContentElementType.Gallery,
ContentElementType.List,
ContentElementType.Paragraph,
ContentElementType.Section,
ContentElementType.Row
}
},
{
ContentElementType.Column, new HashSet
{
ContentElementType.Table
}
},
{
ContentElementType.Video, new HashSet
{
ContentElementType.Videoplayer,
ContentElementType.List,
ContentElementType.Paragraph,
ContentElementType.Section,
ContentElementType.Row
}
},
{
ContentElementType.Videoplayer, new HashSet
{
ContentElementType.Paragraph,
ContentElementType.Section
}
}
};
#endregion Map
///
/// Verify that one element can be included as a child of the second element
///
private bool AllowInclude(ContentElementType elementType, ContentElementType containerType)
{
return _includingMap[elementType].Contains(containerType);
}
///
/// Writing element to content, dependent on current context.
///
private void WriteElement(IContentElement element)
{
if (_containers.Count == 0) EnterSection();
var current = _containers.Peek();
if (false == AllowInclude(element.Type, current.Type))
{
RaiseIncorrectContainerType(current.Type, element.Type);
}
switch (current.Type)
{
case ContentElementType.Section:
(current as Section).Parts.Add(element);
break;
case ContentElementType.Paragraph:
(current as Paragraph).Parts.Add(element);
break;
case ContentElementType.List:
(current as List).Items.Add(element);
break;
case ContentElementType.Row:
(current as Row).Cells.Add(element);
break;
case ContentElementType.Audioplayer:
if (element.Type == ContentElementType.Text)
{
(current as Audioplayer).Title = element as ZeroLevel.DocumentObjectModel.Flow.Text;
}
else
{
(current as Audioplayer).Tracks.Add(element as Audio);
}
break;
case ContentElementType.Videoplayer:
if (element.Type == ContentElementType.Text)
{
(current as Videoplayer).Title = element as ZeroLevel.DocumentObjectModel.Flow.Text;
}
else
{
(current as Videoplayer).Playlist.Add(element as Video);
}
break;
case ContentElementType.Gallery:
if (element.Type == ContentElementType.Text)
{
(current as Gallery).Title = element as ZeroLevel.DocumentObjectModel.Flow.Text;
}
else
{
(current as Gallery).Images.Add(element as Image);
}
break;
case ContentElementType.Table:
if (element.Type == ContentElementType.Column)
{
(current as Table).Columns.Add(element as Column);
}
else if (element.Type == ContentElementType.Row)
{
(current as Table).Rows.Add(element as Row);
}
break;
}
}
private void RaiseIncorrectTypeException(ContentElementType received, ContentElementType expected)
{
throw new InvalidCastException($"Type {received} received instead of {expected}");
}
private void RaiseIncorrectContainerType(ContentElementType containerType, ContentElementType elementType)
{
throw new Exception($"Type {elementType} can not be written to a container of type {containerType}");
}
private void ReduceContainers()
{
while (_containers.Count > 0)
{
var current = _containers.Peek();
switch (current.Type)
{
case ContentElementType.Section:
LeaveSection();
break;
case ContentElementType.Paragraph:
LeaveParagraph();
break;
case ContentElementType.Audioplayer:
LeaveAudioplayer();
break;
case ContentElementType.Videoplayer:
LeaveVideoplayer();
break;
case ContentElementType.Gallery:
LeaveGallery();
break;
case ContentElementType.List:
LeaveList();
break;
case ContentElementType.Table:
LeaveTable();
break;
case ContentElementType.Row:
LeaveRow();
break;
default:
throw new Exception($"Uncknown container type {current.Type}");
}
}
}
#endregion Helpers
#region Containers
public void EnterSection()
{
ReduceContainers();
_containers.Push(new Section());
}
public void LeaveSection()
{
var section = _containers.Pop();
if (section.Type != ContentElementType.Section)
{
RaiseIncorrectTypeException(section.Type, ContentElementType.Section);
}
_content.Sections.Add(section as Section);
}
public void EnterParagraph()
{
if (_containers.Count == 0) EnterSection();
if (_containers.Peek().Type == ContentElementType.Paragraph) LeaveParagraph();
if (false == AllowInclude(ContentElementType.Paragraph, _containers.Peek().Type))
{
RaiseIncorrectContainerType(_containers.Peek().Type, ContentElementType.Paragraph);
}
_containers.Push(new Paragraph());
}
public void LeaveParagraph()
{
var paragraph = _containers.Pop();
if (paragraph.Type != ContentElementType.Paragraph)
{
RaiseIncorrectTypeException(paragraph.Type, ContentElementType.Paragraph);
}
WriteElement(paragraph);
}
public void EnterGallery()
{
if (_containers.Count == 0) EnterSection();
if (false == AllowInclude(ContentElementType.Gallery, _containers.Peek().Type))
{
RaiseIncorrectContainerType(_containers.Peek().Type, ContentElementType.Gallery);
}
_containers.Push(new Gallery());
}
public void LeaveGallery()
{
var gallery = _containers.Pop();
if (gallery.Type != ContentElementType.Gallery)
{
RaiseIncorrectTypeException(gallery.Type, ContentElementType.Gallery);
}
WriteElement(gallery);
}
public void EnterAudioplayer()
{
if (_containers.Count == 0) EnterSection();
if (false == AllowInclude(ContentElementType.Audioplayer, _containers.Peek().Type))
{
RaiseIncorrectContainerType(_containers.Peek().Type, ContentElementType.Audioplayer);
}
_containers.Push(new Audioplayer());
}
public void LeaveAudioplayer()
{
var audioplayer = _containers.Pop();
if (audioplayer.Type != ContentElementType.Audioplayer)
{
RaiseIncorrectTypeException(audioplayer.Type, ContentElementType.Audioplayer);
}
WriteElement(audioplayer);
}
public void EnterVideoplayer()
{
if (_containers.Count == 0) EnterSection();
if (false == AllowInclude(ContentElementType.Videoplayer, _containers.Peek().Type))
{
RaiseIncorrectContainerType(_containers.Peek().Type, ContentElementType.Videoplayer);
}
_containers.Push(new Videoplayer());
}
public void LeaveVideoplayer()
{
var videoplayer = _containers.Pop();
if (videoplayer.Type != ContentElementType.Videoplayer)
{
RaiseIncorrectTypeException(videoplayer.Type, ContentElementType.Videoplayer);
}
WriteElement(videoplayer);
}
#endregion Containers
#region List
public void EnterList()
{
if (_containers.Count == 0) EnterSection();
if (_containers.Peek().Type == ContentElementType.Section)
EnterParagraph();
if (false == AllowInclude(ContentElementType.List, _containers.Peek().Type))
{
RaiseIncorrectContainerType(_containers.Peek().Type, ContentElementType.List);
}
_containers.Push(new List());
}
public void LeaveList()
{
var list = _containers.Pop();
if (list.Type != ContentElementType.List)
{
RaiseIncorrectTypeException(list.Type, ContentElementType.List);
}
WriteElement(list);
}
#endregion List
#region Table
public void EnterTable(string name, string summary)
{
if (_containers.Count == 0) EnterSection();
if (false == AllowInclude(ContentElementType.Table, _containers.Peek().Type))
{
RaiseIncorrectContainerType(_containers.Peek().Type, ContentElementType.Table);
}
_containers.Push(new Table { Name = name, Abstract = summary });
}
public void EnterRow()
{
if (_containers.Count == 0)
throw new Exception("Absent container");
if (_containers.Peek().Type == ContentElementType.Row) LeaveRow();
if (false == AllowInclude(ContentElementType.Row, _containers.Peek().Type))
{
RaiseIncorrectContainerType(_containers.Peek().Type, ContentElementType.Row);
}
_containers.Push(new Row());
}
public void LeaveRow()
{
var row = _containers.Pop();
if (row.Type != ContentElementType.Row)
{
RaiseIncorrectTypeException(row.Type, ContentElementType.Row);
}
WriteElement(row);
}
public void LeaveTable()
{
if (_containers.Peek().Type == ContentElementType.Row)
LeaveRow();
var table = _containers.Pop();
if (table.Type != ContentElementType.Table)
{
RaiseIncorrectTypeException(table.Type, ContentElementType.Table);
}
WriteElement(table);
}
#endregion Table
#region Primitives
public void WriteColumn(Column column)
{
if (column == null)
{
throw new ArgumentNullException(nameof(column));
}
WriteElement(column);
}
public void WriteText(Text text)
{
if (text == null)
{
throw new ArgumentNullException(nameof(text));
}
WriteElement(text);
}
public void WriteText(string text)
{
WriteElement(new Text(text));
}
public void WriteText(string text, TextStyle style)
{
WriteElement(new Text(text) { Style = style });
}
public void WriteHeader(string text)
{
WriteElement(new Text(text) { Style = new TextStyle { Size = TextSize.MediumHeader, Formatting = TextFormatting.None } });
}
public void WriteQuote(Quote quote)
{
if (quote == null)
{
throw new ArgumentNullException(nameof(quote));
}
WriteElement(quote);
}
public void WriteQuote(string quote)
{
WriteElement(new Quote(quote));
}
public void WriteLink(Link link)
{
if (link == null)
{
throw new ArgumentNullException(nameof(link));
}
WriteElement(link);
}
public void WriteLink(string href, string value)
{
WriteElement(new Link(href, value));
}
public void WriteForm(FormContent form)
{
if (form == null)
{
throw new ArgumentNullException(nameof(form));
}
WriteElement(form);
}
public void WriteImage(Image image)
{
if (image == null)
{
throw new ArgumentNullException(nameof(image));
}
WriteElement(image);
}
public void WriteAudio(Audio audio)
{
if (audio == null)
{
throw new ArgumentNullException(nameof(audio));
}
WriteElement(audio);
}
public void WriteVideo(Video video)
{
if (video == null)
{
throw new ArgumentNullException(nameof(video));
}
WriteElement(video);
}
#endregion Primitives
public void Complete()
{
ReduceContainers();
_parent.Content = _content;
}
}
}