Ejemplo n.º 1
0
        ///<summary>
        /// Copies all child nodes from source into heir, recursively.
        /// -If a node appears only in source or only in heir, it is included.
        /// -If a list appears in both source and heir, source's entries are appended to heir's entries.
        /// -If a non-list node appears in both source and heir, heir's node is overwritten.
        ///</summary>
        private static void ApplyInheritance(TydNode source, TydNode heir)
        {
            try
            {
                //They're strings: Copy source over heir
                {
                    TydString sourceStr = source as TydString;
                    if (sourceStr != null)
                    {
                        TydString heirStr = heir as TydString;
                        heirStr.Value = sourceStr.Value;
                        return;
                    }
                }

                //They're tables: Combine all children of source and heir, with source having priority in case of duplicates
                {
                    TydTable sourceObj = source as TydTable;
                    if (sourceObj != null)
                    {
                        TydTable heirObj = (TydTable)heir;
                        for (int i = 0; i < sourceObj.Count; i++)
                        {
                            var sourceChild       = sourceObj[i];
                            var heirMatchingChild = heirObj[sourceChild.Name];
                            if (heirMatchingChild != null)
                            {
                                ApplyInheritance(sourceChild, heirMatchingChild);
                            }
                            else
                            {
                                heirObj.AddChild(sourceChild); //Does this need to be DeepClone?
                            }
                        }
                        return;
                    }
                }

                //They're lists: Append source's entries to heir's entries
                {
                    TydList sourceList = source as TydList;
                    if (sourceList != null)
                    {
                        TydList heirList = (TydList)heir;
                        for (int i = 0; i < sourceList.Count; i++)
                        {
                            heirList.AddChild(sourceList[i]); //Does this need to be DeepClone?
                        }
                        return;
                    }
                }
            }
            catch (Exception e)
            {
                throw new Exception("ApplyInheritance exception: " + e + ".\nsource: (" + source + ")\n" + TydToText.Write(source) + "\ntarget: (" + heir + ")\n" + TydToText.Write(heir));
            }
        }
Ejemplo n.º 2
0
        ///<summary>
        /// Convert a single XML tree into a Tyd tree.
        /// If expectName is false, it'll be parsed as a list item.
        ///</summary>
        public static TydNode TydNodeFromXmlNode(XmlNode xmlRoot)
        {
            if (xmlRoot is XmlComment)
            {
                return(null);
            }

            var newTydName = xmlRoot.Name != "li"
            ? xmlRoot.Name
            : null;

            //Record _attributes here so we can use them later
            var attributes = new Dictionary <string, string>();

            var xmlAttributes = xmlRoot.Attributes;

            if (xmlAttributes != null)
            {
                foreach (XmlAttribute a in xmlAttributes)
                {
                    attributes[a.Name] = a.Value;
                }
            }

            if (xmlRoot.ChildNodes.Count == 1 && xmlRoot.FirstChild is XmlText)
            {
                //It's a string
                return(new TydString(newTydName, xmlRoot.FirstChild.InnerText));
            }
            else if (xmlRoot.HasChildNodes && xmlRoot.FirstChild.Name == "li")
            {
                //Children are named 'li'
                //It's a list

                var tydRoot = new TydList(newTydName);
                tydRoot.SetupAttributes(attributes);
                foreach (XmlNode xmlChild in xmlRoot.ChildNodes)
                {
                    tydRoot.AddChild(TydNodeFromXmlNode(xmlChild));
                }
                return(tydRoot);
            }
            else
            {
                //This case catches _nodes with no children.
                //Note that the case of no children is ambiguous between list and table; we choose list arbitrarily.

                //It's a table
                var tydRoot = new TydTable(newTydName);
                foreach (XmlNode xmlChild in xmlRoot.ChildNodes)
                {
                    tydRoot.AddChild(TydNodeFromXmlNode(xmlChild));
                }
                return(tydRoot);
            }
        }
