The Labs \ Source Viewer \ SSCLI \ System.Xml.XPath.DataBinding \ ContentIterator

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XPathDocumentView.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. #if ENABLEDATABINDING
  16. using System;
  17. using System.Xml;
  18. using System.Xml.XPath;
  19. using System.Xml.Schema;
  20. using System.Collections;
  21. using System.Collections.Generic;
  22. using System.ComponentModel;
  23. using System.Diagnostics;
  24. namespace System.Xml.XPath.DataBinding
  25. {
  26.     /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView"]/*' />
  27.     public sealed class XPathDocumentView : IBindingList, ITypedList
  28.     {
  29.         ArrayList rows;
  30.         Shape rowShape;
  31.         XPathNode ndRoot;
  32.         XPathDocument document;
  33.         string xpath;
  34.         IXmlNamespaceResolver namespaceResolver;
  35.         IXmlNamespaceResolver xpathResolver;
  36.        
  37.         //
  38.         // Constructors
  39.         //
  40.        
  41.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.XPathDocumentView"]/*' />
  42.         public XPathDocumentView(XPathDocument document) : this(document, (IXmlNamespaceResolver)null)
  43.         {
  44.         }
  45.        
  46.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.XPathDocumentView1"]/*' />
  47.         public XPathDocumentView(XPathDocument document, IXmlNamespaceResolver namespaceResolver)
  48.         {
  49.             if (null == document)
  50.                 throw new ArgumentNullException("document");
  51.             this.document = document;
  52.             this.ndRoot = document.Root;
  53.             if (null == this.ndRoot)
  54.                 throw new ArgumentException("document");
  55.             this.namespaceResolver = namespaceResolver;
  56.             ArrayList rows = new ArrayList();
  57.             this.rows = rows;
  58.             Debug.Assert(XPathNodeType.Root == this.ndRoot.NodeType);
  59.             XPathNode nd = this.ndRoot.Child;
  60.             while (null != nd) {
  61.                 if (XPathNodeType.Element == nd.NodeType)
  62.                     rows.Add(nd);
  63.                 nd = nd.Sibling;
  64.             }
  65.             DeriveShapeFromRows();
  66.         }
  67.        
  68.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.XPathDocumentView2"]/*' />
  69.         public XPathDocumentView(XPathDocument document, string xpath) : this(document, xpath, null, true)
  70.         {
  71.         }
  72.        
  73.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.XPathDocumentView3"]/*' />
  74.         public XPathDocumentView(XPathDocument document, string xpath, IXmlNamespaceResolver namespaceResolver) : this(document, xpath, namespaceResolver, false)
  75.         {
  76.         }
  77.        
  78.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.XPathDocumentView4"]/*' />
  79.         public XPathDocumentView(XPathDocument document, string xpath, IXmlNamespaceResolver namespaceResolver, bool showPrefixes)
  80.         {
  81.             if (null == document)
  82.                 throw new ArgumentNullException("document");
  83.             this.xpath = xpath;
  84.             this.document = document;
  85.             this.ndRoot = document.Root;
  86.             if (null == this.ndRoot)
  87.                 throw new ArgumentException("document");
  88.             this.ndRoot = document.Root;
  89.             this.xpathResolver = namespaceResolver;
  90.             if (showPrefixes)
  91.                 this.namespaceResolver = namespaceResolver;
  92.             ArrayList rows = new ArrayList();
  93.             this.rows = rows;
  94.             InitFromXPath(this.ndRoot, xpath);
  95.         }
  96.        
  97.         internal XPathDocumentView(XPathNode root, ArrayList rows, Shape rowShape)
  98.         {
  99.             this.rows = rows;
  100.             this.rowShape = rowShape;
  101.             this.ndRoot = root;
  102.         }
  103.        
  104.         //
  105.         // public properties
  106.        
  107.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Document"]/*' />
  108.         public XPathDocument Document {
  109.             get { return this.document; }
  110.         }
  111.        
  112.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.XPath"]/*' />
  113.         public string XPath {
  114.             get { return xpath; }
  115.         }
  116.        
  117.         //
  118.         // IEnumerable Implementation
  119.        
  120.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.GetEnumerator"]/*' />
  121.         public IEnumerator GetEnumerator()
  122.         {
  123.             return new RowEnumerator(this);
  124.         }
  125.        
  126.         //
  127.         // ICollection implementation
  128.        
  129.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Count"]/*' />
  130.         public int Count {
  131.             get { return this.rows.Count; }
  132.         }
  133.        
  134.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.IsSynchronized"]/*' />
  135.         public bool IsSynchronized {
  136.             get { return false; }
  137.         }
  138.        
  139.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.SyncRoot"]/*' />
  140.         public object SyncRoot {
  141.             get { return null; }
  142.         }
  143.        
  144.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.CopyTo"]/*' />
  145.         public void CopyTo(Array array, int index)
  146.         {
  147.             object o;
  148.             ArrayList rows = this.rows;
  149.             for (int i = 0; i < rows.Count; i++)
  150.                 o = this[i];
  151.             // force creation lazy of row object
  152.             rows.CopyTo(array, index);
  153.         }
  154.        
  155.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.CopyTo2"]/*' />
  156.         /// <devdoc>
  157.         /// <para>strongly typed version of CopyTo, demanded by Fxcop.</para>
  158.         /// </devdoc>
  159.         public void CopyTo(XPathNodeView[] array, int index)
  160.         {
  161.             object o;
  162.             ArrayList rows = this.rows;
  163.             for (int i = 0; i < rows.Count; i++)
  164.                 o = this[i];
  165.             // force creation lazy of row object
  166.             rows.CopyTo(array, index);
  167.         }
  168.        
  169.         //
  170.         // IList Implementation
  171.        
  172.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.IsReadOnly"]/*' />
  173.         bool IList.IsReadOnly {
  174.             get { return true; }
  175.         }
  176.        
  177.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.IsFixedSize"]/*' />
  178.         bool IList.IsFixedSize {
  179.             get { return true; }
  180.         }
  181.        
  182.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Contains"]/*' />
  183.         bool IList.Contains(object value)
  184.         {
  185.             return this.rows.Contains(value);
  186.         }
  187.        
  188.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Remove"]/*' />
  189.         void IList.Remove(object value)
  190.         {
  191.             throw new NotSupportedException("IList.Remove");
  192.         }
  193.        
  194.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.RemoveAt"]/*' />
  195.         void IList.RemoveAt(int index)
  196.         {
  197.             throw new NotSupportedException("IList.RemoveAt");
  198.         }
  199.        
  200.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Clear"]/*' />
  201.         void IList.Clear()
  202.         {
  203.             throw new NotSupportedException("IList.Clear");
  204.         }
  205.        
  206.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Add"]/*' />
  207.         int IList.Add(object value)
  208.         {
  209.             throw new NotSupportedException("IList.Add");
  210.         }
  211.        
  212.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Insert"]/*' />
  213.         void IList.Insert(int index, object value)
  214.         {
  215.             throw new NotSupportedException("IList.Insert");
  216.         }
  217.        
  218.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.IndexOf"]/*' />
  219.         int IList.IndexOf(object value)
  220.         {
  221.             return this.rows.IndexOf(value);
  222.         }
  223.        
  224.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.this"]/*' />
  225.         object IList.this[int index]
  226.         {
  227.             get {
  228.                 object val = this.rows[index];
  229.                 if (val is XPathNodeView)
  230.                     return val;
  231.                 XPathNodeView xiv = FillRow((XPathNode)val, this.rowShape);
  232.                 this.rows[index] = xiv;
  233.                 return xiv;
  234.             }
  235.             set {
  236.                 throw new NotSupportedException("IList.this[]");
  237.             }
  238.         }
  239.        
  240.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Contains2"]/*' />
  241.         /// <devdoc>
  242.         /// <para>strongly typed version of Contains, demanded by Fxcop.</para>
  243.         /// </devdoc>
  244.         public bool Contains(XPathNodeView value)
  245.         {
  246.             return this.rows.Contains(value);
  247.         }
  248.        
  249.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Add2"]/*' />
  250.         /// <devdoc>
  251.         /// <para>strongly typed version of Add, demanded by Fxcop.</para>
  252.         /// </devdoc>
  253.         public int Add(XPathNodeView value)
  254.         {
  255.             throw new NotSupportedException("IList.Add");
  256.         }
  257.        
  258.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Insert2"]/*' />
  259.         /// <devdoc>
  260.         /// <para>strongly typed version of Insert, demanded by Fxcop.</para>
  261.         /// </devdoc>
  262.         public void Insert(int index, XPathNodeView value)
  263.         {
  264.             throw new NotSupportedException("IList.Insert");
  265.         }
  266.        
  267.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.IndexOf2"]/*' />
  268.         /// <devdoc>
  269.         /// <para>strongly typed version of IndexOf, demanded by Fxcop.</para>
  270.         /// </devdoc>
  271.         public int IndexOf(XPathNodeView value)
  272.         {
  273.             return this.rows.IndexOf(value);
  274.         }
  275.        
  276.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Remove2"]/*' />
  277.         /// <devdoc>
  278.         /// <para>strongly typed version of Remove, demanded by Fxcop.</para>
  279.         /// </devdoc>
  280.         public void Remove(XPathNodeView value)
  281.         {
  282.             throw new NotSupportedException("IList.Remove");
  283.         }
  284.        
  285.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Item"]/*' />
  286.         /// <devdoc>
  287.         /// <para>strongly typed version of Item, demanded by Fxcop.</para>
  288.         /// </devdoc>
  289.         public XPathNodeView this[int index]
  290.         {
  291.             get {
  292.                 object val = this.rows[index];
  293.                 XPathNodeView nodeView;
  294.                 nodeView = val as XPathNodeView;
  295.                 if (nodeView != null) {
  296.                     return nodeView;
  297.                 }
  298.                 nodeView = FillRow((XPathNode)val, this.rowShape);
  299.                 this.rows[index] = nodeView;
  300.                 return nodeView;
  301.             }
  302.             set {
  303.                 throw new NotSupportedException("IList.this[]");
  304.             }
  305.         }
  306.        
  307.         //
  308.         // IBindingList Implementation
  309.        
  310.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.AllowEdit"]/*' />
  311.         public bool AllowEdit {
  312.             get { return false; }
  313.         }
  314.        
  315.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.AllowAdd"]/*' />
  316.         public bool AllowAdd {
  317.             get { return false; }
  318.         }
  319.        
  320.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.AllowRemove"]/*' />
  321.         public bool AllowRemove {
  322.             get { return false; }
  323.         }
  324.        
  325.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.AllowNew"]/*' />
  326.         public bool AllowNew {
  327.             get { return false; }
  328.         }
  329.        
  330.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.AddNew"]/*' />
  331.         public object AddNew()
  332.         {
  333.             throw new NotSupportedException("IBindingList.AddNew");
  334.         }
  335.        
  336.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.SupportsChangeNotification"]/*' />
  337.         public bool SupportsChangeNotification {
  338.             get { return false; }
  339.         }
  340.        
  341.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.ListChanged"]/*' />
  342.         public event ListChangedEventHandler ListChanged {
  343.             add {
  344.                 throw new NotSupportedException("IBindingList.ListChanged");
  345.             }
  346.             remove {
  347.                 throw new NotSupportedException("IBindingList.ListChanged");
  348.             }
  349.         }
  350.        
  351.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.SupportsSearching"]/*' />
  352.         public bool SupportsSearching {
  353.             get { return false; }
  354.         }
  355.        
  356.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.SupportsSorting"]/*' />
  357.         public bool SupportsSorting {
  358.             get { return false; }
  359.         }
  360.        
  361.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.IsSorted"]/*' />
  362.         public bool IsSorted {
  363.             get { return false; }
  364.         }
  365.        
  366.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.SortProperty"]/*' />
  367.         public PropertyDescriptor SortProperty {
  368.             get {
  369.                 throw new NotSupportedException("IBindingList.SortProperty");
  370.             }
  371.         }
  372.        
  373.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.SortDirection"]/*' />
  374.         public ListSortDirection SortDirection {
  375.             get {
  376.                 throw new NotSupportedException("IBindingList.SortDirection");
  377.             }
  378.         }
  379.        
  380.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.AddIndex"]/*' />
  381.         public void AddIndex(PropertyDescriptor descriptor)
  382.         {
  383.             throw new NotSupportedException("IBindingList.AddIndex");
  384.         }
  385.        
  386.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.ApplySort"]/*' />
  387.         public void ApplySort(PropertyDescriptor descriptor, ListSortDirection direction)
  388.         {
  389.             throw new NotSupportedException("IBindingList.ApplySort");
  390.         }
  391.        
  392.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Find"]/*' />
  393.         public int Find(PropertyDescriptor propertyDescriptor, object key)
  394.         {
  395.             throw new NotSupportedException("IBindingList.Find");
  396.         }
  397.        
  398.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.RemoveIndex"]/*' />
  399.         public void RemoveIndex(PropertyDescriptor propertyDescriptor)
  400.         {
  401.             throw new NotSupportedException("IBindingList.RemoveIndex");
  402.         }
  403.        
  404.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.RemoveSort"]/*' />
  405.         public void RemoveSort()
  406.         {
  407.             throw new NotSupportedException("IBindingList.RemoveSort");
  408.         }
  409.        
  410.        
  411.         //
  412.         // ITypedList Implementation
  413.        
  414.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.GetListName"]/*' />
  415.         public string GetListName(PropertyDescriptor[] listAccessors)
  416.         {
  417.             if (listAccessors == null) {
  418.                 return this.rowShape.Name;
  419.             }
  420.             else {
  421.                 return listAccessors[listAccessors.Length - 1].Name;
  422.             }
  423.         }
  424.        
  425.         /// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.GetItemProperties"]/*' />
  426.         public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
  427.         {
  428.             Shape shape = null;
  429.             if (listAccessors == null) {
  430.                 shape = this.rowShape;
  431.             }
  432.             else {
  433.                 XPathNodeViewPropertyDescriptor propdesc = listAccessors[listAccessors.Length - 1] as XPathNodeViewPropertyDescriptor;
  434.                 if (null != propdesc)
  435.                     shape = propdesc.Shape;
  436.             }
  437.            
  438.             if (null == shape)
  439.                 throw new ArgumentException("listAccessors");
  440.             return new PropertyDescriptorCollection(shape.PropertyDescriptors);
  441.         }
  442.        
  443.        
  444.         //
  445.         // Internal Implementation
  446.        
  447.        
  448.         internal Shape RowShape {
  449.             get { return this.rowShape; }
  450.         }
  451.        
  452.         internal void SetRows(ArrayList rows)
  453.         {
  454.             Debug.Assert(this.rows == null);
  455.             this.rows = rows;
  456.         }
  457.        
  458.        
  459.         XPathNodeView FillRow(XPathNode ndRow, Shape shape)
  460.         {
  461.             object[] columns;
  462.             XPathNode nd;
  463.             switch (shape.BindingType) {
  464.                 case BindingType.Text:
  465.                 case BindingType.Attribute:
  466.                     columns = new object[1];
  467.                     columns[0] = ndRow;
  468.                     return new XPathNodeView(this, ndRow, columns);
  469.                 case BindingType.Repeat:
  470.                    
  471.                     columns = new object[1];
  472.                     nd = TreeNavigationHelper.GetContentChild(ndRow);
  473.                     columns[0] = FillColumn(new ContentIterator(nd, shape), shape);
  474.                     return new XPathNodeView(this, ndRow, columns);
  475.                 case BindingType.Sequence:
  476.                 case BindingType.Choice:
  477.                 case BindingType.All:
  478.                    
  479.                     int subShapesCount = shape.SubShapes.Count;
  480.                     columns = new object[subShapesCount];
  481.                     if (shape.BindingType == BindingType.Sequence && shape.SubShape(0).BindingType == BindingType.Attribute) {
  482.                         FillAttributes(ndRow, shape, columns);
  483.                     }
  484.                     Shape lastSubShape = (Shape)shape.SubShapes[subShapesCount - 1];
  485.                     if (lastSubShape.BindingType == BindingType.Text) {
  486.                         //Attributes followed by simpe content or mixed content
  487.                         columns[subShapesCount - 1] = ndRow;
  488.                         return new XPathNodeView(this, ndRow, columns);
  489.                     }
  490.                     else {
  491.                         nd = TreeNavigationHelper.GetContentChild(ndRow);
  492.                         return FillSubRow(new ContentIterator(nd, shape), shape, columns);
  493.                     }
  494.                     break;
  495.                 default:
  496.                    
  497.                     // should not map to a row
  498.                     #if DEBUG
  499.                     throw new NotSupportedException("Unable to bind row to: " + shape.BindingType.ToString());
  500.                     #else
  501.                     throw new NotSupportedException();
  502.                     break;
  503.                 #endif
  504.             }
  505.         }
  506.        
  507.         void FillAttributes(XPathNode nd, Shape shape, object[] cols)
  508.         {
  509.             int i = 0;
  510.             while (i < cols.Length) {
  511.                 Shape attrShape = shape.SubShape(i);
  512.                 if (attrShape.BindingType != BindingType.Attribute)
  513.                     break;
  514.                 XmlQualifiedName name = attrShape.AttributeName;
  515.                 XPathNode ndAttr = nd.GetAttribute(name.Name, name.Namespace);
  516.                 if (null != ndAttr)
  517.                     cols[i] = ndAttr;
  518.                 i++;
  519.             }
  520.         }
  521.        
  522.         object FillColumn(ContentIterator iter, Shape shape)
  523.         {
  524.             object val;
  525.             switch (shape.BindingType) {
  526.                 case BindingType.Element:
  527.                     val = iter.Node;
  528.                     iter.Next();
  529.                     break;
  530.                 case BindingType.ElementNested:
  531.                    
  532.                    
  533.                     {
  534.                         ArrayList rows = new ArrayList();
  535.                         rows.Add(iter.Node);
  536.                         iter.Next();
  537.                         val = new XPathDocumentView(null, rows, shape.NestedShape);
  538.                         break;
  539.                     }
  540.                     break;
  541.                 case BindingType.Repeat:
  542.                    
  543.                    
  544.                     {
  545.                         ArrayList rows = new ArrayList();
  546.                         Shape subShape = shape.SubShape(0);
  547.                         if (subShape.BindingType == BindingType.ElementNested) {
  548.                             Shape nestShape = subShape.NestedShape;
  549.                             XPathDocumentView xivc = new XPathDocumentView(null, null, nestShape);
  550.                             XPathNode nd;
  551.                             while (null != (nd = iter.Node) && subShape.IsParticleMatch(iter.Particle)) {
  552.                                 rows.Add(nd);
  553.                                 iter.Next();
  554.                             }
  555.                             xivc.SetRows(rows);
  556.                             val = xivc;
  557.                         }
  558.                         else {
  559.                             XPathDocumentView xivc = new XPathDocumentView(null, null, subShape);
  560.                             XPathNode nd;
  561.                             while (null != (nd = iter.Node) && shape.IsParticleMatch(iter.Particle)) {
  562.                                 rows.Add(xivc.FillSubRow(iter, subShape, null));
  563.                             }
  564.                             xivc.SetRows(rows);
  565.                             val = xivc;
  566.                         }
  567.                         break;
  568.                     }
  569.                     break;
  570.                 case BindingType.Sequence:
  571.                 case BindingType.Choice:
  572.                 case BindingType.All:
  573.                    
  574.                    
  575.                     {
  576.                         XPathDocumentView docview = new XPathDocumentView(null, null, shape);
  577.                         ArrayList rows = new ArrayList();
  578.                         rows.Add(docview.FillSubRow(iter, shape, null));
  579.                         docview.SetRows(rows);
  580.                         val = docview;
  581.                         break;
  582.                     }
  583.                     break;
  584.                 default:
  585.                 case BindingType.Text:
  586.                 case BindingType.Attribute:
  587.                    
  588.                     throw new NotSupportedException();
  589.                     break;
  590.             }
  591.             return val;
  592.         }
  593.        
  594.         XPathNodeView FillSubRow(ContentIterator iter, Shape shape, object[] columns)
  595.         {
  596.             if (null == columns) {
  597.                 int colCount = shape.SubShapes.Count;
  598.                 if (0 == colCount)
  599.                     colCount = 1;
  600.                 columns = new object[colCount];
  601.             }
  602.            
  603.             switch (shape.BindingType) {
  604.                 case BindingType.Element:
  605.                     columns[0] = FillColumn(iter, shape);
  606.                     break;
  607.                 case BindingType.Sequence:
  608.                    
  609.                    
  610.                     {
  611.                         int iPrev = -1;
  612.                         int i;
  613.                         while (null != iter.Node) {
  614.                             i = shape.FindMatchingSubShape(iter.Particle);
  615.                             if (i <= iPrev)
  616.                                 break;
  617.                             columns[i] = FillColumn(iter, shape.SubShape(i));
  618.                             iPrev = i;
  619.                         }
  620.                         break;
  621.                     }
  622.                     break;
  623.                 case BindingType.All:
  624.                    
  625.                    
  626.                     {
  627.                         while (null != iter.Node) {
  628.                             int i = shape.FindMatchingSubShape(iter.Particle);
  629.                             if (-1 == i || null != columns[i])
  630.                                 break;
  631.                             columns[i] = FillColumn(iter, shape.SubShape(i));
  632.                         }
  633.                         break;
  634.                     }
  635.                     break;
  636.                 case BindingType.Choice:
  637.                    
  638.                    
  639.                     {
  640.                         int i = shape.FindMatchingSubShape(iter.Particle);
  641.                         if (-1 != i) {
  642.                             columns[i] = FillColumn(iter, shape.SubShape(i));
  643.                         }
  644.                         break;
  645.                     }
  646.                     break;
  647.                 case BindingType.Repeat:
  648.                 default:
  649.                    
  650.                     // should not map to a row
  651.                     throw new NotSupportedException();
  652.                     break;
  653.             }
  654.             return new XPathNodeView(this, null, columns);
  655.         }
  656.        
  657.         //
  658.         // XPath support
  659.         //
  660.        
  661.         void InitFromXPath(XPathNode ndRoot, string xpath)
  662.         {
  663.             XPathStep[] steps = ParseXPath(xpath, this.xpathResolver);
  664.             ArrayList rows = this.rows;
  665.             rows.Clear();
  666.             PopulateFromXPath(ndRoot, steps, 0);
  667.             DeriveShapeFromRows();
  668.         }
  669.        
  670.         void DeriveShapeFromRows()
  671.         {
  672.             object schemaInfo = null;
  673.             for (int i = 0; (i < rows.Count) && (null == schemaInfo); i++) {
  674.                 XPathNode nd = rows[i] as XPathNode;
  675.                 Debug.Assert(null != nd && (XPathNodeType.Attribute == nd.NodeType || XPathNodeType.Element == nd.NodeType));
  676.                 if (null != nd) {
  677.                     if (XPathNodeType.Attribute == nd.NodeType)
  678.                         schemaInfo = nd.SchemaAttribute;
  679.                     else
  680.                         schemaInfo = nd.SchemaElement;
  681.                 }
  682.             }
  683.             if (0 == rows.Count) {
  684.                 throw new NotImplementedException("XPath failed to match an elements");
  685.             }
  686.             if (null == schemaInfo) {
  687.                 rows.Clear();
  688.                 throw new XmlException(Res.XmlDataBinding_NoSchemaType, (string[])null);
  689.             }
  690.             ShapeGenerator shapeGen = new ShapeGenerator(this.namespaceResolver);
  691.             XmlSchemaElement xse = schemaInfo as XmlSchemaElement;
  692.             if (null != xse)
  693.                 this.rowShape = shapeGen.GenerateFromSchema(xse);
  694.             else
  695.                 this.rowShape = shapeGen.GenerateFromSchema((XmlSchemaAttribute)schemaInfo);
  696.         }
  697.        
  698.         void PopulateFromXPath(XPathNode nd, XPathStep[] steps, int step)
  699.         {
  700.             string ln = steps[step].name.Name;
  701.             string ns = steps[step].name.Namespace;
  702.             if (XPathNodeType.Attribute == steps[step].type) {
  703.                 XPathNode ndAttr = nd.GetAttribute(ln, ns, true);
  704.                 if (null != ndAttr) {
  705.                     if (null != ndAttr.SchemaAttribute)
  706.                         this.rows.Add(ndAttr);
  707.                 }
  708.             }
  709.             else {
  710.                 XPathNode ndChild = TreeNavigationHelper.GetElementChild(nd, ln, ns, true);
  711.                 if (null != ndChild) {
  712.                     int nextStep = step + 1;
  713.                     do {
  714.                         if (steps.Length == nextStep) {
  715.                             if (null != ndChild.SchemaType)
  716.                                 this.rows.Add(ndChild);
  717.                         }
  718.                         else {
  719.                             PopulateFromXPath(ndChild, steps, nextStep);
  720.                         }
  721.                         ndChild = TreeNavigationHelper.GetElementSibling(ndChild, ln, ns, true);
  722.                     }
  723.                     while (null != ndChild);
  724.                 }
  725.             }
  726.         }
  727.        
  728.        
  729.         // This is the limited grammar we support
  730.         // Path ::= '/ ' ( Step '/')* ( QName | '@' QName )
  731.         // Step ::= '.' | QName
  732.         // This is encoded as an array of XPathStep structs
  733.         struct XPathStep
  734.         {
  735.             internal XmlQualifiedName name;
  736.             internal XPathNodeType type;
  737.         }
  738.        
  739.         // Parse xpath (limited to above grammar), using provided namespaceResolver
  740.         // to resolve prefixes.
  741.         XPathStep[] ParseXPath(string xpath, IXmlNamespaceResolver xnr)
  742.         {
  743.             int pos;
  744.             int stepCount = 1;
  745.             for (pos = 1; pos < (xpath.Length - 1); pos++) {
  746.                 if (('/' == xpath[pos]) && ('.' != xpath[pos + 1]))
  747.                     stepCount++;
  748.             }
  749.             XPathStep[] steps = new XPathStep[stepCount];
  750.             pos = 0;
  751.             int i = 0;
  752.             for (;;) {
  753.                 if (pos >= xpath.Length)
  754.                     throw new XmlException(Res.XmlDataBinding_XPathEnd, (string[])null);
  755.                 if ('/' != xpath[pos])
  756.                     throw new XmlException(Res.XmlDataBinding_XPathRequireSlash, (string[])null);
  757.                 pos++;
  758.                 char ch = xpath[pos];
  759.                 if (ch == '.') {
  760.                     pos++;
  761.                     // again...
  762.                 }
  763.                 else if ('@' == ch) {
  764.                     if (0 == i)
  765.                         throw new XmlException(Res.XmlDataBinding_XPathAttrNotFirst, (string[])null);
  766.                     pos++;
  767.                     if (pos >= xpath.Length)
  768.                         throw new XmlException(Res.XmlDataBinding_XPathEnd, (string[])null);
  769.                     steps[i].name = ParseQName(xpath, ref pos, xnr);
  770.                     steps[i].type = XPathNodeType.Attribute;
  771.                     i++;
  772.                     if (pos != xpath.Length)
  773.                         throw new XmlException(Res.XmlDataBinding_XPathAttrLast, (string[])null);
  774.                     break;
  775.                 }
  776.                 else {
  777.                     steps[i].name = ParseQName(xpath, ref pos, xnr);
  778.                     steps[i].type = XPathNodeType.Element;
  779.                     i++;
  780.                     if (pos == xpath.Length)
  781.                         break;
  782.                 }
  783.             }
  784.             Debug.Assert(i == steps.Length);
  785.             return steps;
  786.         }
  787.        
  788.         // Parse a QName from the string, and resolve prefix
  789.         XmlQualifiedName ParseQName(string xpath, ref int pos, IXmlNamespaceResolver xnr)
  790.         {
  791.             string nm = ParseName(xpath, ref pos);
  792.             if (pos < xpath.Length && ':' == xpath[pos]) {
  793.                 pos++;
  794.                 string ns = (null == xnr) ? null : xnr.LookupNamespace(nm);
  795.                 if (null == ns || 0 == ns.Length)
  796.                     throw new XmlException(Res.Sch_UnresolvedPrefix, nm);
  797.                 return new XmlQualifiedName(ParseName(xpath, ref pos), ns);
  798.             }
  799.             else {
  800.                 return new XmlQualifiedName(nm);
  801.             }
  802.         }
  803.        
  804.         // Parse a NCNAME from the string
  805.         string ParseName(string xpath, ref int pos)
  806.         {
  807.             char ch;
  808.             int start = pos++;
  809.             while (pos < xpath.Length && '/' != (ch = xpath[pos]) && ':' != ch)
  810.                 pos++;
  811.             string nm = xpath.Substring(start, pos - start);
  812.             if (!XmlReader.IsName(nm))
  813.                 throw new XmlException(Res.Xml_InvalidNameChars, (string[])null);
  814.             return this.document.NameTable.Add(nm);
  815.         }
  816.        
  817.        
  818.         //
  819.         // Helper classes
  820.         //
  821.        
  822.         class ContentIterator
  823.         {
  824.             XPathNode node;
  825.             ContentValidator contentValidator;
  826.             ValidationState currentState;
  827.             object currentParticle;
  828.            
  829.             public ContentIterator(XPathNode nd, Shape shape)
  830.             {
  831.                 this.node = nd;
  832.                 XmlSchemaElement xse = shape.XmlSchemaElement;
  833.                 Debug.Assert(null != xse);
  834.                 SchemaElementDecl decl = xse.ElementDecl;
  835.                 Debug.Assert(null != decl);
  836.                 this.contentValidator = decl.ContentValidator;
  837.                 this.currentState = new ValidationState();
  838.                 this.contentValidator.InitValidation(this.currentState);
  839.                 this.currentState.ProcessContents = XmlSchemaContentProcessing.Strict;
  840.                 if (nd != null)
  841.                     Advance();
  842.             }
  843.            
  844.             public XPathNode Node {
  845.                 get { return this.node; }
  846.             }
  847.             public object Particle {
  848.                 get { return this.currentParticle; }
  849.             }
  850.            
  851.             public bool Next()
  852.             {
  853.                 if (null != this.node) {
  854.                     this.node = TreeNavigationHelper.GetContentSibling(this.node, XPathNodeType.Element);
  855.                     if (node != null)
  856.                         Advance();
  857.                     return null != this.node;
  858.                 }
  859.                 return false;
  860.             }
  861.            
  862.             private void Advance()
  863.             {
  864.                 XPathNode nd = this.node;
  865.                 int errorCode;
  866.                 this.currentParticle = this.contentValidator.ValidateElement(new XmlQualifiedName(nd.LocalName, nd.NamespaceUri), this.currentState, out errorCode);
  867.                 if (null == this.currentParticle || 0 != errorCode) {
  868.                     this.node = null;
  869.                 }
  870.             }
  871.         }
  872.        
  873.        
  874.         // Helper class to implement enumerator over rows
  875.         // We can't just use ArrayList enumerator because
  876.         // sometims rows may be lazily constructed
  877.         sealed class RowEnumerator : IEnumerator
  878.         {
  879.             XPathDocumentView collection;
  880.             int pos;
  881.            
  882.             internal RowEnumerator(XPathDocumentView collection)
  883.             {
  884.                 this.collection = collection;
  885.                 this.pos = -1;
  886.             }
  887.            
  888.             public object Current {
  889.                 get {
  890.                     if (this.pos < 0 || this.pos >= this.collection.Count)
  891.                         return null;
  892.                     return this.collection[this.pos];
  893.                 }
  894.             }
  895.            
  896.             public void Reset()
  897.             {
  898.                 this.pos = -1;
  899.             }
  900.            
  901.             public bool MoveNext()
  902.             {
  903.                 this.pos++;
  904.                 int max = this.collection.Count;
  905.                 if (this.pos > max)
  906.                     this.pos = max;
  907.                 return this.pos < max;
  908.             }
  909.         }
  910.     }
  911. }
  912. #endif

Developer Fusion