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 char  gEndElement[] = { chOpenAngle, chForwardSlash, chNull };
97 static const char  gEndElement1[]= { chForwardSlash, chNull };
98 
99 static const char  gXMLDecl1[] =
100 {       chOpenAngle, chQuestion, chLatin_x, chLatin_m, chLatin_l
101     ,   chSpace, chLatin_v, chLatin_e, chLatin_r, chLatin_s, chLatin_i
102     ,   chLatin_o, chLatin_n, chEqual, chDoubleQuote, chNull
103 };
104 static const char  gXMLDecl2[] =
105 {       chDoubleQuote, chSpace, chLatin_e, chLatin_n, chLatin_c
106     ,   chLatin_o, chLatin_d, chLatin_i, chLatin_n, chLatin_g, chEqual
107     ,   chDoubleQuote, chNull
108 };
109 
110 static const char  gXMLDecl4[] =
111 {       chDoubleQuote, chQuestion, chCloseAngle
112     ,   chLF, chNull
113 };
114 static const char  gStartCDATA[] =
115 {       chOpenAngle, chBang, chOpenSquare, chLatin_C, chLatin_D,
116         chLatin_A, chLatin_T, chLatin_A, chOpenSquare, chNull
117 };
118 static const char  gEndCDATA[] =
119 {    chCloseSquare, chCloseSquare, chCloseAngle, chNull };
120 static const char  gStartComment[] =
121 {    chOpenAngle, chBang, chDash, chDash, chNull };
122 static const char  gEndComment[] =
123 {    chDash, chDash, chCloseAngle, chNull };
124 
getEncodingName(const char * theEncodingName)125 static char* getEncodingName (const char* theEncodingName)
126 {
127   const char* anEncoding = theEncodingName;
128   if (theEncodingName == NULL)
129   {
130     static const char anUTFEncoding [] =  {chLatin_U, chLatin_T, chLatin_F, chDash, chEight, chNull};
131     anEncoding = anUTFEncoding;
132   }
133 
134   Standard_Integer aLen = 0;
135   while (anEncoding[aLen++] != chNull);
136 
137   char * aResult = new char [aLen];
138   memcpy (aResult, anEncoding, aLen * sizeof (char));
139 
140   return aResult;
141 }
142 
143 //=======================================================================
144 //function : LDOM_XmlWriter
145 //purpose  :
146 //=======================================================================
LDOM_XmlWriter(const char * theEncoding)147 LDOM_XmlWriter::LDOM_XmlWriter (const char * theEncoding)
148  : myEncodingName (::getEncodingName (theEncoding)),
149    myIndent       (0),
150    myCurIndent    (0),
151    myABuffer      (NULL),
152    myABufferLen   (0)
153 {
154   ;
155 }
156 
157 //=======================================================================
158 //function : ~LDOM_XmlWriter
159 //purpose  : Destructor
160 //=======================================================================
~LDOM_XmlWriter()161 LDOM_XmlWriter::~LDOM_XmlWriter ()
162 {
163   delete [] myEncodingName;
164 
165   if (myABuffer != NULL)
166   {
167     delete [] myABuffer;
168   }
169 }
170 
171 //=======================================================================
172 //function : Write
173 //purpose  :
174 //=======================================================================
Write(Standard_OStream & theOStream,const LDOM_Document & aDoc)175 void LDOM_XmlWriter::Write (Standard_OStream& theOStream, const LDOM_Document& aDoc)
176 {
177   Write (theOStream, gXMLDecl1);
178 
179   const char * anXMLversion = "1.0";
180   Write (theOStream, anXMLversion);
181 
182   Write (theOStream, gXMLDecl2);
183   Write (theOStream, myEncodingName);
184   Write (theOStream, gXMLDecl4);
185 
186   Write (theOStream, aDoc.getDocumentElement());
187 }
188 
189 //=======================================================================
190 //function : Write
191 //purpose  :
192 //=======================================================================
Write(Standard_OStream & theOStream,const LDOM_Node & theNode)193 void LDOM_XmlWriter::Write (Standard_OStream& theOStream, const LDOM_Node& theNode)
194 {
195   // Get the name and value out for convenience
196   LDOMString aNodeName  = theNode.getNodeName();
197   LDOMString aNodeValue = theNode.getNodeValue();
198 
199   switch (theNode.getNodeType())
200   {
201     case LDOM_Node::TEXT_NODE :
202     Write (theOStream, aNodeValue);
203     break;
204     case LDOM_Node::ELEMENT_NODE :
205     {
206       const int aMaxNSpaces    = 40;
207       static char aSpaces [] = {
208         chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace,
209         chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace,
210         chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace,
211         chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace,
212         chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace, chSpace,
213         chOpenAngle, chNull };
214       const char * anIndentString = &aSpaces [aMaxNSpaces -  myCurIndent];
215 
216       if (anIndentString < &aSpaces[0])
217       {
218         anIndentString = &aSpaces[0];
219       }
220 
221       // Output the element start tag.
222       Write (theOStream, anIndentString);
223       Write (theOStream, aNodeName.GetString());
224 
225       // Output any attributes of this element
226       const LDOM_Element& anElemToWrite = (const LDOM_Element&)theNode;
227       LDOM_NodeList aListAtt = anElemToWrite.GetAttributesList();
228       Standard_Integer aListInd = aListAtt.getLength();
229 
230       while (aListInd--)
231       {
232         LDOM_Node aChild = aListAtt.item (aListInd);
233         WriteAttribute (theOStream, aChild);
234       }
235 
236       //  Test for the presence of children
237       LDOM_Node aChild = theNode.getFirstChild();
238       if (aChild != 0)
239       {
240         // There are children. Close start-tag, and output children.
241         Write (theOStream, chCloseAngle);
242         if (aChild.getNodeType() == LDOM_Node::ELEMENT_NODE && myIndent > 0)
243         {
244           Write(theOStream, chLF);
245         }
246 
247         Standard_Boolean isChildElem = Standard_False;
248         while( aChild != 0)
249         {
250           isChildElem = (aChild.getNodeType() == LDOM_Node::ELEMENT_NODE);
251           if (isChildElem)
252           {
253             myCurIndent += myIndent;
254           }
255 
256           Write(theOStream, aChild);
257 
258           if (isChildElem)
259           {
260             myCurIndent -= myIndent;
261           }
262 
263           do
264           {
265             aChild = aChild.getNextSibling();
266           } while (aChild.getNodeType() == LDOM_Node::ATTRIBUTE_NODE);
267         }
268 
269         // Done with children.  Output the end tag.
270         if (isChildElem)
271         {
272           Write (theOStream, anIndentString);
273           Write (theOStream, gEndElement1);
274           Write (theOStream, aNodeName.GetString());
275           Write (theOStream, chCloseAngle);
276         }
277         else
278         {
279           Write (theOStream, gEndElement);
280           Write (theOStream, aNodeName.GetString());
281           Write (theOStream, chCloseAngle);
282         }
283       }
284       else
285       {
286         //  There were no children. Output the short form close of
287         //  the element start tag, making it an empty-element tag.
288         Write (theOStream, chForwardSlash);
289         Write (theOStream, chCloseAngle);
290       }
291 
292       if (myIndent > 0)
293       {
294         Write (theOStream, chLF);
295       }
296       break;
297     }
298     case LDOM_Node::CDATA_SECTION_NODE:
299     {
300       Write (theOStream, gStartCDATA);
301       Write (theOStream, aNodeValue);
302       Write (theOStream, gEndCDATA);
303       break;
304     }
305     case LDOM_Node::COMMENT_NODE:
306     {
307       Write (theOStream, gStartComment);
308       Write (theOStream, aNodeValue);
309       Write (theOStream, gEndComment);
310       break;
311     }
312   default:
313 #ifndef _MSC_VER
314       std::cerr << "Unrecognized node type = "
315         << (long)theNode.getNodeType() << std::endl
316 #endif
317   ; }
318 }
319 
320 //=======================================================================
321 //function :
322 //purpose  : Stream out an LDOMString
323 //=======================================================================
Write(Standard_OStream & theOStream,const LDOMBasicString & theString)324 void LDOM_XmlWriter::Write (Standard_OStream& theOStream, const LDOMBasicString& theString)
325 {
326   switch (theString.Type())
327   {
328     case LDOMBasicString::LDOM_Integer:
329     {
330       Standard_Integer aValue;
331       theString.GetInteger (aValue);
332 
333       TCollection_AsciiString aStrValue (aValue);
334       theOStream.write(aStrValue.ToCString(), strlen (aStrValue.ToCString()));
335 
336       break;
337     }
338     case LDOMBasicString::LDOM_AsciiHashed:       // attr names and element tags
339     case LDOMBasicString::LDOM_AsciiDocClear:
340     {
341       const char* aStr = theString.GetString();
342       if (aStr)
343       {
344         const Standard_Size aLen = strlen (aStr);
345         if (aLen > 0)
346         {
347           theOStream.write(aStr, aLen);
348         }
349       }
350     }
351     break;
352     case LDOMBasicString::LDOM_AsciiFree:
353     case LDOMBasicString::LDOM_AsciiDoc:
354     {
355       const char* aStr = theString.GetString();
356       if (aStr)
357       {
358         Standard_Integer aLen;
359         char* encStr = LDOM_CharReference::Encode (aStr, aLen, Standard_False);
360         if (aLen > 0)
361         {
362           theOStream.write(encStr, aLen);
363         }
364 
365         if (encStr != aStr)
366         {
367           delete [] encStr;
368         }
369       }
370     }
371   default: ;
372   }
373 }
374 
375 //=======================================================================
376 //function : Write
377 //purpose  : Stream out a char
378 //=======================================================================
Write(Standard_OStream & theOStream,const char theChar)379 void LDOM_XmlWriter::Write (Standard_OStream& theOStream, const char theChar)
380 {
381   theOStream.write (&theChar, sizeof(char));
382 }
383 
384 //=======================================================================
385 //function : Write
386 //purpose  : Stream out a char *
387 //=======================================================================
Write(Standard_OStream & theOStream,const char * theString)388 void LDOM_XmlWriter::Write (Standard_OStream& theOStream, const char * theString)
389 {
390   Standard_Size aLength = strlen (theString);
391   if (aLength > 0)
392   {
393     theOStream.write (theString, aLength);
394   }
395 }
396 
397 //=======================================================================
398 //function : WriteAttribute()
399 //purpose  : Stream out an XML attribute.
400 //=======================================================================
WriteAttribute(Standard_OStream & theOStream,const LDOM_Node & theAtt)401 void LDOM_XmlWriter::WriteAttribute (Standard_OStream& theOStream, const LDOM_Node& theAtt)
402 {
403   const char* aName = theAtt.getNodeName().GetString();
404   const LDOMString aValueStr = theAtt.getNodeValue();
405 
406   int aLength = 0;
407 
408   // Integer attribute value
409   if (aValueStr.Type() == LDOMBasicString::LDOM_Integer)
410   {
411     Standard_Integer anIntValue;
412     aValueStr.GetInteger (anIntValue);
413 
414     aLength = (Standard_Integer)(20 + strlen (aName));
415     if (aLength > myABufferLen)
416     {
417       if (myABuffer != NULL)
418       {
419         delete [] myABuffer;
420       }
421 
422       myABuffer    = new char [aLength+1];
423       myABufferLen = aLength;
424     }
425     sprintf (myABuffer, "%c%s%c%c%d%c", chSpace, aName, chEqual, chDoubleQuote, anIntValue, chDoubleQuote);
426     aLength = (Standard_Integer)strlen (myABuffer);
427 
428 
429   }
430   else // String attribute value
431   {
432     char* encStr;
433     const char* aValue = aValueStr.GetString();
434     if (aValueStr.Type() == LDOMBasicString::LDOM_AsciiDocClear)
435     {
436       encStr  = (char *) aValue;
437       aLength = (Standard_Integer) (4 + strlen (aValue) + strlen (aName));
438     }
439     else
440     {
441       encStr = LDOM_CharReference::Encode (aValue, aLength, Standard_True);
442       aLength += (Standard_Integer) (4 + strlen (aName));
443     }
444 
445     if (aLength > myABufferLen)
446     {
447       if (myABuffer != NULL)
448       {
449         delete [] myABuffer;
450       }
451 
452       myABuffer    = new char [aLength+1];
453       myABufferLen = aLength;
454     }
455 
456     sprintf (myABuffer, "%c%s%c%c%s%c", chSpace, aName, chEqual, chDoubleQuote, encStr, chDoubleQuote);
457 
458     if (encStr != aValue)
459     {
460       delete [] encStr;
461     }
462   }
463 
464   theOStream.write (myABuffer, aLength);
465 }
466