Ejemplo n.º 3
0
        /*
         *  Possible future features:
         *      - Ability to align string values into a single column
         *      - Ability to write lists/tables with 0 or 1 children on a single line
         *      - Some way to better control which strings get quotes and which don't
         */

        ///<summary>
        /// Writes a given TydNode, along with all its descendants, as a string, at a given indent level.
        /// This method is recursive.
        ///</summary>
        public static string Write(TydNode node, int indent = 0)
        {
            //It's a string
            TydString str = node as TydString;

            if (str != null)
            {
                return(IndentString(indent) + node.Name + " " + StringContentWriteable(str.Value));
            }

            //It's a table
            TydTable tab = node as TydTable;

            if (tab != null)
            {
                StringBuilder sb = new StringBuilder();

                //Intro line
                if (AppendNodeIntro(tab, sb, indent) && tab.Count > 0)
                {
                    sb.AppendLine();
                }

                if (tab.Count == 0)
                {
                    sb.Append(Constants.TableStartChar.ToString() + Constants.TableEndChar.ToString());
                }
                else
                {
                    //Sub-nodes
                    sb.AppendLine(IndentString(indent) + Constants.TableStartChar);
                    for (int i = 0; i < tab.Count; i++)
                    {
                        sb.AppendLine(Write(tab[i], indent + 1));
                    }
                    sb.Append(IndentString(indent) + Constants.TableEndChar);
                }

                return(sb.ToString());
            }

            //It's a list
            TydList list = node as TydList;

            if (list != null)
            {
                StringBuilder sb = new StringBuilder();

                //Intro line
                if (AppendNodeIntro(list, sb, indent) && list.Count > 0)
                {
                    sb.AppendLine();
                }

                if (list.Count == 0)
                {
                    sb.Append(Constants.ListStartChar.ToString() + Constants.ListEndChar.ToString());
                }
                else
                {
                    //Sub-nodes
                    sb.AppendLine(IndentString(indent) + Constants.ListStartChar);
                    for (int i = 0; i < list.Count; i++)
                    {
                        sb.AppendLine(Write(list[i], indent + 1));
                    }
                    sb.Append(IndentString(indent) + Constants.ListEndChar);
                }

                return(sb.ToString());
            }

            throw new ArgumentException();
        }
Ejemplo n.º 4
0
        ///<summary>
        /// Copies all child nodes from source into heir, recursively.
        /// -If a node appears only in source or only in heir, it is included.
        /// -If a list appears in both source and heir, source's entries are appended to heir's entries.
        /// -If a non-list node appears in both source and heir, heir's node is overwritten.
        ///</summary>
        private static void ApplyInheritance(TydNode source, TydNode heir)
        {
            try
            {
                //They're either strings or nulls: We just keep the existing heir's value
                if (source is TydString)
                {
                    return;
                }

                //Heir has noinherit attribute: Skip this inheritance
                {
                    TydCollection heirCol = heir as TydCollection;
                    if (heirCol != null && heirCol.AttributeNoInherit)
                    {
                        return;
                    }
                }

                //They're tables: Combine all children of source and heir. Unique-name source nodes are prepended
                {
                    TydTable sourceObj = source as TydTable;
                    if (sourceObj != null)
                    {
                        TydTable heirTable = (TydTable)heir;
                        for (int i = 0; i < sourceObj.Count; i++)
                        {
                            var sourceChild       = sourceObj[i];
                            var heirMatchingChild = heirTable[sourceChild.Name];

                            if (heirMatchingChild != null)
                            {
                                ApplyInheritance(sourceChild, heirMatchingChild);
                            }
                            else
                            {
                                heirTable.InsertChild(sourceChild, 0); //Does this need to be DeepClone?
                            }
                        }
                        return;
                    }
                }

                //They're lists: Prepend source's children before heir's children
                {
                    TydList sourceList = source as TydList;
                    if (sourceList != null)
                    {
                        TydList heirList = (TydList)heir;
                        for (int i = 0; i < sourceList.Count; i++)
                        {
                            //Insert at i so the nodes stay in the same order from source to heir
                            heirList.InsertChild(sourceList[i], i); //Does this need to be DeepClone?
                        }
                        return;
                    }
                }
            }
            catch (Exception e)
            {
                throw new Exception("ApplyInheritance exception: " + e + ".\nsource: (" + source + ")\n" + TydToText.Write(source) + "\ntarget: (" + heir + ")\n" + TydToText.Write(heir));
            }
        }
