Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
MamlNode.cs1253 linesDownload Raw Back to help
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.ObjectModel;6using System.Globalization;7using System.Reflection;8using System.Text;9using System.Xml;10 11namespace System.Management.Automation12{13    /// <summary>14    /// MamlNode is an xml node in MAML schema. Maml schema includes formatting oriented tags like para, list15    /// etc, which needs to be taken care of during display. As a result, xml node in Maml schema can't be16    /// converted into PSObject directly with XmlNodeAdapter.17    ///18    /// MamlNode class provides logic in converting formatting tags into the format acceptable by monad format19    /// and output engine.20    ///21    /// Following three kinds of formating tags are supported per our agreement with Maml team,22    ///     1. para,23    ///         <para>24    ///             para text here25    ///         </para>26    ///     2. list,27    ///         <list class="ordered|unordered">28    ///             <listItem>29    ///                 <para>30    ///                     listItem Text here31    ///                 </para>32    ///             </listItem>33    ///         </list>34    ///     3. definition list,35    ///         <definitionList>36    ///             <definitionListItem>37    ///                 <term>38    ///                     definition term text here39    ///                 </term>40    ///                 <definition>41    ///                     <para>42    ///                         definition text here43    ///                     </para>44    ///                 </definition>45    ///             </definitionListItem>46    ///         </definitionList>47    /// After processing, content of these three tags will be converted into textItem and its derivations,48    ///     1. para => paraTextItem49    ///         <textItem class="paraTextItem">50    ///             <text>para text here</text>51    ///         </textItem>52    ///     2. list => a list of listTextItem's (which can be ordered or unordered)53    ///         <textItem class="unorderedListTextItem">54    ///             <tag>*</tag>55    ///             <text>text for list item 1</text>56    ///         </textItem>57    ///         <textItem class="unorderedListTextItem">58    ///             <tag>*</tag>59    ///             <text>text for list item 2</text>60    ///         </textItem>61    ///     3. definitionList => a list of definitionTextItem's62    ///         <definitionListItem>63    ///             <term>definition term here</term>64    ///             <definition>definition text here</definition>65    ///         </definitionListItem>66    /// </summary>67    internal class MamlNode68    {69        /// <summary>70        /// Constructor for HelpInfo.71        /// </summary>72        internal MamlNode(XmlNode xmlNode)73        {74            _xmlNode = xmlNode;75        }76 77        private readonly XmlNode _xmlNode;78 79        /// <summary>80        /// Underline xmlNode for this MamlNode object.81        /// </summary>82        /// <value></value>83        internal XmlNode XmlNode84        {85            get86            {87                return _xmlNode;88            }89        }90 91        private PSObject _mshObject;92 93        /// <summary>94        /// MshObject which is converted from XmlNode.95        /// </summary>96        /// <value></value>97        internal PSObject PSObject98        {99            get100            {101                if (_mshObject == null)102                {103                    // There is no XSLT to convert docs to supported maml format104                    // We dont want comments etc to spoil our format.105                    // So remove all unsupported nodes before constructing help106                    // object.107                    RemoveUnsupportedNodes(_xmlNode);108                    _mshObject = GetPSObject(_xmlNode);109                }110 111                return _mshObject;112            }113        }114 115        #region Conversion of xmlNode => PSObject116 117        /// <summary>118        /// Convert an xmlNode into an PSObject. There are four scenarios,119        ///     1. Null xml, this will return an PSObject wrapping a null object.120        ///     2. Atomic xml, which is an xmlNode with only one simple text child node121        ///         <atomicXml attribute="value">122        ///             atomic xml text123        ///         </atomicXml>124        ///        In this case, an PSObject that wraps string "atomic xml text" will be returned with following properties125        ///             attribute => name126        ///     3. Composite xml, which is an xmlNode with structured child nodes, but not a special case for Maml formatting.127        ///         <compositeXml attribute="attribute">128        ///             <singleChildNode>129        ///                 single child node text130        ///             </singleChildNode>131        ///             <dupChildNode>132        ///                 dup child node text 1133        ///             </dupChildNode>134        ///             <dupChildNode>135        ///                 dup child node text 2136        ///             </dupChildNode>137        ///         </compositeXml>138        ///        In this case, an PSObject will base generated based on an inside PSObject,139        ///        which in turn has following properties140        ///             a. property "singleChildNode", with its value an PSObject wrapping string "single child node text"141        ///             b. property "dupChildNode", with its value an PSObject array wrapping strings for two dupChildNode's142        ///        The outside PSObject will have property,143        ///             a. property "attribute", with its value an PSObject wrapping string "attribute"144        ///     4. Maml formatting xml, this is a special case for Composite xml, for example145        ///         <description attribute="value">146        ///             <para>147        ///                 para 1148        ///             </para>149        ///             <list>150        ///                 <listItem>151        ///                     <para>152        ///                         list item 1153        ///                     </para>154        ///                 </listItem>155        ///                 <listItem>156        ///                     <para>157        ///                         list item 2158        ///                     </para>159        ///                 </listItem>160        ///             </list>161        ///             <definitionList>162        ///                 <definitionListItem>163        ///                     <term>164        ///                         term 1165        ///                     </term>166        ///                     <definition>167        ///                         definition list item 1168        ///                     </definition>169        ///                 </definitionListItem>170        ///                 <definitionListItem>171        ///                     <term>172        ///                         term 2173        ///                     </term>174        ///                     <definition>175        ///                         definition list item 2176        ///                     </definition>177        ///                 </definitionListItem>178        ///             </definitionList>179        ///         </description>180        ///         In this case, an PSObject based on an PSObject array will be created. The inside PSObject array181        ///         will contain following items182        ///             . a MamlParaTextItem based on "para 1"183        ///             . a MamlUnorderedListItem based on "list item 1"184        ///             . a MamlUnorderedListItem based on "list item 2"185        ///             . a MamlDefinitionListItem based on "definition list item 1"186        ///             . a MamlDefinitionListItem based on "definition list item 2"187        ///188        ///         The outside PSObject will have a property189        ///             attribute => "value"190        /// </summary>191        /// <param name="xmlNode"></param>192        /// <returns></returns>193        private PSObject GetPSObject(XmlNode xmlNode)194        {195            if (xmlNode == null)196                return new PSObject();197 198            PSObject mshObject = null;199 200            if (IsAtomic(xmlNode))201            {202                mshObject = new PSObject(xmlNode.InnerText.Trim());203            }204            else if (IncludeMamlFormatting(xmlNode))205            {206                mshObject = new PSObject(GetMamlFormattingPSObjects(xmlNode));207            }208            else209            {210                mshObject = new PSObject(GetInsidePSObject(xmlNode));211                // Add typeNames to this MSHObject and create views so that212                // the output is readable. This is done only for complex nodes.213                mshObject.TypeNames.Clear();214 215                if (xmlNode.Attributes["type"] != null)216                {217                    if (string.Equals(xmlNode.Attributes["type"].Value, "field", StringComparison.OrdinalIgnoreCase))218                        mshObject.TypeNames.Add("MamlPSClassHelpInfo#field");219                    else if (string.Equals(xmlNode.Attributes["type"].Value, "method", StringComparison.OrdinalIgnoreCase))220                        mshObject.TypeNames.Add("MamlPSClassHelpInfo#method");221                }222 223                mshObject.TypeNames.Add("MamlCommandHelpInfo#" + xmlNode.LocalName);224            }225 226            if (xmlNode.Attributes != null)227            {228                foreach (XmlNode attribute in xmlNode.Attributes)229                {230                    mshObject.Properties.Add(new PSNoteProperty(attribute.Name, attribute.Value));231                }232            }233 234            return mshObject;235        }236 237        /// <summary>238        /// Get inside PSObject created based on inside nodes of xmlNode.239        ///240        /// The inside PSObject will be based on null. It will created one241        /// property per inside node grouping by node names.242        ///243        /// For example, for xmlNode like,244        ///     <command>245        ///         <name>get-item</name>246        ///         <note>note 1</note>247        ///         <note>note 2</note>248        ///     </command>249        /// It will create an PSObject based on null, with following two properties250        ///     . property 1: name="name" value=an PSObject to wrap string "get-item"251        ///     . property 2: name="note" value=an PSObject array with following two PSObjects252        ///         1. PSObject wrapping string "note 1"253        ///         2. PSObject wrapping string "note 2"254        /// </summary>255        /// <param name="xmlNode"></param>256        /// <returns></returns>257        private PSObject GetInsidePSObject(XmlNode xmlNode)258        {259            Hashtable properties = GetInsideProperties(xmlNode);260 261            PSObject mshObject = new PSObject();262 263            IDictionaryEnumerator enumerator = properties.GetEnumerator();264 265            while (enumerator.MoveNext())266            {267                mshObject.Properties.Add(new PSNoteProperty((string)enumerator.Key, enumerator.Value));268            }269 270            return mshObject;271        }272 273        /// <summary>274        /// This is for getting inside properties of an XmlNode. Properties are275        /// stored in a hashtable with key as property name and value as property value.276        ///277        /// Inside node with same node names will be grouped into one property with278        /// property value as an array.279        ///280        /// For example, for xmlNode like,281        ///     <command>282        ///         <name>get-item</name>283        ///         <note>note 1</note>284        ///         <note>note 2</note>285        ///     </command>286        /// It will create an PSObject based on null, with following two properties287        ///     . property 1: name="name" value=an PSObject to wrap string "get-item"288        ///     . property 2: name="note" value=an PSObject array with following two PSObjects289        ///         1. PSObject wrapping string "note 1"290        ///         2. PSObject wrapping string "note 2"291        ///292        /// Since we don't know whether an node name will be used more than once,293        /// We are making each property value is an array (PSObject[]) to start with.294        /// At the end, SimplifyProperties will be called to reduce PSObject[] containing295        /// only one element to PSObject itself.296        /// </summary>297        /// <param name="xmlNode"></param>298        /// <returns></returns>299        private Hashtable GetInsideProperties(XmlNode xmlNode)300        {301            Hashtable properties = new Hashtable(StringComparer.OrdinalIgnoreCase);302 303            if (xmlNode == null)304                return properties;305 306            if (xmlNode.ChildNodes != null)307            {308                foreach (XmlNode childNode in xmlNode.ChildNodes)309                {310                    AddProperty(properties, childNode.LocalName, GetPSObject(childNode));311                }312            }313 314            return SimplifyProperties(properties);315        }316 317        /// <summary>318        /// Removes unsupported child nodes recursively from the given319        /// xml node so that they wont spoil the format.320        /// </summary>321        /// <param name="xmlNode">322        /// Node whose children are verified for maml.323        /// </param>324        private static void RemoveUnsupportedNodes(XmlNode xmlNode)325        {326            // Start with the first child..327            // We want to modify only children..328            // The current node is taken care by the callee..329            XmlNode childNode = xmlNode.FirstChild;330            while (childNode != null)331            {332                // We dont want Comments..so remove..333                if (childNode.NodeType == XmlNodeType.Comment)334                {335                    XmlNode nodeToRemove = childNode;336                    childNode = childNode.NextSibling;337                    // Remove this node and its children if any..338                    xmlNode.RemoveChild(nodeToRemove);339                }340                else341                {342                    // Search children...343                    RemoveUnsupportedNodes(childNode);344                    childNode = childNode.NextSibling;345                }346            }347        }348 349        /// <summary>350        /// This is for adding a property into a property hashtable.351        ///352        /// As mentioned in comment of GetInsideProperties, property values stored in353        /// property hashtable is an array to begin with.354        ///355        /// The property value to be added is an mshObject whose base object can be an356        /// PSObject array itself. In that case, each PSObject in the array will be357        /// added separately into the property value array. This case can only happen when358        /// an node with maml formatting node inside is treated. The side effect of this359        /// is that the properties for outside mshObject will be lost. An example of this360        /// is that,361        /// <command>362        ///     <description attrib1="value1">363        ///         <para></para>364        ///         <list></list>365        ///         <definitionList></definitionList>366        ///     </description>367        /// </command>368        /// After the processing, PSObject corresponding to command will have an property369        /// with name "description" and a value of an PSObject array created based on370        /// maml formatting node inside "description" node. The attribute of description node371        /// "attrib1" will be lost. This seems to be OK with current practice of authoring372        /// monad command help.373        /// </summary>374        /// <param name="properties">Property hashtable.</param>375        /// <param name="name">Property name.</param>376        /// <param name="mshObject">Property value.</param>377        private static void AddProperty(Hashtable properties, string name, PSObject mshObject)378        {379            ArrayList propertyValues = (ArrayList)properties[name];380 381            if (propertyValues == null)382            {383                propertyValues = new ArrayList();384 385                properties[name] = propertyValues;386            }387 388            if (mshObject == null)389                return;390 391            if (mshObject.BaseObject is PSCustomObject || !mshObject.BaseObject.GetType().Equals(typeof(PSObject[])))392            {393                propertyValues.Add(mshObject);394                return;395            }396 397            PSObject[] mshObjects = (PSObject[])mshObject.BaseObject;398 399            for (int i = 0; i < mshObjects.Length; i++)400            {401                propertyValues.Add(mshObjects[i]);402            }403 404            return;405        }406 407        /// <summary>408        /// This is for simplifying property value array of only one element.409        ///410        /// As mentioned in comments for GetInsideProperties, this is needed411        /// to reduce an array of only one PSObject into the PSObject itself.412        ///413        /// A side effect of this function is to turn property values from414        /// ArrayList into PSObject[].415        /// </summary>416        /// <param name="properties"></param>417        /// <returns></returns>418        private static Hashtable SimplifyProperties(Hashtable properties)419        {420            if (properties == null)421                return null;422 423            Hashtable result = new Hashtable(StringComparer.OrdinalIgnoreCase);424            IDictionaryEnumerator enumerator = properties.GetEnumerator();425 426            while (enumerator.MoveNext())427            {428                ArrayList propertyValues = (ArrayList)enumerator.Value;429 430                if (propertyValues == null || propertyValues.Count == 0)431                    continue;432 433                if (propertyValues.Count == 1)434                {435                    if (!IsMamlFormattingPSObject((PSObject)propertyValues[0]))436                    {437                        PSObject mshObject = (PSObject)propertyValues[0];438 439                        // Even for strings or other basic types, they need to be contained in PSObject in case440                        // there is attributes for this object.441 442                        result[enumerator.Key] = mshObject;443 444                        continue;445                    }446                }447 448                result[enumerator.Key] = propertyValues.ToArray(typeof(PSObject));449            }450 451            return result;452        }453 454        /// <summary>455        /// An xmlNode is atomic if it contains no structured inside nodes.456        /// </summary>457        /// <param name="xmlNode"></param>458        /// <returns></returns>459        private static bool IsAtomic(XmlNode xmlNode)460        {461            if (xmlNode == null)462                return false;463 464            if (xmlNode.ChildNodes == null)465                return true;466 467            if (xmlNode.ChildNodes.Count > 1)468                return false;469 470            if (xmlNode.ChildNodes.Count == 0)471                return true;472 473            if (xmlNode.ChildNodes[0].GetType().Equals(typeof(XmlText)))474                return true;475 476            return false;477        }478 479        #endregion480 481        #region Maml formatting482 483        /// <summary>484        /// Check whether an xmlNode contains childnodes which is for485        /// maml formatting.486        /// </summary>487        /// <param name="xmlNode"></param>488        /// <returns></returns>489        private static bool IncludeMamlFormatting(XmlNode xmlNode)490        {491            if (xmlNode == null)492                return false;493 494            if (xmlNode.ChildNodes == null || xmlNode.ChildNodes.Count == 0)495                return false;496 497            foreach (XmlNode childNode in xmlNode.ChildNodes)498            {499                if (IsMamlFormattingNode(childNode))500                {501                    return true;502                }503            }504 505            return false;506        }507 508        /// <summary>509        /// Check whether a node is for maml formatting. This include following nodes,510        ///     a. para511        ///     b. list512        ///     c. definitionList.513        /// </summary>514        /// <param name="xmlNode"></param>515        /// <returns></returns>516        private static bool IsMamlFormattingNode(XmlNode xmlNode)517        {518            if (xmlNode.LocalName.Equals("para", StringComparison.OrdinalIgnoreCase))519                return true;520 521            if (xmlNode.LocalName.Equals("list", StringComparison.OrdinalIgnoreCase))522                return true;523 524            if (xmlNode.LocalName.Equals("definitionList", StringComparison.OrdinalIgnoreCase))525                return true;526 527            return false;528        }529 530        /// <summary>531        /// Check whether an mshObject is created from a maml formatting node.532        /// </summary>533        /// <param name="mshObject"></param>534        /// <returns></returns>535        private static bool IsMamlFormattingPSObject(PSObject mshObject)536        {537            Collection<string> typeNames = mshObject.TypeNames;538 539            if (typeNames == null || typeNames.Count == 0)540                return false;541 542            return typeNames[typeNames.Count - 1].Equals("MamlTextItem", StringComparison.OrdinalIgnoreCase);543        }544 545        /// <summary>546        /// Convert an xmlNode containing maml formatting nodes into an PSObject array.547        ///548        /// For example, for node,549        ///    <description attribute="value">550        ///        <para>551        ///            para 1552        ///        </para>553        ///        <list>554        ///            <listItem>555        ///                <para>556        ///                    list item 1557        ///                </para>558        ///            </listItem>559        ///            <listItem>560        ///                <para>561        ///                    list item 2562        ///                </para>563        ///            </listItem>564        ///        </list>565        ///        <definitionList>566        ///            <definitionListItem>567        ///                <term>568        ///                    term 1569        ///                </term>570        ///                <definition>571        ///                    definition list item 1572        ///                </definition>573        ///            </definitionListItem>574        ///            <definitionListItem>575        ///                <term>576        ///                    term 2577        ///                </term>578        ///                <definition>579        ///                    definition list item 2580        ///                </definition>581        ///            </definitionListItem>582        ///        </definitionList>583        ///    </description>584        ///    In this case, an PSObject based on an PSObject array will be created. The inside PSObject array585        ///    will contain following items586        ///        . a MamlParaTextItem based on "para 1"587        ///        . a MamlUnorderedListItem based on "list item 1"588        ///        . a MamlUnorderedListItem based on "list item 2"589        ///        . a MamlDefinitionListItem based on "definition list item 1"590        ///        . a MamlDefinitionListItem based on "definition list item 2"591        /// </summary>592        /// <param name="xmlNode"></param>593        /// <returns></returns>594        private PSObject[] GetMamlFormattingPSObjects(XmlNode xmlNode)595        {596            ArrayList mshObjects = new ArrayList();597 598            int paraNodes = GetParaMamlNodeCount(xmlNode.ChildNodes);599            int count = 0;600            // Don't trim the content if this is an "introduction" node.601            bool trim = !string.Equals(xmlNode.Name, "maml:introduction", StringComparison.OrdinalIgnoreCase);602            foreach (XmlNode childNode in xmlNode.ChildNodes)603            {604                if (childNode.LocalName.Equals("para", StringComparison.OrdinalIgnoreCase))605                {606                    ++count;607                    PSObject paraPSObject = GetParaPSObject(childNode, count != paraNodes, trim: trim);608                    if (paraPSObject != null)609                        mshObjects.Add(paraPSObject);610                    continue;611                }612 613                if (childNode.LocalName.Equals("list", StringComparison.OrdinalIgnoreCase))614                {615                    ArrayList listPSObjects = GetListPSObjects(childNode);616 617                    for (int i = 0; i < listPSObjects.Count; i++)618                    {619                        mshObjects.Add(listPSObjects[i]);620                    }621 622                    continue;623                }624 625                if (childNode.LocalName.Equals("definitionList", StringComparison.OrdinalIgnoreCase))626                {627                    ArrayList definitionListPSObjects = GetDefinitionListPSObjects(childNode);628 629                    for (int i = 0; i < definitionListPSObjects.Count; i++)630                    {631                        mshObjects.Add(definitionListPSObjects[i]);632                    }633 634                    continue;635                }636 637                // If we get here, there is some tags that is not supported by maml.638                WriteMamlInvalidChildNodeError(xmlNode, childNode);639            }640 641            return (PSObject[])mshObjects.ToArray(typeof(PSObject));642        }643 644        /// <summary>645        /// Gets the number of para nodes.646        /// </summary>647        /// <param name="nodes"></param>648        /// <returns></returns>649        private static int GetParaMamlNodeCount(XmlNodeList nodes)650        {651            int i = 0;652 653            foreach (XmlNode childNode in nodes)654            {655                if (childNode.LocalName.Equals("para", StringComparison.OrdinalIgnoreCase))656                {657                    if (childNode.InnerText.Trim().Equals(string.Empty))658                    {659                        continue;660                    }661 662                    ++i;663                }664            }665 666            return i;667        }668 669        /// <summary>670        /// Write an error to helpsystem to indicate an invalid maml child node.671        /// </summary>672        /// <param name="node"></param>673        /// <param name="childNode"></param>674        private void WriteMamlInvalidChildNodeError(XmlNode node, XmlNode childNode)675        {676            ErrorRecord errorRecord = new ErrorRecord(new ParentContainsErrorRecordException("MamlInvalidChildNodeError"), "MamlInvalidChildNodeError", ErrorCategory.SyntaxError, null);677            errorRecord.ErrorDetails = new ErrorDetails(typeof(MamlNode).Assembly, "HelpErrors", "MamlInvalidChildNodeError", node.LocalName, childNode.LocalName, GetNodePath(node));678            this.Errors.Add(errorRecord);679        }680 681        /// <summary>682        /// Write an error to help system to indicate an invalid child node count.683        /// </summary>684        /// <param name="node"></param>685        /// <param name="childNodeName"></param>686        /// <param name="count"></param>687        private void WriteMamlInvalidChildNodeCountError(XmlNode node, string childNodeName, int count)688        {689            ErrorRecord errorRecord = new ErrorRecord(new ParentContainsErrorRecordException("MamlInvalidChildNodeCountError"), "MamlInvalidChildNodeCountError", ErrorCategory.SyntaxError, null);690            errorRecord.ErrorDetails = new ErrorDetails(typeof(MamlNode).Assembly, "HelpErrors", "MamlInvalidChildNodeCountError", node.LocalName, childNodeName, count, GetNodePath(node));691            this.Errors.Add(errorRecord);692        }693 694        private static string GetNodePath(XmlNode xmlNode)695        {696            if (xmlNode == null)697                return string.Empty;698 699            if (xmlNode.ParentNode == null)700                return "\\" + xmlNode.LocalName;701 702            return GetNodePath(xmlNode.ParentNode) + "\\" + xmlNode.LocalName + GetNodeIndex(xmlNode);703        }704 705        private static string GetNodeIndex(XmlNode xmlNode)706        {707            if (xmlNode == null || xmlNode.ParentNode == null)708                return string.Empty;709 710            int index = 0;711            int total = 0;712 713            foreach (XmlNode siblingNode in xmlNode.ParentNode.ChildNodes)714            {715                if (siblingNode == xmlNode)716                {717                    index = total++;718                    continue;719                }720 721                if (siblingNode.LocalName.Equals(xmlNode.LocalName, StringComparison.OrdinalIgnoreCase))722                {723                    total++;724                }725            }726 727            if (total > 1)728            {729                return "[" + index.ToString("d", CultureInfo.CurrentCulture) + "]";730            }731 732            return string.Empty;733        }734 735        /// <summary>736        /// Convert a para node into an mshObject.737        ///738        /// For example,739        ///    <para>740        ///        para text741        ///    </para>742        ///    In this case, an PSObject of type "MamlParaTextItem" will be created with following property743        ///        a. text="para text"744        /// </summary>745        /// <param name="xmlNode"></param>746        /// <param name="newLine"></param>747        /// <param name="trim"></param>748        /// <returns></returns>749        private static PSObject GetParaPSObject(XmlNode xmlNode, bool newLine, bool trim = true)750        {751            if (xmlNode == null)752                return null;753 754            if (!xmlNode.LocalName.Equals("para", StringComparison.OrdinalIgnoreCase))755                return null;756 757            PSObject mshObject = new PSObject();758 759            StringBuilder sb = new StringBuilder();760 761            if (newLine && !xmlNode.InnerText.Trim().Equals(string.Empty))762            {763                sb.AppendLine(xmlNode.InnerText.Trim());764            }765            else766            {767                var innerText = xmlNode.InnerText;768                if (trim)769                {770                    innerText = innerText.Trim();771                }772 773                sb.Append(innerText);774            }775 776            mshObject.Properties.Add(new PSNoteProperty("Text", sb.ToString()));777 778            mshObject.TypeNames.Clear();779            mshObject.TypeNames.Add("MamlParaTextItem");780            mshObject.TypeNames.Add("MamlTextItem");781 782            return mshObject;783        }784 785        /// <summary>786        /// Convert a list node into an PSObject array.787        ///788        /// For example,789        ///    <list class="ordered">790        ///        <listItem>791        ///            <para>792        ///                text for list item 1793        ///            </para>794        ///        </listItem>795        ///        <listItem>796        ///            <para>797        ///                text for list item 2798        ///            </para>799        ///        </listItem>800        ///    </list>801        /// In this case, an array of PSObject, each of type "MamlOrderedListText" will be created with following802        /// two properties,803        ///        a. tag=" 1. " or " 2. "804        ///        b. text="text for list item 1" or "text for list item 2"805        /// In the case of unordered list, similar PSObject will created with type to be "MamlUnorderedListText" and tag="*"806        /// </summary>807        /// <param name="xmlNode"></param>808        /// <returns></returns>809        private ArrayList GetListPSObjects(XmlNode xmlNode)810        {811            ArrayList mshObjects = new ArrayList();812 813            if (xmlNode == null)814                return mshObjects;815 816            if (!xmlNode.LocalName.Equals("list", StringComparison.OrdinalIgnoreCase))817                return mshObjects;818 819            if (xmlNode.ChildNodes == null || xmlNode.ChildNodes.Count == 0)820                return mshObjects;821 822            bool ordered = IsOrderedList(xmlNode);823            int index = 1;824 825            foreach (XmlNode childNode in xmlNode.ChildNodes)826            {827                if (childNode.LocalName.Equals("listItem", StringComparison.OrdinalIgnoreCase))828                {829                    PSObject listItemPSObject = GetListItemPSObject(childNode, ordered, ref index);830 831                    if (listItemPSObject != null)832                        mshObjects.Add(listItemPSObject);833 834                    continue;835                }836 837                // If we get here, there is some tags that is not supported by maml.838                WriteMamlInvalidChildNodeError(xmlNode, childNode);839            }840 841            return mshObjects;842        }843 844        /// <summary>845        /// Check whether a list is ordered or not.846        /// </summary>847        /// <param name="xmlNode"></param>848        /// <returns></returns>849        private static bool IsOrderedList(XmlNode xmlNode)850        {851            if (xmlNode == null)852                return false;853 854            if (xmlNode.Attributes == null || xmlNode.Attributes.Count == 0)855                return false;856 857            foreach (XmlNode attribute in xmlNode.Attributes)858            {859                if (attribute.Name.Equals("class", StringComparison.OrdinalIgnoreCase)860                    && attribute.Value.Equals("ordered", StringComparison.OrdinalIgnoreCase))861                {862                    return true;863                }864            }865 866            return false;867        }868 869        /// <summary>870        /// Convert an listItem node into an PSObject with property "tag" and "text"871        /// </summary>872        /// <param name="xmlNode"></param>873        /// <param name="ordered"></param>874        /// <param name="index"></param>875        /// <returns></returns>876        private PSObject GetListItemPSObject(XmlNode xmlNode, bool ordered, ref int index)877        {878            if (xmlNode == null)879                return null;880 881            if (!xmlNode.LocalName.Equals("listItem", StringComparison.OrdinalIgnoreCase))882                return null;883 884            string text = string.Empty;885 886            if (xmlNode.ChildNodes.Count > 1)887            {888                WriteMamlInvalidChildNodeCountError(xmlNode, "para", 1);889            }890 891            foreach (XmlNode childNode in xmlNode.ChildNodes)892            {893                if (childNode.LocalName.Equals("para", StringComparison.OrdinalIgnoreCase))894                {895                    text = childNode.InnerText.Trim();896                    continue;897                }898 899                WriteMamlInvalidChildNodeError(xmlNode, childNode);900            }901 902            string tag = string.Empty;903            if (ordered)904            {905                tag = index.ToString("d2", CultureInfo.CurrentCulture);906                tag += ". ";907                index++;908            }909            else910            {911                tag = "* ";912            }913 914            PSObject mshObject = new PSObject();915 916            mshObject.Properties.Add(new PSNoteProperty("Text", text));917            mshObject.Properties.Add(new PSNoteProperty("Tag", tag));918 919            mshObject.TypeNames.Clear();920            if (ordered)921            {922                mshObject.TypeNames.Add("MamlOrderedListTextItem");923            }924            else925            {926                mshObject.TypeNames.Add("MamlUnorderedListTextItem");927            }928 929            mshObject.TypeNames.Add("MamlTextItem");930 931            return mshObject;932        }933 934        /// <summary>935        /// Convert definitionList node into an array of PSObject, an for936        /// each definitionListItem node inside this node.937        /// </summary>938        /// <param name="xmlNode"></param>939        /// <returns></returns>940        private ArrayList GetDefinitionListPSObjects(XmlNode xmlNode)941        {942            ArrayList mshObjects = new ArrayList();943 944            if (xmlNode == null)945                return mshObjects;946 947            if (!xmlNode.LocalName.Equals("definitionList", StringComparison.OrdinalIgnoreCase))948                return mshObjects;949 950            if (xmlNode.ChildNodes == null || xmlNode.ChildNodes.Count == 0)951                return mshObjects;952 953            foreach (XmlNode childNode in xmlNode.ChildNodes)954            {955                if (childNode.LocalName.Equals("definitionListItem", StringComparison.OrdinalIgnoreCase))956                {957                    PSObject definitionListItemPSObject = GetDefinitionListItemPSObject(childNode);958 959                    if (definitionListItemPSObject != null)960                        mshObjects.Add(definitionListItemPSObject);961 962                    continue;963                }964 965                // If we get here, we found some node that is not supported.966                WriteMamlInvalidChildNodeError(xmlNode, childNode);967            }968 969            return mshObjects;970        }971 972        /// <summary>973        /// Convert an definitionListItem node into an PSObject974        ///975        /// For example976        ///        <definitionListItem>977        ///            <term>978        ///                term text979        ///            </term>980        ///            <definition>981        ///                <para>982        ///                    definition text983        ///                </para>984        ///            </definition>985        ///        </definitionListItem>986        /// In this case, an PSObject of type "definitionListText" will be created with following987        /// properties988        ///        a. term="term text"989        ///        b. definition="definition text"990        /// </summary>991        /// <param name="xmlNode"></param>992        /// <returns></returns>993        private PSObject GetDefinitionListItemPSObject(XmlNode xmlNode)994        {995            if (xmlNode == null)996                return null;997 998            if (!xmlNode.LocalName.Equals("definitionListItem", StringComparison.OrdinalIgnoreCase))999                return null;1000 1001            string term = null;1002            string definition = null;1003 1004            foreach (XmlNode childNode in xmlNode.ChildNodes)1005            {1006                if (childNode.LocalName.Equals("term", StringComparison.OrdinalIgnoreCase))1007                {1008                    term = childNode.InnerText.Trim();1009                    continue;1010                }1011 1012                if (childNode.LocalName.Equals("definition", StringComparison.OrdinalIgnoreCase))1013                {1014                    definition = GetDefinitionText(childNode);1015                    continue;1016                }1017 1018                // If we get here, we found some node that is not supported.1019                WriteMamlInvalidChildNodeError(xmlNode, childNode);1020            }1021 1022            if (string.IsNullOrEmpty(term))1023                return null;1024 1025            PSObject mshObject = new PSObject();1026 1027            mshObject.Properties.Add(new PSNoteProperty("Term", term));1028            mshObject.Properties.Add(new PSNoteProperty("Definition", definition));1029 1030            mshObject.TypeNames.Clear();1031            mshObject.TypeNames.Add("MamlDefinitionTextItem");1032            mshObject.TypeNames.Add("MamlTextItem");1033 1034            return mshObject;1035        }1036 1037        /// <summary>1038        /// Get the text for definition. The will treat some intermediate nodes like "definition" and "para"1039        /// </summary>1040        /// <param name="xmlNode"></param>1041        /// <returns></returns>1042        private string GetDefinitionText(XmlNode xmlNode)1043        {1044            if (xmlNode == null)1045                return null;1046 1047            if (!xmlNode.LocalName.Equals("definition", StringComparison.OrdinalIgnoreCase))1048                return null;1049 1050            if (xmlNode.ChildNodes == null || xmlNode.ChildNodes.Count == 0)1051                return string.Empty;1052 1053            if (xmlNode.ChildNodes.Count > 1)1054            {1055                WriteMamlInvalidChildNodeCountError(xmlNode, "para", 1);1056            }1057 1058            string text = string.Empty;1059 1060            foreach (XmlNode childNode in xmlNode.ChildNodes)1061            {1062                if (childNode.LocalName.Equals("para", StringComparison.OrdinalIgnoreCase))1063                {1064                    text = childNode.InnerText.Trim();1065                    continue;1066                }1067 1068                WriteMamlInvalidChildNodeError(xmlNode, childNode);1069            }1070 1071            return text;1072        }1073 1074        #endregion1075 1076        #region Preformatted string processing1077 1078        /// <summary>1079        /// This is for getting preformatted text from an xml document.1080        ///1081        /// Normally in xml document, preformatted text will be indented by1082        /// a fix amount based on its position. The task of this function1083        /// is to remove that fixed amount from the text.1084        ///1085        /// For example, in xml,1086        /// <preformatted>1087        ///     void function()1088        ///     {1089        ///         // call some other function here;1090        ///     }1091        /// </preformatted>1092        /// we can find that the preformatted text are indented unanimously1093        /// by 4 spaces because of its position in xml.1094        ///1095        /// After massaging in this function, the result text will be,1096        ///1097        /// void function1098        /// {1099        ///     // call some other function here;1100        /// }1101        ///1102        /// please notice that the indention is reduced.1103        /// </summary>1104        /// <param name="text"></param>1105        /// <returns></returns>1106        private static string GetPreformattedText(string text)1107        {1108            // we are assuming tabsize=4 here.1109            // It is discouraged to use tab in preformatted text.1110 1111            string noTabText = text.Replace("\t", "    ");1112            string[] lines = noTabText.Split('\n');1113            string[] trimedLines = TrimLines(lines);1114 1115            if (trimedLines == null || trimedLines.Length == 0)1116                return string.Empty;1117 1118            int minIndentation = GetMinIndentation(trimedLines);1119 1120            string[] shortedLines = new string[trimedLines.Length];1121            for (int i = 0; i < trimedLines.Length; i++)1122            {1123                if (IsEmptyLine(trimedLines[i]))1124                {1125                    shortedLines[i] = trimedLines[i];1126                }1127                else1128                {1129                    shortedLines[i] = trimedLines[i].Remove(0, minIndentation);1130                }1131            }1132 1133            StringBuilder result = new StringBuilder();1134            for (int i = 0; i < shortedLines.Length; i++)1135            {1136                result.AppendLine(shortedLines[i]);1137            }1138 1139            return result.ToString();1140        }1141 1142        /// <summary>1143        /// Trim empty lines from the either end of an string array.1144        /// </summary>1145        /// <param name="lines">Lines to trim.</param>1146        /// <returns>An string array with empty lines trimed on either end.</returns>1147        private static string[] TrimLines(string[] lines)1148        {1149            if (lines == null || lines.Length == 0)1150                return null;1151 1152            int i = 0;1153            for (i = 0; i < lines.Length; i++)1154            {1155                if (!IsEmptyLine(lines[i]))1156                    break;1157            }1158 1159            int start = i;1160 1161            if (start == lines.Length)1162                return null;1163 1164            for (i = lines.Length - 1; i >= start; i--)1165            {1166                if (!IsEmptyLine(lines[i]))1167                    break;1168            }1169 1170            int end = i;1171 1172            string[] result = new string[end - start + 1];1173            for (i = start; i <= end; i++)1174            {1175                result[i - start] = lines[i];1176            }1177 1178            return result;1179        }1180 1181        /// <summary>1182        /// Get minimum indentation of a paragraph.1183        /// </summary>1184        /// <param name="lines"></param>1185        /// <returns></returns>1186        private static int GetMinIndentation(string[] lines)1187        {1188            int minIndentation = -1;1189 1190            for (int i = 0; i < lines.Length; i++)1191            {1192                if (IsEmptyLine(lines[i]))1193                    continue;1194 1195                int indentation = GetIndentation(lines[i]);1196 1197                if (minIndentation < 0 || indentation < minIndentation)1198                    minIndentation = indentation;1199            }1200 

Showing the first 1,200 of 1253 lines. Download the file for the rest.