DTMXPathDocumentWriter.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. //
  2. // Mono.Xml.XPath.DTMXPathDocumentWriter
  3. //
  4. // Author:
  5. // Atsushi Enomoto ([email protected])
  6. //
  7. // (C) 2003 Atsushi Enomoto
  8. //
  9. using System;
  10. using System.Collections;
  11. using System.IO;
  12. using System.Xml;
  13. using System.Xml.Schema;
  14. using System.Xml.XPath;
  15. namespace Mono.Xml.XPath
  16. {
  17. #if OUTSIDE_SYSTEM_XML
  18. public
  19. #else
  20. internal
  21. #endif
  22. class DTMXPathDocumentWriter : XmlWriter
  23. {
  24. public DTMXPathDocumentWriter (XmlNameTable nt, int defaultCapacity)
  25. {
  26. nameTable = nt == null ? new NameTable () : nt;
  27. nodeCapacity = defaultCapacity;
  28. attributeCapacity = nodeCapacity;
  29. idTable = new Hashtable ();
  30. nodes = new DTMXPathLinkedNode [nodeCapacity];
  31. attributes = new DTMXPathAttributeNode [attributeCapacity];
  32. namespaces = new DTMXPathNamespaceNode [0];
  33. Init ();
  34. }
  35. XmlNameTable nameTable;
  36. int nodeCapacity = 200;
  37. int attributeCapacity = 200;
  38. int nsCapacity = 10;
  39. // Linked Node
  40. DTMXPathLinkedNode [] nodes;
  41. // Attribute
  42. DTMXPathAttributeNode [] attributes;
  43. // NamespaceNode
  44. DTMXPathNamespaceNode [] namespaces;
  45. // idTable [string value] -> int nodeId
  46. Hashtable idTable;
  47. int nodeIndex;
  48. int attributeIndex;
  49. int nsIndex;
  50. int parentForFirstChild;
  51. // for attribute processing; should be reset per each element.
  52. int firstAttributeIndex;
  53. int lastNsIndexInCurrent;
  54. int attrIndexAtStart;
  55. int nsIndexAtStart;
  56. int prevSibling;
  57. int lastNsInScope;
  58. // They are only used in Writer
  59. int writerDepth;
  60. WriteState state;
  61. bool openNamespace;
  62. public DTMXPathDocument CreateDocument ()
  63. {
  64. return new DTMXPathDocument (nameTable,
  65. nodes,
  66. attributes,
  67. namespaces,
  68. idTable
  69. );
  70. }
  71. public void Init ()
  72. {
  73. // index 0 is dummy. No node (including Root) is assigned to this index
  74. // So that we can easily compare index != 0 instead of index < 0.
  75. // (Difference between jnz or jbe in 80x86.)
  76. AddNode (0, 0, 0, 0, XPathNodeType.All, "", false, "", "", "", "", "", 0, 0, 0);
  77. nodeIndex++;
  78. AddAttribute (0, null, null, null, null, null, 0, 0);
  79. AddNsNode (0, null, null, 0);
  80. nsIndex++;
  81. AddNsNode (1, "xml", XmlNamespaces.XML, 0);
  82. // add root.
  83. AddNode (0, 0, 0, -1, XPathNodeType.Root, null, false, "", "", "", "", "", 1, 0, 0);
  84. this.nodeIndex = 1;
  85. this.lastNsInScope = 1;
  86. this.parentForFirstChild = nodeIndex;
  87. state = WriteState.Content;
  88. }
  89. private int GetParentIndex ()
  90. {
  91. if (parentForFirstChild >= 0)
  92. return parentForFirstChild;
  93. int parent = nodeIndex;
  94. if (nodes [nodeIndex].Depth >= writerDepth) {
  95. // if not, then current node is parent.
  96. while (writerDepth <= nodes [parent].Depth)
  97. parent = nodes [parent].Parent;
  98. }
  99. return parent;
  100. }
  101. private int GetPreviousSiblingIndex ()
  102. {
  103. int prevSibling = nodeIndex;
  104. if (parentForFirstChild >= 0)
  105. prevSibling = 0;
  106. else
  107. while (nodes [prevSibling].Depth != writerDepth)
  108. prevSibling = nodes [prevSibling].Parent;
  109. return prevSibling;
  110. }
  111. private void UpdateTreeForAddition ()
  112. {
  113. int parent = GetParentIndex ();
  114. prevSibling = GetPreviousSiblingIndex ();
  115. nodeIndex++;
  116. if (prevSibling != 0)
  117. nodes [prevSibling].NextSibling = nodeIndex;
  118. if (parentForFirstChild >= 0)
  119. nodes [parent].FirstChild = nodeIndex;
  120. parentForFirstChild = -1;
  121. }
  122. private void CloseStartElement ()
  123. {
  124. if (attrIndexAtStart != attributeIndex)
  125. nodes [nodeIndex].FirstAttribute = attrIndexAtStart + 1;
  126. if (nsIndexAtStart != nsIndex) {
  127. nodes [nodeIndex].FirstNamespace = nsIndex;
  128. lastNsInScope = nsIndex;
  129. }
  130. if (!nodes [nodeIndex].IsEmptyElement)
  131. parentForFirstChild = nodeIndex;
  132. state = WriteState.Content;
  133. writerDepth++;
  134. }
  135. #region Adding Nodes
  136. private void SetNodeArrayLength (int size)
  137. {
  138. DTMXPathLinkedNode [] newArr = new DTMXPathLinkedNode [size];
  139. Array.Copy (nodes, newArr, System.Math.Min (size, nodes.Length));
  140. nodes = newArr;
  141. }
  142. private void SetAttributeArrayLength (int size)
  143. {
  144. DTMXPathAttributeNode [] newArr =
  145. new DTMXPathAttributeNode [size];
  146. Array.Copy (attributes, newArr, System.Math.Min (size, attributes.Length));
  147. attributes = newArr;
  148. }
  149. private void SetNsArrayLength (int size)
  150. {
  151. DTMXPathNamespaceNode [] newArr =
  152. new DTMXPathNamespaceNode [size];
  153. Array.Copy (namespaces, newArr, System.Math.Min (size, namespaces.Length));
  154. namespaces = newArr;
  155. }
  156. // Here followings are skipped: firstChild, nextSibling,
  157. public void AddNode (int parent, int firstAttribute, int previousSibling, int depth, XPathNodeType nodeType, string baseUri, bool isEmptyElement, string localName, string ns, string prefix, string value, string xmlLang, int namespaceNode, int lineNumber, int linePosition)
  158. {
  159. if (nodes.Length < nodeIndex + 1) {
  160. nodeCapacity *= 4;
  161. SetNodeArrayLength (nodeCapacity);
  162. }
  163. #if DTM_CLASS
  164. nodes [nodeIndex] = new DTMXPathLinkedNode ();
  165. #endif
  166. nodes [nodeIndex].FirstChild = 0; // dummy
  167. nodes [nodeIndex].Parent = parent;
  168. nodes [nodeIndex].FirstAttribute = firstAttribute;
  169. nodes [nodeIndex].PreviousSibling = previousSibling;
  170. nodes [nodeIndex].NextSibling = 0; // dummy
  171. nodes [nodeIndex].Depth = depth;
  172. nodes [nodeIndex].NodeType = nodeType;
  173. nodes [nodeIndex].BaseURI = baseUri;
  174. nodes [nodeIndex].IsEmptyElement = isEmptyElement;
  175. nodes [nodeIndex].LocalName = localName;
  176. nodes [nodeIndex].NamespaceURI = ns;
  177. nodes [nodeIndex].Prefix = prefix;
  178. nodes [nodeIndex].Value = value;
  179. nodes [nodeIndex].XmlLang = xmlLang;
  180. nodes [nodeIndex].FirstNamespace = namespaceNode;
  181. nodes [nodeIndex].LineNumber = lineNumber;
  182. nodes [nodeIndex].LinePosition = linePosition;
  183. }
  184. // Followings are skipped: nextAttribute,
  185. public void AddAttribute (int ownerElement, string localName, string ns, string prefix, string value, object schemaType, int lineNumber, int linePosition)
  186. {
  187. if (attributes.Length < attributeIndex + 1) {
  188. attributeCapacity *= 4;
  189. SetAttributeArrayLength (attributeCapacity);
  190. }
  191. #if DTM_CLASS
  192. attributes [attributeIndex] = new DTMXPathAttributeNode ();
  193. #endif
  194. attributes [attributeIndex].OwnerElement = ownerElement;
  195. attributes [attributeIndex].LocalName = localName;
  196. attributes [attributeIndex].NamespaceURI = ns;
  197. attributes [attributeIndex].Prefix = prefix;
  198. attributes [attributeIndex].Value = value;
  199. attributes [attributeIndex].SchemaType = schemaType;
  200. attributes [attributeIndex].LineNumber = lineNumber;
  201. attributes [attributeIndex].LinePosition = linePosition;
  202. }
  203. // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
  204. public void AddNsNode (int declaredElement, string name, string ns, int nextNs)
  205. {
  206. if (namespaces.Length < nsIndex + 1) {
  207. nsCapacity *= 4;
  208. SetNsArrayLength (nsCapacity);
  209. }
  210. #if DTM_CLASS
  211. namespaces [nsIndex] = new DTMXPathNamespaceNode ();
  212. #endif
  213. namespaces [nsIndex].DeclaredElement = declaredElement;
  214. namespaces [nsIndex].Name = name;
  215. namespaces [nsIndex].Namespace = ns;
  216. namespaces [nsIndex].NextNamespace = nextNs;
  217. }
  218. #endregion
  219. #region XmlWriter methods
  220. // They are not supported
  221. public override string XmlLang { get { return null; } }
  222. public override XmlSpace XmlSpace { get { return XmlSpace.None; } }
  223. public override WriteState WriteState { get { return state; } }
  224. public override void Close ()
  225. {
  226. // Fixup arrays
  227. SetNodeArrayLength (nodeIndex + 1);
  228. SetAttributeArrayLength (attributeIndex + 1);
  229. SetNsArrayLength (nsIndex + 1);
  230. }
  231. public override void Flush ()
  232. {
  233. // do nothing
  234. }
  235. public override string LookupPrefix (string ns)
  236. {
  237. int tmp = nsIndex;
  238. while (tmp != 0) {
  239. if (namespaces [tmp].Namespace == ns)
  240. return namespaces [tmp].Name;
  241. tmp = namespaces [tmp].NextNamespace;
  242. }
  243. return null;
  244. }
  245. public override void WriteCData (string data)
  246. {
  247. AddTextNode (data);
  248. }
  249. private void AddTextNode (string data)
  250. {
  251. switch (state) {
  252. case WriteState.Element:
  253. CloseStartElement ();
  254. break;
  255. case WriteState.Content:
  256. break;
  257. default:
  258. throw new InvalidOperationException ("Invalid document state for CDATA section: " + state);
  259. }
  260. // When text after text, just add the value, and return.
  261. if (nodes [nodeIndex].Depth == writerDepth) {
  262. switch (nodes [nodeIndex].NodeType) {
  263. case XPathNodeType.Text:
  264. case XPathNodeType.SignificantWhitespace:
  265. nodes [nodeIndex].Value += data;
  266. if (IsWhitespace (data))
  267. nodes [nodeIndex].NodeType = XPathNodeType.SignificantWhitespace;
  268. else
  269. nodes [nodeIndex].NodeType = XPathNodeType.Text;
  270. return;
  271. }
  272. }
  273. int parent = GetParentIndex ();
  274. UpdateTreeForAddition ();
  275. AddNode (parent,
  276. 0, // attribute
  277. prevSibling,
  278. writerDepth,
  279. XPathNodeType.Text,
  280. null,
  281. false,
  282. null,
  283. String.Empty,
  284. String.Empty,
  285. data,
  286. null,
  287. 0, // nsIndex
  288. 0, // line info
  289. 0);
  290. }
  291. private void CheckTopLevelNode ()
  292. {
  293. switch (state) {
  294. case WriteState.Element:
  295. CloseStartElement ();
  296. break;
  297. case WriteState.Content:
  298. case WriteState.Prolog:
  299. case WriteState.Start:
  300. break;
  301. default:
  302. throw new InvalidOperationException ("Invalid document state for CDATA section: " + state);
  303. }
  304. }
  305. public override void WriteComment (string data)
  306. {
  307. CheckTopLevelNode ();
  308. int parent = GetParentIndex ();
  309. UpdateTreeForAddition ();
  310. AddNode (parent,
  311. 0, // attribute
  312. prevSibling,
  313. writerDepth,
  314. XPathNodeType.Comment,
  315. null,
  316. false,
  317. null,
  318. String.Empty,
  319. String.Empty,
  320. data,
  321. null,
  322. 0, // nsIndex
  323. 0, // line info
  324. 0);
  325. }
  326. public override void WriteProcessingInstruction (string name, string data)
  327. {
  328. CheckTopLevelNode ();
  329. int parent = GetParentIndex ();
  330. UpdateTreeForAddition ();
  331. AddNode (parent,
  332. 0, // attribute
  333. prevSibling,
  334. writerDepth,
  335. XPathNodeType.ProcessingInstruction,
  336. null,
  337. false,
  338. name,
  339. String.Empty,
  340. String.Empty,
  341. data,
  342. null,
  343. 0, // nsIndex
  344. 0, // line info
  345. 0);
  346. }
  347. public override void WriteWhitespace (string data)
  348. {
  349. CheckTopLevelNode ();
  350. int parent = GetParentIndex ();
  351. UpdateTreeForAddition ();
  352. AddNode (parent,
  353. 0, // attribute
  354. prevSibling,
  355. writerDepth,
  356. XPathNodeType.Whitespace,
  357. null,
  358. false,
  359. null,
  360. String.Empty,
  361. String.Empty,
  362. data,
  363. null,
  364. 0, // nsIndex
  365. 0, // line info
  366. 0);
  367. }
  368. public override void WriteStartDocument ()
  369. {
  370. // do nothing
  371. }
  372. public override void WriteStartDocument (bool standalone)
  373. {
  374. // do nothing
  375. }
  376. public override void WriteEndDocument ()
  377. {
  378. // do nothing
  379. }
  380. public override void WriteStartElement (string prefix, string localName, string ns)
  381. {
  382. switch (state) {
  383. case WriteState.Element:
  384. CloseStartElement ();
  385. break;
  386. case WriteState.Start:
  387. case WriteState.Prolog:
  388. case WriteState.Content:
  389. break;
  390. default:
  391. throw new InvalidOperationException ("Invalid document state for writing element: " + state);
  392. }
  393. int parent = GetParentIndex ();
  394. UpdateTreeForAddition ();
  395. WriteStartElement (parent, prevSibling, prefix, localName, ns);
  396. state = WriteState.Element;
  397. }
  398. private void WriteStartElement (int parent, int previousSibling, string prefix, string localName, string ns)
  399. {
  400. PrepareStartElement (previousSibling);
  401. AddNode (parent,
  402. 0, // dummy:firstAttribute
  403. previousSibling,
  404. writerDepth,
  405. XPathNodeType.Element,
  406. null,
  407. false,
  408. localName,
  409. ns,
  410. prefix,
  411. "", // Element has no internal value.
  412. null,
  413. lastNsInScope,
  414. 0,
  415. 0);
  416. }
  417. private void PrepareStartElement (int previousSibling)
  418. {
  419. firstAttributeIndex = 0;
  420. lastNsIndexInCurrent = 0;
  421. attrIndexAtStart = attributeIndex;
  422. nsIndexAtStart = nsIndex;
  423. while (namespaces [lastNsInScope].DeclaredElement == previousSibling) {
  424. lastNsInScope = namespaces [lastNsInScope].NextNamespace;
  425. }
  426. }
  427. public override void WriteEndElement ()
  428. {
  429. WriteEndElement (false);
  430. }
  431. public override void WriteFullEndElement ()
  432. {
  433. WriteEndElement (true);
  434. }
  435. private void WriteEndElement (bool full)
  436. {
  437. switch (state) {
  438. case WriteState.Element:
  439. CloseStartElement ();
  440. break;
  441. case WriteState.Content:
  442. break;
  443. default:
  444. throw new InvalidOperationException ("Invalid state for writing EndElement: " + state);
  445. }
  446. parentForFirstChild = -1;
  447. if (nodes [nodeIndex].NodeType == XPathNodeType.Element) {
  448. if (!full)
  449. nodes [nodeIndex].IsEmptyElement = true;
  450. }
  451. writerDepth--;
  452. }
  453. public override void WriteStartAttribute (string prefix, string localName, string ns)
  454. {
  455. if (state != WriteState.Element)
  456. throw new InvalidOperationException ("Invalid document state for attribute: " + state);
  457. state = WriteState.Attribute;
  458. if (ns == XmlNamespaces.XMLNS)
  459. ProcessNamespace ((prefix == null || prefix == String.Empty) ? "" : localName, String.Empty); // dummy: Value should be completed
  460. else
  461. ProcessAttribute (prefix, localName, ns, String.Empty); // dummy: Value should be completed
  462. }
  463. private void ProcessNamespace (string prefix, string ns)
  464. {
  465. nsIndex++;
  466. int nextTmp = lastNsIndexInCurrent == 0 ? nodes [nodeIndex].FirstNamespace : lastNsIndexInCurrent;
  467. this.AddNsNode (nodeIndex,
  468. prefix,
  469. ns,
  470. nextTmp);
  471. lastNsIndexInCurrent = nsIndex;
  472. openNamespace = true;
  473. }
  474. private void ProcessAttribute (string prefix, string localName, string ns, string value)
  475. {
  476. attributeIndex ++;
  477. this.AddAttribute (nodeIndex,
  478. localName,
  479. ns,
  480. prefix != null ? prefix : String.Empty,
  481. value,
  482. null,
  483. 0,
  484. 0);
  485. if (firstAttributeIndex == 0)
  486. firstAttributeIndex = attributeIndex;
  487. else
  488. attributes [attributeIndex - 1].NextAttribute = attributeIndex;
  489. }
  490. public override void WriteEndAttribute ()
  491. {
  492. if (state != WriteState.Attribute)
  493. throw new InvalidOperationException ();
  494. openNamespace = false;
  495. state = WriteState.Element;
  496. }
  497. public override void WriteString (string text)
  498. {
  499. if (WriteState == WriteState.Attribute) {
  500. if (openNamespace)
  501. namespaces [nsIndex].Namespace += text;
  502. else
  503. attributes [attributeIndex].Value += text;
  504. }
  505. else
  506. AddTextNode (text);
  507. }
  508. // Well, they cannot be supported, but actually used to
  509. // disable-output-escaping = "true"
  510. public override void WriteRaw (string data)
  511. {
  512. WriteString (data);
  513. }
  514. public override void WriteRaw (char [] data, int start, int len)
  515. {
  516. WriteString (new string (data, start, len));
  517. }
  518. public override void WriteName (string name)
  519. {
  520. WriteString (name);
  521. }
  522. public override void WriteNmToken (string name)
  523. {
  524. WriteString (name);
  525. }
  526. public override void WriteBase64 (byte [] buffer, int index, int count)
  527. {
  528. throw new NotSupportedException ();
  529. }
  530. public override void WriteBinHex (byte [] buffer, int index, int count)
  531. {
  532. throw new NotSupportedException ();
  533. }
  534. public override void WriteChars (char [] buffer, int index, int count)
  535. {
  536. throw new NotSupportedException ();
  537. }
  538. public override void WriteCharEntity (char c)
  539. {
  540. throw new NotSupportedException ();
  541. }
  542. public override void WriteDocType (string name, string pub, string sys, string intSubset)
  543. {
  544. throw new NotSupportedException ();
  545. }
  546. public override void WriteEntityRef (string name)
  547. {
  548. throw new NotSupportedException ();
  549. }
  550. public override void WriteQualifiedName (string localName, string ns)
  551. {
  552. throw new NotSupportedException ();
  553. }
  554. public override void WriteSurrogateCharEntity (char high, char low)
  555. {
  556. throw new NotSupportedException ();
  557. }
  558. private bool IsWhitespace (string data)
  559. {
  560. for (int i = 0; i < data.Length; i++) {
  561. switch (data [i]) {
  562. case ' ':
  563. case '\r':
  564. case '\n':
  565. case '\t':
  566. continue;
  567. default:
  568. return false;
  569. }
  570. }
  571. return true;
  572. }
  573. #endregion
  574. }
  575. }