Ejemplo n.º 5
0
        ///<summary>
        /// Recursively parses the string 'doc' starting at char index 'startIndex' and ending when there is an unmatched closing bracket or EOF.
        /// doc should have any opening bracket already stripped off.
        /// This recursive method is used both for parsing files, as well as for parsing specific entries inside files.
        ///</summary>
        private static IEnumerable <TydNode> Parse(string doc, int startIndex, TydNode parent, bool expectNames = true)
        {
            int p = startIndex;

            //Main loop
            while (true)
            {
                string recordName        = null;
                string recordAttClass    = null;
                string recordAttHandle   = null;
                string recordAttSource   = null;
                bool   recordAttAbstract = false;

                try
                {
                    //Skip insubstantial chars
                    p = NextSubstanceIndex(doc, p);

                    //We reached EOF, so we're finished
                    if (p == doc.Length)
                    {
                        yield break;
                    }

                    //We reached a closing bracket, so we're finished with this record
                    if (doc[p] == Constants.TableEndChar || doc[p] == Constants.ListEndChar)
                    {
                        yield break;
                    }

                    //Read the record name if we're not reading anonymous records
                    if (expectNames)
                    {
                        recordName = ReadSymbol(doc, ref p);
                    }

                    //Skip whitespace
                    p = NextSubstanceIndex(doc, p);

                    //Read attributes
                    while (doc[p] == Constants.AttributeStartChar)
                    {
                        //Skip past the '*' character
                        p++;

                        //Read the att name
                        string attName = ReadSymbol(doc, ref p);
                        if (attName == Constants.AbstractAttributeName)
                        {
                            //Just reading the abstract name indicates it's abstract, no value is needed
                            recordAttAbstract = true;
                        }
                        else
                        {
                            p = NextSubstanceIndex(doc, p);

                            //Read the att value
                            string attVal = ReadSymbol(doc, ref p);
                            switch (attName)
                            {
                            case Constants.ClassAttributeName: recordAttClass = attVal; break;

                            case Constants.HandleAttributeName: recordAttHandle = attVal; break;

                            case Constants.SourceAttributeName: recordAttSource = attVal; break;

                            default: throw new Exception("Unknown attribute name '" + attName + "' at " + IndexToLocationString(doc, p));
                            }
                        }

                        p = NextSubstanceIndex(doc, p);
                    }
                }
                catch (Exception e)
                {
                    throw new Exception("Exception parsing Tyd headers at " + IndexToLocationString(doc, p) + ": " + e.ToString(), e);
                }

                //Read the record value.
                //After this is complete, p should be pointing at the char after the last char of the record.
                if (doc[p] == Constants.TableStartChar)
                {
                    //It's a table
                    TydTable newTable = new TydTable(recordName, parent, IndexToLine(doc, p));

                    //Skip past the opening bracket
                    p++;

                    p = NextSubstanceIndex(doc, p);

                    //Recursively parse all of new child's children and add them to it
                    try
                    {
                        foreach (var subNode in Parse(doc, p, newTable, expectNames: true))
                        {
                            if (usedNames.Contains(subNode.Name))
                            {
                                throw new FormatException("Duplicate record name " + subNode.Name + " at " + IndexToLocationString(doc, p));
                            }
                            usedNames.Add(subNode.Name);

                            newTable.AddChild(subNode);
                            p = subNode.docIndexEnd + 1;
                        }
                    }
                    finally
                    {
                        usedNames.Clear();
                    }

                    p = NextSubstanceIndex(doc, p);

                    if (doc[p] != Constants.TableEndChar)
                    {
                        throw new FormatException("Expected ']' at " + IndexToLocationString(doc, p));
                    }

                    newTable.docIndexEnd = p;
                    newTable.SetupAttributes(recordAttClass, recordAttHandle, recordAttSource, recordAttAbstract);
                    yield return(newTable);

                    //Move pointer one past the closing bracket
                    p++;
                }
                else if (doc[p] == Constants.ListStartChar)
                {
                    //It's a list
                    TydList newList = new TydList(recordName, parent, IndexToLine(doc, p));

                    //Skip past the opening bracket
                    p++;

                    p = NextSubstanceIndex(doc, p);

                    //Recursively parse all of new child's children and add them to it
                    foreach (var subNode in Parse(doc, p, newList, expectNames: false))
                    {
                        newList.AddChild(subNode);
                        p = subNode.docIndexEnd + 1;
                    }
                    p = NextSubstanceIndex(doc, p);

                    if (doc[p] != Constants.ListEndChar)
                    {
                        throw new FormatException("Expected " + Constants.ListEndChar + " at " + IndexToLocationString(doc, p));
                    }

                    newList.docIndexEnd = p;
                    newList.SetupAttributes(recordAttClass, recordAttHandle, recordAttSource, recordAttAbstract);
                    yield return(newList);

                    //Move pointer one past the closing bracket
                    p++;
                }
                else
                {
                    //It's a string
                    int    pStart = p;
                    string val;
                    ParseStringValue(doc, ref p, out val);

                    var strNode = new TydString(recordName, val, parent, IndexToLine(doc, pStart));
                    strNode.docIndexEnd = p - 1;
                    yield return(strNode);
                }
            }
        }
