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