Integration of OCCT 6.5.0 from SVN
[occt.git] / src / LDOM / LDOM_XmlWriter.cxx
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 }