Ejemplo n.º 6
0
        ///<summary>
        /// Recursively parses the string 'doc' starting at char index 'startIndex' and ending when there is an unmatched closing bracket or EOF.
        /// doc should have any opening bracket already stripped off.
        /// This recursive method is used both for parsing files, as well as for parsing specific entries inside files.
        ///</summary>
        private static List <TydNode> Parse(string doc, int startIndex, ref int currentLine, bool expectNames = true, List <TydNode> res = null, bool breakOnFirst = false)
        {
            var p = startIndex;

            res = res ?? new List <TydNode>();
            //Main loop
            while (true)
            {
                string recordName = null;
                Dictionary <string, string> attributes = null;

                try
                {
                    var before = currentLine;
                    //Skip insubstantial chars
                    p = NextSubstanceIndex(doc, p, ref currentLine);

                    //We reached EOF, so we're finished
                    if (p == doc.Length)
                    {
                        return(res);
                    }

                    //Unhandled semicolon, likely after list or table, ignore it according to spec
                    if (doc[p] == ';')
                    {
                        p++;
                        continue;
                    }

                    //We reached a closing bracket, so we're finished with this record
                    if (doc[p] == Constants.TableEndChar || doc[p] == Constants.ListEndChar)
                    {
                        //To avoid counting lines twice from parent call, we subtract last new lines counted
                        currentLine = before;
                        return(res);
                    }

                    //Read the record _name if we're not reading anonymous records
                    if (expectNames)
                    {
                        recordName = ReadSymbol(doc, currentLine, ref p);
                    }

                    //Skip whitespace
                    p = NextSubstanceIndex(doc, p, ref currentLine);

                    //Read _attributes
                    while (doc[p] == Constants.AttributeStartChar)
                    {
                        //Skip past the '*' character
                        p++;

                        //Read the att _name
                        var attName = ReadSymbol(doc, currentLine, ref p);
                        p = NextSubstanceIndex(doc, p, ref currentLine);
                        if (doc[p] == Constants.AttributeStartChar || doc[p] == Constants.CommentChar || doc[p] == Constants.TableStartChar)
                        {
                            //No attribute value defined
                            SetAttributeValue(ref attributes, attName, null);
                        }
                        else
                        {
                            //Read the att value
                            string value;
                            ParseStringValue(doc, ref p, ref currentLine, out value);
                            SetAttributeValue(ref attributes, attName, value);
                            p = NextSubstanceIndex(doc, p, ref currentLine);
                        }
                    }
                }
                catch (Exception e)
                {
                    throw new Exception("Exception parsing Tyd headers at " + IndexToLocationString(doc, currentLine, p) + ": " + e.ToString(), e);
                }

                //Read the record value.
                //After this is complete, p should be pointing at the char after the last char of the record.
                if (doc[p] == Constants.TableStartChar)
                {
                    //It's a table
                    var newTable = new TydTable(recordName, currentLine);

                    //Skip past the opening bracket
                    p++;

                    p = NextSubstanceIndex(doc, p, ref currentLine);

                    //Recursively parse all of new child's children and add them to it
                    foreach (var subNode in Parse(doc, p, ref currentLine, true, newTable.Nodes))
                    {
                        subNode.Parent = newTable;
                        p = subNode.DocIndexEnd + 1;
                    }

                    p = NextSubstanceIndex(doc, p, ref currentLine);

                    if (doc[p] != Constants.TableEndChar)
                    {
                        throw new FormatException("Expected " + Constants.TableEndChar + " at " + IndexToLocationString(doc, currentLine, p));
                    }

                    newTable.DocIndexEnd = p;
                    newTable.SetupAttributes(attributes);
                    res.Add(newTable);

                    //Move pointer one past the closing bracket
                    p++;
                }
                else if (doc[p] == Constants.ListStartChar)
                {
                    //It's a list
                    var pStart  = p;
                    var newList = new TydList(recordName, currentLine);

                    //Skip past the opening bracket
                    p++;

                    p = NextSubstanceIndex(doc, p, ref currentLine);

                    //Recursively parse all of new child's children and add them to it
                    foreach (var subNode in Parse(doc, p, ref currentLine, false, newList.Nodes))
                    {
                        subNode.Parent = newList;
                        p = subNode.DocIndexEnd + 1;
                    }
                    p = NextSubstanceIndex(doc, p, ref currentLine);

                    if (p >= doc.Length || doc[p] != Constants.ListEndChar)
                    {
                        throw new FormatException(string.Format("Expected {0} from {1} at {2}",
                                                                Constants.ListEndChar, IndexToLocationString(doc, newList.DocLine, pStart), IndexToLocationString(doc, currentLine, p)));
                    }

                    newList.DocIndexEnd = p;
                    newList.SetupAttributes(attributes);
                    res.Add(newList);
                    if (breakOnFirst)
                    {
                        return(res);
                    }
                    //Move pointer one past the closing bracket
                    p++;
                }
                else
                {
                    //It's a string
                    var    pStart = p;
                    string val;
                    ParseStringValue(doc, ref p, ref currentLine, out val);

                    var strNode = new TydString(recordName, val, currentLine);
                    strNode.DocIndexEnd = p - 1;
                    res.Add(strNode);
                    if (breakOnFirst)
                    {
                        return(res);
                    }
                }
            }
        }
