a8f0e70fd7facde51055f881a0209ed0a9f18d56
[occt.git] / src / LDOM / LDOMParser.cxx
1 // Created on: 2001-07-20
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 //AGV 060302: Input from istream
17 //            AGV 130302: Return error if there are data after the root element
18
19 //#define LDOM_PARSER_TRACE
20
21 #include <LDOMParser.hxx>
22 #include <LDOM_MemManager.hxx>
23 #include <LDOM_XmlReader.hxx>
24 #include <LDOM_BasicText.hxx>
25 #include <LDOM_CharReference.hxx>
26 #include <TCollection_ExtendedString.hxx>
27 #include <OSD_OpenFile.hxx>
28
29 #include <fcntl.h>
30 #ifdef _MSC_VER
31 #include <io.h>
32 #else
33 #include <unistd.h>
34 #endif
35
36 //=======================================================================
37 //function : ~LDOMParser
38 //purpose  : 
39 //=======================================================================
40
41 LDOMParser::~LDOMParser()
42 {
43   if (myReader) delete myReader;
44 }
45
46 //=======================================================================
47 //function : ReadRecord
48 //purpose  : Take the next lexical element from XML stream
49 //=======================================================================
50
51 #ifdef LDOM_PARSER_TRACE
52 static
53 #else
54 inline
55 #endif
56         LDOM_XmlReader::RecordType ReadRecord (LDOM_XmlReader&  aReader,
57                                                Standard_IStream& theIStream,
58                                                LDOM_OSStream&   aData)
59 {
60 #ifdef LDOM_PARSER_TRACE
61   static aCounter = 0;
62   ++ aCounter;
63 #endif
64   const LDOM_XmlReader::RecordType aType = aReader.ReadRecord (theIStream, aData);
65 #ifdef LDOM_PARSER_TRACE
66   static FILE * ff = NULL;
67   TCollection_AsciiString aTraceFileName;
68 #ifdef _WIN32
69   aTraceFileName = TCollection_AsciiString (getenv("TEMP")) + "\\ldom.trace";
70 #else
71   aTraceFileName = "/tmp/ldom.trace";
72 #endif
73   ff = fopen (aTraceFileName.ToCString(),ff ? "at": "wt");
74   const char * aDataType;
75   switch (aType) {
76   case LDOM_XmlReader::XML_UNKNOWN:       aDataType= "XML_UNKNOWN      "; break;
77   case LDOM_XmlReader::XML_HEADER:        aDataType= "XML_HEADER       "; break;
78   case LDOM_XmlReader::XML_DOCTYPE:       aDataType= "XML_DOCTYPE      "; break;
79   case LDOM_XmlReader::XML_COMMENT:       aDataType= "XML_COMMENT      "; break;
80   case LDOM_XmlReader::XML_START_ELEMENT: aDataType= "XML_START_ELEMENT"; break;
81   case LDOM_XmlReader::XML_END_ELEMENT:   aDataType= "XML_END_ELEMENT  "; break;
82   case LDOM_XmlReader::XML_FULL_ELEMENT:  aDataType= "XML_FULL_ELEMENT "; break;
83   case LDOM_XmlReader::XML_TEXT:          aDataType= "XML_TEXT         "; break;
84   case LDOM_XmlReader::XML_CDATA:         aDataType= "XML_CDATA        "; break;
85   case LDOM_XmlReader::XML_EOF:           aDataType= "XML_EOF          ";
86   }
87   char * aStr = aData.str();
88   fprintf (ff, "%5d %s: %s\n", aCounter, aDataType, aStr);
89   delete [] aStr;
90   fclose (ff);
91 #endif
92   return aType;
93 }
94
95 //=======================================================================
96 //function : GetError
97 //purpose  : Return text describing a parsing error
98 //=======================================================================
99
100 const TCollection_AsciiString& LDOMParser::GetError
101                                 (TCollection_AsciiString& aData) const
102 {
103   char * aStr =(char *)myCurrentData.str();
104   aData = aStr;
105   delete [] aStr;
106   return myError;
107 }
108
109 //=======================================================================
110 //function : parse
111 //purpose  :
112 //=======================================================================
113
114 Standard_Boolean LDOMParser::parse (istream& anInput,
115                                     const Standard_Boolean theTagPerStep,
116                                     const Standard_Boolean theWithoutRoot)
117 {
118   // Open the DOM Document
119   myDocument = new LDOM_MemManager (20000);
120   myError.Clear();
121
122   // Create the Reader instance
123   if (myReader) delete myReader;
124   myReader = new LDOM_XmlReader (myDocument, myError, theTagPerStep);
125
126   // Parse
127   return ParseDocument (anInput, theWithoutRoot);
128 }
129
130 //=======================================================================
131 //function : parse
132 //purpose  :
133 //=======================================================================
134
135 Standard_Boolean LDOMParser::parse (const char * const aFileName)
136 {
137   std::ifstream aFileStream;
138   OSD_OpenStream (aFileStream, aFileName, std::ios::in);
139
140   if (aFileStream.good())
141   {
142     return parse (aFileStream);
143   }
144   else
145   {
146     myError = "Fatal XML error: Cannot open XML file";
147     return Standard_True;
148   }
149 }
150
151 //=======================================================================
152 //function : ParseDocument
153 //purpose  : parse the whole document (abstracted from the XML source)
154 //=======================================================================
155
156 Standard_Boolean LDOMParser::ParseDocument (istream& theIStream, const Standard_Boolean theWithoutRoot)
157 {
158   Standard_Boolean      isError   = Standard_False;
159   Standard_Boolean      isElement = Standard_False;
160   Standard_Boolean      isDoctype = Standard_False;
161
162   Standard_Boolean      isInsertFictRootElement = Standard_False;
163
164   for(;;) {
165     LDOM_XmlReader::RecordType aType = (theWithoutRoot && !isInsertFictRootElement ?
166                                         LDOM_XmlReader::XML_START_ELEMENT : 
167                                         ReadRecord (*myReader, theIStream, myCurrentData));
168     switch (aType) {
169     case LDOM_XmlReader::XML_HEADER:
170       if (isDoctype || isElement) {
171         myError = "Unexpected XML declaration";
172         isError = Standard_True;
173         break;
174       }
175       continue;
176     case LDOM_XmlReader::XML_DOCTYPE:
177       if (isElement) {
178         myError = "Unexpected DOCTYPE declaration";
179         isError = Standard_True;
180         break;
181       }
182       isDoctype = Standard_True;
183     case LDOM_XmlReader::XML_COMMENT:
184       continue;
185     case LDOM_XmlReader::XML_FULL_ELEMENT:
186       if (isElement == Standard_False) {
187         isElement = Standard_True;
188         myDocument -> myRootElement = &myReader -> GetElement ();
189         if (startElement()) {
190           isError = Standard_True;
191           myError = "User abort at startElement()";
192           break;
193         }
194         if (endElement()) {
195           isError = Standard_True;
196           myError = "User abort at endElement()";
197           break;
198         }
199         continue;
200       }
201     case LDOM_XmlReader::XML_START_ELEMENT:
202       if (isElement == Standard_False) {
203         isElement = Standard_True;
204
205         if (theWithoutRoot && !isInsertFictRootElement)
206         {
207           isInsertFictRootElement = Standard_True;
208
209           // create fiction root element
210           TCollection_AsciiString aFicName ("document");
211           myReader->CreateElement (aFicName.ToCString(), aFicName.Length());
212         }
213         
214         myDocument->myRootElement = &myReader->GetElement();
215         
216         if (startElement()) {
217           isError = Standard_True;
218           myError = "User abort at startElement()";
219           break;
220         }
221         isError = ParseElement (theIStream);
222         if (isError) break;
223         continue;
224       }
225       isError = Standard_True;
226       myError = "Expected comment or end-of-file";
227     case LDOM_XmlReader::XML_END_ELEMENT:
228       if (endElement()) {
229         isError = Standard_True;
230         myError = "User abort at endElement()";
231       }
232     case LDOM_XmlReader::XML_EOF:
233       break;
234     case LDOM_XmlReader::XML_UNKNOWN:
235       if (isElement) {
236     default:
237         myError = "Unexpected data beyond the Document Element";
238       }
239       isError = Standard_True;
240     }
241     break;
242   }
243   return isError;
244 }
245
246 //=======================================================================
247 //function : ParseElement
248 //purpose  : parse one element, given the type of its XML presentation
249 //=======================================================================
250
251 Standard_Boolean LDOMParser::ParseElement (Standard_IStream& theIStream)
252 {
253   Standard_Boolean  isError = Standard_False;
254   const LDOM_BasicElement * aParent = &myReader->GetElement();
255   const LDOM_BasicNode    * aLastChild = NULL;
256   for(;;) {
257     LDOM_Node::NodeType aLocType;
258     LDOMBasicString     aTextValue;
259     char *aTextStr;
260     LDOM_XmlReader::RecordType aType = ReadRecord (* myReader, theIStream, myCurrentData);
261     switch (aType) {
262     case LDOM_XmlReader::XML_UNKNOWN:
263       isError = Standard_True;
264       break;
265     case LDOM_XmlReader::XML_FULL_ELEMENT:
266       aParent -> AppendChild (&myReader -> GetElement(), aLastChild);
267       if (startElement()) {
268         isError = Standard_True;
269         myError = "User abort at startElement()";
270         break;
271       }
272       if (endElement()) {
273         isError = Standard_True;
274         myError = "User abort at endElement()";
275         break;
276       }
277       break;
278     case LDOM_XmlReader::XML_START_ELEMENT:
279       aParent -> AppendChild (&myReader -> GetElement(), aLastChild);
280       if (startElement()) {
281         isError = Standard_True;
282         myError = "User abort at startElement()";
283         break;
284       }
285       isError = ParseElement (theIStream);
286       break;
287     case LDOM_XmlReader::XML_END_ELEMENT:
288       {
289         Standard_CString aParentName = Standard_CString(aParent->GetTagName());
290         aTextStr = (char *)myCurrentData.str();
291         if (strcmp(aTextStr, aParentName) != 0) {
292           myError = "Expected end tag \'";
293           myError += aParentName;
294           myError += "\'";
295           isError = Standard_True;
296         }
297         else if (endElement()) {
298           isError = Standard_True;
299           myError = "User abort at endElement()";
300         }
301         delete [] aTextStr;
302       }
303       return isError;
304     case LDOM_XmlReader::XML_TEXT:
305       aLocType = LDOM_Node::TEXT_NODE;
306       {
307         Standard_Integer aTextLen;
308         aTextStr = LDOM_CharReference::Decode ((char *)myCurrentData.str(), aTextLen);
309         // try to convert to integer
310         if (IsDigit(aTextStr[0])) {
311           if (LDOM_XmlReader::getInteger (aTextValue, aTextStr,
312                                           aTextStr + aTextLen))
313             aTextValue = LDOMBasicString (aTextStr, aTextLen, myDocument);
314         } else
315           aTextValue = LDOMBasicString (aTextStr, aTextLen, myDocument);
316       }
317       goto create_text_node;
318     case LDOM_XmlReader::XML_COMMENT:
319       aLocType = LDOM_Node::COMMENT_NODE;
320       {
321         Standard_Integer aTextLen;
322         aTextStr = LDOM_CharReference::Decode ((char *)myCurrentData.str(), aTextLen);
323         aTextValue = LDOMBasicString (aTextStr, aTextLen, myDocument);
324       }
325       goto create_text_node;
326     case LDOM_XmlReader::XML_CDATA:
327       aLocType = LDOM_Node::CDATA_SECTION_NODE;
328       aTextStr = (char *)myCurrentData.str();
329       aTextValue = LDOMBasicString(aTextStr,myCurrentData.Length(),myDocument);
330     create_text_node:
331       {
332         LDOM_BasicNode& aTextNode =
333           LDOM_BasicText::Create (aLocType, aTextValue, myDocument);
334         aParent -> AppendChild (&aTextNode, aLastChild);
335       }
336       delete [] aTextStr;
337       break;
338     case LDOM_XmlReader::XML_EOF:
339       myError = "Inexpected end of file";
340       isError = Standard_True;
341       break;
342     default: ;
343     }
344     if (isError) break;
345   }
346   return isError;
347 }
348
349 //=======================================================================
350 //function : startElement
351 //purpose  : virtual hook on 'StartElement' event for descendant classes
352 //=======================================================================
353
354 Standard_Boolean LDOMParser::startElement ()
355 {
356   return Standard_False;
357 }
358
359 //=======================================================================
360 //function : endElement
361 //purpose  : virtual hook on 'EndElement' event for descendant classes
362 //=======================================================================
363
364 Standard_Boolean LDOMParser::endElement ()
365 {
366   return Standard_False;
367 }
368
369 //=======================================================================
370 //function : getCurrentElement
371 //purpose  : 
372 //=======================================================================
373
374 LDOM_Element LDOMParser::getCurrentElement () const
375 {
376   return LDOM_Element (myReader -> GetElement(), myDocument);
377 }
378
379 //=======================================================================
380 //function : getDocument
381 //purpose  : 
382 //=======================================================================
383
384 LDOM_Document LDOMParser::getDocument ()
385 {
386   return myDocument -> Self();
387 }
388