ASCU_ALL/MigraDoc.DocumentObjectModel/DocumentObjectModel.IO/DdlParser.cs

2722 lines
92 KiB
C#
Raw Normal View History

2020-09-04 12:49:15 +05:00
#region MigraDoc - Creating Documents on the Fly
//
// Authors:
// Stefan Lange
// Klaus Potzesny
// David Stephensen
//
// Copyright (c) 2001-2017 empira Software GmbH, Cologne Area (Germany)
//
// http://www.pdfsharp.com
// http://www.migradoc.com
// http://sourceforge.net/projects/pdfsharp
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#endregion
using System;
using System.Diagnostics;
using System.Globalization;
using MigraDoc.DocumentObjectModel.publics;
using MigraDoc.DocumentObjectModel.Tables;
using MigraDoc.DocumentObjectModel.Shapes;
using MigraDoc.DocumentObjectModel.Shapes.Charts;
namespace MigraDoc.DocumentObjectModel.IO
{
/// <summary>
/// A simple hand-coded parser for MigraDoc DDL.
/// </summary>
public class DdlParser
{
/// <summary>
/// Initializes a new instance of the DdlParser class.
/// </summary>
public DdlParser(string ddl, DdlReaderErrors errors)
: this(String.Empty, ddl, errors)
{ }
/// <summary>
/// Initializes a new instance of the DdlParser class.
/// </summary>
public DdlParser(string fileName, string ddl, DdlReaderErrors errors)
{
_errors = errors ?? new DdlReaderErrors();
_scanner = new DdlScanner(fileName, ddl, errors);
}
/// <summary>
/// Parses the keyword <20>\document<6E>.
/// </summary>
public Document ParseDocument(Document document)
{
if (document == null)
document = new Document();
MoveToCode();
AssertSymbol(Symbol.Document);
ReadCode();
if (Symbol == Symbol.BracketLeft)
ParseAttributes(document);
AssertSymbol(Symbol.BraceLeft);
// Styles come first
ReadCode();
if (Symbol == Symbol.Styles)
ParseStyles(document.Styles);
// A document with no sections is valid and has zero pages.
while (Symbol == Symbol.Section)
ParseSection(document.Sections);
AssertSymbol(Symbol.BraceRight);
ReadCode();
AssertCondition(Symbol == Symbol.Eof, DomMsgID.EndOfFileExpected);
return document;
}
/// <summary>
/// Parses one of the keywords <20>\document<6E>, <20>\styles<65>, <20>\section<6F>, <20>\table<6C>, <20>\textframe<6D>, <20>\chart<72>
/// and <20>\paragraph<70> and returns the corresponding DocumentObject or DocumentObjectCollection.
/// </summary>
public DocumentObject ParseDocumentObject()
{
DocumentObject obj = null;
MoveToCode();
switch (Symbol)
{
case Symbol.Document:
obj = ParseDocument(null);
break;
case Symbol.Styles:
obj = ParseStyles(new Styles());
break;
case Symbol.Section:
obj = ParseSection(new Sections());
break;
case Symbol.Table:
obj = new Table();
ParseTable(null, (Table)obj);
break;
case Symbol.TextFrame:
DocumentElements elems = new DocumentElements();
ParseTextFrame(elems);
obj = elems[0];
break;
case Symbol.Chart:
throw new NotImplementedException();
case Symbol.Paragraph:
obj = new DocumentElements();
ParseParagraph((DocumentElements)obj);
break;
default:
ThrowParserException(DomMsgID.UnexpectedSymbol);
break;
}
ReadCode();
AssertCondition(Symbol == Symbol.Eof, DomMsgID.EndOfFileExpected);
return obj;
}
/// <summary>
/// Parses the keyword <20>\styles<65>.
/// </summary>
private Styles ParseStyles(Styles styles)
{
MoveToCode();
AssertSymbol(Symbol.Styles);
ReadCode(); // read '{'
AssertSymbol(Symbol.BraceLeft);
ReadCode(); // read first style name
// An empty \styles block is valid.
while (Symbol == Symbol.Identifier || Symbol == Symbol.StringLiteral)
ParseStyleDefinition(styles);
AssertSymbol(Symbol.BraceRight);
ReadCode(); // read beyond '}'
return styles;
}
/// <summary>
/// Parses a style definition block within the keyword <20>\styles<65>.
/// </summary>
private Style ParseStyleDefinition(Styles styles)
{
// StyleName [: BaseStyleName]
// {
// ...
// }
Style style = null;
try
{
string styleName = _scanner.Token;
string baseStyleName = null;
if (Symbol != Symbol.Identifier && Symbol != Symbol.StringLiteral)
ThrowParserException(DomMsgID.StyleNameExpected, styleName);
ReadCode();
if (Symbol == Symbol.Colon)
{
ReadCode();
if (Symbol != Symbol.Identifier && Symbol != Symbol.StringLiteral)
ThrowParserException(DomMsgID.StyleNameExpected, styleName);
// If baseStyle is not valid, choose InvalidStyleName by default.
baseStyleName = _scanner.Token;
if (styles.GetIndex(baseStyleName) == -1)
{
ReportParserInfo(DdlErrorLevel.Warning, DomMsgID.UseOfUndefinedBaseStyle, baseStyleName);
baseStyleName = StyleNames.InvalidStyleName;
}
ReadCode();
}
// Get or create style.
style = styles[styleName];
if (style != null)
{
// Reset base style.
if (baseStyleName != null)
style.BaseStyle = baseStyleName;
}
else
{
// Style does not exist and no base style is given, choose InvalidStyleName by default.
if (String.IsNullOrEmpty(baseStyleName))
{
baseStyleName = StyleNames.InvalidStyleName;
ReportParserInfo(DdlErrorLevel.Warning, DomMsgID.UseOfUndefinedStyle, styleName);
}
style = styles.AddStyle(styleName, baseStyleName);
}
// Parse definition (if any).
if (Symbol == Symbol.BraceLeft)
{
ParseAttributeBlock(style);
}
}
catch (DdlParserException ex)
{
ReportParserException(ex);
AdjustToNextBlock();
}
return style;
}
/// <summary>
/// Determines if the current symbol is a header or footer.
/// </summary>
private bool IsHeaderFooter()
{
Symbol sym = Symbol;
return (sym == Symbol.Header || sym == Symbol.Footer ||
sym == Symbol.PrimaryHeader || sym == Symbol.PrimaryFooter ||
sym == Symbol.EvenPageHeader || sym == Symbol.EvenPageFooter ||
sym == Symbol.FirstPageHeader || sym == Symbol.FirstPageFooter);
}
/// <summary>
/// Parses the keyword <20>\section<6F>.
/// </summary>
private Section ParseSection(Sections sections)
{
Debug.Assert(sections != null);
MoveToCode();
AssertSymbol(Symbol.Section);
Section section = null;
try
{
section = sections.AddSection();
ReadCode(); // read '[' or '{'
if (Symbol == Symbol.BracketLeft)
ParseAttributes(section);
AssertSymbol(Symbol.BraceLeft);
// Consider the case that the keyword <20>\paragraph<70> can be omitted.
if (IsParagraphContent())
{
Paragraph paragraph = section.Elements.AddParagraph();
ParseParagraphContent(section.Elements, paragraph);
}
else
{
ReadCode(); // read beyond '{'
// 1st parse headers and footers
while (IsHeaderFooter())
ParseHeaderFooter(section);
// 2nd parse all other stuff
ParseDocumentElements(section.Elements, Symbol.Section);
}
AssertSymbol(Symbol.BraceRight);
ReadCode(); // read beyond '}'
}
catch (DdlParserException ex)
{
ReportParserException(ex);
AdjustToNextBlock();
}
return section;
}
/// <summary>
/// Parses the keywords <20>\header<65>.
/// </summary>
private void ParseHeaderFooter(Section section)
{
if (section == null)
throw new ArgumentNullException("section");
try
{
Symbol hdrFtrSym = Symbol;
bool isHeader = hdrFtrSym == Symbol.Header ||
hdrFtrSym == Symbol.PrimaryHeader ||
hdrFtrSym == Symbol.FirstPageHeader ||
hdrFtrSym == Symbol.EvenPageHeader;
// Recall that the styles "Header" resp. "Footer" are used as default if
// no other style was given. But this belongs to the rendering process,
// not to the DDL parser. Therefore no code here belongs to that.
HeaderFooter headerFooter = new HeaderFooter();
ReadCode(); // read '[' or '{'
if (Symbol == Symbol.BracketLeft)
ParseAttributes(headerFooter);
AssertSymbol(Symbol.BraceLeft);
if (IsParagraphContent())
{
Paragraph paragraph = headerFooter.Elements.AddParagraph();
ParseParagraphContent(headerFooter.Elements, paragraph);
}
else
{
ReadCode(); // parse '{'
ParseDocumentElements(headerFooter.Elements, Symbol.HeaderOrFooter);
}
AssertSymbol(Symbol.BraceRight);
ReadCode(); // parse beyond '{'
HeadersFooters headersFooters = isHeader ? section.Headers : section.Footers;
if (hdrFtrSym == Symbol.Header || hdrFtrSym == Symbol.Footer)
{
headersFooters.Primary = headerFooter.Clone();
headersFooters.EvenPage = headerFooter.Clone();
headersFooters.FirstPage = headerFooter.Clone();
}
else
{
switch (hdrFtrSym)
{
case Symbol.PrimaryHeader:
case Symbol.PrimaryFooter:
headersFooters.Primary = headerFooter;
break;
case Symbol.EvenPageHeader:
case Symbol.EvenPageFooter:
headersFooters.EvenPage = headerFooter;
break;
case Symbol.FirstPageHeader:
case Symbol.FirstPageFooter:
headersFooters.FirstPage = headerFooter;
break;
}
}
}
catch (DdlParserException ex)
{
ReportParserException(ex);
AdjustToNextBlock();
}
}
/// <summary>
/// Determines whether the next text is paragraph content or document element.
/// </summary>
private bool IsParagraphContent()
{
if (MoveToParagraphContent())
{
if (_scanner.Char == Chars.BackSlash)
{
Symbol symbol = _scanner.PeekKeyword();
switch (symbol)
{
case Symbol.Bold:
case Symbol.Italic:
case Symbol.Underline:
case Symbol.Field:
case Symbol.Font:
case Symbol.FontColor:
case Symbol.FontSize:
case Symbol.Footnote:
case Symbol.Hyperlink:
case Symbol.Symbol:
case Symbol.Chr:
case Symbol.Tab:
case Symbol.LineBreak:
case Symbol.Space:
case Symbol.SoftHyphen:
return true;
}
return false;
}
return true;
}
return false;
}
/// <summary>
/// Parses the document elements of a <20>\paragraph<70>, <20>\cell<6C> or comparable.
/// </summary>
private DocumentElements ParseDocumentElements(DocumentElements elements, Symbol context)
{
//
// This is clear:
// \section { Hallo World! }
// All section content will be treated as paragraph content.
//
// but this is ambiguous:
// \section { \image(...) }
// It could be an image inside a paragraph or at the section level.
// In this case it will be treated as an image on section level.
//
// If this is not your intention it must be like this:
// \section { \paragraph { \image(...) } }
//
while (TokenType == TokenType.KeyWord)
{
switch (Symbol)
{
case Symbol.Paragraph:
ParseParagraph(elements);
break;
case Symbol.PageBreak:
ParsePageBreak(elements);
break;
case Symbol.Table:
ParseTable(elements, null);
break;
case Symbol.TextFrame:
ParseTextFrame(elements);
break;
case Symbol.Image:
ParseImage(elements.AddImage(""), false);
break;
case Symbol.Chart:
ParseChart(elements);
break;
case Symbol.Barcode:
ParseBarcode(elements);
break;
default:
ThrowParserException(DomMsgID.UnexpectedSymbol, _scanner.Token);
break;
}
}
return elements;
}
/// <summary>
/// Parses the keyword <20>\paragraph<70>.
/// </summary>
private void ParseParagraph(DocumentElements elements)
{
MoveToCode();
AssertSymbol(Symbol.Paragraph);
Paragraph paragraph = elements.AddParagraph();
try
{
ReadCode(); // read '[' or '{'
if (Symbol == Symbol.BracketLeft)
ParseAttributes(paragraph);
// Empty paragraphs without braces are valid.
if (Symbol == Symbol.BraceLeft)
{
ParseParagraphContent(elements, paragraph);
AssertSymbol(Symbol.BraceRight);
ReadCode(); // read beyond '}'
}
}
catch (DdlParserException ex)
{
ReportParserException(ex);
AdjustToNextBlock();
}
}
/// <summary>
/// Parses the inner text of a paragraph, i.e. stops on BraceRight and treats empty
/// line as paragraph separator.
/// </summary>
private void ParseParagraphContent(DocumentElements elements, Paragraph paragraph)
{
Paragraph para = paragraph ?? elements.AddParagraph();
while (para != null)
{
ParseFormattedText(para.Elements, 0);
if (Symbol != Symbol.BraceRight && Symbol != Symbol.Eof)
{
para = elements.AddParagraph();
}
else
para = null;
}
}
/// <summary>
/// Removes the last blank from the text. Used before a tab, a linebreak or a space will be
/// added to the text.
/// </summary>
private void RemoveTrailingBlank(ParagraphElements elements)
{
DocumentObject dom = elements.LastObject;
Text text = dom as Text;
if (text != null)
{
if (text.Content.EndsWith(" "))
text.Content = text.Content.Remove(text.Content.Length - 1, 1);
}
}
/// <summary>
/// Parses the inner text of a paragraph. Parsing ends if '}' is reached or an empty
/// line occurs on nesting level 0.
/// </summary>
private void ParseFormattedText(ParagraphElements elements, int nestingLevel)
{
MoveToParagraphContent();
bool loop = true;
bool rootLevel = nestingLevel == 0;
ReadText(rootLevel);
while (loop)
{
switch (Symbol)
{
case Symbol.Eof:
ThrowParserException(DomMsgID.UnexpectedEndOfFile);
break;
case Symbol.EmptyLine:
elements.AddCharacter(SymbolName.ParaBreak);
ReadText(rootLevel);
break;
case Symbol.BraceRight:
loop = false;
break;
case Symbol.Comment:
// Ignore comments.
ReadText(rootLevel);
break;
case Symbol.Text:
elements.AddText(Token);
ReadText(rootLevel);
break;
case Symbol.Tab:
RemoveTrailingBlank(elements);
elements.AddTab();
_scanner.MoveToNonWhiteSpaceOrEol();
ReadText(rootLevel);
break;
case Symbol.LineBreak:
RemoveTrailingBlank(elements);
elements.AddLineBreak();
_scanner.MoveToNonWhiteSpaceOrEol();
ReadText(rootLevel);
break;
case Symbol.Bold:
ParseBoldItalicEtc(elements.AddFormattedText(TextFormat.Bold), nestingLevel + 1);
ReadText(rootLevel);
break;
case Symbol.Italic:
ParseBoldItalicEtc(elements.AddFormattedText(TextFormat.Italic), nestingLevel + 1);
ReadText(rootLevel);
break;
case Symbol.Underline:
ParseBoldItalicEtc(elements.AddFormattedText(TextFormat.Underline), nestingLevel + 1);
ReadText(rootLevel);
break;
case Symbol.Font:
ParseFont(elements.AddFormattedText(), nestingLevel + 1);
ReadText(rootLevel);
break;
case Symbol.FontSize:
ParseFontSize(elements.AddFormattedText(), nestingLevel + 1);
ReadText(rootLevel);
break;
case Symbol.FontColor:
ParseFontColor(elements.AddFormattedText(), nestingLevel + 1);
ReadText(rootLevel);
break;
case Symbol.Image:
ParseImage(elements.AddImage(""), true);
ReadText(rootLevel);
break;
case Symbol.Field:
ParseField(elements, nestingLevel + 1);
ReadText(rootLevel);
break;
case Symbol.Footnote:
ParseFootnote(elements, nestingLevel + 1);
ReadText(rootLevel);
break;
case Symbol.Hyperlink:
ParseHyperlink(elements, nestingLevel + 1);
ReadText(rootLevel);
break;
case Symbol.Space:
RemoveTrailingBlank(elements);
ParseSpace(elements, nestingLevel + 1);
_scanner.MoveToNonWhiteSpaceOrEol();
ReadText(rootLevel);
break;
case Symbol.Symbol:
ParseSymbol(elements);
ReadText(rootLevel);
break;
case Symbol.Chr:
ParseChr(elements);
ReadText(rootLevel);
break;
default:
ThrowParserException(DomMsgID.UnexpectedSymbol, Token);
break;
}
}
}
/// <summary>
/// Parses the keywords <20>\bold<6C>, <20>\italic<69>, and <20>\underline<6E>.
/// </summary>
private void ParseBoldItalicEtc(FormattedText formattedText, int nestingLevel)
{
ReadCode();
AssertSymbol(Symbol.BraceLeft);
ParseFormattedText(formattedText.Elements, nestingLevel);
AssertSymbol(Symbol.BraceRight);
}
/// <summary>
/// Parses the keyword <20>\font<6E>.
/// </summary>
private void ParseFont(FormattedText formattedText, int nestingLevel)
{
AssertSymbol(Symbol.Font);
ReadCode();
if (Symbol == Symbol.ParenLeft)
{
formattedText.Style = ParseElementName();
ReadCode();
}
if (Symbol == Symbol.BracketLeft)
ParseAttributes(formattedText);
AssertSymbol(Symbol.BraceLeft);
ParseFormattedText(formattedText.Elements, nestingLevel);
AssertSymbol(Symbol.BraceRight);
}
/// <summary>
/// Parses code like <20>("name")<29>.
/// </summary>
private string ParseElementName()
{
AssertSymbol(Symbol.ParenLeft);
ReadCode();
if (Symbol != Symbol.StringLiteral)
ThrowParserException(DomMsgID.StringExpected, Token);
string name = Token;
ReadCode();
AssertSymbol(Symbol.ParenRight);
return name;
}
/// <summary>
/// Parses the keyword <20>\fontsize<7A>.
/// </summary>
private void ParseFontSize(FormattedText formattedText, int nestingLevel)
{
AssertSymbol(Symbol.FontSize);
ReadCode();
AssertSymbol(Symbol.ParenLeft);
ReadCode();
//NYI: Check token for correct Unit format
formattedText.Font.Size = Token;
ReadCode();
AssertSymbol(Symbol.ParenRight);
ReadCode();
AssertSymbol(Symbol.BraceLeft);
ParseFormattedText(formattedText.Elements, nestingLevel);
AssertSymbol(Symbol.BraceRight);
}
/// <summary>
/// Parses the keyword <20>\fontcolor<6F>.
/// </summary>
private void ParseFontColor(FormattedText formattedText, int nestingLevel)
{
AssertSymbol(Symbol.FontColor);
ReadCode(); // read '('
AssertSymbol(Symbol.ParenLeft);
ReadCode(); // read color token
Color color = ParseColor();
formattedText.Font.Color = color;
AssertSymbol(Symbol.ParenRight);
ReadCode();
AssertSymbol(Symbol.BraceLeft);
ParseFormattedText(formattedText.Elements, nestingLevel);
AssertSymbol(Symbol.BraceRight);
}
/// <summary>
/// Parses the keyword <20>\symbol<6F> resp. <20>\(<28>.
/// </summary>
private void ParseSymbol(ParagraphElements elements)
{
AssertSymbol(Symbol.Symbol);
ReadCode(); // read '('
AssertSymbol(Symbol.ParenLeft);
const char ch = (char)0;
SymbolName symtype = 0;
int count = 1;
ReadCode(); // read name
if (TokenType == TokenType.Identifier)
{
try
{
if (Enum.IsDefined(typeof(SymbolName), Token))
{
AssertCondition(IsSymbolType(Token), DomMsgID.InvalidSymbolType, Token);
symtype = (SymbolName)Enum.Parse(typeof(SymbolName), Token, true);
}
}
catch (Exception ex)
{
ThrowParserException(ex, DomMsgID.InvalidEnum, Token);
}
}
else
{
ThrowParserException(DomMsgID.UnexpectedSymbol, Token);
}
ReadCode(); // read integer or identifier
if (Symbol == Symbol.Comma)
{
ReadCode(); // read integer
if (TokenType == TokenType.IntegerLiteral)
count = _scanner.GetTokenValueAsInt();
ReadCode();
}
AssertSymbol(Symbol.ParenRight);
if (symtype != 0)
elements.AddCharacter(symtype, count);
else
elements.AddCharacter(ch, count);
}
/// <summary>
/// Parses the keyword <20>\chr<68>.
/// </summary>
private void ParseChr(ParagraphElements elements)
{
AssertSymbol(Symbol.Chr);
ReadCode(); // read '('
AssertSymbol(Symbol.ParenLeft);
char ch = (char)0;
SymbolName symtype = 0;
int count = 1;
ReadCode(); // read integer
if (TokenType == TokenType.IntegerLiteral)
{
int val = _scanner.GetTokenValueAsInt();
if (val >= 1 && val < 256)
ch = (char)val;
else
ThrowParserException(DomMsgID.OutOfRange, "1 - 255");
}
else
{
ThrowParserException(DomMsgID.UnexpectedSymbol, Token);
}
ReadCode(); // read integer or identifier
if (Symbol == Symbol.Comma)
{
ReadCode(); // read integer
if (TokenType == TokenType.IntegerLiteral)
count = _scanner.GetTokenValueAsInt();
ReadCode();
}
AssertSymbol(Symbol.ParenRight);
if (symtype != 0)
elements.AddCharacter(symtype, count);
else
elements.AddCharacter(ch, count);
}
/// <summary>
/// Parses the keyword <20>\field<6C>.
/// </summary>
private void ParseField(ParagraphElements elements, int nestingLevel)
{
AssertSymbol(Symbol.Field);
ReadCode(); // read '('
AssertSymbol(Symbol.ParenLeft);
ReadCode(); // read identifier
AssertSymbol(Symbol.Identifier);
string fieldType = Token.ToLower();
ReadCode(); // read ')'
AssertSymbol(Symbol.ParenRight);
DocumentObject field = null;
switch (fieldType)
{
case "date":
field = elements.AddDateField();
break;
case "page":
field = elements.AddPageField();
break;
case "numpages":
field = elements.AddNumPagesField();
break;
case "info":
field = elements.AddInfoField(0);
break;
case "sectionpages":
field = elements.AddSectionPagesField();
break;
case "section":
field = elements.AddSectionField();
break;
case "bookmark":
field = elements.AddBookmark("");
break;
case "pageref":
field = elements.AddPageRefField("");
break;
}
AssertCondition(field != null, DomMsgID.InvalidFieldType, Token);
if (_scanner.PeekSymbol() == Symbol.BracketLeft)
{
ReadCode(); // read '['
ParseAttributes(field, false);
}
}
/// <summary>
/// Parses the keyword <20>\footnote<74>.
/// </summary>
private void ParseFootnote(ParagraphElements elements, int nestingLevel)
{
AssertSymbol(Symbol.Footnote);
ReadCode();
Footnote footnote = elements.AddFootnote();
if (Symbol == Symbol.BracketLeft)
ParseAttributes(footnote);
AssertSymbol(Symbol.BraceLeft);
// The keyword <20>\paragraph<70> is typically omitted.
if (IsParagraphContent())
{
Paragraph paragraph = footnote.Elements.AddParagraph();
ParseParagraphContent(footnote.Elements, paragraph);
}
else
{
ReadCode(); // read beyond '{'
ParseDocumentElements(footnote.Elements, Symbol.Footnote);
}
AssertSymbol(Symbol.BraceRight);
}
/// <summary>
/// Parses the keyword <20>\hyperlink<6E>.
/// </summary>
private void ParseHyperlink(ParagraphElements elements, int nestingLevel)
{
AssertSymbol(Symbol.Hyperlink);
ReadCode();
Hyperlink hyperlink = elements.AddHyperlink("");
//NYI: Without name and type the hyperlink is senseless, so attributes need to be checked
if (Symbol == Symbol.BracketLeft)
ParseAttributes(hyperlink);
AssertSymbol(Symbol.BraceLeft);
ParseFormattedText(hyperlink.Elements, nestingLevel);
AssertSymbol(Symbol.BraceRight);
}
/// <summary>
/// Parses the keyword <20>\space<63>.
/// </summary>
private void ParseSpace(ParagraphElements elements, int nestingLevel)
{
// Samples
// \space
// \space(5)
// \space(em)
// \space(em,5)
AssertSymbol(Symbol.Space);
Character space = elements.AddSpace(1);
// <20>\space<63> can stand alone
if (_scanner.PeekSymbol() == Symbol.ParenLeft)
{
ReadCode(); // read '('
AssertSymbol(Symbol.ParenLeft);
ReadCode(); // read beyond '('
if (Symbol == Symbol.Identifier)
{
string type = Token;
if (!IsSpaceType(type))
ThrowParserException(DomMsgID.InvalidEnum, type);
space.SymbolName = (SymbolName)Enum.Parse(typeof(SymbolName), type, true);
ReadCode(); // read ',' or ')'
if (Symbol == Symbol.Comma)
{
ReadCode(); // read integer
AssertSymbol(Symbol.IntegerLiteral);
space.Count = _scanner.GetTokenValueAsInt();
ReadCode(); // read ')'
}
}
else if (Symbol == Symbol.IntegerLiteral)
{
space.Count = _scanner.GetTokenValueAsInt();
ReadCode();
}
AssertSymbol(Symbol.ParenRight);
}
}
/// <summary>
/// Parses a page break in a document elements container.
/// </summary>
private void ParsePageBreak(DocumentElements elements)
{
AssertSymbol(Symbol.PageBreak);
elements.AddPageBreak();
ReadCode();
}
/// <summary>
/// Parses the keyword <20>\table<6C>.
/// </summary>
private void ParseTable(DocumentElements elements, Table table)
{
Table tbl = table;
try
{
if (tbl == null)
tbl = elements.AddTable();
MoveToCode();
AssertSymbol(Symbol.Table);
ReadCode();
if (_scanner.Symbol == Symbol.BracketLeft)
ParseAttributes(tbl);
AssertSymbol(Symbol.BraceLeft);
ReadCode();
// Table must start with <20>\columns<6E>...
AssertSymbol(Symbol.Columns);
ParseColumns(tbl);
// ...followed by <20>\rows<77>.
AssertSymbol(Symbol.Rows);
ParseRows(tbl);
AssertSymbol(Symbol.BraceRight);
ReadCode(); // read beyond '}'
}
catch (DdlParserException ex)
{
ReportParserException(ex);
AdjustToNextBlock();
}
}
/// <summary>
/// Parses the keyword <20>\columns<6E>.
/// </summary>
private void ParseColumns(Table table)
{
Debug.Assert(table != null);
Debug.Assert(Symbol == Symbol.Columns);
ReadCode();
if (Symbol == Symbol.BracketLeft)
ParseAttributes(table.Columns);
AssertSymbol(Symbol.BraceLeft);
ReadCode();
bool loop = true;
while (loop)
{
switch (Symbol)
{
case Symbol.Eof:
ThrowParserException(DomMsgID.UnexpectedEndOfFile);
break;
case Symbol.BraceRight:
loop = false;
ReadCode();
break;
case Symbol.Column:
ParseColumn(table.AddColumn());
break;
default:
AssertSymbol(Symbol.Column);
break;
}
}
}
/// <summary>
/// Parses the keyword <20>\column<6D>.
/// </summary>
private void ParseColumn(Column column)
{
Debug.Assert(column != null);
Debug.Assert(Symbol == Symbol.Column);
ReadCode();
if (Symbol == Symbol.BracketLeft)
ParseAttributes(column);
// Read empty content
if (Symbol == Symbol.BraceLeft)
{
ReadCode();
AssertSymbol(Symbol.BraceRight);
ReadCode();
}
}
/// <summary>
/// Parses the keyword <20>\rows<77>.
/// </summary>
private void ParseRows(Table table)
{
Debug.Assert(table != null);
Debug.Assert(Symbol == Symbol.Rows);
ReadCode();
if (Symbol == Symbol.BracketLeft)
ParseAttributes(table.Rows);
AssertSymbol(Symbol.BraceLeft);
ReadCode();
bool loop = true;
while (loop)
{
switch (Symbol)
{
case Symbol.Eof:
ThrowParserException(DomMsgID.UnexpectedEndOfFile);
break;
case Symbol.BraceRight:
ReadCode(); // read '}'
loop = false;
break;
case Symbol.Row:
ParseRow(table.AddRow());
break;
default:
AssertSymbol(Symbol.Row);
break;
}
}
}
/// <summary>
/// Parses the keyword <20>\row<6F>.
/// </summary>
private void ParseRow(Row row)
{
Debug.Assert(row != null);
Debug.Assert(Symbol == Symbol.Row);
ReadCode();
if (Symbol == Symbol.BracketLeft)
ParseAttributes(row);
if (Symbol == Symbol.BraceLeft)
{
ReadCode();
bool loop = true;
int idx = 0;
//int cells = row.Cells.Count;
while (loop)
{
switch (Symbol)
{
case Symbol.Eof:
ThrowParserException(DomMsgID.UnexpectedEndOfFile);
break;
case Symbol.BraceRight:
loop = false;
ReadCode();
break;
case Symbol.Cell:
ParseCell(row[idx]);
idx++;
break;
default:
ThrowParserException(DomMsgID.UnexpectedSymbol, Token);
break;
}
}
}
}
/// <summary>
/// Parses the keyword <20>\cell<6C>.
/// </summary>
private void ParseCell(Cell cell)
{
Debug.Assert(cell != null);
Debug.Assert(Symbol == Symbol.Cell);
ReadCode();
if (Symbol == Symbol.BracketLeft)
ParseAttributes(cell);
// Empty cells without braces are valid.
if (Symbol == Symbol.BraceLeft)
{
if (IsParagraphContent())
{
ParseParagraphContent(cell.Elements, null);
}
else
{
ReadCode();
if (Symbol != Symbol.BraceRight)
ParseDocumentElements(cell.Elements, Symbol.Cell);
}
AssertSymbol(Symbol.BraceRight);
ReadCode(); // read '}'
}
}
/// <summary>
/// Parses the keyword <20>\image<67>.
/// </summary>
private void ParseImage(Image image, bool paragraphContent)
{
// Future syntax by example
// \image("Name")
// \image("Name")[...]
// \image{base64...} //NYI
// \image[...]{base64...} //NYI
Debug.Assert(image != null);
try
{
MoveToCode();
AssertSymbol(Symbol.Image);
ReadCode();
if (_scanner.Symbol == Symbol.ParenLeft)
image.Name = ParseElementName();
if (_scanner.PeekSymbol() == Symbol.BracketLeft)
{
ReadCode();
ParseAttributes(image, !paragraphContent);
}
else if (!paragraphContent)
ReadCode(); // We are a part of a section, cell etc.; read beyond ')'.
}
catch (DdlParserException ex)
{
ReportParserException(ex);
AdjustToNextBlock();
}
}
/// <summary>
/// Parses the keyword <20>\textframe<6D>.
/// </summary>
private void ParseTextFrame(DocumentElements elements)
{
Debug.Assert(elements != null);
TextFrame textFrame = elements.AddTextFrame();
try
{
ReadCode();
if (_scanner.Symbol == Symbol.BracketLeft)
ParseAttributes(textFrame);
AssertSymbol(Symbol.BraceLeft);
if (IsParagraphContent())
{
ParseParagraphContent(textFrame.Elements, null);
}
else
{
ReadCode(); // read '{'
ParseDocumentElements(textFrame.Elements, Symbol.TextFrame);
}
AssertSymbol(Symbol.BraceRight);
ReadCode(); // read beyond '}'
}
catch (DdlParserException ex)
{
ReportParserException(ex);
AdjustToNextBlock();
}
}
private void ParseBarcode(DocumentElements elements)
{
// Syntax:
// 1. \barcode(Code)
// 2. \barcode(Code)[...]
// 3. \barcode(Code, Type)
// 4. \barcode(Code, Type)[...]
try
{
ReadCode();
AssertSymbol(Symbol.ParenLeft, DomMsgID.MissingParenLeft, GetSymbolText(Symbol.Barcode));
ReadCode();
AssertSymbol(Symbol.StringLiteral, DomMsgID.UnexpectedSymbol);
Barcode barcode = elements.AddBarcode();
barcode.SetValue("Code", Token);
ReadCode();
if (Symbol == Symbol.Comma)
{
ReadCode();
AssertSymbol(Symbol.Identifier, DomMsgID.IdentifierExpected, Token);
BarcodeType barcodeType = (BarcodeType)Enum.Parse(typeof(BarcodeType), Token, true);
barcode.SetValue("type", barcodeType);
ReadCode();
}
AssertSymbol(Symbol.ParenRight, DomMsgID.MissingParenRight, GetSymbolText(Symbol.Barcode));
ReadCode();
if (Symbol == Symbol.BracketLeft)
ParseAttributes(barcode);
//barcode->ConsistencyCheck(mInfoHandler->Infos());
}
catch (DdlParserException pe)
{
ReportParserException(pe);
AdjustToNextBlock();
}
}
/// <summary>
/// Parses the keyword <20>\chart<72>.
/// </summary>
private void ParseChart(DocumentElements elements)
{
// Syntax:
// 1. \chartarea(Type){...}
// 2. \chartarea(Type)[...]{...}
//
// Usage of header-, bottom-, footer-, left- and rightarea are similar.
ChartType chartType = 0;
try
{
ReadCode(); // read '('
AssertSymbol(Symbol.ParenLeft, DomMsgID.MissingParenLeft, GetSymbolText(Symbol.Chart));
ReadCode(); // ChartType name
AssertSymbol(Symbol.Identifier, DomMsgID.IdentifierExpected, Token);
string chartTypeName = Token;
ReadCode(); // read ')'
AssertSymbol(Symbol.ParenRight, DomMsgID.MissingParenRight, GetSymbolText(Symbol.Chart));
try
{
chartType = (ChartType)Enum.Parse(typeof(ChartType), chartTypeName, true);
}
catch (Exception ex)
{
ThrowParserException(ex, DomMsgID.UnknownChartType, chartTypeName);
}
Chart chart = elements.AddChart(chartType);
ReadCode();
if (Symbol == Symbol.BracketLeft)
ParseAttributes(chart);
AssertSymbol(Symbol.BraceLeft, DomMsgID.MissingBraceLeft, GetSymbolText(Symbol.Chart));
ReadCode(); // read beyond '{'
bool fContinue = true;
while (fContinue)
{
switch (Symbol)
{
case Symbol.Eof:
ThrowParserException(DomMsgID.UnexpectedEndOfFile);
break;
case Symbol.BraceRight:
fContinue = false;
break;
case Symbol.PlotArea:
ParseArea(chart.PlotArea);
break;
case Symbol.HeaderArea:
ParseArea(chart.HeaderArea);
break;
case Symbol.FooterArea:
ParseArea(chart.FooterArea);
break;
case Symbol.TopArea:
ParseArea(chart.TopArea);
break;
case Symbol.BottomArea:
ParseArea(chart.BottomArea);
break;
case Symbol.LeftArea:
ParseArea(chart.LeftArea);
break;
case Symbol.RightArea:
ParseArea(chart.RightArea);
break;
case Symbol.XAxis:
ParseAxes(chart.XAxis, Symbol);
break;
case Symbol.YAxis:
ParseAxes(chart.YAxis, Symbol);
break;
case Symbol.ZAxis:
ParseAxes(chart.ZAxis, Symbol);
break;
case Symbol.Series:
ParseSeries(chart.SeriesCollection.AddSeries());
break;
case Symbol.XValues:
ParseSeries(chart.XValues.AddXSeries());
break;
default:
ThrowParserException(DomMsgID.UnexpectedSymbol, Token);
break;
}
}
ReadCode(); // read beyond '}'
}
catch (DdlParserException pe)
{
ReportParserException(pe);
AdjustToNextBlock();
}
}
/// <summary>
/// Parses the keyword <20>\plotarea<65> inside a chart.
/// </summary>
private void ParseArea(PlotArea area)
{
// Syntax:
// 1. \plotarea{...}
// 2. \plotarea[...]{...} //???
try
{
ReadCode();
if (Symbol == Symbol.BracketLeft)
{
ParseAttributes(area, false);
ReadCode();
}
if (Symbol != Symbol.BraceLeft)
return;
bool fContinue = true;
while (fContinue)
{
ReadCode();
switch (Symbol)
{
case Symbol.BraceRight:
fContinue = false;
break;
default:
// Alles ignorieren? Warnung ausgeben?
break;
}
}
AssertSymbol(Symbol.BraceRight);
ReadCode(); // read beyond '}'
}
catch (DdlParserException pe)
{
ReportParserException(pe);
AdjustToNextBlock();
}
}
/// <summary>
/// Parses the keywords <20>\headerarea<65>, <20>\toparea<65>, <20>\bottomarea<65>, <20>\footerarea<65>,
/// <20>\leftarea<65> or <20>\rightarea<65> inside a chart.
/// </summary>
private void ParseArea(TextArea area)
{
// Syntax:
// 1. \toparea{...}
// 2. \toparea[...]{...}
//
// Usage of header-, bottom-, footer-, left- and rightarea are similar.
try
{
ReadCode();
if (Symbol == Symbol.BracketLeft)
{
ParseAttributes(area, false);
ReadCode();
}
if (Symbol != Symbol.BraceLeft)
return;
if (IsParagraphContent())
ParseParagraphContent(area.Elements, null);
else
{
ReadCode(); // read beyond '{'
bool fContinue = true;
while (fContinue)
{
switch (Symbol)
{
case Symbol.BraceRight:
fContinue = false;
break;
case Symbol.Legend:
ParseLegend(area.AddLegend());
break;
case Symbol.Paragraph:
ParseParagraph(area.Elements);
break;
case Symbol.Table:
ParseTable(null, area.AddTable());
break;
case Symbol.TextFrame:
ParseTextFrame(area.Elements);
break;
case Symbol.Image:
Image image = new Image();
ParseImage(image, false);
area.Elements.Add(image);
break;
default:
ThrowParserException(DomMsgID.UnexpectedSymbol, Token);
break;
}
}
}
AssertSymbol(Symbol.BraceRight);
ReadCode(); // read beyond '}'
}
catch (DdlParserException pe)
{
ReportParserException(pe);
AdjustToNextBlock();
}
}
/// <summary>
/// Parses the keywords <20>\xaxis<69>, <20>\yaxis<69> or <20>\zaxis<69> inside a chart.
/// </summary>
private void ParseAxes(Axis axis, Symbol symbolAxis)
{
// Syntax:
// 1. \xaxis[...]
// 2. \xaxis[...]{...} //???
//
// Usage of yaxis and zaxis are similar.
try
{
ReadCode();
if (Symbol == Symbol.BracketLeft)
{
ParseAttributes(axis, false);
ReadCode();
}
if (Symbol != Symbol.BraceLeft)
return;
while (Symbol != Symbol.BraceRight)
ReadCode();
AssertSymbol(Symbol.BraceRight, DomMsgID.MissingBraceRight, GetSymbolText(symbolAxis));
ReadCode(); // read beyond '}'
}
catch (DdlParserException pe)
{
ReportParserException(pe);
AdjustToNextBlock();
}
}
/// <summary>
/// Parses the keyword <20>\series<65> inside a chart.
/// </summary>
private void ParseSeries(Series series)
{
// Syntax:
// 1. \series{...}
// 2. \series[...]{...}
try
{
ReadCode();
if (Symbol == Symbol.BracketLeft)
ParseAttributes(series);
AssertSymbol(Symbol.BraceLeft, DomMsgID.MissingBraceLeft, GetSymbolText(Symbol.Series));
ReadCode(); // read beyond '{'
bool fContinue = true;
bool fFoundComma = true;
while (fContinue)
{
switch (Symbol)
{
case Symbol.Eof:
ThrowParserException(DomMsgID.UnexpectedEndOfFile);
break;
case Symbol.BraceRight:
fContinue = false;
break;
case Symbol.Comma:
fFoundComma = true;
ReadCode();
break;
case Symbol.Point:
AssertCondition(fFoundComma, DomMsgID.MissingComma);
ParsePoint(series.Add(0.0));
fFoundComma = false;
break;
case Symbol.Null:
AssertCondition(fFoundComma, DomMsgID.MissingComma);
series.AddBlank();
fFoundComma = false;
ReadCode();
break;
default:
AssertCondition(fFoundComma, DomMsgID.MissingComma);
series.Add(_scanner.GetTokenValueAsReal());
fFoundComma = false;
ReadCode();
break;
}
}
AssertSymbol(Symbol.BraceRight, DomMsgID.MissingBraceRight, GetSymbolText(Symbol.Series));
ReadCode(); // read beyond '}'
}
catch (DdlParserException pe)
{
ReportParserException(pe);
AdjustToNextBlock();
}
}
/// <summary>
/// Parses the keyword <20>\xvalues<65> inside a chart.
/// </summary>
private void ParseSeries(XSeries series)
{
// Syntax:
// 1. \xvalues{...}
try
{
ReadCode();
AssertSymbol(Symbol.BraceLeft, DomMsgID.MissingBraceLeft, GetSymbolText(Symbol.Series));
bool fFoundComma = true;
bool fContinue = true;
while (fContinue)
{
ReadCode();
switch (Symbol)
{
case Symbol.Eof:
ThrowParserException(DomMsgID.UnexpectedEndOfFile);
break;
case Symbol.BraceRight:
fContinue = false;
break;
case Symbol.Comma:
fFoundComma = true;
break;
case Symbol.Null:
AssertCondition(fFoundComma, DomMsgID.MissingComma);
series.AddBlank();
fFoundComma = false;
break;
case Symbol.StringLiteral:
case Symbol.IntegerLiteral:
case Symbol.RealLiteral:
case Symbol.HexIntegerLiteral:
AssertCondition(fFoundComma, DomMsgID.MissingComma);
series.Add(Token);
fFoundComma = false;
break;
default:
ThrowParserException(DomMsgID.UnexpectedSymbol, Token);
break;
}
}
AssertSymbol(Symbol.BraceRight, DomMsgID.MissingBraceRight, GetSymbolText(Symbol.Series));
ReadCode(); // read beyond '}'
}
catch (DdlParserException pe)
{
ReportParserException(pe);
AdjustToNextBlock();
}
}
/// <summary>
/// Parses the keyword <20>\point<6E> inside a series.
/// </summary>
private void ParsePoint(Point point)
{
// Syntax:
// 1. \point{...}
// 2. \point[...]{...}
try
{
ReadCode();
if (Symbol == Symbol.BracketLeft)
ParseAttributes(point);
AssertSymbol(Symbol.BraceLeft, DomMsgID.MissingBraceLeft, GetSymbolText(Symbol.Point));
ReadCode(); // read beyond '{'
point.Value = _scanner.GetTokenValueAsReal();
ReadCode(); // read '}'
AssertSymbol(Symbol.BraceRight, DomMsgID.MissingBraceRight, GetSymbolText(Symbol.Point));
ReadCode(); // read beyond '}'
}
catch (DdlParserException pe)
{
ReportParserException(pe);
AdjustToNextBlock();
}
}
/// <summary>
/// Parses the keyword <20>\legend<6E> inside a textarea.
/// </summary>
private void ParseLegend(Legend legend)
{
// Syntax:
// 1. \legend
// 2. \legend[...]
// 3. \legend[...]{...}
try
{
ReadCode();
if (Symbol == Symbol.BracketLeft)
{
ParseAttributes(legend, false);
ReadCode();
}
// Empty legends are allowed.
if (Symbol != Symbol.BraceLeft)
return;
AdjustToNextBlock(); // consume/ignore all content
}
catch (DdlParserException pe)
{
ReportParserException(pe);
AdjustToNextBlock();
}
}
/// <summary>
/// Parses an attribute declaration block enclosed in brackets <20>[<5B>]<5D>. If readNextSymbol is
/// set to true, the closing bracket will be read.
/// </summary>
private void ParseAttributes(DocumentObject element, bool readNextSymbol)
{
AssertSymbol(Symbol.BracketLeft);
ReadCode(); // read beyond '['
while (Symbol == Symbol.Identifier)
ParseAttributeStatement(element);
AssertSymbol(Symbol.BracketRight);
// Do not read ']' when parsing in paragraph content.
if (readNextSymbol)
ReadCode(); // read beyond ']'
}
/// <summary>
/// Parses an attribute declaration block enclosed in brackets <20>[<5B>]<5D>.
/// </summary>
private void ParseAttributes(DocumentObject element)
{
ParseAttributes(element, true);
}
/// <summary>
/// Parses a single statement in an attribute declaration block.
/// </summary>
private void ParseAttributeStatement(DocumentObject doc)
{
// Syntax is easy
// identifier: xxxxx
// or
// sequence of identifiers: xxx.yyy.zzz
//
// followed by: <20>=<3D>, <20>+=<3D>, <20>-=<3D>, or <20>{<7B>
//
// Parser of rhs depends on the type of the l-value.
if (doc == null)
throw new ArgumentNullException("doc");
string valueName = "";
try
{
valueName = _scanner.Token;
ReadCode();
// Resolve path, if it exists.
object val;
while (Symbol == Symbol.Dot)
{
#if DEBUG_
if (valueName == "TabStops")
valueName.GetType();
#endif
Debug.Assert(doc != null, "Make ReSharper happy.");
val = doc.GetValue(valueName);
if (val == null)
{
DocumentObject documentObject = doc;
val = documentObject.CreateValue(valueName);
doc.SetValue(valueName, val);
}
AssertCondition(val != null, DomMsgID.InvalidValueName, valueName);
doc = val as DocumentObject;
AssertCondition(doc != null, DomMsgID.SymbolIsNotAnObject, valueName);
ReadCode();
AssertCondition(Symbol == Symbol.Identifier, DomMsgID.InvalidValueName, _scanner.Token);
valueName = _scanner.Token;
AssertCondition(valueName[0] != '_', DomMsgID.NoAccess, _scanner.Token);
#if DEBUG_
if (valueName == "TabStops")
valueName.GetType();
#endif
ReadCode();
}
Debug.Assert(doc != null, "Make ReSharper happy.");
switch (Symbol)
{
case Symbol.Assign:
//DomValueDescriptor is needed from assignment routine.
ValueDescriptor pvd = doc.Meta[valueName];
AssertCondition(pvd != null, DomMsgID.InvalidValueName, valueName);
ParseAssign(doc, pvd);
break;
case Symbol.PlusAssign:
case Symbol.MinusAssign:
// Hard-coded for TabStops only...
if (!(doc is ParagraphFormat))
ThrowParserException(DomMsgID.SymbolNotAllowed, _scanner.Token);
if (String.Compare(valueName, "TabStops", StringComparison.OrdinalIgnoreCase) != 0)
ThrowParserException(DomMsgID.InvalidValueForOperation, valueName, _scanner.Token);
ParagraphFormat paragraphFormat = (ParagraphFormat)doc;
TabStops tabStops = paragraphFormat.TabStops;
if (true) // HACK in ParseAttributeStatement
{
bool fAddItem = Symbol == Symbol.PlusAssign;
TabStop tabStop = new TabStop();
ReadCode();
if (Symbol == Symbol.BraceLeft)
{
ParseAttributeBlock(tabStop);
}
else if (Symbol == Symbol.StringLiteral || Symbol == Symbol.RealLiteral || Symbol == Symbol.IntegerLiteral)
{
// Special hack for tab stops...
Unit unit = Token;
tabStop.SetValue("Position", unit);
ReadCode();
}
else
ThrowParserException(DomMsgID.UnexpectedSymbol, Token);
if (fAddItem)
tabStops.AddTabStop(tabStop);
else
tabStops.RemoveTabStop(tabStop.Position);
}
break;
case Symbol.BraceLeft:
val = doc.GetValue(valueName);
AssertCondition(val != null, DomMsgID.InvalidValueName, valueName);
DocumentObject doc2 = val as DocumentObject;
if (doc2 != null)
ParseAttributeBlock(doc2);
else
ThrowParserException(DomMsgID.SymbolIsNotAnObject, valueName);
break;
default:
ThrowParserException(DomMsgID.SymbolNotAllowed, _scanner.Token);
return;
}
}
catch (DdlParserException ex)
{
ReportParserException(ex);
AdjustToNextBlock();
}
catch (ArgumentException e)
{
ReportParserException(e, DomMsgID.InvalidAssignment, valueName);
}
}
/// <summary>
/// Parses an attribute declaration block enclosed in braces <20>{<7B>}<7D>.
/// </summary>
private void ParseAttributeBlock(DocumentObject element)
{
// Technically the same as ParseAttributes
AssertSymbol(Symbol.BraceLeft);
ReadCode(); // move beyond '{'
while (Symbol == Symbol.Identifier)
ParseAttributeStatement(element);
AssertSymbol(Symbol.BraceRight);
ReadCode(); // move beyond '}'
}
/// <summary>
/// Parses an assign statement in an attribute declaration block.
/// </summary>
private void ParseAssign(DocumentObject dom, ValueDescriptor vd)
{
if (dom == null)
throw new ArgumentNullException("dom");
if (vd == null)
throw new ArgumentNullException("vd");
if (Symbol == Symbol.Assign)
ReadCode();
Type valType = vd.ValueType;
try
{
if (valType == typeof(string))
ParseStringAssignment(dom, vd);
else if (valType == typeof(int))
ParseIntegerAssignment(dom, vd);
else if (valType == typeof(Unit))
ParseUnitAssignment(dom, vd);
else if (valType == typeof(double) || valType == typeof(float))
ParseRealAssignment(dom, vd);
else if (valType == typeof(bool))
ParseBoolAssignment(dom, vd);
#if !NETFX_CORE
else if (typeof(Enum).IsAssignableFrom(valType))
#else
else if (typeof(Enum).GetTypeInfo().IsAssignableFrom(valType.GetTypeInfo()))
#endif
ParseEnumAssignment(dom, vd);
else if (valType == typeof(Color))
ParseColorAssignment(dom, vd);
#if !NETFX_CORE
else if (typeof(ValueType).IsAssignableFrom(valType))
#else
else if (typeof(ValueType).GetTypeInfo().IsAssignableFrom(valType.GetTypeInfo()))
#endif
{
ParseValueTypeAssignment(dom, vd);
}
#if !NETFX_CORE
else if (typeof(DocumentObject).IsAssignableFrom(valType))
#else
else if (typeof(DocumentObject).GetTypeInfo().IsAssignableFrom(valType.GetTypeInfo()))
#endif
{
ParseDocumentObjectAssignment(dom, vd);
}
else
{
AdjustToNextStatement();
ThrowParserException(DomMsgID.InvalidType, vd.ValueType.Name, vd.ValueName);
}
}
catch (Exception ex)
{
ReportParserException(ex, DomMsgID.InvalidAssignment, vd.ValueName);
}
}
/// <summary>
/// Parses the assignment to a boolean l-value.
/// </summary>
private void ParseBoolAssignment(DocumentObject dom, ValueDescriptor vd)
{
AssertCondition(Symbol == Symbol.True || Symbol == Symbol.False, DomMsgID.BoolExpected,
_scanner.Token);
dom.SetValue(vd.ValueName, Symbol == Symbol.True);
ReadCode();
}
/// <summary>
/// Parses the assignment to an integer l-value.
/// </summary>
private void ParseIntegerAssignment(DocumentObject dom, ValueDescriptor vd)
{
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.HexIntegerLiteral || Symbol == Symbol.StringLiteral,
DomMsgID.IntegerExpected, Token);
int n = Int32.Parse(_scanner.Token, CultureInfo.InvariantCulture);
dom.SetValue(vd.ValueName, n);
ReadCode();
}
/// <summary>
/// Parses the assignment to a floating point l-value.
/// </summary>
private void ParseRealAssignment(DocumentObject dom, ValueDescriptor vd)
{
AssertCondition(Symbol == Symbol.RealLiteral || Symbol == Symbol.IntegerLiteral || Symbol == Symbol.StringLiteral,
DomMsgID.RealExpected, _scanner.Token);
double r = double.Parse(_scanner.Token, CultureInfo.InvariantCulture);
dom.SetValue(vd.ValueName, r);
ReadCode();
}
/// <summary>
/// Parses the assignment to a Unit l-value.
/// </summary>
private void ParseUnitAssignment(DocumentObject dom, ValueDescriptor vd)
{
AssertCondition(Symbol == Symbol.RealLiteral || Symbol == Symbol.IntegerLiteral || Symbol == Symbol.StringLiteral,
DomMsgID.RealExpected, _scanner.Token);
Unit unit = Token;
dom.SetValue(vd.ValueName, unit);
ReadCode();
}
/// <summary>
/// Parses the assignment to a string l-value.
/// </summary>
private void ParseStringAssignment(DocumentObject dom, ValueDescriptor vd)
{
AssertCondition(Symbol == Symbol.StringLiteral, DomMsgID.StringExpected, _scanner.Token);
vd.SetValue(dom, Token); //dom.SetValue(vd.ValueName, scanner.Token);
ReadCode(); // read next token
}
/// <summary>
/// Parses the assignment to an enum l-value.
/// </summary>
private void ParseEnumAssignment(DocumentObject dom, ValueDescriptor vd)
{
AssertSymbol(Symbol.Identifier, DomMsgID.IdentifierExpected, _scanner.Token);
try
{
object val = Enum.Parse(vd.ValueType, Token, true);
dom.SetValue(vd.ValueName, val);
}
catch (Exception ex)
{
ThrowParserException(ex, DomMsgID.InvalidEnum, _scanner.Token, vd.ValueName);
}
ReadCode(); // read next token
}
/// <summary>
/// Parses the assignment to a struct (i.e. LeftPosition) l-value.
/// </summary>
private void ParseValueTypeAssignment(DocumentObject dom, ValueDescriptor vd)
{
object val = vd.GetValue(dom, GV.ReadWrite);
try
{
INullableValue ival = (INullableValue)val;
ival.SetValue(Token);
dom.SetValue(vd.ValueName, val);
ReadCode();
}
catch (Exception ex)
{
ReportParserException(ex, DomMsgID.InvalidAssignment, vd.ValueName);
}
}
/// <summary>
/// Parses the assignment to a DocumentObject l-value.
/// </summary>
private void ParseDocumentObjectAssignment(DocumentObject dom, ValueDescriptor vd)
{
// Create value if it does not exist
object val = vd.GetValue(dom, GV.ReadWrite);
//DocumentObject docObj = (DocumentObject)val;
try
{
if (Symbol == Symbol.Null)
{
//string name = vd.ValueName;
Type type = vd.ValueType;
if (typeof(Border) == type)
((Border)val).Clear();
else if (typeof(Borders) == type)
((Borders)val).ClearAll();
else if (typeof(Shading) == type)
((Shading)val).Clear();
else if (typeof(TabStops) == type)
{
TabStops tabStops = (TabStops)vd.GetValue(dom, GV.ReadWrite);
tabStops.ClearAll();
}
else
ThrowParserException(DomMsgID.NullAssignmentNotSupported, vd.ValueName);
ReadCode();
}
else
{
throw new Exception("Case: TopPosition");
//dom.SetValue(vd.ValueName, docObj);
}
}
catch (Exception ex)
{
ReportParserException(ex, DomMsgID.InvalidAssignment, vd.ValueName);
}
}
/// <summary>
/// Parses the assignment to a Value l-value.
/// </summary>
private void ParseValueAssignment(DocumentObject dom, ValueDescriptor vd)
{
try
{
// What ever it is, send it to SetValue.
dom.SetValue(vd.ValueName, Token);
}
catch (Exception ex)
{
ThrowParserException(ex, DomMsgID.InvalidEnum, _scanner.Token, vd.ValueName);
}
ReadCode(); // read next token
}
/// <summary>
/// Parses the assignment to a Color l-value.
/// </summary>
private void ParseColorAssignment(DocumentObject dom, ValueDescriptor vd)
{
object val = vd.GetValue(dom, GV.ReadWrite);
Color color = ParseColor();
dom.SetValue(vd.ValueName, color);
}
/// <summary>
/// Parses a color. It can be <20>green<65>, <20>123456<35>, <20>0xFFABCDEF<45>,
/// <20>RGB(r, g, b)<29>, <20>CMYK(c, m, y, k)<29>, <20>CMYK(a, c, m, y, k)<29>, <20>GRAY(g)<29>, or <20>"MyColor"<22>.
/// </summary>
private Color ParseColor()
{
MoveToCode();
Color color = Color.Empty;
if (Symbol == Symbol.Identifier)
{
switch (Token)
{
case "RGB":
color = ParseRGB();
break;
case "CMYK":
color = ParseCMYK();
break;
case "HSB":
throw new NotImplementedException("ParseColor(HSB)");
case "Lab":
throw new NotImplementedException("ParseColor(Lab)");
case "GRAY":
color = ParseGray();
break;
default: // Must be color enum
try
{
color = Color.Parse(Token);
ReadCode(); // read token
}
catch (Exception ex)
{
ThrowParserException(ex, DomMsgID.InvalidColor, _scanner.Token);
}
break;
}
}
else if (Symbol == Symbol.IntegerLiteral || Symbol == Symbol.HexIntegerLiteral)
{
color = new Color(_scanner.GetTokenValueAsUInt());
ReadCode(); // read beyond literal
}
else if (Symbol == Symbol.StringLiteral)
{
throw new NotImplementedException("ParseColor(color-name)");
}
else
ThrowParserException(DomMsgID.StringExpected, _scanner.Token);
return color;
}
/// <summary>
/// Parses <20>RGB(r, g, b)<29>.
/// </summary>
private Color ParseRGB()
{
uint r, g, b;
ReadCode(); // read '('
AssertSymbol(Symbol.ParenLeft);
ReadCode(); // read red value
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.HexIntegerLiteral,
DomMsgID.IntegerExpected, _scanner.Token);
r = _scanner.GetTokenValueAsUInt();
AssertCondition(r >= 0 && r <= 255, DomMsgID.InvalidRange, "0 - 255");
ReadCode(); // read ','
AssertSymbol(Symbol.Comma);
ReadCode(); // read green value
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.HexIntegerLiteral,
DomMsgID.IntegerExpected, _scanner.Token);
g = _scanner.GetTokenValueAsUInt();
AssertCondition(g >= 0 && g <= 255, DomMsgID.InvalidRange, "0 - 255");
ReadCode(); // read ','
AssertSymbol(Symbol.Comma);
ReadCode(); // read blue value
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.HexIntegerLiteral,
DomMsgID.IntegerExpected, _scanner.Token);
b = _scanner.GetTokenValueAsUInt();
AssertCondition(b >= 0 && b <= 255, DomMsgID.InvalidRange, "0 - 255");
ReadCode(); // read ')'
AssertSymbol(Symbol.ParenRight);
ReadCode(); // read next token
return new Color(0xFF000000 | (r << 16) | (g << 8) | b);
}
/// <summary>
/// Parses <20>CMYK(c, m, y, k)<29> or <20>CMYK(a, c, m, y, k)<29>.
/// </summary>
private Color ParseCMYK()
{
ReadCode(); // read '('
AssertSymbol(Symbol.ParenLeft);
ReadCode(); // read v1 value
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.RealLiteral,
DomMsgID.NumberExpected, _scanner.Token);
double v1 = _scanner.GetTokenValueAsReal();
AssertCondition(v1 >= 0.0f && v1 <= 100.0f, DomMsgID.InvalidRange, "0.0 - 100.0");
ReadCode(); // read ','
AssertSymbol(Symbol.Comma);
ReadCode(); // read v2 value
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.RealLiteral,
DomMsgID.NumberExpected, _scanner.Token);
double v2 = _scanner.GetTokenValueAsReal();
AssertCondition(v2 >= 0.0f && v2 <= 100.0f, DomMsgID.InvalidRange, "0.0 - 100.0");
ReadCode(); // read ','
AssertSymbol(Symbol.Comma);
ReadCode(); // read v3 value
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.RealLiteral,
DomMsgID.NumberExpected, _scanner.Token);
double v3 = _scanner.GetTokenValueAsReal();
AssertCondition(v3 >= 0.0f && v3 <= 100.0f, DomMsgID.InvalidRange, "0.0 - 100.0");
ReadCode(); // read ','
AssertSymbol(Symbol.Comma);
ReadCode(); // read v4 value
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.RealLiteral,
DomMsgID.NumberExpected, _scanner.Token);
double v4 = _scanner.GetTokenValueAsReal();
AssertCondition(v4 >= 0.0f && v4 <= 100.0, DomMsgID.InvalidRange, "0.0 - 100.0");
ReadCode(); // read ')' or ','
bool hasAlpha = false;
double v5 = 0;
if (Symbol == Symbol.Comma)
{
hasAlpha = true;
ReadCode(); // read v5 value
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.RealLiteral,
DomMsgID.NumberExpected, _scanner.Token);
v5 = _scanner.GetTokenValueAsReal();
AssertCondition(v5 >= 0.0f && v5 <= 100.0, DomMsgID.InvalidRange, "0.0 - 100.0");
ReadCode(); // read ')'
}
AssertSymbol(Symbol.ParenRight);
ReadCode(); // read next token
double a, c, m, y, k;
if (hasAlpha)
{
a = v1; c = v2; m = v3; y = v4; k = v5;
}
else
{
a = 100.0; c = v1; m = v2; y = v3; k = v4;
}
return Color.FromCmyk(a, c, m, y, k);
}
/// <summary>
/// Parses <20>GRAY(g)<29>.
/// </summary>
private Color ParseGray()
{
ReadCode(); // read '('
AssertSymbol(Symbol.ParenLeft);
ReadCode(); // read gray value
AssertCondition(Symbol == Symbol.IntegerLiteral || Symbol == Symbol.HexIntegerLiteral,
DomMsgID.IntegerExpected, _scanner.Token);
double gray = _scanner.GetTokenValueAsReal();
AssertCondition(gray >= 0.0f && gray <= 100.0f, DomMsgID.InvalidRange, "0.0 - 100.0");
ReadCode(); // read ')'
AssertSymbol(Symbol.ParenRight);
ReadCode(); // read next token
uint g = (uint)((1 - gray / 100.0) * 255 + 0.5);
return new Color(0xff000000 + (g << 16) + (g << 8) + g);
}
/// <summary>
/// Determines the name/text of the given symbol.
/// </summary>
private string GetSymbolText(Symbol docSym)
{
return KeyWords.NameFromSymbol(docSym);
}
/// <summary>
/// Returns whether the specified type is a valid SpaceType.
/// </summary>
private bool IsSpaceType(string type)
{
if (type == null)
throw new ArgumentNullException("type");
if (type == "")
throw new ArgumentException("type");
if (Enum.IsDefined(typeof(SymbolName), type))
{
SymbolName symbolName = (SymbolName)Enum.Parse(typeof(SymbolName), type, false); // symbols are case sensitive
switch (symbolName)
{
case SymbolName.Blank:
case SymbolName.Em:
//case SymbolName.Em4: // same as SymbolName.EmQuarter
case SymbolName.EmQuarter:
case SymbolName.En:
return true;
}
}
return false;
}
/// <summary>
/// Returns whether the specified type is a valid enum for \symbol.
/// </summary>
private bool IsSymbolType(string type)
{
if (type == null)
throw new ArgumentNullException("type");
if (type == "")
throw new ArgumentException("type");
if (Enum.IsDefined(typeof(SymbolName), type))
{
SymbolName symbolName = (SymbolName)Enum.Parse(typeof(SymbolName), type, false); // symbols are case sensitive
switch (symbolName)
{
case SymbolName.Euro:
case SymbolName.Copyright:
case SymbolName.Trademark:
case SymbolName.RegisteredTrademark:
case SymbolName.Bullet:
case SymbolName.Not:
case SymbolName.EmDash:
case SymbolName.EnDash:
case SymbolName.NonBreakableBlank:
//case SymbolName.HardBlank: //same as SymbolName.NonBreakableBlank:
return true;
}
}
return false;
}
/// <summary>
/// If cond is evaluated to false, a DdlParserException with the specified error will be thrown.
/// </summary>
private void AssertCondition(bool cond, DomMsgID error, params object[] args)
{
if (!cond)
ThrowParserException(error, args);
}
/// <summary>
/// If current symbol is not equal symbol a DdlParserException will be thrown.
/// </summary>
private void AssertSymbol(Symbol symbol)
{
if (Symbol != symbol)
ThrowParserException(DomMsgID.SymbolExpected, KeyWords.NameFromSymbol(symbol), Token);
}
/// <summary>
/// If current symbol is not equal symbol a DdlParserException with the specified message id
/// will be thrown.
/// </summary>
private void AssertSymbol(Symbol symbol, DomMsgID err)
{
if (Symbol != symbol)
ThrowParserException(err, KeyWords.NameFromSymbol(symbol), Token);
}
/// <summary>
/// If current symbol is not equal symbol a DdlParserException with the specified message id
/// will be thrown.
/// </summary>
private void AssertSymbol(Symbol symbol, DomMsgID err, params object[] parms)
{
if (Symbol != symbol)
ThrowParserException(err, KeyWords.NameFromSymbol(symbol), parms);
}
/// <summary>
/// Creates an ErrorInfo based on the given errorlevel, error and parms and adds it to the ErrorManager2.
/// </summary>
private void ReportParserInfo(DdlErrorLevel level, DomMsgID errorCode, params string[] parms)
{
string message = DomSR.FormatMessage(errorCode, parms);
DdlReaderError error = new DdlReaderError(level, message, (int)errorCode,
_scanner.DocumentFileName, _scanner.CurrentLine, _scanner.CurrentLinePos);
_errors.AddError(error);
}
/// <summary>
/// Creates an ErrorInfo based on the given error and parms and adds it to the ErrorManager2.
/// </summary>
private void ReportParserException(DomMsgID error, params string[] parms)
{
ReportParserException(null, error, parms);
}
/// <summary>
/// Adds the ErrorInfo from the ErrorInfoException2 to the ErrorManager2.
/// </summary>
private void ReportParserException(DdlParserException ex)
{
_errors.AddError(ex.Error);
}
/// <summary>
/// Creates an ErrorInfo based on the given inner exception, error and parms and adds it to the ErrorManager2.
/// </summary>
private void ReportParserException(Exception innerException, DomMsgID errorCode, params string[] parms)
{
string message = "";
if (innerException != null)
message = ": " + innerException;
message += DomSR.FormatMessage(errorCode, parms);
DdlReaderError error = new DdlReaderError(DdlErrorLevel.Error, message, (int)errorCode,
_scanner.DocumentFileName, _scanner.CurrentLine, _scanner.CurrentLinePos);
_errors.AddError(error);
}
/// <summary>
/// Creates an ErrorInfo based on the DomMsgID and the specified parameters.
/// Throws a DdlParserException with that ErrorInfo.
/// </summary>
private void ThrowParserException(DomMsgID errorCode, params object[] parms)
{
string message = DomSR.FormatMessage(errorCode, parms);
DdlReaderError error = new DdlReaderError(DdlErrorLevel.Error, message, (int)errorCode,
_scanner.DocumentFileName, _scanner.CurrentLine, _scanner.CurrentLinePos);
throw new DdlParserException(error);
}
/// <summary>
/// Determines the error message based on the DomMsgID and the parameters.
/// Throws a DdlParserException with that error message and the Exception as the inner exception.
/// </summary>
private void ThrowParserException(Exception innerException, DomMsgID errorCode, params object[] parms)
{
string message = DomSR.FormatMessage(errorCode, parms);
throw new DdlParserException(message, innerException);
}
/// <summary>
/// Used for exception handling. Sets the DDL stream to the next valid position behind
/// the current block.
/// </summary>
private void AdjustToNextBlock()
{
bool skipClosingBraceOrBracket = (Symbol == Symbol.BraceLeft || Symbol == Symbol.BracketLeft);
ReadCode();
bool finish = false;
while (!finish)
{
switch (Symbol)
{
case Symbol.BraceLeft:
case Symbol.BracketLeft:
AdjustToNextBlock();
break;
case Symbol.BraceRight:
case Symbol.BracketRight:
if (skipClosingBraceOrBracket)
ReadCode();
finish = true;
break;
case Symbol.Eof:
ThrowParserException(DomMsgID.UnexpectedEndOfFile);
break;
default:
AdjustToNextStatement();
break;
}
}
}
/// <summary>
/// Used for exception handling. Sets the DDL stream to the next valid position behind
/// the current statement.
/// </summary>
private void AdjustToNextStatement()
{
bool finish = false;
while (!finish)
{
switch (Symbol)
{
case Symbol.Assign:
//read one more symbol
ReadCode();
break;
default:
ReadCode();
finish = true;
break;
}
}
}
/// <summary>
/// Shortcut for scanner.ReadCode().
/// Reads the next DDL token. Comments are ignored.
/// </summary>
private Symbol ReadCode()
{
return _scanner.ReadCode();
}
/// <summary>
/// Shortcut for scanner.ReadText().
/// Reads either text or \keyword from current position.
/// </summary>
private Symbol ReadText(bool rootLevel)
{
return _scanner.ReadText(rootLevel);
}
/// <summary>
/// Shortcut for scanner.MoveToCode().
/// Moves to the next DDL token if Symbol is not set to a valid position.
/// </summary>
private void MoveToCode()
{
_scanner.MoveToCode();
}
/// <summary>
/// Shortcut for scanner.MoveToParagraphContent().
/// Moves to the first character the content of a paragraph starts with. Empty lines
/// and comments are skipped. Returns true if such a character exists, and false if the
/// paragraph ends without content.
/// </summary>
public bool MoveToParagraphContent()
{
return _scanner.MoveToParagraphContent();
}
/// <summary>
/// Shortcut for scanner.MoveToNextParagraphContentLine().
/// Moves to the first character of the content of a paragraph beyond an EOL.
/// Returns true if such a character exists and belongs to the current paragraph.
/// Returns false if a new line (at root level) or '}' occurs. If a new line caused
/// the end of the paragraph, the DDL cursor is moved to the next valid content
/// character or '}' respectively.
/// </summary>
public bool MoveToNextParagraphContentLine(bool rootLevel)
{
return _scanner.MoveToNextParagraphContentLine(rootLevel);
}
/// <summary>
/// Gets the current symbol from the scanner.
/// </summary>
private Symbol Symbol
{
get { return _scanner.Symbol; }
}
/// <summary>
/// Gets the current token from the scanner.
/// </summary>
private string Token
{
get { return _scanner.Token; }
}
/// <summary>
/// Gets the current token type from the scanner.
/// </summary>
private TokenType TokenType
{
get { return _scanner.TokenType; }
}
private readonly DdlScanner _scanner;
private readonly DdlReaderErrors _errors;
}
}