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

  1. //------------------------------------------------------------------------------
  2. // <copyright file="QilXmlReader.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.IO;
  20. using System.Globalization;
  21. using System.Text;
  22. using System.Text.RegularExpressions;
  23. using System.Reflection;
  24. using System.Xml;
  25. using System.Xml.Schema;
  26. using System.Xml.Xsl;
  27. namespace System.Xml.Xsl.Qil
  28. {
  29.    
  30.     /// <summary>
  31.     /// Read the output of QilXmlWriter.
  32.     /// </summary>
  33.     /// <remarks>This internal class allows roundtripping between the Xml serialization format for
  34.     /// QIL and the in-memory data structure.</remarks>
  35.     internal sealed class QilXmlReader
  36.     {
  37.         private static Regex lineInfoRegex = new Regex("\\[(\\d+),(\\d+) -- (\\d+),(\\d+)\\]");
  38.         private static Regex typeInfoRegex = new Regex("(\\w+);([\\w|\\|]+);(\\w+)");
  39.         private static Dictionary<string, MethodInfo> nameToFactoryMethod;
  40.        
  41.         private QilFactory f;
  42.         private XmlReader r;
  43.         private Stack<QilList> stk;
  44.         private bool inFwdDecls;
  45.         private Dictionary<string, QilNode> scope, fwdDecls;
  46.        
  47.         static QilXmlReader()
  48.         {
  49.             nameToFactoryMethod = new Dictionary<string, MethodInfo>();
  50.            
  51.             // Build table that maps QilNodeType name to factory method info
  52.             foreach (MethodInfo mi in typeof(QilFactory).GetMethods(BindingFlags.Public | BindingFlags.Instance)) {
  53.                 ParameterInfo[] parms = mi.GetParameters();
  54.                 int i;
  55.                
  56.                 // Only match methods that take QilNode parameters
  57.                 for (i = 0; i < parms.Length; i++) {
  58.                     if (parms[i].ParameterType != typeof(QilNode))
  59.                         break;
  60.                 }
  61.                
  62.                 if (i == parms.Length) {
  63.                     // Enter the method that takes the maximum number of parameters
  64.                     if (!nameToFactoryMethod.ContainsKey(mi.Name) || nameToFactoryMethod[mi.Name].GetParameters().Length < parms.Length)
  65.                         nameToFactoryMethod[mi.Name] = mi;
  66.                 }
  67.             }
  68.         }
  69.        
  70.         public QilXmlReader(XmlReader r)
  71.         {
  72.             this.r = r;
  73.             this.f = new QilFactory();
  74.         }
  75.        
  76.         public QilExpression Read()
  77.         {
  78.             this.stk = new Stack<QilList>();
  79.             this.inFwdDecls = false;
  80.             this.scope = new Dictionary<string, QilNode>();
  81.             this.fwdDecls = new Dictionary<string, QilNode>();
  82.            
  83.             this.stk.Push(f.Sequence());
  84.            
  85.             while (r.Read()) {
  86.                 switch (r.NodeType) {
  87.                     case XmlNodeType.Element:
  88.                         bool emptyElem = r.IsEmptyElement;
  89.                        
  90.                         // XmlReader does not give an event for empty elements, so synthesize one
  91.                         if (StartElement() && emptyElem)
  92.                             EndElement();
  93.                         break;
  94.                     case XmlNodeType.EndElement:
  95.                        
  96.                         EndElement();
  97.                         break;
  98.                     case XmlNodeType.Whitespace:
  99.                     case XmlNodeType.SignificantWhitespace:
  100.                     case XmlNodeType.XmlDeclaration:
  101.                     case XmlNodeType.Comment:
  102.                     case XmlNodeType.ProcessingInstruction:
  103.                        
  104.                         break;
  105.                     default:
  106.                        
  107.                         Debug.Fail("Unexpected event " + r.NodeType + ", value " + r.Value);
  108.                         break;
  109.                 }
  110.             }
  111.            
  112.             Debug.Assert(this.fwdDecls.Keys.Count == 0, "One or more forward declarations were never defined");
  113.             Debug.Assert(this.stk.Peek()[0].NodeType == QilNodeType.QilExpression, "Serialized qil tree did not contain a QilExpression node");
  114.             return (QilExpression)this.stk.Peek()[0];
  115.         }
  116.        
  117.         private bool StartElement()
  118.         {
  119.             QilNode nd;
  120.             ReaderAnnotation ann = new ReaderAnnotation();
  121.             string s;
  122.            
  123.             // Special case certain element names
  124.             s = r.LocalName;
  125.             switch (r.LocalName) {
  126.                 case "LiteralString":
  127.                     nd = f.LiteralString(ReadText());
  128.                     break;
  129.                 case "LiteralInt32":
  130.                    
  131.                     nd = f.LiteralInt32(Int32.Parse(ReadText(), CultureInfo.InvariantCulture));
  132.                     break;
  133.                 case "LiteralInt64":
  134.                    
  135.                     nd = f.LiteralInt64(Int64.Parse(ReadText(), CultureInfo.InvariantCulture));
  136.                     break;
  137.                 case "LiteralDouble":
  138.                    
  139.                     nd = f.LiteralDouble(Double.Parse(ReadText(), CultureInfo.InvariantCulture));
  140.                     break;
  141.                 case "LiteralDecimal":
  142.                    
  143.                     nd = f.LiteralDecimal(Decimal.Parse(ReadText(), CultureInfo.InvariantCulture));
  144.                     break;
  145.                 case "LiteralType":
  146.                    
  147.                     nd = f.LiteralType(ParseType(ReadText()));
  148.                     break;
  149.                 case "LiteralQName":
  150.                    
  151.                     nd = ParseName(r.GetAttribute("name"));
  152.                     Debug.Assert(nd != null, "LiteralQName element must have a name attribute");
  153.                     Debug.Assert(r.IsEmptyElement, "LiteralQName element must be empty");
  154.                     break;
  155.                 case "For":
  156.                 case "Let":
  157.                 case "Parameter":
  158.                 case "Function":
  159.                 case "RefTo":
  160.                    
  161.                     ann.Id = r.GetAttribute("id");
  162.                     ann.Name = ParseName(r.GetAttribute("name"));
  163.                     goto default;
  164.                     break;
  165.                 case "XsltInvokeEarlyBound":
  166.                    
  167.                     ann.ClrNamespace = r.GetAttribute("clrNamespace");
  168.                     goto default;
  169.                     break;
  170.                 case "ForwardDecls":
  171.                    
  172.                     this.inFwdDecls = true;
  173.                     goto default;
  174.                     break;
  175.                 default:
  176.                    
  177.                     // Create sequence
  178.                     nd = f.Sequence();
  179.                     break;
  180.             }
  181.            
  182.             // Save xml type and source line information
  183.             ann.XmlType = ParseType(r.GetAttribute("xmlType"));
  184.             ;
  185.             nd.SourceLine = ParseLineInfo(r.GetAttribute("lineInfo"));
  186.             nd.Annotation = ann;
  187.            
  188.             if (nd is QilList) {
  189.                 // Push new parent list onto stack
  190.                 this.stk.Push((QilList)nd);
  191.                 return true;
  192.             }
  193.            
  194.             // Add node to its parent's list
  195.             this.stk.Peek().Add(nd);
  196.             return false;
  197.         }
  198.        
  199.         private void EndElement()
  200.         {
  201.             MethodInfo facMethod = null;
  202.             object[] facArgs;
  203.             QilList list;
  204.             QilNode nd;
  205.             ReaderAnnotation ann;
  206.            
  207.             list = this.stk.Pop();
  208.             ann = (ReaderAnnotation)list.Annotation;
  209.            
  210.             // Special case certain element names
  211.             string s = r.LocalName;
  212.             switch (r.LocalName) {
  213.                 case "QilExpression":
  214.                    
  215.                     {
  216.                         Debug.Assert(list.Count > 0, "QilExpression node requires a Root expression");
  217.                         QilExpression qil = f.QilExpression(list[list.Count - 1]);
  218.                        
  219.                         // Be flexible on order and presence of QilExpression children
  220.                         for (int i = 0; i < list.Count - 1; i++) {
  221.                             switch (list[i].NodeType) {
  222.                                 case QilNodeType.True:
  223.                                 case QilNodeType.False:
  224.                                     qil.IsDebug = list[i].NodeType == QilNodeType.True;
  225.                                     break;
  226.                                 case QilNodeType.FunctionList:
  227.                                    
  228.                                     qil.FunctionList = (QilList)list[i];
  229.                                     break;
  230.                                 case QilNodeType.GlobalVariableList:
  231.                                    
  232.                                     qil.GlobalVariableList = (QilList)list[i];
  233.                                     break;
  234.                                 case QilNodeType.GlobalParameterList:
  235.                                    
  236.                                     qil.GlobalParameterList = (QilList)list[i];
  237.                                     break;
  238.                             }
  239.                         }
  240.                         nd = qil;
  241.                         break;
  242.                     }
  243.                     break;
  244.                 case "ForwardDecls":
  245.                    
  246.                     this.inFwdDecls = false;
  247.                     return;
  248.                 case "Parameter":
  249.                 case "Let":
  250.                 case "For":
  251.                 case "Function":
  252.                    
  253.                    
  254.                     {
  255.                         string id = ann.Id;
  256.                         QilName name = ann.Name;
  257.                         Debug.Assert(id != null, r.LocalName + " must have an id attribute");
  258.                         Debug.Assert(!this.inFwdDecls || ann.XmlType != null, "Forward decl for " + r.LocalName + " '" + id + "' must have an xmlType attribute");
  259.                        
  260.                         // Create node (may be discarded later if it was already declared in forward declarations section)
  261.                         switch (r.LocalName) {
  262.                             case "Parameter":
  263.                                 Debug.Assert(list.Count <= (this.inFwdDecls ? 0 : 1), "Parameter '" + id + "' must have 0 or 1 arguments");
  264.                                 Debug.Assert(ann.XmlType != null, "Parameter '" + id + "' must have an xmlType attribute");
  265.                                 if (this.inFwdDecls || list.Count == 0)
  266.                                     nd = f.Parameter(null, name, ann.XmlType);
  267.                                 else
  268.                                     nd = f.Parameter(list[0], name, ann.XmlType);
  269.                                 break;
  270.                             case "Let":
  271.                                
  272.                                 Debug.Assert(list.Count == (this.inFwdDecls ? 0 : 1), "Let '" + id + "' must have 0 or 1 arguments");
  273.                                 if (this.inFwdDecls)
  274.                                     nd = f.Let(f.Unknown(ann.XmlType));
  275.                                 else
  276.                                     nd = f.Let(list[0]);
  277.                                 break;
  278.                             case "For":
  279.                                
  280.                                 Debug.Assert(list.Count == 1, "For '" + id + "' must have 1 argument");
  281.                                 nd = f.For(list[0]);
  282.                                 break;
  283.                             default:
  284.                                
  285.                                 Debug.Assert(list.Count == (this.inFwdDecls ? 2 : 3), "Function '" + id + "' must have 2 or 3 arguments");
  286.                                 if (this.inFwdDecls)
  287.                                     nd = f.Function(list[0], list[1], ann.XmlType);
  288.                                 else
  289.                                     nd = f.Function(list[0], list[1], list[2], ann.XmlType != null ? ann.XmlType : list[1].XmlType);
  290.                                 break;
  291.                         }
  292.                        
  293.                         // Set DebugName
  294.                         if (name != null)
  295.                             ((QilReference)nd).DebugName = name.ToString();
  296.                        
  297.                         if (this.inFwdDecls) {
  298.                             Debug.Assert(!this.scope.ContainsKey(id), "Multiple nodes have id '" + id + "'");
  299.                             this.fwdDecls[id] = nd;
  300.                             this.scope[id] = nd;
  301.                         }
  302.                         else {
  303.                             if (this.fwdDecls.ContainsKey(id)) {
  304.                                 // Replace forward declaration
  305.                                 Debug.Assert(r.LocalName == Enum.GetName(typeof(QilNodeType), nd.NodeType), "Id '" + id + "' is not not bound to a " + r.LocalName + " forward decl");
  306.                                 nd = this.fwdDecls[id];
  307.                                 this.fwdDecls.Remove(id);
  308.                                
  309.                                 if (list.Count > 0)
  310.                                     nd[0] = list[0];
  311.                                 if (list.Count > 1)
  312.                                     nd[1] = list[1];
  313.                             }
  314.                             else {
  315.                                 // Put reference in scope
  316.                                 Debug.Assert(!this.scope.ContainsKey(id), "Id '" + id + "' is already in scope");
  317.                                 this.scope[id] = nd;
  318.                             }
  319.                         }
  320.                         nd.Annotation = ann;
  321.                         break;
  322.                     }
  323.                     break;
  324.                 case "RefTo":
  325.                    
  326.                    
  327.                     {
  328.                         // Lookup reference
  329.                         string id = ann.Id;
  330.                         Debug.Assert(id != null, r.LocalName + " must have an id attribute");
  331.                        
  332.                         Debug.Assert(this.scope.ContainsKey(id), "Id '" + id + "' is not in scope");
  333.                         this.stk.Peek().Add(this.scope[id]);
  334.                         return;
  335.                     }
  336.                     break;
  337.                 case "Sequence":
  338.                    
  339.                     nd = f.Sequence(list);
  340.                     break;
  341.                 case "FunctionList":
  342.                    
  343.                     nd = f.FunctionList(list);
  344.                     break;
  345.                 case "GlobalVariableList":
  346.                    
  347.                     nd = f.GlobalVariableList(list);
  348.                     break;
  349.                 case "GlobalParameterList":
  350.                    
  351.                     nd = f.GlobalParameterList(list);
  352.                     break;
  353.                 case "ActualParameterList":
  354.                    
  355.                     nd = f.ActualParameterList(list);
  356.                     break;
  357.                 case "FormalParameterList":
  358.                    
  359.                     nd = f.FormalParameterList(list);
  360.                     break;
  361.                 case "SortKeyList":
  362.                    
  363.                     nd = f.SortKeyList(list);
  364.                     break;
  365.                 case "BranchList":
  366.                    
  367.                     nd = f.BranchList(list);
  368.                     break;
  369.                 case "XsltInvokeEarlyBound":
  370.                    
  371.                    
  372.                     {
  373.                         Debug.Assert(ann.ClrNamespace != null, "XsltInvokeEarlyBound must have a clrNamespace attribute");
  374.                         Debug.Assert(list.Count == 2, "XsltInvokeEarlyBound must have exactly 2 arguments");
  375.                         Debug.Assert(list.XmlType != null, "XsltInvokeEarlyBound must have an xmlType attribute");
  376.                         MethodInfo mi = null;
  377.                         QilName name = (QilName)list[0];
  378.                        
  379.                         foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) {
  380.                             Type t = asm.GetType(ann.ClrNamespace);
  381.                             if (t != null) {
  382.                                 mi = t.GetMethod(name.LocalName);
  383.                                 break;
  384.                             }
  385.                         }
  386.                        
  387.                         Debug.Assert(mi != null, "Cannot find method " + ann.ClrNamespace + "." + name.ToString());
  388.                        
  389.                         nd = f.XsltInvokeEarlyBound(name, f.LiteralObject(mi), list[1], ann.XmlType);
  390.                         break;
  391.                     }
  392.                     break;
  393.                 default:
  394.                    
  395.                    
  396.                     {
  397.                         // Find factory method which will be used to construct the Qil node
  398.                         Debug.Assert(nameToFactoryMethod.ContainsKey(r.LocalName), "Method " + r.LocalName + " could not be found on QilFactory");
  399.                         facMethod = nameToFactoryMethod[r.LocalName];
  400.                         Debug.Assert(facMethod.GetParameters().Length == list.Count, "NodeType " + r.LocalName + " does not allow " + list.Count + " parameters");
  401.                        
  402.                         // Create factory method arguments
  403.                         facArgs = new object[list.Count];
  404.                         for (int i = 0; i < facArgs.Length; i++)
  405.                             facArgs[i] = list[i];
  406.                        
  407.                         // Create node and set its properties
  408.                         nd = (QilNode)facMethod.Invoke(f, facArgs);
  409.                         break;
  410.                     }
  411.                     break;
  412.             }
  413.            
  414.             nd.SourceLine = list.SourceLine;
  415.            
  416.             // Add node to its parent's list
  417.             this.stk.Peek().Add(nd);
  418.         }
  419.        
  420.         private string ReadText()
  421.         {
  422.             string s = string.Empty;
  423.            
  424.             if (!r.IsEmptyElement) {
  425.                 while (r.Read()) {
  426.                     switch (r.NodeType) {
  427.                         case XmlNodeType.Text:
  428.                         case XmlNodeType.SignificantWhitespace:
  429.                         case XmlNodeType.Whitespace:
  430.                             s += r.Value;
  431.                             continue;
  432.                     }
  433.                    
  434.                     break;
  435.                 }
  436.             }
  437.            
  438.             return s;
  439.         }
  440.        
  441.         private ISourceLineInfo ParseLineInfo(string s)
  442.         {
  443.             if (s != null && s.Length > 0) {
  444.                 Match m = lineInfoRegex.Match(s);
  445.                 Debug.Assert(m.Success && m.Groups.Count == 5, "Malformed lineInfo attribute");
  446.                 return new SourceLineInfo("", Int32.Parse(m.Groups[1].Value, CultureInfo.InvariantCulture), Int32.Parse(m.Groups[2].Value, CultureInfo.InvariantCulture), Int32.Parse(m.Groups[3].Value, CultureInfo.InvariantCulture), Int32.Parse(m.Groups[4].Value, CultureInfo.InvariantCulture));
  447.             }
  448.             return null;
  449.         }
  450.        
  451.         private XmlQueryType ParseType(string s)
  452.         {
  453.             if (s != null && s.Length > 0) {
  454.                 Match m = typeInfoRegex.Match(s);
  455.                 Debug.Assert(m.Success && m.Groups.Count == 4, "Malformed Type info");
  456.                
  457.                 XmlQueryCardinality qc = new XmlQueryCardinality(m.Groups[1].Value);
  458.                 bool strict = bool.Parse(m.Groups[3].Value);
  459.                
  460.                 string[] codes = m.Groups[2].Value.Split('|');
  461.                 XmlQueryType[] types = new XmlQueryType[codes.Length];
  462.                
  463.                 for (int i = 0; i < codes.Length; i++)
  464.                     types[i] = XmlQueryTypeFactory.Type((XmlTypeCode)Enum.Parse(typeof(XmlTypeCode), codes[i]), strict);
  465.                
  466.                 return XmlQueryTypeFactory.Product(XmlQueryTypeFactory.Choice(types), qc);
  467.             }
  468.             return null;
  469.         }
  470.        
  471.         private QilName ParseName(string name)
  472.         {
  473.             string prefix;
  474.             string local;
  475.             string uri;
  476.             int idx;
  477.            
  478.             if (name != null && name.Length > 0) {
  479.                 // If name contains '}' character, then namespace is non-empty
  480.                 idx = name.LastIndexOf('}');
  481.                 if (idx != -1 && name[0] == '{') {
  482.                     uri = name.Substring(1, idx - 1);
  483.                     name = name.Substring(idx + 1);
  484.                 }
  485.                 else {
  486.                     uri = string.Empty;
  487.                 }
  488.                
  489.                 // Parse QName
  490.                 ValidateNames.ParseQNameThrow(name, out prefix, out local);
  491.                
  492.                 return f.LiteralQName(local, uri, prefix);
  493.             }
  494.             return null;
  495.         }
  496.        
  497.         private class ReaderAnnotation
  498.         {
  499.             public string Id;
  500.             public QilName Name;
  501.             public XmlQueryType XmlType;
  502.             public string ClrNamespace;
  503.         }
  504.     }
  505. }

Developer Fusion