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;
52 idTable = new Hashtable ();
54 nodes = new DTMXPathLinkedNode [nodeCapacity];
55 attributes = new DTMXPathAttributeNode [attributeCapacity];
56 namespaces = new DTMXPathNamespaceNode [nsCapacity];
61 XmlNameTable nameTable;
63 int attributeCapacity;
67 DTMXPathLinkedNode [] nodes;
70 DTMXPathAttributeNode [] attributes;
73 DTMXPathNamespaceNode [] namespaces;
75 // idTable [string value] -> int nodeId
81 int [] parentStack = new int [10];
82 int parentStackIndex = 0;
84 // for attribute processing; should be reset per each element.
92 // They are only used in Writer
98 public DTMXPathDocument CreateDocument ()
102 return new DTMXPathDocument (nameTable,
112 // index 0 is dummy. No node (including Root) is assigned to this index
113 // So that we can easily compare index != 0 instead of index < 0.
114 // (Difference between jnz or jbe in 80x86.)
115 AddNode (0, 0, 0, XPathNodeType.All, "", false, "", "", "", "", "", 0, 0, 0);
117 AddAttribute (0, null, null, null, null, 0, 0);
118 AddNsNode (0, null, null, 0);
120 AddNsNode (1, "xml", XmlNamespaces.XML, 0);
123 AddNode (0, 0, 0, XPathNodeType.Root, null, false, "", "", "", "", "", 1, 0, 0);
126 this.lastNsInScope = 1;
127 parentStack [0] = nodeIndex;
129 state = WriteState.Content;
132 private int GetParentIndex ()
134 return parentStack [parentStackIndex];
137 private int GetPreviousSiblingIndex ()
139 int parent = parentStack [parentStackIndex];
140 if (parent == nodeIndex)
142 int prevSibling = nodeIndex;
143 while (nodes [prevSibling].Parent != parent)
144 prevSibling = nodes [prevSibling].Parent;
148 private void UpdateTreeForAddition ()
150 int parent = GetParentIndex ();
151 prevSibling = GetPreviousSiblingIndex ();
155 if (prevSibling != 0)
156 nodes [prevSibling].NextSibling = nodeIndex;
157 if (parent == nodeIndex - 1)
158 nodes [parent].FirstChild = nodeIndex;
161 private void CloseStartElement ()
163 if (attrIndexAtStart != attributeIndex)
164 nodes [nodeIndex].FirstAttribute = attrIndexAtStart + 1;
165 if (nsIndexAtStart != nsIndex) {
166 nodes [nodeIndex].FirstNamespace = nsIndex;
167 lastNsInScope = nsIndex;
171 if (parentStack.Length == parentStackIndex) {
172 int [] tmp = new int [parentStackIndex * 2];
173 Array.Copy (parentStack, tmp, parentStackIndex);
176 parentStack [parentStackIndex] = nodeIndex;
178 state = WriteState.Content;
\r
182 private void SetNodeArrayLength (int size)
184 DTMXPathLinkedNode [] newArr = new DTMXPathLinkedNode [size];
185 Array.Copy (nodes, newArr, System.Math.Min (size, nodes.Length));
189 private void SetAttributeArrayLength (int size)
191 DTMXPathAttributeNode [] newArr =
192 new DTMXPathAttributeNode [size];
193 Array.Copy (attributes, newArr, System.Math.Min (size, attributes.Length));
197 private void SetNsArrayLength (int size)
199 DTMXPathNamespaceNode [] newArr =
200 new DTMXPathNamespaceNode [size];
201 Array.Copy (namespaces, newArr, System.Math.Min (size, namespaces.Length));
205 // Here followings are skipped: firstChild, nextSibling,
206 public void AddNode (int parent, int firstAttribute, int previousSibling, XPathNodeType nodeType, string baseUri, bool isEmptyElement, string localName, string ns, string prefix, string value, string xmlLang, int namespaceNode, int lineNumber, int linePosition)
208 if (nodes.Length < nodeIndex + 1) {
210 SetNodeArrayLength (nodeCapacity);
214 nodes [nodeIndex] = new DTMXPathLinkedNode ();
216 nodes [nodeIndex].FirstChild = 0; // dummy
217 nodes [nodeIndex].Parent = parent;
218 nodes [nodeIndex].FirstAttribute = firstAttribute;
219 nodes [nodeIndex].PreviousSibling = previousSibling;
220 nodes [nodeIndex].NextSibling = 0; // dummy
221 nodes [nodeIndex].NodeType = nodeType;
222 nodes [nodeIndex].BaseURI = baseUri;
223 nodes [nodeIndex].IsEmptyElement = isEmptyElement;
224 nodes [nodeIndex].LocalName = localName;
225 nodes [nodeIndex].NamespaceURI = ns;
226 nodes [nodeIndex].Prefix = prefix;
227 nodes [nodeIndex].Value = value;
228 nodes [nodeIndex].XmlLang = xmlLang;
229 nodes [nodeIndex].FirstNamespace = namespaceNode;
230 nodes [nodeIndex].LineNumber = lineNumber;
231 nodes [nodeIndex].LinePosition = linePosition;
234 // Followings are skipped: nextAttribute,
235 public void AddAttribute (int ownerElement, string localName, string ns, string prefix, string value, int lineNumber, int linePosition)
237 if (attributes.Length < attributeIndex + 1) {
238 attributeCapacity *= 4;
239 SetAttributeArrayLength (attributeCapacity);
243 attributes [attributeIndex] = new DTMXPathAttributeNode ();
245 attributes [attributeIndex].OwnerElement = ownerElement;
246 attributes [attributeIndex].LocalName = localName;
247 attributes [attributeIndex].NamespaceURI = ns;
248 attributes [attributeIndex].Prefix = prefix;
249 attributes [attributeIndex].Value = value;
250 attributes [attributeIndex].LineNumber = lineNumber;
251 attributes [attributeIndex].LinePosition = linePosition;
254 // Followings are skipped: nextNsNode (may be next attribute in the same element, or ancestors' nsNode)
255 public void AddNsNode (int declaredElement, string name, string ns, int nextNs)
257 if (namespaces.Length < nsIndex + 1) {
259 SetNsArrayLength (nsCapacity);
263 namespaces [nsIndex] = new DTMXPathNamespaceNode ();
265 namespaces [nsIndex].DeclaredElement = declaredElement;
266 namespaces [nsIndex].Name = name;
267 namespaces [nsIndex].Namespace = ns;
268 namespaces [nsIndex].NextNamespace = nextNs;
272 #region XmlWriter methods
273 // They are not supported
274 public override string XmlLang { get { return null; } }
275 public override XmlSpace XmlSpace { get { return XmlSpace.None; } }
277 public override WriteState WriteState { get { return state; } }
279 public override void Close ()
282 SetNodeArrayLength (nodeIndex + 1);
283 SetAttributeArrayLength (attributeIndex + 1);
284 SetNsArrayLength (nsIndex + 1);
288 public override void Flush ()
293 public override string LookupPrefix (string ns)
297 if (namespaces [tmp].Namespace == ns)
298 return namespaces [tmp].Name;
299 tmp = namespaces [tmp].NextNamespace;
304 public override void WriteCData (string data)
\r
306 AddTextNode (data);
\r
309 private void AddTextNode (string data)
\r
312 case WriteState.Element:
\r
313 CloseStartElement ();
\r
315 case WriteState.Content:
\r
318 throw new InvalidOperationException ("Invalid document state for CDATA section: " + state);
\r
321 // When text after text, just add the value, and return.
\r
322 if (nodes [nodeIndex].Parent == parentStack [parentStackIndex]) {
323 switch (nodes [nodeIndex].NodeType) {
\r
324 case XPathNodeType.Text:
\r
325 case XPathNodeType.SignificantWhitespace:
\r
326 string value = nodes [nodeIndex].Value + data;
327 nodes [nodeIndex].Value = value;
328 if (IsWhitespace (value))
329 nodes [nodeIndex].NodeType = XPathNodeType.SignificantWhitespace;
\r
331 nodes [nodeIndex].NodeType = XPathNodeType.Text;
\r
336 int parent = GetParentIndex ();
\r
337 UpdateTreeForAddition ();
\r
355 private void CheckTopLevelNode ()
\r
358 case WriteState.Element:
\r
359 CloseStartElement ();
\r
361 case WriteState.Content:
\r
362 case WriteState.Prolog:
\r
363 case WriteState.Start:
\r
366 throw new InvalidOperationException ("Invalid document state for CDATA section: " + state);
\r
370 public override void WriteComment (string data)
\r
372 CheckTopLevelNode ();
\r
374 int parent = GetParentIndex ();
\r
375 UpdateTreeForAddition ();
\r
380 XPathNodeType.Comment,
393 public override void WriteProcessingInstruction (string name, string data)
\r
395 CheckTopLevelNode ();
\r
397 int parent = GetParentIndex ();
\r
398 UpdateTreeForAddition ();
\r
403 XPathNodeType.ProcessingInstruction,
416 public override void WriteWhitespace (string data)
\r
418 CheckTopLevelNode ();
\r
420 int parent = GetParentIndex ();
\r
421 UpdateTreeForAddition ();
\r
426 XPathNodeType.Whitespace,
439 public override void WriteStartDocument ()
\r
444 public override void WriteStartDocument (bool standalone)
\r
449 public override void WriteEndDocument ()
\r
454 public override void WriteStartElement (string prefix, string localName, string ns)
\r
457 case WriteState.Element:
\r
458 CloseStartElement ();
\r
460 case WriteState.Start:
\r
461 case WriteState.Prolog:
\r
462 case WriteState.Content:
\r
465 throw new InvalidOperationException ("Invalid document state for writing element: " + state);
\r
468 int parent = GetParentIndex ();
\r
469 UpdateTreeForAddition ();
\r
471 WriteStartElement (parent, prevSibling, prefix, localName, ns);
472 state = WriteState.Element;
475 private void WriteStartElement (int parent, int previousSibling, string prefix, string localName, string ns)
477 PrepareStartElement (previousSibling);
480 0, // dummy:firstAttribute
482 XPathNodeType.Element,
488 "", // Element has no internal value.
495 private void PrepareStartElement (int previousSibling)
497 hasAttributes = false;
499 attrIndexAtStart = attributeIndex;
500 nsIndexAtStart = nsIndex;
502 while (namespaces [lastNsInScope].DeclaredElement == previousSibling) {
503 lastNsInScope = namespaces [lastNsInScope].NextNamespace;
507 public override void WriteEndElement ()
\r
509 WriteEndElement (false);
\r
512 public override void WriteFullEndElement ()
\r
514 WriteEndElement (true);
\r
517 private void WriteEndElement (bool full)
\r
520 case WriteState.Element:
\r
521 CloseStartElement ();
\r
523 case WriteState.Content:
\r
526 throw new InvalidOperationException ("Invalid state for writing EndElement: " + state);
\r
529 if (nodes [nodeIndex].NodeType == XPathNodeType.Element) {
\r
531 nodes [nodeIndex].IsEmptyElement = true;
\r
535 public override void WriteStartAttribute (string prefix, string localName, string ns)
\r
537 if (state != WriteState.Element)
\r
538 throw new InvalidOperationException ("Invalid document state for attribute: " + state);
\r
540 state = WriteState.Attribute;
\r
541 if (ns == XmlNamespaces.XMLNS)
542 ProcessNamespace ((prefix == null || prefix == String.Empty) ? "" : localName, String.Empty); // dummy: Value should be completed
544 ProcessAttribute (prefix, localName, ns, String.Empty); // dummy: Value should be completed
547 private void ProcessNamespace (string prefix, string ns)
549 int nextTmp = hasLocalNs ? nsIndex : nodes [nodeIndex].FirstNamespace;
553 this.AddNsNode (nodeIndex,
558 openNamespace = true;
561 private void ProcessAttribute (string prefix, string localName, string ns, string value)
565 this.AddAttribute (nodeIndex,
568 prefix != null ? prefix : String.Empty,
573 attributes [attributeIndex - 1].NextAttribute = attributeIndex;
575 hasAttributes = true;
578 public override void WriteEndAttribute ()
\r
580 if (state != WriteState.Attribute)
\r
581 throw new InvalidOperationException ();
\r
583 openNamespace = false;
\r
584 state = WriteState.Element;
\r
587 public override void WriteString (string text)
\r
589 if (WriteState == WriteState.Attribute) {
\r
591 namespaces [nsIndex].Namespace += text;
\r
593 attributes [attributeIndex].Value += text;
\r
596 AddTextNode (text);
\r
599 // Well, they cannot be supported, but actually used to
\r
600 // disable-output-escaping = "true"
\r
601 public override void WriteRaw (string data)
\r
603 WriteString (data);
\r
606 public override void WriteRaw (char [] data, int start, int len)
\r
608 WriteString (new string (data, start, len));
\r
611 public override void WriteName (string name)
\r
613 WriteString (name);
\r
616 public override void WriteNmToken (string name)
\r
618 WriteString (name);
\r
621 public override void WriteBase64 (byte [] buffer, int index, int count)
\r
623 throw new NotSupportedException ();
\r
626 public override void WriteBinHex (byte [] buffer, int index, int count)
\r
628 throw new NotSupportedException ();
\r
631 public override void WriteChars (char [] buffer, int index, int count)
\r
633 throw new NotSupportedException ();
\r
636 public override void WriteCharEntity (char c)
\r
638 throw new NotSupportedException ();
\r
641 public override void WriteDocType (string name, string pub, string sys, string intSubset)
\r
643 throw new NotSupportedException ();
\r
646 public override void WriteEntityRef (string name)
648 throw new NotSupportedException ();
\r
651 public override void WriteQualifiedName (string localName, string ns)
\r
653 throw new NotSupportedException ();
\r
656 public override void WriteSurrogateCharEntity (char high, char low)
\r
658 throw new NotSupportedException ();
\r
661 private bool IsWhitespace (string data)
663 for (int i = 0; i < data.Length; i++) {