XPathDocumentView.cs 32 KB

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