7fd59977 |
1 | // File: LDOM_XmlWriter.cxx |
2 | // Created: 28.06.01 10:32:53 |
3 | // Author: Alexander GRIGORIEV |
4 | // Copyright: OpenCascade 2001 |
5 | // History: |
6 | |
7 | |
8 | #include <LDOM_XmlWriter.hxx> |
9 | #include <LDOM_Document.hxx> |
10 | #include <LDOM_CharReference.hxx> |
11 | |
12 | #define chOpenAngle '<' |
13 | #define chCloseAngle '>' |
14 | #define chOpenSquare '[' |
15 | #define chCloseSquare ']' |
16 | #define chQuestion '?' |
17 | #define chForwardSlash '/' |
18 | #define chLF '\n' |
19 | #define chNull '\0' |
20 | #define chEqual '=' |
21 | #define chDash '-' |
22 | #define chBang '!' |
23 | #define chSpace ' ' |
24 | #define chDoubleQuote '\"' |
25 | #define chZero '0' |
26 | #define chOne '1' |
27 | #define chTwo '2' |
28 | #define chThree '3' |
29 | #define chFour '4' |
30 | #define chFive '5' |
31 | #define chSix '6' |
32 | #define chSeven '7' |
33 | #define chEight '8' |
34 | #define chNine '9' |
35 | #define chLatin_a 'a' |
36 | #define chLatin_b 'b' |
37 | #define chLatin_c 'c' |
38 | #define chLatin_d 'd' |
39 | #define chLatin_e 'e' |
40 | #define chLatin_f 'f' |
41 | #define chLatin_g 'g' |
42 | #define chLatin_h 'h' |
43 | #define chLatin_i 'i' |
44 | #define chLatin_j 'j' |
45 | #define chLatin_k 'k' |
46 | #define chLatin_l 'l' |
47 | #define chLatin_m 'm' |
48 | #define chLatin_n 'n' |
49 | #define chLatin_o 'o' |
50 | #define chLatin_p 'p' |
51 | #define chLatin_q 'q' |
52 | #define chLatin_r 'r' |
53 | #define chLatin_s 's' |
54 | #define chLatin_t 't' |
55 | #define chLatin_u 'u' |
56 | #define chLatin_v 'v' |
57 | #define chLatin_w 'w' |
58 | #define chLatin_x 'x' |
59 | #define chLatin_y 'y' |
60 | #define chLatin_z 'z' |
61 | #define chLatin_A 'A' |
62 | #define chLatin_B 'B' |
63 | #define chLatin_C 'C' |
64 | #define chLatin_D 'D' |
65 | #define chLatin_E 'E' |
66 | #define chLatin_F 'F' |
67 | #define chLatin_G 'G' |
68 | #define chLatin_H 'H' |
69 | #define chLatin_I 'I' |
70 | #define chLatin_J 'J' |
71 | #define chLatin_K 'K' |
72 | #define chLatin_L 'L' |
73 | #define chLatin_M 'M' |
74 | #define chLatin_N 'N' |
75 | #define chLatin_O 'O' |
76 | #define chLatin_P 'P' |
77 | #define chLatin_Q 'Q' |
78 | #define chLatin_R 'R' |
79 | #define chLatin_S 'S' |
80 | #define chLatin_T 'T' |
81 | #define chLatin_U 'U' |
82 | #define chLatin_V 'V' |
83 | #define chLatin_W 'W' |
84 | #define chLatin_X 'X' |
85 | #define chLatin_Y 'Y' |
86 | #define chLatin_Z 'Z' |
87 | |
88 | static const LXMLCh gEndElement[] = { chOpenAngle, chForwardSlash, chNull }; |
89 | static const LXMLCh gEndElement1[]= { chForwardSlash, chNull }; |
90 | static const LXMLCh gEndPI[] = { chQuestion, chCloseAngle, chNull }; |
91 | static const LXMLCh gStartPI[] = { chOpenAngle, chQuestion, chNull }; |
92 | static const LXMLCh gXMLDecl1[] = |
93 | { chOpenAngle, chQuestion, chLatin_x, chLatin_m, chLatin_l |
94 | , chSpace, chLatin_v, chLatin_e, chLatin_r, chLatin_s, chLatin_i |
95 | , chLatin_o, chLatin_n, chEqual, chDoubleQuote, chNull |
96 | }; |
97 | static const LXMLCh gXMLDecl2[] = |
98 | { chDoubleQuote, chSpace, chLatin_e, chLatin_n, chLatin_c |
99 | , chLatin_o, chLatin_d, chLatin_i, chLatin_n, chLatin_g, chEqual |
100 | , chDoubleQuote, chNull |
101 | }; |
102 | static const LXMLCh gXMLDecl3[] = |
103 | { chDoubleQuote, chSpace, chLatin_s, chLatin_t, chLatin_a |
104 | , chLatin_n, chLatin_d, chLatin_a, chLatin_l, chLatin_o |
105 | , chLatin_n, chLatin_e, chEqual, chDoubleQuote, chNull |
106 | }; |
107 | static const LXMLCh gXMLDecl4[] = |
108 | { chDoubleQuote, chQuestion, chCloseAngle |
109 | , chLF, chNull |
110 | }; |
111 | static const LXMLCh gStartCDATA[] = |
112 | { chOpenAngle, chBang, chOpenSquare, chLatin_C, chLatin_D, |
113 | chLatin_A, chLatin_T, chLatin_A, chOpenSquare, chNull |
114 | }; |
115 | static const LXMLCh gEndCDATA[] = |
116 | { chCloseSquare, chCloseSquare, chCloseAngle, chNull }; |
117 | static const LXMLCh gStartComment[] = |
118 | { chOpenAngle, chBang, chDash, chDash, chNull }; |
119 | static const LXMLCh gEndComment[] = |
120 | { chDash, chDash, chCloseAngle, chNull }; |
121 | static const LXMLCh gStartDoctype[] = |
122 | { chOpenAngle, chBang, chLatin_D, chLatin_O, chLatin_C, chLatin_T, |
123 | chLatin_Y, chLatin_P, chLatin_E, chSpace, chNull |
124 | }; |
125 | static const LXMLCh gPublic[] = |
126 | { chLatin_P, chLatin_U, chLatin_B, chLatin_L, chLatin_I, |
127 | chLatin_C, chSpace, chDoubleQuote, chNull |
128 | }; |
129 | static const LXMLCh gSystem[] = |
130 | { chLatin_S, chLatin_Y, chLatin_S, chLatin_T, chLatin_E, |
131 | chLatin_M, chSpace, chDoubleQuote, chNull |
132 | }; |
133 | static const LXMLCh gStartEntity[] = |
134 | { chOpenAngle, chBang, chLatin_E, chLatin_N, chLatin_T, chLatin_I, |
135 | chLatin_T, chLatin_Y, chSpace, chNull |
136 | }; |
137 | static const LXMLCh gNotation[] = |
138 | { chLatin_N, chLatin_D, chLatin_A, chLatin_T, chLatin_A, |
139 | chSpace, chDoubleQuote, chNull |
140 | }; |
141 | |
142 | static LXMLCh * getEncodingName (const LXMLCh * theEncodingName) |
143 | { |
144 | const LXMLCh * anEncoding = theEncodingName; |
145 | if (theEncodingName == NULL) |
146 | { |
147 | // anEncoding = // US-ASCII |
148 | // { chLatin_U, chLatin_S, chDash, chLatin_A, chLatin_S, chLatin_C, chLatin_I, |
149 | // chLatin_I, chNull }; |
150 | static const LXMLCh anUTFEncoding [] = // UTF-8 |
151 | { chLatin_U, chLatin_T, chLatin_F, chDash, chEight, chNull }; |
152 | anEncoding = anUTFEncoding; |
153 | } |
154 | Standard_Integer aLen = 0; |
155 | while (anEncoding[aLen++] != chNull); |
156 | LXMLCh * aResult = new LXMLCh [aLen]; |
157 | memcpy (aResult, anEncoding, aLen * sizeof (LXMLCh)); |
158 | return aResult; |
159 | } |
160 | |
161 | //======================================================================= |
162 | //function : LH3D_LXMLWriter() |
163 | //purpose : Constructor |
164 | //======================================================================= |
165 | LDOM_XmlWriter::LDOM_XmlWriter (FILE * aFile, |
166 | const LXMLCh * theEncoding) |
167 | : myFile (aFile), |
168 | myEncodingName (::getEncodingName (theEncoding)), |
169 | myIndent (0), |
170 | myCurIndent (0), |
171 | myABuffer (NULL), |
172 | myABufferLen (0) |
173 | {} |
174 | |
175 | //======================================================================= |
176 | //function : ~LDOM_XmlWriter |
177 | //purpose : Destructor |
178 | //======================================================================= |
179 | |
180 | LDOM_XmlWriter::~LDOM_XmlWriter () |
181 | { |
182 | delete [] myEncodingName; |
183 | if (myABuffer != NULL) delete [] myABuffer; |
184 | } |
185 | |
186 | //======================================================================= |
187 | //function : operator << |
188 | //purpose : |
189 | //======================================================================= |
190 | |
191 | LDOM_XmlWriter& LDOM_XmlWriter::operator << (const LDOM_Document& aDoc) |
192 | { |
193 | const char * anXMLversion = "1.0"; |
194 | * this << gXMLDecl1 << anXMLversion |
195 | << gXMLDecl2 << myEncodingName << gXMLDecl4; |
196 | |
197 | return (* this << aDoc.getDocumentElement()); |
198 | } |
199 | |
200 | //======================================================================= |
201 | //function : operator << |
202 | //purpose : Stream out an LDOMString |
203 | //======================================================================= |
204 | |
205 | inline LDOM_XmlWriter& LDOM_XmlWriter::operator << |
206 | (const LDOMBasicString& aString) |
207 | { |
208 | switch (aString.Type()) { |
209 | case LDOMBasicString::LDOM_Integer: |
210 | { |
211 | Standard_Integer aValue; |
212 | aString.GetInteger (aValue); |
213 | fprintf (myFile, "%d", aValue); |
214 | break; |
215 | } |
216 | case LDOMBasicString::LDOM_AsciiHashed: // attr names and element tags |
217 | case LDOMBasicString::LDOM_AsciiDocClear: |
218 | { |
219 | const char * str = aString.GetString(); |
220 | if (str) { |
221 | const Standard_Integer aLen = strlen (str); |
222 | if (aLen > 0) fwrite (str, aLen, 1, myFile); |
223 | } |
224 | } |
225 | break; |
226 | case LDOMBasicString::LDOM_AsciiFree: |
227 | case LDOMBasicString::LDOM_AsciiDoc: |
228 | { |
229 | const char * str = aString.GetString(); |
230 | if (str) { |
231 | Standard_Integer aLen; |
232 | char * encStr = LDOM_CharReference::Encode(str, aLen, Standard_False); |
233 | if (aLen > 0) fwrite (encStr, aLen, 1, myFile); |
234 | if (encStr != str) delete [] encStr; |
235 | } |
236 | } |
237 | default: ; |
238 | } |
239 | return * this; |
240 | } |
241 | |
242 | //======================================================================= |
243 | //function : operator<<() |
244 | //purpose : Stream out a char *. |
245 | //======================================================================= |
246 | inline LDOM_XmlWriter& LDOM_XmlWriter::operator << (const LXMLCh * aString) |
247 | { |
248 | unsigned int aLength = strlen (aString); |
249 | if (aLength > 0) fwrite ((void *) aString, aLength, 1, myFile); |
250 | return * this; |
251 | } |
252 | |
253 | //======================================================================= |
254 | //function : operator<<() |
255 | //purpose : Stream out a character. |
256 | //======================================================================= |
257 | inline LDOM_XmlWriter& LDOM_XmlWriter::operator << (const LXMLCh aChar) |
258 | { |
259 | fputc (aChar, myFile); |
260 | return * this; |
261 | } |
262 | |
263 | //======================================================================= |
264 | //function : WriteAttribute() |
265 | //purpose : Stream out an XML attribute. |
266 | //======================================================================= |
267 | void LDOM_XmlWriter::WriteAttribute (const LDOM_Node& theAtt) |
268 | { |
269 | int aLength; |
270 | const char * aName = theAtt.getNodeName().GetString(); |
271 | const LDOMString aValueStr = theAtt.getNodeValue(); |
272 | |
273 | // Integer attribute value |
274 | if (aValueStr.Type() == LDOMBasicString::LDOM_Integer) { |
275 | Standard_Integer anIntValue; |
276 | aValueStr.GetInteger (anIntValue); |
277 | aLength = 20 + strlen (aName); |
278 | if (aLength > myABufferLen) { |
279 | if (myABuffer != NULL) delete [] myABuffer; |
280 | myABuffer = new char [aLength+1]; |
281 | myABufferLen = aLength; |
282 | } |
283 | sprintf (myABuffer, "%c%s%c%c%d%c", chSpace, aName, |
284 | chEqual, chDoubleQuote, anIntValue, chDoubleQuote); |
285 | aLength = strlen (myABuffer); |
286 | |
287 | // String attribute value |
288 | } else { |
289 | const char * aValue = aValueStr.GetString(); |
290 | char * encStr; |
291 | if (aValueStr.Type() == LDOMBasicString::LDOM_AsciiDocClear) { |
292 | encStr = (char *) aValue; |
293 | aLength = 4 + strlen (aValue) + strlen (aName); |
294 | } else { |
295 | encStr = LDOM_CharReference::Encode (aValue, aLength, Standard_True); |
296 | aLength += 4 + strlen (aName); |
297 | } |
298 | if (aLength > myABufferLen) { |
299 | if (myABuffer != NULL) delete [] myABuffer; |
300 | myABuffer = new char [aLength+1]; |
301 | myABufferLen = aLength; |
302 | } |
303 | sprintf (myABuffer, "%c%s%c%c%s%c", chSpace, aName, |
304 | chEqual, chDoubleQuote, encStr, chDoubleQuote); |
305 | if (encStr != aValue) delete [] encStr; |
306 | } |
307 | fwrite ((void *) myABuffer, aLength, 1, myFile); |
308 | } |
309 | |
310 | //======================================================================= |
311 | //function : operator<<() |
312 | //purpose : Stream out a DOM node, and, recursively, all of its children. |
313 | // This function is the heart of writing a DOM tree out as XML source. |
314 | // Give it a document node and it will do the whole thing. |
315 | //======================================================================= |
316 | LDOM_XmlWriter& LDOM_XmlWriter::operator<< (const LDOM_Node& theNodeToWrite) |
317 | { |
318 | // Get the name and value out for convenience |
319 | LDOMString aNodeName = theNodeToWrite.getNodeName(); |
320 | LDOMString aNodeValue = theNodeToWrite.getNodeValue(); |
321 | // unsigned long dwLent = aNodeValue.length(); |
322 | |
323 | switch (theNodeToWrite.getNodeType()) |
324 | { |
325 | case LDOM_Node::TEXT_NODE : |
326 | * this << aNodeValue; |
327 | break; |
328 | case LDOM_Node::ELEMENT_NODE : |
329 | { |
330 | const int aMaxNSpaces = 40; |
331 | static LXMLCh aSpaces [] = { |
332 | chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, |
333 | chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, |
334 | chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, |
335 | chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, |
336 | chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, |
337 | chOpenAngle, chNull }; |
338 | const LXMLCh * anIndentString = &aSpaces [aMaxNSpaces - myCurIndent]; |
339 | if (anIndentString < &aSpaces[0]) anIndentString = &aSpaces[0]; |
340 | |
341 | // Output the element start tag. |
342 | * this << anIndentString << aNodeName.GetString(); |
343 | |
344 | // Output any attributes of this element |
345 | const LDOM_Element& anElemToWrite = (const LDOM_Element&) theNodeToWrite; |
346 | LDOM_NodeList aListAtt = anElemToWrite.GetAttributesList (); |
347 | Standard_Integer aListInd = aListAtt.getLength(); |
348 | while (aListInd--) { |
349 | LDOM_Node aChild = aListAtt.item (aListInd); |
350 | WriteAttribute (aChild); |
351 | } |
352 | |
353 | // Test for the presence of children |
354 | LDOM_Node aChild = theNodeToWrite.getFirstChild(); |
355 | if (aChild != 0) |
356 | { |
357 | // There are children. Close start-tag, and output children. |
358 | * this << chCloseAngle; |
359 | if (aChild.getNodeType() == LDOM_Node::ELEMENT_NODE && myIndent > 0) |
360 | * this << chLF; |
361 | Standard_Boolean isChildElem = Standard_False; |
362 | while( aChild != 0) |
363 | { |
364 | isChildElem = (aChild.getNodeType() == LDOM_Node::ELEMENT_NODE); |
365 | if (isChildElem) myCurIndent += myIndent; |
366 | *this << aChild; |
367 | if (isChildElem) myCurIndent -= myIndent; |
368 | do aChild = aChild.getNextSibling(); |
369 | while (aChild.getNodeType() == LDOM_Node::ATTRIBUTE_NODE); |
370 | } |
371 | // Done with children. Output the end tag. |
372 | // |
373 | if (isChildElem) |
374 | * this << anIndentString |
375 | << gEndElement1 << aNodeName.GetString() << chCloseAngle; |
376 | else |
377 | * this << gEndElement << aNodeName.GetString() << chCloseAngle; |
378 | } |
379 | else |
380 | { |
381 | // There were no children. Output the short form close of |
382 | // the element start tag, making it an empty-element tag. |
383 | * this << chForwardSlash << chCloseAngle; |
384 | } |
385 | if (myIndent > 0) |
386 | * this << chLF; |
387 | break; |
388 | } |
389 | case LDOM_Node::CDATA_SECTION_NODE: |
390 | { |
391 | * this << gStartCDATA << aNodeValue << gEndCDATA; |
392 | break; |
393 | } |
394 | case LDOM_Node::COMMENT_NODE: |
395 | { |
396 | * this << gStartComment << aNodeValue << gEndComment; |
397 | break; |
398 | } |
399 | default: |
400 | #ifndef WNT |
401 | cerr << "Unrecognized node type = " |
402 | << (long)theNodeToWrite.getNodeType() << endl |
403 | #endif |
404 | ; } |
405 | return *this; |
406 | } |