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