Ejemplo n.º 7
0
        ///<summary>
        /// Convert a single XML tree into a Tyd tree.
        /// If expectName is false, it'll be parsed as a list item.
        ///</summary>
        public static TydNode TydNodeFromXmlNode(XmlNode xmlRoot, TydNode tydParent)
        {
            if (xmlRoot is XmlComment)
            {
                return(null);
            }

            string newTydName = xmlRoot.Name != "li"
            ? xmlRoot.Name
            : null;

            //Record attributes here so we can use them later
            string attClass      = null;
            string attHandle     = null;
            string attSource     = null;
            bool   attAbstract   = false;
            var    xmlAttributes = xmlRoot.Attributes;

            if (xmlAttributes != null)
            {
                foreach (XmlAttribute a in xmlAttributes)
                {
                    if (a.Name == "Class")
                    {
                        attClass = a.Value;
                    }
                    else if (a.Name == "Name")
                    {
                        attHandle = a.Value;
                    }
                    else if (a.Name == "ParentName")
                    {
                        attSource = a.Value;
                    }
                    else if (a.Name == "Abstract" && a.Value == "True")
                    {
                        attAbstract = true;
                    }
                }
            }

            if (xmlRoot.ChildNodes.Count == 1 && xmlRoot.FirstChild is XmlText)
            {
                //It's a string
                return(new TydString(newTydName, xmlRoot.FirstChild.InnerText, tydParent));
            }
            else if (xmlRoot.HasChildNodes && xmlRoot.FirstChild.Name == "li")
            {
                //Children are named 'li'
                //It's a list

                TydList tydRoot = new TydList(newTydName, tydParent);
                tydRoot.SetupAttributes(attClass, attHandle, attSource, attAbstract);
                foreach (XmlNode xmlChild in xmlRoot.ChildNodes)
                {
                    tydRoot.AddChild(TydNodeFromXmlNode(xmlChild, tydRoot));
                }
                return(tydRoot);
            }
            else
            {
                //This case catches nodes with no children.
                //Note that the case of no children is ambiguous between list and table; we choose list arbitrarily.

                //It's a table
                TydTable tydRoot = new TydTable(newTydName, tydParent);
                tydRoot.SetupAttributes(attClass, attHandle, attSource, attAbstract);
                foreach (XmlNode xmlChild in xmlRoot.ChildNodes)
                {
                    tydRoot.AddChild(TydNodeFromXmlNode(xmlChild, tydRoot));
                }
                return(tydRoot);
            }
        }