2 // Mono.Xml.XPath.DTMXPathDocumentWriter2
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // (C) 2004 Novell Inc.
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Collections;
34 using System.Xml.Schema;
35 using System.Xml.XPath;
37 namespace Mono.Xml.XPath
39 #if OUTSIDE_SYSTEM_XML
44 class DTMXPathDocumentWriter2 : XmlWriter
46 public DTMXPathDocumentWriter2 (XmlNameTable nt, int defaultCapacity)
48 nameTable = nt == null ? new NameTable () : nt;
49 nodeCapacity = defaultCapacity;
50 attributeCapacity = nodeCapacity;
52 idTable = new Hashtable ();
54 nodes = new DTMXPathLinkedNode2 [nodeCapacity];
55 attributes = new DTMXPathAttributeNode2 [attributeCapacity];
56 namespaces = new DTMXPathNamespaceNode2 [nsCapacity];
58 atomicStringPool = new string [20];
59 nonAtomicStringPool = new string [20];
64 XmlNameTable nameTable;
66 int attributeCapacity;
70 DTMXPathLinkedNode2 [] nodes;
73 DTMXPathAttributeNode2 [] attributes;
76 DTMXPathNamespaceNode2 [] namespaces;
79 string [] atomicStringPool;
80 string [] nonAtomicStringPool;
82 // idTable [string value] -> int nodeId
88 int parentForFirstChild;
90 // for attribute processing; should be reset per each element.
91 int firstAttributeIndex;
92 int lastNsIndexInCurrent;
99 // They are only used in Writer
105 public DTMXPathDocument2 CreateDocument ()
109 return new DTMXPathDocument2 (nameTable,
121 // string pool index 0 to 3 are fixed.
122 atomicStringPool [0] = nonAtomicStringPool [0] = "";
123 atomicStringPool [1] = nonAtomicStringPool [1] = null;
124 atomicStringPool [2] = nonAtomicStringPool [2] = XmlNamespaces.XML;
125 atomicStringPool [3] = nonAtomicStringPool [3] = XmlNamespaces.XMLNS;
127 // index 0 is dummy. No node (including Root) is assigned to this index
128 // So that we can easily compare index != 0 instead of index < 0.
129 // (Difference between jnz or jbe in 80x86.)
130 AddNode (0, 0, 0, 0, XPathNodeType.All, "", false, "", "", "", "", "", 0, 0, 0);
132 AddAttribute (0, "", "", "", "", 0, 0);
133 AddNsNode (0, "", "", 0);
135 AddNsNode (1, "xml", XmlNamespaces.XML, 0);
138 AddNode (0, 0, 0, -1, XPathNodeType.Root, "", false, "", "", "", "", "", 1, 0, 0);
141 this.lastNsInScope = 1;
142 this.parentForFirstChild = nodeIndex;
144 state = WriteState.Content;
147 private int GetParentIndex ()
149 if (parentForFirstChild >= 0)
150 return parentForFirstChild;
152 int parent = nodeIndex;
153 if (nodes [nodeIndex].Depth >= writerDepth) {
154 // if not, then current node is parent.
155 while (writerDepth <= nodes [parent].Depth)
156 parent = nodes [parent].Parent;
161 private int GetPreviousSiblingIndex ()
163 int prevSibling = nodeIndex;
164 if (parentForFirstChild >= 0)
167 while (nodes [prevSibling].Depth != writerDepth)
168 prevSibling = nodes [prevSibling].Parent;
172 private void UpdateTreeForAddition ()
174 int parent = GetParentIndex ();
175 prevSibling = GetPreviousSiblingIndex ();
179 if (prevSibling != 0)
180 nodes [prevSibling].NextSibling = nodeIndex;
181 if (parentForFirstChild >= 0)
182 nodes [parent].FirstChild = nodeIndex;
184 parentForFirstChild = -1;
187 private void CloseStartElement ()
189 if (attrIndexAtStart != attributeIndex)
190 nodes [nodeIndex].FirstAttribute = attrIndexAtStart + 1;
191 if (nsIndexAtStart != nsIndex) {
192 nodes [nodeIndex].FirstNamespace = nsIndex;
193 lastNsInScope = nsIndex;
196 if (!nodes [nodeIndex].IsEmptyElement)
197 parentForFirstChild = nodeIndex;
199 state = WriteState.Content;
\r
205 private int AtomicIndex (string s)
212 for (; i < atomicStringPool.Length; i++) {
213 if (atomicStringPool [i] == null) {
214 atomicStringPool [i] = s;
217 else if (Object.ReferenceEquals (s,
218 atomicStringPool [i]))
221 string [] newArr = new string [i * 2];
222 Array.Copy (atomicStringPool, newArr, i);
223 atomicStringPool = newArr;
224 atomicStringPool [i] = s;
228 private int NonAtomicIndex (string s)
235 for (; i < nonAtomicStringPool.Length; i++) {
236 if (nonAtomicStringPool [i] == null) {
237 nonAtomicStringPool [i] = s;
240 else if (s == nonAtomicStringPool [i])
243 string [] newArr = new string [i * 2];
244 Array.Copy (nonAtomicStringPool, newArr, i);
245 nonAtomicStringPool = newArr;
246 nonAtomicStringPool [i] = s;
250 private void SetNodeArrayLength (int size)
252 DTMXPathLinkedNode2 [] newArr = new DTMXPathLinkedNode2 [size];
253 Array.Copy (nodes, newArr, System.Math.Min (size, nodes.Length));
257 private void SetAttributeArrayLength (int size)
259 DTMXPathAttributeNode2 [] newArr =
260 new DTMXPathAttributeNode2 [size];
261 Array.Copy (attributes, newArr, System.Math.Min (size, attributes.Length));
265 private void SetNsArrayLength (int size)
267 DTMXPathNamespaceNode2 [] newArr =
268 new DTMXPathNamespaceNode2 [size];
269 Array.Copy (namespaces, newArr, System.Math.Min (size, namespaces.Length));
273 // Here followings are skipped: firstChild, nextSibling,
274 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)
276 if (nodes.Length < nodeIndex + 1) {
278 SetNodeArrayLength (nodeCapacity);
282 nodes [nodeIndex] = new DTMXPathLinkedNode2 ();
284 nodes [nodeIndex].FirstChild = 0; // dummy
285 nodes [nodeIndex].Parent = parent;
286 nodes [nodeIndex].FirstAttribute = firstAttribute;
287 nodes [nodeIndex].PreviousSibling = previousSibling;
288 nodes [nodeIndex].NextSibling = 0; // dummy
289 nodes [nodeIndex].Depth = depth;
290 nodes [nodeIndex].NodeType = nodeType;
291 nodes [nodeIndex].BaseURI = AtomicIndex (baseUri);
292 nodes [nodeIndex].IsEmptyElement = isEmptyElement;
293 nodes [nodeIndex].LocalName = AtomicIndex (localName);
294 nodes [nodeIndex].NamespaceURI = AtomicIndex (ns);
295 nodes [nodeIndex].Prefix = AtomicIndex (prefix);
296 nodes [nodeIndex].Value = NonAtomicIndex (value);
297 nodes [nodeIndex].XmlLang = AtomicIndex (xmlLang);
298 nodes [nodeIndex].FirstNamespace = namespaceNode;
299 nodes [nodeIndex].LineNumber = lineNumber;
300 nodes [nodeIndex].LinePosition = linePosition;
303 // Followings are skipped: nextAttribute,
304 public void AddAttribute (int ownerElement, string localName, string ns, string prefix, string value, int lineNumber, int linePosition)
306 if (attributes.Length < attributeIndex + 1) {
307 attributeCapacity *= 4;
308 SetAttributeArrayLength (attributeCapacity);
312 attributes [attributeIndex] = new DTMXPathAttributeNode2 ();
314 attributes [attributeIndex].OwnerElement = ownerElement;
315 attributes [attributeIndex].LocalName = AtomicIndex (localName);
316 attributes [attributeIndex].NamespaceURI = AtomicIndex (ns);
317 attributes [attributeIndex].Prefix = AtomicIndex (prefix);
318 attributes [attributeIndex].Value = NonAtomicIndex (value);
319 attributes [attributeIndex].LineNumber = lineNumber;
320 attributes [attributeIndex].LinePosition = linePosition;
323 // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
324 public void AddNsNode (int declaredElement, string name, string ns, int nextNs)
326 if (namespaces.Length < nsIndex + 1) {
328 SetNsArrayLength (nsCapacity);
332 namespaces [nsIndex] = new DTMXPathNamespaceNode2 ();
334 namespaces [nsIndex].DeclaredElement = declaredElement;
335 namespaces [nsIndex].Name = AtomicIndex (name);
336 namespaces [nsIndex].Namespace = AtomicIndex (ns);
337 namespaces [nsIndex].NextNamespace = nextNs;
341 #region XmlWriter methods
342 // They are not supported
343 public override string XmlLang { get { return null; } }
344 public override XmlSpace XmlSpace { get { return XmlSpace.None; } }
346 public override WriteState WriteState { get { return state; } }
348 public override void Close ()
351 SetNodeArrayLength (nodeIndex + 1);
352 SetAttributeArrayLength (attributeIndex + 1);
353 SetNsArrayLength (nsIndex + 1);
357 public override void Flush ()
362 public override string LookupPrefix (string ns)
366 if (atomicStringPool [namespaces [tmp].Namespace] == ns)
367 return atomicStringPool [namespaces [tmp].Name];
368 tmp = namespaces [tmp].NextNamespace;
373 public override void WriteCData (string data)
\r
375 AddTextNode (data);
\r
378 private void AddTextNode (string data)
\r
381 case WriteState.Element:
\r
382 CloseStartElement ();
\r
384 case WriteState.Content:
\r
387 throw new InvalidOperationException ("Invalid document state for CDATA section: " + state);
\r
390 // When text after text, just add the value, and return.
\r
391 if (nodes [nodeIndex].Depth == writerDepth) {
\r
392 switch (nodes [nodeIndex].NodeType) {
\r
393 case XPathNodeType.Text:
\r
394 case XPathNodeType.SignificantWhitespace:
395 string value = nonAtomicStringPool [nodes [nodeIndex].Value] + data;
396 nodes [nodeIndex].Value = NonAtomicIndex (value);
\r
397 if (IsWhitespace (value))
\r
398 nodes [nodeIndex].NodeType = XPathNodeType.SignificantWhitespace;
\r
400 nodes [nodeIndex].NodeType = XPathNodeType.Text;
\r
405 int parent = GetParentIndex ();
\r
406 UpdateTreeForAddition ();
\r
425 private void CheckTopLevelNode ()
\r
428 case WriteState.Element:
\r
429 CloseStartElement ();
\r
431 case WriteState.Content:
\r
432 case WriteState.Prolog:
\r
433 case WriteState.Start:
\r
436 throw new InvalidOperationException ("Invalid document state for CDATA section: " + state);
\r
440 public override void WriteComment (string data)
\r
442 CheckTopLevelNode ();
\r
444 int parent = GetParentIndex ();
\r
445 UpdateTreeForAddition ();
\r
451 XPathNodeType.Comment,
464 public override void WriteProcessingInstruction (string name, string data)
\r
466 CheckTopLevelNode ();
\r
468 int parent = GetParentIndex ();
\r
469 UpdateTreeForAddition ();
\r
475 XPathNodeType.ProcessingInstruction,
488 public override void WriteWhitespace (string data)
\r
490 CheckTopLevelNode ();
\r
492 int parent = GetParentIndex ();
\r
493 UpdateTreeForAddition ();
\r
499 XPathNodeType.Whitespace,
512 public override void WriteStartDocument ()
\r
517 public override void WriteStartDocument (bool standalone)
\r
522 public override void WriteEndDocument ()
\r
527 public override void WriteStartElement (string prefix, string localName, string ns)
\r
530 case WriteState.Element:
\r
531 CloseStartElement ();
\r
533 case WriteState.Start:
\r
534 case WriteState.Prolog:
\r
535 case WriteState.Content:
\r
538 throw new InvalidOperationException ("Invalid document state for writing element: " + state);
\r
541 int parent = GetParentIndex ();
\r
542 UpdateTreeForAddition ();
\r
544 WriteStartElement (parent, prevSibling, prefix, localName, ns);
545 state = WriteState.Element;
548 private void WriteStartElement (int parent, int previousSibling, string prefix, string localName, string ns)
550 PrepareStartElement (previousSibling);
553 0, // dummy:firstAttribute
556 XPathNodeType.Element,
562 "", // Element has no internal value.
569 private void PrepareStartElement (int previousSibling)
571 firstAttributeIndex = 0;
572 lastNsIndexInCurrent = 0;
573 attrIndexAtStart = attributeIndex;
574 nsIndexAtStart = nsIndex;
576 while (namespaces [lastNsInScope].DeclaredElement == previousSibling) {
577 lastNsInScope = namespaces [lastNsInScope].NextNamespace;
581 public override void WriteEndElement ()
\r
583 WriteEndElement (false);
\r
586 public override void WriteFullEndElement ()
\r
588 WriteEndElement (true);
\r
591 private void WriteEndElement (bool full)
\r
594 case WriteState.Element:
\r
595 CloseStartElement ();
\r
597 case WriteState.Content:
\r
600 throw new InvalidOperationException ("Invalid state for writing EndElement: " + state);
\r
602 parentForFirstChild = -1;
\r
603 if (nodes [nodeIndex].NodeType == XPathNodeType.Element) {
\r
605 nodes [nodeIndex].IsEmptyElement = true;
\r
611 public override void WriteStartAttribute (string prefix, string localName, string ns)
\r
613 if (state != WriteState.Element)
\r
614 throw new InvalidOperationException ("Invalid document state for attribute: " + state);
\r
616 state = WriteState.Attribute;
\r
617 if (ns == XmlNamespaces.XMLNS)
618 ProcessNamespace ((prefix == null || prefix == String.Empty) ? "" : localName, String.Empty); // dummy: Value should be completed
620 ProcessAttribute (prefix, localName, ns, String.Empty); // dummy: Value should be completed
623 private void ProcessNamespace (string prefix, string ns)
627 int nextTmp = lastNsIndexInCurrent == 0 ? nodes [nodeIndex].FirstNamespace : lastNsIndexInCurrent;
629 this.AddNsNode (nodeIndex,
633 lastNsIndexInCurrent = nsIndex;
634 openNamespace = true;
637 private void ProcessAttribute (string prefix, string localName, string ns, string value)
641 this.AddAttribute (nodeIndex,
644 prefix != null ? prefix : String.Empty,
648 if (firstAttributeIndex == 0)
649 firstAttributeIndex = attributeIndex;
651 attributes [attributeIndex - 1].NextAttribute = attributeIndex;
654 public override void WriteEndAttribute ()
\r
656 if (state != WriteState.Attribute)
\r
657 throw new InvalidOperationException ();
\r
659 openNamespace = false;
\r
660 state = WriteState.Element;
\r
663 public override void WriteString (string text)
\r
665 if (WriteState == WriteState.Attribute) {
666 if (openNamespace) {
\r
667 string value = atomicStringPool [namespaces [nsIndex].Namespace] + text;
668 namespaces [nsIndex].Namespace = AtomicIndex (value);
670 string value = nonAtomicStringPool [attributes [attributeIndex].Value] + text;
671 attributes [attributeIndex].Value = NonAtomicIndex (value);
675 AddTextNode (text);
\r
678 // Well, they cannot be supported, but actually used to
\r
679 // disable-output-escaping = "true"
\r
680 public override void WriteRaw (string data)
\r
682 WriteString (data);
\r
685 public override void WriteRaw (char [] data, int start, int len)
\r
687 WriteString (new string (data, start, len));
\r
690 public override void WriteName (string name)
\r
692 WriteString (name);
\r
695 public override void WriteNmToken (string name)
\r
697 WriteString (name);
\r
700 public override void WriteBase64 (byte [] buffer, int index, int count)
\r
702 throw new NotSupportedException ();
\r
705 public override void WriteBinHex (byte [] buffer, int index, int count)
\r
707 throw new NotSupportedException ();
\r
710 public override void WriteChars (char [] buffer, int index, int count)
\r
712 throw new NotSupportedException ();
\r
715 public override void WriteCharEntity (char c)
\r
717 throw new NotSupportedException ();
\r
720 public override void WriteDocType (string name, string pub, string sys, string intSubset)
\r
722 throw new NotSupportedException ();
\r
725 public override void WriteEntityRef (string name)
727 throw new NotSupportedException ();
\r
730 public override void WriteQualifiedName (string localName, string ns)
\r
732 throw new NotSupportedException ();
\r
735 public override void WriteSurrogateCharEntity (char high, char low)
\r
737 throw new NotSupportedException ();
\r
740 private bool IsWhitespace (string data)
742 for (int i = 0; i < data.Length; i++) {