The Labs \ Source Viewer \ SSCLI \ System.Xml.Xsl.Qil \ NameAnnotation

  1. //------------------------------------------------------------------------------
  2. // <copyright file="QilXmlWriter.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using System.Globalization;
  20. using System.Text;
  21. using System.Xml;
  22. namespace System.Xml.Xsl.Qil
  23. {
  24.    
  25.     /// <summary>
  26.     /// If an annotation implements this interface, then QilXmlWriter will call ToString() on the annotation
  27.     /// and serialize the result (if non-empty).
  28.     /// </summary>
  29.     interface IQilAnnotation
  30.     {
  31.         string Name {
  32.             get;
  33.         }
  34.     }
  35.    
  36.    
  37.     /// <summary>
  38.     /// An example of QilVisitor. Prints the QilExpression tree as XML.
  39.     /// </summary>
  40.     /// <remarks>
  41.     /// <para>The QilXmlWriter Visits every node in the tree, printing out an XML representation of
  42.     /// each node. Several formatting options are available, including whether or not to include annotations
  43.     /// and type information. When full information is printed out, the graph can be reloaded from
  44.     /// its serialized form using QilXmlReader.</para>
  45.     /// <para>The XML format essentially uses one XML element for each node in the QIL graph.
  46.     /// Node properties such as type information are serialized as XML attributes.
  47.     /// Annotations are serialized as processing-instructions in front of a node.</para>
  48.     /// <para>Feel free to subclass this visitor to customize its behavior.</para>
  49.     /// </remarks>
  50.     internal class QilXmlWriter : QilScopedVisitor
  51.     {
  52.         protected XmlWriter writer;
  53.         protected Options options;
  54.         private NameGenerator ngen;
  55.        
  56.         [Flags()]
  57.         public enum Options
  58.         {
  59.             None = 0,
  60.             // No options selected
  61.             Annotations = 1,
  62.             // Print annotations
  63.             TypeInfo = 2,
  64.             // Print type information using "G" option
  65.             RoundTripTypeInfo = 4,
  66.             // Print type information using "S" option
  67.             LineInfo = 8,
  68.             // Print source line information
  69.             NodeIdentity = 16,
  70.             // Print node identity (only works if QIL_TRACE_NODE_CREATION is defined)
  71.             NodeLocation = 32
  72.             // Print node creation location (only works if QIL_TRACE_NODE_CREATION is defined)
  73.         }
  74.        
  75.         /// <summary>
  76.         /// Construct a QilXmlWriter.
  77.         /// </summary>
  78.         public QilXmlWriter(XmlWriter writer) : this(writer, Options.Annotations | Options.TypeInfo | Options.LineInfo | Options.NodeIdentity | Options.NodeLocation)
  79.         {
  80.         }
  81.        
  82.         /// <summary>
  83.         /// Construct a QilXmlWriter.
  84.         /// </summary>
  85.         public QilXmlWriter(XmlWriter writer, Options options)
  86.         {
  87.             this.writer = writer;
  88.             this.ngen = new NameGenerator();
  89.             this.options = options;
  90.         }
  91.        
  92.         /// <summary>
  93.         /// Serialize a QilExpression graph as XML.
  94.         /// </summary>
  95.         /// <param name="q">the QilExpression graph</param>
  96.         public void ToXml(QilNode node)
  97.         {
  98.             VisitAssumeReference(node);
  99.         }
  100.        
  101.         //-----------------------------------------------
  102.         // QilXmlWrite methods
  103.         //-----------------------------------------------
  104.        
  105.         /// <summary>
  106.         /// Write all annotations as comments:
  107.         /// 1. string -- <!-- (string) ann -->
  108.         /// 2. IQilAnnotation -- <!-- ann.Name = ann.ToString() -->
  109.         /// 3. IList<object> -- recursively call WriteAnnotations for each object in list
  110.         /// 4. otherwise, do not write the annotation
  111.         /// </summary>
  112.         protected virtual void WriteAnnotations(object ann)
  113.         {
  114.             string s = null;
  115.             string name = null;
  116.            
  117.             if (ann == null) {
  118.                 return;
  119.             }
  120.             else if (ann is string) {
  121.                 s = ann as string;
  122.             }
  123.             else if (ann is IQilAnnotation) {
  124.                 // Get annotation's name and string value
  125.                 IQilAnnotation qilann = ann as IQilAnnotation;
  126.                 name = qilann.Name;
  127.                 s = ann.ToString();
  128.             }
  129.             else if (ann is IList<object>) {
  130.                 IList<object> list = (IList<object>)ann;
  131.                
  132.                 foreach (object annItem in list)
  133.                     WriteAnnotations(annItem);
  134.                 return;
  135.             }
  136.            
  137.             if (s != null && s.Length != 0)
  138.                 this.writer.WriteComment(name != null && name.Length != 0 ? name + ": " + s : s);
  139.         }
  140.        
  141.         /// <summary>
  142.         /// Called in order to write out source line information.
  143.         /// </summary>
  144.         protected virtual void WriteLineInfo(QilNode node)
  145.         {
  146.             this.writer.WriteAttributeString("lineInfo", string.Format(CultureInfo.InvariantCulture, "[{0},{1} -- {2},{3}]", node.SourceLine.StartLine, node.SourceLine.StartPos, node.SourceLine.EndLine, node.SourceLine.EndPos));
  147.         }
  148.        
  149.         /// <summary>
  150.         /// Called in order to write out the xml type of a node.
  151.         /// </summary>
  152.         protected virtual void WriteXmlType(QilNode node)
  153.         {
  154.             this.writer.WriteAttributeString("xmlType", node.XmlType.ToString((this.options & Options.RoundTripTypeInfo) != 0 ? "S" : "G"));
  155.         }
  156.        
  157.        
  158.         //-----------------------------------------------
  159.         // QilVisitor overrides
  160.         //-----------------------------------------------
  161.        
  162.         /// <summary>
  163.         /// Override certain node types in order to add additional attributes, suppress children, etc.
  164.         /// </summary>
  165.         protected override QilNode VisitChildren(QilNode node)
  166.         {
  167.             if (node is QilLiteral) {
  168.                 // If literal is not handled elsewhere, print its string value
  169.                 this.writer.WriteString(((QilLiteral)node).Value.ToString());
  170.                 return node;
  171.             }
  172.             else if (node is QilReference) {
  173.                 QilReference reference = (QilReference)node;
  174.                
  175.                 // Write the generated identifier for this iterator
  176.                 this.writer.WriteAttributeString("id", this.ngen.NameOf(node));
  177.                
  178.                 // Write the debug name of this reference (if it's defined) as a "name" attribute
  179.                 if (reference.DebugName != null)
  180.                     this.writer.WriteAttributeString("name", reference.DebugName.ToString());
  181.                
  182.                 if (node.NodeType == QilNodeType.Parameter) {
  183.                     // Don't visit parameter's name, or its default value if it is null
  184.                     QilParameter param = (QilParameter)node;
  185.                    
  186.                     if (param.DefaultValue != null)
  187.                         Visit(param.DefaultValue);
  188.                    
  189.                     return node;
  190.                 }
  191.             }
  192.            
  193.             return base.VisitChildren(node);
  194.         }
  195.        
  196.         /// <summary>
  197.         /// Write references to functions or iterators like this: <RefTo id="$a"/>.
  198.         /// </summary>
  199.         protected override QilNode VisitReference(QilNode node)
  200.         {
  201.             QilReference reference = (QilReference)node;
  202.             string name = ngen.NameOf(node);
  203.             if (name == null)
  204.                 name = "OUT-OF-SCOPE REFERENCE";
  205.            
  206.             this.writer.WriteStartElement("RefTo");
  207.             this.writer.WriteAttributeString("id", name);
  208.             if (reference.DebugName != null)
  209.                 this.writer.WriteAttributeString("name", reference.DebugName.ToString());
  210.             this.writer.WriteEndElement();
  211.            
  212.             return node;
  213.         }
  214.        
  215.         /// <summary>
  216.         /// Scan through the external parameters, global variables, and function list for forward references.
  217.         /// </summary>
  218.         protected override QilNode VisitQilExpression(QilExpression qil)
  219.         {
  220.             IList<QilNode> fdecls = new ForwardRefFinder().Find(qil);
  221.             if (fdecls != null && fdecls.Count > 0) {
  222.                 this.writer.WriteStartElement("ForwardDecls");
  223.                 foreach (QilNode n in fdecls) {
  224.                     // i.e. <Function id="$a"/>
  225.                     this.writer.WriteStartElement(Enum.GetName(typeof(QilNodeType), n.NodeType));
  226.                     this.writer.WriteAttributeString("id", this.ngen.NameOf(n));
  227.                     WriteXmlType(n);
  228.                    
  229.                     if (n.NodeType == QilNodeType.Function) {
  230.                         // Visit Arguments and SideEffects operands
  231.                         Visit(n[0]);
  232.                         Visit(n[2]);
  233.                     }
  234.                    
  235.                     this.writer.WriteEndElement();
  236.                 }
  237.                 this.writer.WriteEndElement();
  238.             }
  239.            
  240.             return VisitChildren(qil);
  241.         }
  242.        
  243.         /// <summary>
  244.         /// Serialize literal types using either "S" or "G" formatting, depending on the option which has been set.
  245.         /// </summary>
  246.         protected override QilNode VisitLiteralType(QilLiteral value)
  247.         {
  248.             this.writer.WriteString(((XmlQueryType)value).ToString((this.options & Options.TypeInfo) != 0 ? "G" : "S"));
  249.             return value;
  250.         }
  251.        
  252.         /// <summary>
  253.         /// Serialize literal QName as three separate attributes.
  254.         /// </summary>
  255.         protected override QilNode VisitLiteralQName(QilName value)
  256.         {
  257.             this.writer.WriteAttributeString("name", value.ToString());
  258.             return value;
  259.         }
  260.        
  261.        
  262.         //-----------------------------------------------
  263.         // QilScopedVisitor overrides
  264.         //-----------------------------------------------
  265.        
  266.         /// <summary>
  267.         /// Annotate this iterator or function with a generated name.
  268.         /// </summary>
  269.         protected override void BeginScope(QilNode node)
  270.         {
  271.             this.ngen.NameOf(node);
  272.         }
  273.        
  274.         /// <summary>
  275.         /// Clear the name annotation on this iterator or function.
  276.         /// </summary>
  277.         protected override void EndScope(QilNode node)
  278.         {
  279.             this.ngen.ClearName(node);
  280.         }
  281.        
  282.         /// <summary>
  283.         /// By default, call WriteStartElement for every node type.
  284.         /// </summary>
  285.         protected override void BeforeVisit(QilNode node)
  286.         {
  287.             base.BeforeVisit(node);
  288.            
  289.             // Write the annotations in front of the element, to avoid issues with attributes
  290.             // and make it easier to round-trip
  291.             if ((this.options & Options.Annotations) != 0)
  292.                 WriteAnnotations(node.Annotation);
  293.            
  294.             // Call WriteStartElement
  295.             this.writer.WriteStartElement("", Enum.GetName(typeof(QilNodeType), node.NodeType), "");
  296.            
  297.             // Write common attributes
  298.             #if QIL_TRACE_NODE_CREATION
  299.             if ((this.options & Options.NodeIdentity) != 0)
  300.                 this.writer.WriteAttributeString("nodeId", node.NodeId.ToString());
  301.            
  302.             if ((this.options & Options.NodeLocation) != 0)
  303.                 this.writer.WriteAttributeString("nodeLoc", node.NodeLocation);
  304.             #endif
  305.             if ((this.options & (Options.TypeInfo | Options.RoundTripTypeInfo)) != 0)
  306.                 WriteXmlType(node);
  307.            
  308.             if ((this.options & Options.LineInfo) != 0 && node.SourceLine != null)
  309.                 WriteLineInfo(node);
  310.         }
  311.        
  312.         /// <summary>
  313.         /// By default, call WriteEndElement for every node type.
  314.         /// </summary>
  315.         protected override void AfterVisit(QilNode node)
  316.         {
  317.             this.writer.WriteEndElement();
  318.            
  319.             base.AfterVisit(node);
  320.         }
  321.        
  322.        
  323.         //-----------------------------------------------
  324.         // Helper methods
  325.         //-----------------------------------------------
  326.        
  327.         /// <summary>
  328.         /// Find list of all iterators and functions which are referenced before they have been declared.
  329.         /// </summary>
  330.         internal class ForwardRefFinder : QilVisitor
  331.         {
  332.             private List<QilNode> fwdrefs = new List<QilNode>();
  333.             private List<QilNode> backrefs = new List<QilNode>();
  334.            
  335.             public IList<QilNode> Find(QilExpression qil)
  336.             {
  337.                 Visit(qil);
  338.                 return this.fwdrefs;
  339.             }
  340.            
  341.             /// <summary>
  342.             /// Add iterators and functions to backrefs list as they are visited.
  343.             /// </summary>
  344.             protected override QilNode Visit(QilNode node)
  345.             {
  346.                 if (node is QilIterator || node is QilFunction)
  347.                     this.backrefs.Add(node);
  348.                
  349.                 return base.Visit(node);
  350.             }
  351.            
  352.             /// <summary>
  353.             /// If reference is not in scope, then it must be a forward reference.
  354.             /// </summary>
  355.             protected override QilNode VisitReference(QilNode node)
  356.             {
  357.                 if (!this.backrefs.Contains(node) && !this.fwdrefs.Contains(node))
  358.                     this.fwdrefs.Add(node);
  359.                
  360.                 return node;
  361.             }
  362.         }
  363.        
  364.         //=================================== Helper class: NameGenerator =========================================
  365.        
  366.         private sealed class NameGenerator
  367.         {
  368.             StringBuilder name;
  369.             int len;
  370.             int zero;
  371.             char start;
  372.             char end;
  373.            
  374.             /// <summary>
  375.             /// Construct a new name generator with prefix "$" and alphabetical mode.
  376.             /// </summary>
  377.             public NameGenerator()
  378.             {
  379.                 string prefix = "$";
  380.                 len = zero = prefix.Length;
  381.                 start = 'a';
  382.                 end = 'z';
  383.                 name = new StringBuilder(prefix, len + 2);
  384.                 name.Append(start);
  385.             }
  386.            
  387.             /// <summary>
  388.             /// Skolem function for names.
  389.             /// </summary>
  390.             /// <returns>a unique name beginning with the prefix</returns>
  391.             public string NextName()
  392.             {
  393.                 string result = name.ToString();
  394.                
  395.                 char c = name[len];
  396.                 if (c == end) {
  397.                     name[len] = start;
  398.                     int i = len;
  399.                     for (; i-- > zero && name[i] == end;)
  400.                         name[i] = start;
  401.                    
  402.                     if (i < zero) {
  403.                         len++;
  404.                         name.Append(start);
  405.                     }
  406.                     else
  407.                         name[i]++;
  408.                 }
  409.                 else
  410.                     name[len] = ++c;
  411.                
  412.                 return result;
  413.             }
  414.            
  415.             /// <summary>
  416.             /// Lookup or generate a name for a node. Uses annotations to store the name on the node.
  417.             /// </summary>
  418.             /// <param name="i">the node</param>
  419.             /// <returns>the node name (unique across nodes)</returns>
  420.             public string NameOf(QilNode n)
  421.             {
  422.                 string name = null;
  423.                
  424.                 object old = n.Annotation;
  425.                 NameAnnotation a = old as NameAnnotation;
  426.                 if (a == null) {
  427.                     name = NextName();
  428.                     n.Annotation = new NameAnnotation(name, old);
  429.                 }
  430.                 else {
  431.                     name = a.Name;
  432.                 }
  433.                 return name;
  434.             }
  435.            
  436.             /// <summary>
  437.             /// Clear name annotation from a node.
  438.             /// </summary>
  439.             /// <param name="n">the node</param>
  440.             public void ClearName(QilNode n)
  441.             {
  442.                 if (n.Annotation is NameAnnotation)
  443.                     n.Annotation = ((NameAnnotation)n.Annotation).PriorAnnotation;
  444.             }
  445.            
  446.             /// <summary>
  447.             /// Class used to hold our annotations on the graph
  448.             /// </summary>
  449.             private class NameAnnotation : ListBase<object>
  450.             {
  451.                 public string Name;
  452.                 public object PriorAnnotation;
  453.                
  454.                 public NameAnnotation(string s, object a)
  455.                 {
  456.                     Name = s;
  457.                     PriorAnnotation = a;
  458.                 }
  459.                
  460.                 public override int Count {
  461.                     get { return 1; }
  462.                 }
  463.                
  464.                 public override object this[int index]
  465.                 {
  466.                     get {
  467.                         if (index == 0)
  468.                             return PriorAnnotation;
  469.                        
  470.                         throw new IndexOutOfRangeException();
  471.                     }
  472.                     set {
  473.                         throw new NotSupportedException();
  474.                     }
  475.                 }
  476.             }
  477.         }
  478.     }
  479. }

Developer Fusion