**** Merged from MCS ****
[mono-project.git] / mcs / class / Mono.Xml.Ext / Mono.Xml.XPath / DTMXPathDocumentWriter.cs
blob0dd7c7849ad0f2684f4f65c88c55705a639419a8
1 //
2 // Mono.Xml.XPath.DTMXPathDocumentWriter
3 //
4 // Author:
5 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
6 //
7 // (C) 2003 Atsushi Enomoto
8 //
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:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
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.
30 using System;
31 using System.Collections;
32 using System.IO;
33 using System.Xml;
34 using System.Xml.Schema;
35 using System.Xml.XPath;
37 namespace Mono.Xml.XPath
39 #if OUTSIDE_SYSTEM_XML
40 public
41 #else
42 internal
43 #endif
44 class DTMXPathDocumentWriter : XmlWriter
46 public DTMXPathDocumentWriter (XmlNameTable nt, int defaultCapacity)
48 nameTable = nt == null ? new NameTable () : nt;
49 nodeCapacity = defaultCapacity;
50 attributeCapacity = nodeCapacity;
51 idTable = new Hashtable ();
53 nodes = new DTMXPathLinkedNode [nodeCapacity];
54 attributes = new DTMXPathAttributeNode [attributeCapacity];
55 namespaces = new DTMXPathNamespaceNode [0];
57 Init ();
60 XmlNameTable nameTable;
61 int nodeCapacity = 200;
62 int attributeCapacity = 200;
63 int nsCapacity = 10;
65 // Linked Node
66 DTMXPathLinkedNode [] nodes;
68 // Attribute
69 DTMXPathAttributeNode [] attributes;
71 // NamespaceNode
72 DTMXPathNamespaceNode [] namespaces;
74 // idTable [string value] -> int nodeId
75 Hashtable idTable;
77 int nodeIndex;
78 int attributeIndex;
79 int nsIndex;
80 int parentForFirstChild;
82 // for attribute processing; should be reset per each element.
83 int firstAttributeIndex;
84 int lastNsIndexInCurrent;
85 int attrIndexAtStart;
86 int nsIndexAtStart;
88 int prevSibling;
89 int lastNsInScope;
91 // They are only used in Writer
92 int writerDepth;
93 WriteState state;
94 bool openNamespace;
96 public DTMXPathDocument CreateDocument ()
98 return new DTMXPathDocument (nameTable,
99 nodes,
100 attributes,
101 namespaces,
102 idTable
106 public void Init ()
108 // index 0 is dummy. No node (including Root) is assigned to this index
109 // So that we can easily compare index != 0 instead of index < 0.
110 // (Difference between jnz or jbe in 80x86.)
111 AddNode (0, 0, 0, 0, XPathNodeType.All, "", false, "", "", "", "", "", 0, 0, 0);
112 nodeIndex++;
113 AddAttribute (0, null, null, null, null, null, 0, 0);
114 AddNsNode (0, null, null, 0);
115 nsIndex++;
116 AddNsNode (1, "xml", XmlNamespaces.XML, 0);
118 // add root.
119 AddNode (0, 0, 0, -1, XPathNodeType.Root, null, false, "", "", "", "", "", 1, 0, 0);
121 this.nodeIndex = 1;
122 this.lastNsInScope = 1;
123 this.parentForFirstChild = nodeIndex;
125 state = WriteState.Content;
128 private int GetParentIndex ()
130 if (parentForFirstChild >= 0)
131 return parentForFirstChild;
133 int parent = nodeIndex;
134 if (nodes [nodeIndex].Depth >= writerDepth) {
135 // if not, then current node is parent.
136 while (writerDepth <= nodes [parent].Depth)
137 parent = nodes [parent].Parent;
139 return parent;
142 private int GetPreviousSiblingIndex ()
144 int prevSibling = nodeIndex;
145 if (parentForFirstChild >= 0)
146 prevSibling = 0;
147 else
148 while (nodes [prevSibling].Depth != writerDepth)
149 prevSibling = nodes [prevSibling].Parent;
150 return prevSibling;
153 private void UpdateTreeForAddition ()
155 int parent = GetParentIndex ();
156 prevSibling = GetPreviousSiblingIndex ();
158 nodeIndex++;
160 if (prevSibling != 0)
161 nodes [prevSibling].NextSibling = nodeIndex;
162 if (parentForFirstChild >= 0)
163 nodes [parent].FirstChild = nodeIndex;
165 parentForFirstChild = -1;
168 private void CloseStartElement ()
170 if (attrIndexAtStart != attributeIndex)
171 nodes [nodeIndex].FirstAttribute = attrIndexAtStart + 1;
172 if (nsIndexAtStart != nsIndex) {
173 nodes [nodeIndex].FirstNamespace = nsIndex;
174 lastNsInScope = nsIndex;
177 if (!nodes [nodeIndex].IsEmptyElement)
178 parentForFirstChild = nodeIndex;
180 state = WriteState.Content;
181 writerDepth++;
184 #region Adding Nodes
185 private void SetNodeArrayLength (int size)
187 DTMXPathLinkedNode [] newArr = new DTMXPathLinkedNode [size];
188 Array.Copy (nodes, newArr, System.Math.Min (size, nodes.Length));
189 nodes = newArr;
192 private void SetAttributeArrayLength (int size)
194 DTMXPathAttributeNode [] newArr =
195 new DTMXPathAttributeNode [size];
196 Array.Copy (attributes, newArr, System.Math.Min (size, attributes.Length));
197 attributes = newArr;
200 private void SetNsArrayLength (int size)
202 DTMXPathNamespaceNode [] newArr =
203 new DTMXPathNamespaceNode [size];
204 Array.Copy (namespaces, newArr, System.Math.Min (size, namespaces.Length));
205 namespaces = newArr;
208 // Here followings are skipped: firstChild, nextSibling,
209 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)
211 if (nodes.Length < nodeIndex + 1) {
212 nodeCapacity *= 4;
213 SetNodeArrayLength (nodeCapacity);
216 #if DTM_CLASS
217 nodes [nodeIndex] = new DTMXPathLinkedNode ();
218 #endif
219 nodes [nodeIndex].FirstChild = 0; // dummy
220 nodes [nodeIndex].Parent = parent;
221 nodes [nodeIndex].FirstAttribute = firstAttribute;
222 nodes [nodeIndex].PreviousSibling = previousSibling;
223 nodes [nodeIndex].NextSibling = 0; // dummy
224 nodes [nodeIndex].Depth = depth;
225 nodes [nodeIndex].NodeType = nodeType;
226 nodes [nodeIndex].BaseURI = baseUri;
227 nodes [nodeIndex].IsEmptyElement = isEmptyElement;
228 nodes [nodeIndex].LocalName = localName;
229 nodes [nodeIndex].NamespaceURI = ns;
230 nodes [nodeIndex].Prefix = prefix;
231 nodes [nodeIndex].Value = value;
232 nodes [nodeIndex].XmlLang = xmlLang;
233 nodes [nodeIndex].FirstNamespace = namespaceNode;
234 nodes [nodeIndex].LineNumber = lineNumber;
235 nodes [nodeIndex].LinePosition = linePosition;
238 // Followings are skipped: nextAttribute,
239 public void AddAttribute (int ownerElement, string localName, string ns, string prefix, string value, object schemaType, int lineNumber, int linePosition)
241 if (attributes.Length < attributeIndex + 1) {
242 attributeCapacity *= 4;
243 SetAttributeArrayLength (attributeCapacity);
246 #if DTM_CLASS
247 attributes [attributeIndex] = new DTMXPathAttributeNode ();
248 #endif
249 attributes [attributeIndex].OwnerElement = ownerElement;
250 attributes [attributeIndex].LocalName = localName;
251 attributes [attributeIndex].NamespaceURI = ns;
252 attributes [attributeIndex].Prefix = prefix;
253 attributes [attributeIndex].Value = value;
254 attributes [attributeIndex].SchemaType = schemaType;
255 attributes [attributeIndex].LineNumber = lineNumber;
256 attributes [attributeIndex].LinePosition = linePosition;
259 // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
260 public void AddNsNode (int declaredElement, string name, string ns, int nextNs)
262 if (namespaces.Length < nsIndex + 1) {
263 nsCapacity *= 4;
264 SetNsArrayLength (nsCapacity);
267 #if DTM_CLASS
268 namespaces [nsIndex] = new DTMXPathNamespaceNode ();
269 #endif
270 namespaces [nsIndex].DeclaredElement = declaredElement;
271 namespaces [nsIndex].Name = name;
272 namespaces [nsIndex].Namespace = ns;
273 namespaces [nsIndex].NextNamespace = nextNs;
275 #endregion
277 #region XmlWriter methods
278 // They are not supported
279 public override string XmlLang { get { return null; } }
280 public override XmlSpace XmlSpace { get { return XmlSpace.None; } }
282 public override WriteState WriteState { get { return state; } }
284 public override void Close ()
286 // Fixup arrays
287 SetNodeArrayLength (nodeIndex + 1);
288 SetAttributeArrayLength (attributeIndex + 1);
289 SetNsArrayLength (nsIndex + 1);
292 public override void Flush ()
294 // do nothing
297 public override string LookupPrefix (string ns)
299 int tmp = nsIndex;
300 while (tmp != 0) {
301 if (namespaces [tmp].Namespace == ns)
302 return namespaces [tmp].Name;
303 tmp = namespaces [tmp].NextNamespace;
305 return null;
308 public override void WriteCData (string data)
310 AddTextNode (data);
313 private void AddTextNode (string data)
315 switch (state) {
316 case WriteState.Element:
317 CloseStartElement ();
318 break;
319 case WriteState.Content:
320 break;
321 default:
322 throw new InvalidOperationException ("Invalid document state for CDATA section: " + state);
325 // When text after text, just add the value, and return.
326 if (nodes [nodeIndex].Depth == writerDepth) {
327 switch (nodes [nodeIndex].NodeType) {
328 case XPathNodeType.Text:
329 case XPathNodeType.SignificantWhitespace:
330 nodes [nodeIndex].Value += data;
331 if (IsWhitespace (data))
332 nodes [nodeIndex].NodeType = XPathNodeType.SignificantWhitespace;
333 else
334 nodes [nodeIndex].NodeType = XPathNodeType.Text;
335 return;
339 int parent = GetParentIndex ();
340 UpdateTreeForAddition ();
342 AddNode (parent,
343 0, // attribute
344 prevSibling,
345 writerDepth,
346 XPathNodeType.Text,
347 null,
348 false,
349 null,
350 String.Empty,
351 String.Empty,
352 data,
353 null,
354 0, // nsIndex
355 0, // line info
359 private void CheckTopLevelNode ()
361 switch (state) {
362 case WriteState.Element:
363 CloseStartElement ();
364 break;
365 case WriteState.Content:
366 case WriteState.Prolog:
367 case WriteState.Start:
368 break;
369 default:
370 throw new InvalidOperationException ("Invalid document state for CDATA section: " + state);
374 public override void WriteComment (string data)
376 CheckTopLevelNode ();
378 int parent = GetParentIndex ();
379 UpdateTreeForAddition ();
381 AddNode (parent,
382 0, // attribute
383 prevSibling,
384 writerDepth,
385 XPathNodeType.Comment,
386 null,
387 false,
388 null,
389 String.Empty,
390 String.Empty,
391 data,
392 null,
393 0, // nsIndex
394 0, // line info
398 public override void WriteProcessingInstruction (string name, string data)
400 CheckTopLevelNode ();
402 int parent = GetParentIndex ();
403 UpdateTreeForAddition ();
405 AddNode (parent,
406 0, // attribute
407 prevSibling,
408 writerDepth,
409 XPathNodeType.ProcessingInstruction,
410 null,
411 false,
412 name,
413 String.Empty,
414 String.Empty,
415 data,
416 null,
417 0, // nsIndex
418 0, // line info
422 public override void WriteWhitespace (string data)
424 CheckTopLevelNode ();
426 int parent = GetParentIndex ();
427 UpdateTreeForAddition ();
429 AddNode (parent,
430 0, // attribute
431 prevSibling,
432 writerDepth,
433 XPathNodeType.Whitespace,
434 null,
435 false,
436 null,
437 String.Empty,
438 String.Empty,
439 data,
440 null,
441 0, // nsIndex
442 0, // line info
446 public override void WriteStartDocument ()
448 // do nothing
451 public override void WriteStartDocument (bool standalone)
453 // do nothing
456 public override void WriteEndDocument ()
458 // do nothing
461 public override void WriteStartElement (string prefix, string localName, string ns)
463 switch (state) {
464 case WriteState.Element:
465 CloseStartElement ();
466 break;
467 case WriteState.Start:
468 case WriteState.Prolog:
469 case WriteState.Content:
470 break;
471 default:
472 throw new InvalidOperationException ("Invalid document state for writing element: " + state);
475 int parent = GetParentIndex ();
476 UpdateTreeForAddition ();
478 WriteStartElement (parent, prevSibling, prefix, localName, ns);
479 state = WriteState.Element;
482 private void WriteStartElement (int parent, int previousSibling, string prefix, string localName, string ns)
484 PrepareStartElement (previousSibling);
486 AddNode (parent,
487 0, // dummy:firstAttribute
488 previousSibling,
489 writerDepth,
490 XPathNodeType.Element,
491 null,
492 false,
493 localName,
495 prefix,
496 "", // Element has no internal value.
497 null,
498 lastNsInScope,
503 private void PrepareStartElement (int previousSibling)
505 firstAttributeIndex = 0;
506 lastNsIndexInCurrent = 0;
507 attrIndexAtStart = attributeIndex;
508 nsIndexAtStart = nsIndex;
510 while (namespaces [lastNsInScope].DeclaredElement == previousSibling) {
511 lastNsInScope = namespaces [lastNsInScope].NextNamespace;
515 public override void WriteEndElement ()
517 WriteEndElement (false);
520 public override void WriteFullEndElement ()
522 WriteEndElement (true);
525 private void WriteEndElement (bool full)
527 switch (state) {
528 case WriteState.Element:
529 CloseStartElement ();
530 break;
531 case WriteState.Content:
532 break;
533 default:
534 throw new InvalidOperationException ("Invalid state for writing EndElement: " + state);
536 parentForFirstChild = -1;
537 if (nodes [nodeIndex].NodeType == XPathNodeType.Element) {
538 if (!full)
539 nodes [nodeIndex].IsEmptyElement = true;
542 writerDepth--;
545 public override void WriteStartAttribute (string prefix, string localName, string ns)
547 if (state != WriteState.Element)
548 throw new InvalidOperationException ("Invalid document state for attribute: " + state);
550 state = WriteState.Attribute;
551 if (ns == XmlNamespaces.XMLNS)
552 ProcessNamespace ((prefix == null || prefix == String.Empty) ? "" : localName, String.Empty); // dummy: Value should be completed
553 else
554 ProcessAttribute (prefix, localName, ns, String.Empty); // dummy: Value should be completed
557 private void ProcessNamespace (string prefix, string ns)
559 nsIndex++;
561 int nextTmp = lastNsIndexInCurrent == 0 ? nodes [nodeIndex].FirstNamespace : lastNsIndexInCurrent;
563 this.AddNsNode (nodeIndex,
564 prefix,
566 nextTmp);
567 lastNsIndexInCurrent = nsIndex;
568 openNamespace = true;
571 private void ProcessAttribute (string prefix, string localName, string ns, string value)
573 attributeIndex ++;
575 this.AddAttribute (nodeIndex,
576 localName,
577 ns,
578 prefix != null ? prefix : String.Empty,
579 value,
580 null,
583 if (firstAttributeIndex == 0)
584 firstAttributeIndex = attributeIndex;
585 else
586 attributes [attributeIndex - 1].NextAttribute = attributeIndex;
589 public override void WriteEndAttribute ()
591 if (state != WriteState.Attribute)
592 throw new InvalidOperationException ();
594 openNamespace = false;
595 state = WriteState.Element;
598 public override void WriteString (string text)
600 if (WriteState == WriteState.Attribute) {
601 if (openNamespace)
602 namespaces [nsIndex].Namespace += text;
603 else
604 attributes [attributeIndex].Value += text;
606 else
607 AddTextNode (text);
610 // Well, they cannot be supported, but actually used to
611 // disable-output-escaping = "true"
612 public override void WriteRaw (string data)
614 WriteString (data);
617 public override void WriteRaw (char [] data, int start, int len)
619 WriteString (new string (data, start, len));
622 public override void WriteName (string name)
624 WriteString (name);
627 public override void WriteNmToken (string name)
629 WriteString (name);
632 public override void WriteBase64 (byte [] buffer, int index, int count)
634 throw new NotSupportedException ();
637 public override void WriteBinHex (byte [] buffer, int index, int count)
639 throw new NotSupportedException ();
642 public override void WriteChars (char [] buffer, int index, int count)
644 throw new NotSupportedException ();
647 public override void WriteCharEntity (char c)
649 throw new NotSupportedException ();
652 public override void WriteDocType (string name, string pub, string sys, string intSubset)
654 throw new NotSupportedException ();
657 public override void WriteEntityRef (string name)
659 throw new NotSupportedException ();
662 public override void WriteQualifiedName (string localName, string ns)
664 throw new NotSupportedException ();
667 public override void WriteSurrogateCharEntity (char high, char low)
669 throw new NotSupportedException ();
672 private bool IsWhitespace (string data)
674 for (int i = 0; i < data.Length; i++) {
675 switch (data [i]) {
676 case ' ':
677 case '\r':
678 case '\n':
679 case '\t':
680 continue;
681 default:
682 return false;
685 return true;
687 #endregion