e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
822 lines
32 KiB
C#
822 lines
32 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="XPathDocumentView.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
// <owner current="true" primary="true">derekdb</owner>
|
|
//------------------------------------------------------------------------------
|
|
#if ENABLEDATABINDING
|
|
using System;
|
|
using System.Xml;
|
|
using System.Xml.XPath;
|
|
using System.Xml.Schema;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
|
|
namespace System.Xml.XPath.DataBinding
|
|
{
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView"]/*' />
|
|
public sealed class XPathDocumentView : IBindingList, ITypedList {
|
|
ArrayList rows;
|
|
Shape rowShape;
|
|
XPathNode ndRoot;
|
|
XPathDocument document;
|
|
string xpath;
|
|
IXmlNamespaceResolver namespaceResolver;
|
|
IXmlNamespaceResolver xpathResolver;
|
|
|
|
//
|
|
// Constructors
|
|
//
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.XPathDocumentView"]/*' />
|
|
public XPathDocumentView(XPathDocument document)
|
|
: this(document, (IXmlNamespaceResolver)null) {
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.XPathDocumentView1"]/*' />
|
|
public XPathDocumentView(XPathDocument document, IXmlNamespaceResolver namespaceResolver) {
|
|
if (null == document)
|
|
throw new ArgumentNullException("document");
|
|
this.document = document;
|
|
this.ndRoot = document.Root;
|
|
if (null == this.ndRoot)
|
|
throw new ArgumentException("document");
|
|
this.namespaceResolver = namespaceResolver;
|
|
ArrayList rows = new ArrayList();
|
|
this.rows = rows;
|
|
Debug.Assert(XPathNodeType.Root == this.ndRoot.NodeType);
|
|
XPathNode nd = this.ndRoot.Child;
|
|
while (null != nd) {
|
|
if (XPathNodeType.Element == nd.NodeType)
|
|
rows.Add(nd);
|
|
nd = nd.Sibling;
|
|
}
|
|
DeriveShapeFromRows();
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.XPathDocumentView2"]/*' />
|
|
public XPathDocumentView(XPathDocument document, string xpath)
|
|
: this(document, xpath, null, true) {
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.XPathDocumentView3"]/*' />
|
|
public XPathDocumentView(XPathDocument document, string xpath, IXmlNamespaceResolver namespaceResolver)
|
|
: this(document, xpath, namespaceResolver, false) {
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.XPathDocumentView4"]/*' />
|
|
public XPathDocumentView(XPathDocument document, string xpath, IXmlNamespaceResolver namespaceResolver, bool showPrefixes) {
|
|
if (null == document)
|
|
throw new ArgumentNullException("document");
|
|
this.xpath = xpath;
|
|
this.document = document;
|
|
this.ndRoot = document.Root;
|
|
if (null == this.ndRoot)
|
|
throw new ArgumentException("document");
|
|
this.ndRoot = document.Root;
|
|
this.xpathResolver = namespaceResolver;
|
|
if (showPrefixes)
|
|
this.namespaceResolver = namespaceResolver;
|
|
ArrayList rows = new ArrayList();
|
|
this.rows = rows;
|
|
InitFromXPath(this.ndRoot, xpath);
|
|
}
|
|
|
|
internal XPathDocumentView(XPathNode root, ArrayList rows, Shape rowShape) {
|
|
this.rows = rows;
|
|
this.rowShape = rowShape;
|
|
this.ndRoot = root;
|
|
}
|
|
|
|
//
|
|
// public properties
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Document"]/*' />
|
|
public XPathDocument Document { get { return this.document; } }
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.XPath"]/*' />
|
|
public String XPath { get { return xpath; } }
|
|
|
|
//
|
|
// IEnumerable Implementation
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.GetEnumerator"]/*' />
|
|
public IEnumerator GetEnumerator() {
|
|
return new RowEnumerator(this);
|
|
}
|
|
|
|
//
|
|
// ICollection implementation
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Count"]/*' />
|
|
public int Count {
|
|
get { return this.rows.Count; }
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.IsSynchronized"]/*' />
|
|
public bool IsSynchronized {
|
|
get { return false ; }
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.SyncRoot"]/*' />
|
|
public object SyncRoot {
|
|
get { return null; }
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.CopyTo"]/*' />
|
|
public void CopyTo(Array array, int index) {
|
|
object o;
|
|
ArrayList rows = this.rows;
|
|
for (int i=0; i < rows.Count; i++)
|
|
o = this[i]; // force creation lazy of row object
|
|
rows.CopyTo(array, index);
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.CopyTo2"]/*' />
|
|
/// <devdoc>
|
|
/// <para>strongly typed version of CopyTo, demanded by Fxcop.</para>
|
|
/// </devdoc>
|
|
public void CopyTo(XPathNodeView[] array, int index) {
|
|
object o;
|
|
ArrayList rows = this.rows;
|
|
for (int i=0; i < rows.Count; i++)
|
|
o = this[i]; // force creation lazy of row object
|
|
rows.CopyTo(array, index);
|
|
}
|
|
|
|
//
|
|
// IList Implementation
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.IsReadOnly"]/*' />
|
|
bool IList.IsReadOnly {
|
|
get { return true; }
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.IsFixedSize"]/*' />
|
|
bool IList.IsFixedSize {
|
|
get { return true; }
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Contains"]/*' />
|
|
bool IList.Contains(object value) {
|
|
return this.rows.Contains(value);
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Remove"]/*' />
|
|
void IList.Remove(object value) {
|
|
throw new NotSupportedException("IList.Remove");
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.RemoveAt"]/*' />
|
|
void IList.RemoveAt(int index) {
|
|
throw new NotSupportedException("IList.RemoveAt");
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Clear"]/*' />
|
|
void IList.Clear() {
|
|
throw new NotSupportedException("IList.Clear");
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Add"]/*' />
|
|
int IList.Add(object value) {
|
|
throw new NotSupportedException("IList.Add");
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Insert"]/*' />
|
|
void IList.Insert(int index, object value) {
|
|
throw new NotSupportedException("IList.Insert");
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.IndexOf"]/*' />
|
|
int IList.IndexOf( object value ) {
|
|
return this.rows.IndexOf(value);
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.this"]/*' />
|
|
object IList.this[int index] {
|
|
get {
|
|
object val = this.rows[index];
|
|
if (val is XPathNodeView)
|
|
return val;
|
|
XPathNodeView xiv = FillRow((XPathNode)val, this.rowShape);
|
|
this.rows[index] = xiv;
|
|
return xiv;
|
|
}
|
|
set {
|
|
throw new NotSupportedException("IList.this[]");
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Contains2"]/*' />
|
|
/// <devdoc>
|
|
/// <para>strongly typed version of Contains, demanded by Fxcop.</para>
|
|
/// </devdoc>
|
|
public bool Contains(XPathNodeView value) {
|
|
return this.rows.Contains(value);
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Add2"]/*' />
|
|
/// <devdoc>
|
|
/// <para>strongly typed version of Add, demanded by Fxcop.</para>
|
|
/// </devdoc>
|
|
public int Add(XPathNodeView value) {
|
|
throw new NotSupportedException("IList.Add");
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Insert2"]/*' />
|
|
/// <devdoc>
|
|
/// <para>strongly typed version of Insert, demanded by Fxcop.</para>
|
|
/// </devdoc>
|
|
public void Insert(int index, XPathNodeView value) {
|
|
throw new NotSupportedException("IList.Insert");
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.IndexOf2"]/*' />
|
|
/// <devdoc>
|
|
/// <para>strongly typed version of IndexOf, demanded by Fxcop.</para>
|
|
/// </devdoc>
|
|
public int IndexOf(XPathNodeView value) {
|
|
return this.rows.IndexOf(value);
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Remove2"]/*' />
|
|
/// <devdoc>
|
|
/// <para>strongly typed version of Remove, demanded by Fxcop.</para>
|
|
/// </devdoc>
|
|
public void Remove(XPathNodeView value) {
|
|
throw new NotSupportedException("IList.Remove");
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Item"]/*' />
|
|
/// <devdoc>
|
|
/// <para>strongly typed version of Item, demanded by Fxcop.</para>
|
|
/// </devdoc>
|
|
public XPathNodeView this[int index] {
|
|
get {
|
|
object val = this.rows[index];
|
|
XPathNodeView nodeView;
|
|
nodeView = val as XPathNodeView;
|
|
if (nodeView != null) {
|
|
return nodeView;
|
|
}
|
|
nodeView = FillRow((XPathNode)val, this.rowShape);
|
|
this.rows[index] = nodeView;
|
|
return nodeView;
|
|
}
|
|
set {
|
|
throw new NotSupportedException("IList.this[]");
|
|
}
|
|
}
|
|
|
|
//
|
|
// IBindingList Implementation
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.AllowEdit"]/*' />
|
|
public bool AllowEdit {
|
|
get { return false; }
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.AllowAdd"]/*' />
|
|
public bool AllowAdd {
|
|
get { return false; }
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.AllowRemove"]/*' />
|
|
public bool AllowRemove {
|
|
get { return false; }
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.AllowNew"]/*' />
|
|
public bool AllowNew {
|
|
get { return false; }
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.AddNew"]/*' />
|
|
public object AddNew() {
|
|
throw new NotSupportedException("IBindingList.AddNew");
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.SupportsChangeNotification"]/*' />
|
|
public bool SupportsChangeNotification {
|
|
get { return false; }
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.ListChanged"]/*' />
|
|
public event ListChangedEventHandler ListChanged {
|
|
add {
|
|
throw new NotSupportedException("IBindingList.ListChanged");
|
|
}
|
|
remove {
|
|
throw new NotSupportedException("IBindingList.ListChanged");
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.SupportsSearching"]/*' />
|
|
public bool SupportsSearching {
|
|
get { return false; }
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.SupportsSorting"]/*' />
|
|
public bool SupportsSorting {
|
|
get { return false; }
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.IsSorted"]/*' />
|
|
public bool IsSorted {
|
|
get { return false; }
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.SortProperty"]/*' />
|
|
public PropertyDescriptor SortProperty {
|
|
get { throw new NotSupportedException("IBindingList.SortProperty"); }
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.SortDirection"]/*' />
|
|
public ListSortDirection SortDirection {
|
|
get { throw new NotSupportedException("IBindingList.SortDirection"); }
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.AddIndex"]/*' />
|
|
public void AddIndex( PropertyDescriptor descriptor ) {
|
|
throw new NotSupportedException("IBindingList.AddIndex");
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.ApplySort"]/*' />
|
|
public void ApplySort( PropertyDescriptor descriptor, ListSortDirection direction ) {
|
|
throw new NotSupportedException("IBindingList.ApplySort");
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.Find"]/*' />
|
|
public int Find(PropertyDescriptor propertyDescriptor, object key) {
|
|
throw new NotSupportedException("IBindingList.Find");
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.RemoveIndex"]/*' />
|
|
public void RemoveIndex(PropertyDescriptor propertyDescriptor) {
|
|
throw new NotSupportedException("IBindingList.RemoveIndex");
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.RemoveSort"]/*' />
|
|
public void RemoveSort() {
|
|
throw new NotSupportedException("IBindingList.RemoveSort");
|
|
}
|
|
|
|
|
|
//
|
|
// ITypedList Implementation
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.GetListName"]/*' />
|
|
public string GetListName(PropertyDescriptor[] listAccessors) {
|
|
if( listAccessors == null ) {
|
|
return this.rowShape.Name;
|
|
}
|
|
else {
|
|
return listAccessors[listAccessors.Length-1].Name;
|
|
}
|
|
}
|
|
|
|
/// <include file='doc\XPathDocumentView.uex' path='docs/doc[@for="XPathDocumentView.GetItemProperties"]/*' />
|
|
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors) {
|
|
Shape shape = null;
|
|
if( listAccessors == null ) {
|
|
shape = this.rowShape;
|
|
}
|
|
else {
|
|
XPathNodeViewPropertyDescriptor propdesc = listAccessors[listAccessors.Length-1] as XPathNodeViewPropertyDescriptor;
|
|
if (null != propdesc)
|
|
shape = propdesc.Shape;
|
|
}
|
|
|
|
if (null == shape)
|
|
throw new ArgumentException("listAccessors");
|
|
return new PropertyDescriptorCollection(shape.PropertyDescriptors);
|
|
}
|
|
|
|
|
|
//
|
|
// Internal Implementation
|
|
|
|
|
|
internal Shape RowShape { get { return this.rowShape; } }
|
|
|
|
internal void SetRows(ArrayList rows) {
|
|
Debug.Assert(this.rows == null);
|
|
this.rows = rows;
|
|
}
|
|
|
|
|
|
XPathNodeView FillRow(XPathNode ndRow, Shape shape) {
|
|
object[] columns;
|
|
XPathNode nd;
|
|
switch (shape.BindingType) {
|
|
case BindingType.Text:
|
|
case BindingType.Attribute:
|
|
columns = new object[1];
|
|
columns[0] = ndRow;
|
|
return new XPathNodeView(this, ndRow, columns);
|
|
|
|
case BindingType.Repeat:
|
|
columns = new object[1];
|
|
nd = TreeNavigationHelper.GetContentChild(ndRow);
|
|
columns[0] = FillColumn(new ContentIterator(nd, shape), shape);
|
|
return new XPathNodeView(this, ndRow, columns);
|
|
|
|
case BindingType.Sequence:
|
|
case BindingType.Choice:
|
|
case BindingType.All:
|
|
int subShapesCount = shape.SubShapes.Count;
|
|
columns = new object[subShapesCount];
|
|
if (shape.BindingType == BindingType.Sequence
|
|
&& shape.SubShape(0).BindingType == BindingType.Attribute) {
|
|
FillAttributes(ndRow, shape, columns);
|
|
}
|
|
Shape lastSubShape = (Shape)shape.SubShapes[subShapesCount - 1];
|
|
if (lastSubShape.BindingType == BindingType.Text) { //Attributes followed by simpe content or mixed content
|
|
columns[subShapesCount - 1] = ndRow;
|
|
return new XPathNodeView(this, ndRow, columns);
|
|
}
|
|
else {
|
|
nd = TreeNavigationHelper.GetContentChild(ndRow);
|
|
return FillSubRow(new ContentIterator(nd, shape), shape, columns);
|
|
}
|
|
|
|
default:
|
|
// should not map to a row
|
|
#if DEBUG
|
|
throw new NotSupportedException("Unable to bind row to: "+shape.BindingType.ToString());
|
|
#else
|
|
throw new NotSupportedException();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void FillAttributes(XPathNode nd, Shape shape, object[] cols) {
|
|
int i = 0;
|
|
while (i < cols.Length) {
|
|
Shape attrShape = shape.SubShape(i);
|
|
if (attrShape.BindingType != BindingType.Attribute)
|
|
break;
|
|
XmlQualifiedName name = attrShape.AttributeName;
|
|
XPathNode ndAttr = nd.GetAttribute( name.Name, name.Namespace );
|
|
if (null != ndAttr)
|
|
cols[i] = ndAttr;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
object FillColumn(ContentIterator iter, Shape shape) {
|
|
object val;
|
|
switch (shape.BindingType) {
|
|
case BindingType.Element:
|
|
val = iter.Node;
|
|
iter.Next();
|
|
break;
|
|
|
|
case BindingType.ElementNested: {
|
|
ArrayList rows = new ArrayList();
|
|
rows.Add(iter.Node);
|
|
iter.Next();
|
|
val = new XPathDocumentView(null, rows, shape.NestedShape);
|
|
break;
|
|
}
|
|
|
|
case BindingType.Repeat: {
|
|
ArrayList rows = new ArrayList();
|
|
Shape subShape = shape.SubShape(0);
|
|
if (subShape.BindingType == BindingType.ElementNested) {
|
|
Shape nestShape = subShape.NestedShape;
|
|
XPathDocumentView xivc = new XPathDocumentView(null, null, nestShape);
|
|
XPathNode nd;
|
|
while (null != (nd = iter.Node)
|
|
&& subShape.IsParticleMatch(iter.Particle)) {
|
|
rows.Add(nd);
|
|
iter.Next();
|
|
}
|
|
xivc.SetRows(rows);
|
|
val = xivc;
|
|
}
|
|
else {
|
|
XPathDocumentView xivc = new XPathDocumentView(null, null, subShape);
|
|
XPathNode nd;
|
|
while (null != (nd = iter.Node)
|
|
&& shape.IsParticleMatch(iter.Particle)) {
|
|
rows.Add(xivc.FillSubRow(iter, subShape, null));
|
|
}
|
|
xivc.SetRows(rows);
|
|
val = xivc;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case BindingType.Sequence:
|
|
case BindingType.Choice:
|
|
case BindingType.All: {
|
|
XPathDocumentView docview = new XPathDocumentView(null, null, shape);
|
|
ArrayList rows = new ArrayList();
|
|
rows.Add(docview.FillSubRow(iter, shape, null));
|
|
docview.SetRows(rows);
|
|
val = docview;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
case BindingType.Text:
|
|
case BindingType.Attribute:
|
|
throw new NotSupportedException();
|
|
}
|
|
return val;
|
|
}
|
|
|
|
XPathNodeView FillSubRow(ContentIterator iter, Shape shape, object[] columns) {
|
|
if (null == columns) {
|
|
int colCount = shape.SubShapes.Count;
|
|
if (0 == colCount)
|
|
colCount = 1;
|
|
columns = new object[colCount];
|
|
}
|
|
|
|
switch (shape.BindingType) {
|
|
case BindingType.Element:
|
|
columns[0] = FillColumn(iter, shape);
|
|
break;
|
|
|
|
case BindingType.Sequence: {
|
|
int iPrev = -1;
|
|
int i;
|
|
while (null != iter.Node) {
|
|
i = shape.FindMatchingSubShape(iter.Particle);
|
|
if (i <= iPrev)
|
|
break;
|
|
columns[i] = FillColumn(iter, shape.SubShape(i));
|
|
iPrev = i;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case BindingType.All: {
|
|
while (null != iter.Node) {
|
|
int i = shape.FindMatchingSubShape(iter.Particle);
|
|
if (-1 == i || null != columns[i])
|
|
break;
|
|
columns[i] = FillColumn(iter, shape.SubShape(i));
|
|
}
|
|
break;
|
|
}
|
|
|
|
case BindingType.Choice: {
|
|
int i = shape.FindMatchingSubShape(iter.Particle);
|
|
if (-1 != i) {
|
|
columns[i] = FillColumn(iter, shape.SubShape(i));
|
|
}
|
|
break;
|
|
}
|
|
|
|
case BindingType.Repeat:
|
|
default:
|
|
// should not map to a row
|
|
throw new NotSupportedException();
|
|
}
|
|
return new XPathNodeView(this, null, columns);
|
|
}
|
|
|
|
//
|
|
// XPath support
|
|
//
|
|
|
|
void InitFromXPath(XPathNode ndRoot, string xpath) {
|
|
XPathStep[] steps = ParseXPath(xpath, this.xpathResolver);
|
|
ArrayList rows = this.rows;
|
|
rows.Clear();
|
|
PopulateFromXPath(ndRoot, steps, 0);
|
|
DeriveShapeFromRows();
|
|
}
|
|
|
|
void DeriveShapeFromRows() {
|
|
object schemaInfo = null;
|
|
for (int i=0; (i<rows.Count) && (null==schemaInfo); i++) {
|
|
XPathNode nd = rows[i] as XPathNode;
|
|
Debug.Assert(null != nd && (XPathNodeType.Attribute == nd.NodeType || XPathNodeType.Element == nd.NodeType));
|
|
if (null != nd) {
|
|
if (XPathNodeType.Attribute == nd.NodeType)
|
|
schemaInfo = nd.SchemaAttribute;
|
|
else
|
|
schemaInfo = nd.SchemaElement;
|
|
}
|
|
}
|
|
if (0 == rows.Count) {
|
|
// TODO:
|
|
throw new NotImplementedException("XPath failed to match an elements");
|
|
}
|
|
if (null == schemaInfo) {
|
|
rows.Clear();
|
|
throw new XmlException(Res.XmlDataBinding_NoSchemaType, (string[])null);
|
|
}
|
|
ShapeGenerator shapeGen = new ShapeGenerator(this.namespaceResolver);
|
|
XmlSchemaElement xse = schemaInfo as XmlSchemaElement;
|
|
if (null != xse)
|
|
this.rowShape = shapeGen.GenerateFromSchema(xse);
|
|
else
|
|
this.rowShape = shapeGen.GenerateFromSchema((XmlSchemaAttribute)schemaInfo);
|
|
}
|
|
|
|
void PopulateFromXPath(XPathNode nd, XPathStep[] steps, int step) {
|
|
string ln = steps[step].name.Name;
|
|
string ns = steps[step].name.Namespace;
|
|
if (XPathNodeType.Attribute == steps[step].type) {
|
|
XPathNode ndAttr = nd.GetAttribute( ln, ns, true);
|
|
if (null != ndAttr) {
|
|
if (null != ndAttr.SchemaAttribute)
|
|
this.rows.Add(ndAttr);
|
|
}
|
|
}
|
|
else {
|
|
XPathNode ndChild = TreeNavigationHelper.GetElementChild(nd, ln, ns, true);
|
|
if (null != ndChild) {
|
|
int nextStep = step+1;
|
|
do {
|
|
if (steps.Length == nextStep) {
|
|
if (null != ndChild.SchemaType)
|
|
this.rows.Add(ndChild);
|
|
}
|
|
else {
|
|
PopulateFromXPath(ndChild, steps, nextStep);
|
|
}
|
|
ndChild = TreeNavigationHelper.GetElementSibling(ndChild, ln, ns, true);
|
|
} while (null != ndChild);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// This is the limited grammar we support
|
|
// Path ::= '/ ' ( Step '/')* ( QName | '@' QName )
|
|
// Step ::= '.' | QName
|
|
// This is encoded as an array of XPathStep structs
|
|
struct XPathStep {
|
|
internal XmlQualifiedName name;
|
|
internal XPathNodeType type;
|
|
}
|
|
|
|
// Parse xpath (limited to above grammar), using provided namespaceResolver
|
|
// to resolve prefixes.
|
|
XPathStep[] ParseXPath(string xpath, IXmlNamespaceResolver xnr) {
|
|
int pos;
|
|
int stepCount = 1;
|
|
for (pos=1; pos<(xpath.Length-1); pos++) {
|
|
if ( ('/' == xpath[pos]) && ('.' != xpath[pos+1]) )
|
|
stepCount++;
|
|
}
|
|
XPathStep[] steps = new XPathStep[stepCount];
|
|
pos = 0;
|
|
int i = 0;
|
|
for (;;) {
|
|
if (pos >= xpath.Length)
|
|
throw new XmlException(Res.XmlDataBinding_XPathEnd, (string[])null);
|
|
if ('/' != xpath[pos])
|
|
throw new XmlException(Res.XmlDataBinding_XPathRequireSlash, (string[])null);
|
|
pos++;
|
|
char ch = xpath[pos];
|
|
if (ch == '.') {
|
|
pos++;
|
|
// again...
|
|
}
|
|
else if ('@' == ch) {
|
|
if (0 == i)
|
|
throw new XmlException(Res.XmlDataBinding_XPathAttrNotFirst, (string[])null);
|
|
pos++;
|
|
if (pos >= xpath.Length)
|
|
throw new XmlException(Res.XmlDataBinding_XPathEnd, (string[])null);
|
|
steps[i].name = ParseQName(xpath, ref pos, xnr);
|
|
steps[i].type = XPathNodeType.Attribute;
|
|
i++;
|
|
if (pos != xpath.Length)
|
|
throw new XmlException(Res.XmlDataBinding_XPathAttrLast, (string[])null);
|
|
break;
|
|
}
|
|
else {
|
|
steps[i].name = ParseQName(xpath, ref pos, xnr);
|
|
steps[i].type = XPathNodeType.Element;
|
|
i++;
|
|
if (pos == xpath.Length)
|
|
break;
|
|
}
|
|
}
|
|
Debug.Assert(i == steps.Length);
|
|
return steps;
|
|
}
|
|
|
|
// Parse a QName from the string, and resolve prefix
|
|
XmlQualifiedName ParseQName(string xpath, ref int pos, IXmlNamespaceResolver xnr) {
|
|
string nm = ParseName(xpath, ref pos);
|
|
if (pos < xpath.Length && ':' == xpath[pos]) {
|
|
pos++;
|
|
string ns = (null==xnr) ? null : xnr.LookupNamespace(nm);
|
|
if (null == ns || 0 == ns.Length)
|
|
throw new XmlException(Res.Sch_UnresolvedPrefix, nm);
|
|
return new XmlQualifiedName(ParseName(xpath, ref pos), ns);
|
|
}
|
|
else {
|
|
return new XmlQualifiedName(nm);
|
|
}
|
|
}
|
|
|
|
// Parse a NCNAME from the string
|
|
string ParseName(string xpath, ref int pos) {
|
|
char ch;
|
|
int start = pos++;
|
|
while (pos < xpath.Length
|
|
&& '/' != (ch = xpath[pos])
|
|
&& ':' != ch)
|
|
pos++;
|
|
string nm = xpath.Substring(start, pos - start);
|
|
if (!XmlReader.IsName(nm))
|
|
throw new XmlException(Res.Xml_InvalidNameChars, (string[])null);
|
|
return this.document.NameTable.Add(nm);
|
|
}
|
|
|
|
|
|
//
|
|
// Helper classes
|
|
//
|
|
|
|
class ContentIterator {
|
|
XPathNode node;
|
|
ContentValidator contentValidator;
|
|
ValidationState currentState;
|
|
object currentParticle;
|
|
|
|
public ContentIterator(XPathNode nd, Shape shape) {
|
|
this.node = nd;
|
|
XmlSchemaElement xse = shape.XmlSchemaElement;
|
|
Debug.Assert(null != xse);
|
|
SchemaElementDecl decl = xse.ElementDecl;
|
|
Debug.Assert(null != decl);
|
|
this.contentValidator = decl.ContentValidator;
|
|
this.currentState = new ValidationState();
|
|
this.contentValidator.InitValidation(this.currentState);
|
|
this.currentState.ProcessContents = XmlSchemaContentProcessing.Strict;
|
|
if (nd != null)
|
|
Advance();
|
|
}
|
|
|
|
public XPathNode Node { get { return this.node; } }
|
|
public object Particle { get { return this.currentParticle; } }
|
|
|
|
public bool Next() {
|
|
if (null != this.node) {
|
|
this.node = TreeNavigationHelper.GetContentSibling(this.node, XPathNodeType.Element);
|
|
if (node != null)
|
|
Advance();
|
|
return null != this.node;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void Advance() {
|
|
XPathNode nd = this.node;
|
|
int errorCode;
|
|
this.currentParticle = this.contentValidator.ValidateElement(new XmlQualifiedName(nd.LocalName, nd.NamespaceUri), this.currentState, out errorCode);
|
|
if (null == this.currentParticle || 0 != errorCode) {
|
|
this.node = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Helper class to implement enumerator over rows
|
|
// We can't just use ArrayList enumerator because
|
|
// sometims rows may be lazily constructed
|
|
sealed class RowEnumerator : IEnumerator {
|
|
XPathDocumentView collection;
|
|
int pos;
|
|
|
|
internal RowEnumerator(XPathDocumentView collection) {
|
|
this.collection = collection;
|
|
this.pos = -1;
|
|
}
|
|
|
|
public object Current {
|
|
get {
|
|
if (this.pos < 0 || this.pos >= this.collection.Count)
|
|
return null;
|
|
return this.collection[this.pos];
|
|
}
|
|
}
|
|
|
|
public void Reset() {
|
|
this.pos = -1;
|
|
}
|
|
|
|
public bool MoveNext() {
|
|
this.pos++;
|
|
int max = this.collection.Count;
|
|
if (this.pos > max)
|
|
this.pos = max;
|
|
return this.pos < max;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif |