2 // Mono.Xml.XPath.DTMXPathDocumentWriter
5 // Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
7 // (C) 2003 Atsushi Enomoto
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 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];
60 XmlNameTable nameTable;
61 int nodeCapacity = 200;
62 int attributeCapacity = 200;
66 DTMXPathLinkedNode [] nodes;
69 DTMXPathAttributeNode [] attributes;
72 DTMXPathNamespaceNode [] namespaces;
74 // idTable [string value] -> int nodeId
80 int parentForFirstChild;
82 // for attribute processing; should be reset per each element.
83 int firstAttributeIndex;
84 int lastNsIndexInCurrent;
91 // They are only used in Writer
96 public DTMXPathDocument CreateDocument ()
98 return new DTMXPathDocument (nameTable,
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);
113 AddAttribute (0, null, null, null, null, 0, 0);
114 AddNsNode (0, null, null, 0);
116 AddNsNode (1, "xml", XmlNamespaces.XML, 0);
119 AddNode (0, 0, 0, -1, XPathNodeType.Root, null, false, "", "", "", "", "", 1, 0, 0);
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;
142 private int GetPreviousSiblingIndex ()
144 int prevSibling = nodeIndex;
145 if (parentForFirstChild >= 0)
148 while (nodes [prevSibling].Depth != writerDepth)
149 prevSibling = nodes [prevSibling].Parent;
153 private void UpdateTreeForAddition ()
155 int parent = GetParentIndex ();
156 prevSibling = GetPreviousSiblingIndex ();
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;
\r
185 private void SetNodeArrayLength (int size)
187 DTMXPathLinkedNode [] newArr = new DTMXPathLinkedNode [size];
188 Array.Copy (nodes, newArr, System.Math.Min (size, nodes.Length));
192 private void SetAttributeArrayLength (int size)
194 DTMXPathAttributeNode [] newArr =
195 new DTMXPathAttributeNode [size];
196 Array.Copy (attributes, newArr, System.Math.Min (size, attributes.Length));
200 private void SetNsArrayLength (int size)
202 DTMXPathNamespaceNode [] newArr =
203 new DTMXPathNamespaceNode [size];
204 Array.Copy (namespaces, newArr, System.Math.Min (size, namespaces.Length));
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) {
213 SetNodeArrayLength (nodeCapacity);
217 nodes [nodeIndex] = new DTMXPathLinkedNode ();
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, int lineNumber, int linePosition)
241 if (attributes.Length < attributeIndex + 1) {
242 attributeCapacity *= 4;
243 SetAttributeArrayLength (attributeCapacity);
247 attributes [attributeIndex] = new DTMXPathAttributeNode ();
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].LineNumber = lineNumber;
255 attributes [attributeIndex].LinePosition = linePosition;
258 // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
259 public void AddNsNode (int declaredElement, string name, string ns, int nextNs)
261 if (namespaces.Length < nsIndex + 1) {
263 SetNsArrayLength (nsCapacity);
267 namespaces [nsIndex] = new DTMXPathNamespaceNode ();
269 namespaces [nsIndex].DeclaredElement = declaredElement;
270 namespaces [nsIndex].Name = name;
271 namespaces [nsIndex].Namespace = ns;
272 namespaces [nsIndex].NextNamespace = nextNs;
276 #region XmlWriter methods
277 // They are not supported
278 public override string XmlLang { get { return null; } }
279 public override XmlSpace XmlSpace { get { return XmlSpace.None; } }
281 public override WriteState WriteState { get { return state; } }
283 public override void Close ()
286 SetNodeArrayLength (nodeIndex + 1);
287 SetAttributeArrayLength (attributeIndex + 1);
288 SetNsArrayLength (nsIndex + 1);
291 public override void Flush ()
296 public override string LookupPrefix (string ns)
300 if (namespaces [tmp].Namespace == ns)
301 return namespaces [tmp].Name;
302 tmp = namespaces [tmp].NextNamespace;
307 public override void WriteCData (string data)
\r
309 AddTextNode (data);
\r
312 private void AddTextNode (string data)
\r
315 case WriteState.Element:
\r
316 CloseStartElement ();
\r
318 case WriteState.Content:
\r
321 throw new InvalidOperationException ("Invalid document state for CDATA section: " + state);
\r
324 // When text after text, just add the value, and return.
\r
325 if (nodes [nodeIndex].Depth == writerDepth) {
\r
326 switch (nodes [nodeIndex].NodeType) {
\r
327 case XPathNodeType.Text:
\r
328 case XPathNodeType.SignificantWhitespace:
\r
329 nodes [nodeIndex].Value += data;
\r
330 if (IsWhitespace (data))
\r
331 nodes [nodeIndex].NodeType = XPathNodeType.SignificantWhitespace;
\r
333 nodes [nodeIndex].NodeType = XPathNodeType.Text;
\r
338 int parent = GetParentIndex ();
\r
339 UpdateTreeForAddition ();
\r
358 private void CheckTopLevelNode ()
\r
361 case WriteState.Element:
\r
362 CloseStartElement ();
\r
364 case WriteState.Content:
\r
365 case WriteState.Prolog:
\r
366 case WriteState.Start:
\r
369 throw new InvalidOperationException ("Invalid document state for CDATA section: " + state);
\r
373 public override void WriteComment (string data)
\r
375 CheckTopLevelNode ();
\r
377 int parent = GetParentIndex ();
\r
378 UpdateTreeForAddition ();
\r
384 XPathNodeType.Comment,
397 public override void WriteProcessingInstruction (string name, string data)
\r
399 CheckTopLevelNode ();
\r
401 int parent = GetParentIndex ();
\r
402 UpdateTreeForAddition ();
\r
408 XPathNodeType.ProcessingInstruction,
421 public override void WriteWhitespace (string data)
\r
423 CheckTopLevelNode ();
\r
425 int parent = GetParentIndex ();
\r
426 UpdateTreeForAddition ();
\r
432 XPathNodeType.Whitespace,
445 public override void WriteStartDocument ()
\r
450 public override void WriteStartDocument (bool standalone)
\r
455 public override void WriteEndDocument ()
\r
460 public override void WriteStartElement (string prefix, string localName, string ns)
\r
463 case WriteState.Element:
\r
464 CloseStartElement ();
\r
466 case WriteState.Start:
\r
467 case WriteState.Prolog:
\r
468 case WriteState.Content:
\r
471 throw new InvalidOperationException ("Invalid document state for writing element: " + state);
\r
474 int parent = GetParentIndex ();
\r
475 UpdateTreeForAddition ();
\r
477 WriteStartElement (parent, prevSibling, prefix, localName, ns);
478 state = WriteState.Element;
481 private void WriteStartElement (int parent, int previousSibling, string prefix, string localName, string ns)
483 PrepareStartElement (previousSibling);
486 0, // dummy:firstAttribute
489 XPathNodeType.Element,
495 "", // Element has no internal value.
502 private void PrepareStartElement (int previousSibling)
504 firstAttributeIndex = 0;
505 lastNsIndexInCurrent = 0;
506 attrIndexAtStart = attributeIndex;
507 nsIndexAtStart = nsIndex;
509 while (namespaces [lastNsInScope].DeclaredElement == previousSibling) {
510 lastNsInScope = namespaces [lastNsInScope].NextNamespace;
514 public override void WriteEndElement ()
\r
516 WriteEndElement (false);
\r
519 public override void WriteFullEndElement ()
\r
521 WriteEndElement (true);
\r
524 private void WriteEndElement (bool full)
\r
527 case WriteState.Element:
\r
528 CloseStartElement ();
\r
530 case WriteState.Content:
\r
533 throw new InvalidOperationException ("Invalid state for writing EndElement: " + state);
\r
535 parentForFirstChild = -1;
\r
536 if (nodes [nodeIndex].NodeType == XPathNodeType.Element) {
\r
538 nodes [nodeIndex].IsEmptyElement = true;
\r
544 public override void WriteStartAttribute (string prefix, string localName, string ns)
\r
546 if (state != WriteState.Element)
\r
547 throw new InvalidOperationException ("Invalid document state for attribute: " + state);
\r
549 state = WriteState.Attribute;
\r
550 if (ns == XmlNamespaces.XMLNS)
551 ProcessNamespace ((prefix == null || prefix == String.Empty) ? "" : localName, String.Empty); // dummy: Value should be completed
553 ProcessAttribute (prefix, localName, ns, String.Empty); // dummy: Value should be completed
556 private void ProcessNamespace (string prefix, string ns)
560 int nextTmp = lastNsIndexInCurrent == 0 ? nodes [nodeIndex].FirstNamespace : lastNsIndexInCurrent;
562 this.AddNsNode (nodeIndex,
566 lastNsIndexInCurrent = nsIndex;
567 openNamespace = true;
570 private void ProcessAttribute (string prefix, string localName, string ns, string value)
574 this.AddAttribute (nodeIndex,
577 prefix != null ? prefix : String.Empty,
581 if (firstAttributeIndex == 0)
582 firstAttributeIndex = attributeIndex;
584 attributes [attributeIndex - 1].NextAttribute = attributeIndex;
587 public override void WriteEndAttribute ()
\r
589 if (state != WriteState.Attribute)
\r
590 throw new InvalidOperationException ();
\r
592 openNamespace = false;
\r
593 state = WriteState.Element;
\r
596 public override void WriteString (string text)
\r
598 if (WriteState == WriteState.Attribute) {
\r
600 namespaces [nsIndex].Namespace += text;
\r
602 attributes [attributeIndex].Value += text;
\r
605 AddTextNode (text);
\r
608 // Well, they cannot be supported, but actually used to
\r
609 // disable-output-escaping = "true"
\r
610 public override void WriteRaw (string data)
\r
612 WriteString (data);
\r
615 public override void WriteRaw (char [] data, int start, int len)
\r
617 WriteString (new string (data, start, len));
\r
620 public override void WriteName (string name)
\r
622 WriteString (name);
\r
625 public override void WriteNmToken (string name)
\r
627 WriteString (name);
\r
630 public override void WriteBase64 (byte [] buffer, int index, int count)
\r
632 throw new NotSupportedException ();
\r
635 public override void WriteBinHex (byte [] buffer, int index, int count)
\r
637 throw new NotSupportedException ();
\r
640 public override void WriteChars (char [] buffer, int index, int count)
\r
642 throw new NotSupportedException ();
\r
645 public override void WriteCharEntity (char c)
\r
647 throw new NotSupportedException ();
\r
650 public override void WriteDocType (string name, string pub, string sys, string intSubset)
\r
652 throw new NotSupportedException ();
\r
655 public override void WriteEntityRef (string name)
657 throw new NotSupportedException ();
\r
660 public override void WriteQualifiedName (string localName, string ns)
\r
662 throw new NotSupportedException ();
\r
665 public override void WriteSurrogateCharEntity (char high, char low)
\r
667 throw new NotSupportedException ();
\r
670 private bool IsWhitespace (string data)
672 for (int i = 0; i < data.Length; i++) {