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, 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
;
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, object schemaType
, 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
].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) {
264 SetNsArrayLength (nsCapacity
);
268 namespaces
[nsIndex
] = new DTMXPathNamespaceNode ();
270 namespaces
[nsIndex
].DeclaredElement
= declaredElement
;
271 namespaces
[nsIndex
].Name
= name
;
272 namespaces
[nsIndex
].Namespace
= ns
;
273 namespaces
[nsIndex
].NextNamespace
= nextNs
;
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 ()
287 SetNodeArrayLength (nodeIndex
+ 1);
288 SetAttributeArrayLength (attributeIndex
+ 1);
289 SetNsArrayLength (nsIndex
+ 1);
292 public override void Flush ()
297 public override string LookupPrefix (string ns
)
301 if (namespaces
[tmp
].Namespace
== ns
)
302 return namespaces
[tmp
].Name
;
303 tmp
= namespaces
[tmp
].NextNamespace
;
308 public override void WriteCData (string data
)
313 private void AddTextNode (string data
)
316 case WriteState
.Element
:
317 CloseStartElement ();
319 case WriteState
.Content
:
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
;
334 nodes
[nodeIndex
].NodeType
= XPathNodeType
.Text
;
339 int parent
= GetParentIndex ();
340 UpdateTreeForAddition ();
359 private void CheckTopLevelNode ()
362 case WriteState
.Element
:
363 CloseStartElement ();
365 case WriteState
.Content
:
366 case WriteState
.Prolog
:
367 case WriteState
.Start
:
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 ();
385 XPathNodeType
.Comment
,
398 public override void WriteProcessingInstruction (string name
, string data
)
400 CheckTopLevelNode ();
402 int parent
= GetParentIndex ();
403 UpdateTreeForAddition ();
409 XPathNodeType
.ProcessingInstruction
,
422 public override void WriteWhitespace (string data
)
424 CheckTopLevelNode ();
426 int parent
= GetParentIndex ();
427 UpdateTreeForAddition ();
433 XPathNodeType
.Whitespace
,
446 public override void WriteStartDocument ()
451 public override void WriteStartDocument (bool standalone
)
456 public override void WriteEndDocument ()
461 public override void WriteStartElement (string prefix
, string localName
, string ns
)
464 case WriteState
.Element
:
465 CloseStartElement ();
467 case WriteState
.Start
:
468 case WriteState
.Prolog
:
469 case WriteState
.Content
:
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
);
487 0, // dummy:firstAttribute
490 XPathNodeType
.Element
,
496 "", // Element has no internal value.
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
)
528 case WriteState
.Element
:
529 CloseStartElement ();
531 case WriteState
.Content
:
534 throw new InvalidOperationException ("Invalid state for writing EndElement: " + state
);
536 parentForFirstChild
= -1;
537 if (nodes
[nodeIndex
].NodeType
== XPathNodeType
.Element
) {
539 nodes
[nodeIndex
].IsEmptyElement
= true;
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
554 ProcessAttribute (prefix
, localName
, ns
, String
.Empty
); // dummy: Value should be completed
557 private void ProcessNamespace (string prefix
, string ns
)
561 int nextTmp
= lastNsIndexInCurrent
== 0 ? nodes
[nodeIndex
].FirstNamespace
: lastNsIndexInCurrent
;
563 this.AddNsNode (nodeIndex
,
567 lastNsIndexInCurrent
= nsIndex
;
568 openNamespace
= true;
571 private void ProcessAttribute (string prefix
, string localName
, string ns
, string value)
575 this.AddAttribute (nodeIndex
,
578 prefix
!= null ? prefix
: String
.Empty
,
583 if (firstAttributeIndex
== 0)
584 firstAttributeIndex
= attributeIndex
;
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
) {
602 namespaces
[nsIndex
].Namespace
+= text
;
604 attributes
[attributeIndex
].Value
+= text
;
610 // Well, they cannot be supported, but actually used to
611 // disable-output-escaping = "true"
612 public override void WriteRaw (string 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
)
627 public override void WriteNmToken (string 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
++) {