0029151: GCC 7.1 warnings "this statement may fall through" [-Wimplicit-fallthrough=]
[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       continue;
184     case LDOM_XmlReader::XML_COMMENT:
185       continue;
186     case LDOM_XmlReader::XML_FULL_ELEMENT:
187       if (isElement == Standard_False) {
188         isElement = Standard_True;
189         myDocument -> myRootElement = &myReader -> GetElement ();
190         if (startElement()) {
191           isError = Standard_True;
192           myError = "User abort at startElement()";
193           break;
194         }
195         if (endElement()) {
196           isError = Standard_True;
197           myError = "User abort at endElement()";
198           break;
199         }
200         continue;
201       }
202       isError = Standard_True;
203       myError = "Expected comment or end-of-file";
204       break;
205     case LDOM_XmlReader::XML_START_ELEMENT:
206       if (isElement == Standard_False) {
207         isElement = Standard_True;
208
209         if (theWithoutRoot && !isInsertFictRootElement)
210         {
211           isInsertFictRootElement = Standard_True;
212
213           // create fiction root element
214           TCollection_AsciiString aFicName ("document");
215           myReader->CreateElement (aFicName.ToCString(), aFicName.Length());
216         }
217         
218         myDocument->myRootElement = &myReader->GetElement();
219         
220         if (startElement()) {
221           isError = Standard_True;
222           myError = "User abort at startElement()";
223           break;
224         }
225         isError = ParseElement (theIStream);
226         if (isError) break;
227         continue;
228       }
229       isError = Standard_True;
230       myError = "Expected comment or end-of-file";
231       break;
232     case LDOM_XmlReader::XML_END_ELEMENT:
233       if (endElement()) {
234         isError = Standard_True;
235         myError = "User abort at endElement()";
236       }
237       break;
238     case LDOM_XmlReader::XML_EOF:
239       break;
240     case LDOM_XmlReader::XML_UNKNOWN:
241       if (isElement) {
242     default:
243         myError = "Unexpected data beyond the Document Element";
244       }
245       isError = Standard_True;
246     }
247     break;
248   }
249   return isError;
250 }
251
252 //=======================================================================
253 //function : ParseElement
254 //purpose  : parse one element, given the type of its XML presentation
255 //=======================================================================
256
257 Standard_Boolean LDOMParser::ParseElement (Standard_IStream& theIStream)
258 {
259   Standard_Boolean  isError = Standard_False;
260   const LDOM_BasicElement * aParent = &myReader->GetElement();
261   const LDOM_BasicNode    * aLastChild = NULL;
262   for(;;) {
263     LDOM_Node::NodeType aLocType;
264     LDOMBasicString     aTextValue;
265     char *aTextStr;
266     LDOM_XmlReader::RecordType aType = ReadRecord (* myReader, theIStream, myCurrentData);
267     switch (aType) {
268     case LDOM_XmlReader::XML_UNKNOWN:
269       isError = Standard_True;
270       break;
271     case LDOM_XmlReader::XML_FULL_ELEMENT:
272       aParent -> AppendChild (&myReader -> GetElement(), aLastChild);
273       if (startElement()) {
274         isError = Standard_True;
275         myError = "User abort at startElement()";
276         break;
277       }
278       if (endElement()) {
279         isError = Standard_True;
280         myError = "User abort at endElement()";
281         break;
282       }
283       break;
284     case LDOM_XmlReader::XML_START_ELEMENT:
285       aParent -> AppendChild (&myReader -> GetElement(), aLastChild);
286       if (startElement()) {
287         isError = Standard_True;
288         myError = "User abort at startElement()";
289         break;
290       }
291       isError = ParseElement (theIStream);
292       break;
293     case LDOM_XmlReader::XML_END_ELEMENT:
294       {
295         Standard_CString aParentName = Standard_CString(aParent->GetTagName());
296         aTextStr = (char *)myCurrentData.str();
297         if (strcmp(aTextStr, aParentName) != 0) {
298           myError = "Expected end tag \'";
299           myError += aParentName;
300           myError += "\'";
301           isError = Standard_True;
302         }
303         else if (endElement()) {
304           isError = Standard_True;
305           myError = "User abort at endElement()";
306         }
307         delete [] aTextStr;
308       }
309       return isError;
310     case LDOM_XmlReader::XML_TEXT:
311       aLocType = LDOM_Node::TEXT_NODE;
312       {
313         Standard_Integer aTextLen;
314         aTextStr = LDOM_CharReference::Decode ((char *)myCurrentData.str(), aTextLen);
315         // try to convert to integer
316         if (IsDigit(aTextStr[0])) {
317           if (LDOM_XmlReader::getInteger (aTextValue, aTextStr,
318                                           aTextStr + aTextLen))
319             aTextValue = LDOMBasicString (aTextStr, aTextLen, myDocument);
320         } else
321           aTextValue = LDOMBasicString (aTextStr, aTextLen, myDocument);
322       }
323       goto create_text_node;
324     case LDOM_XmlReader::XML_COMMENT:
325       aLocType = LDOM_Node::COMMENT_NODE;
326       {
327         Standard_Integer aTextLen;
328         aTextStr = LDOM_CharReference::Decode ((char *)myCurrentData.str(), aTextLen);
329         aTextValue = LDOMBasicString (aTextStr, aTextLen, myDocument);
330       }
331       goto create_text_node;
332     case LDOM_XmlReader::XML_CDATA:
333       aLocType = LDOM_Node::CDATA_SECTION_NODE;
334       aTextStr = (char *)myCurrentData.str();
335       aTextValue = LDOMBasicString(aTextStr,myCurrentData.Length(),myDocument);
336     create_text_node:
337       {
338         LDOM_BasicNode& aTextNode =
339           LDOM_BasicText::Create (aLocType, aTextValue, myDocument);
340         aParent -> AppendChild (&aTextNode, aLastChild);
341       }
342       delete [] aTextStr;
343       break;
344     case LDOM_XmlReader::XML_EOF:
345       myError = "Inexpected end of file";
346       isError = Standard_True;
347       break;
348     default: ;
349     }
350     if (isError) break;
351   }
352   return isError;
353 }
354
355 //=======================================================================
356 //function : startElement
357 //purpose  : virtual hook on 'StartElement' event for descendant classes
358 //=======================================================================
359
360 Standard_Boolean LDOMParser::startElement ()
361 {
362   return Standard_False;
363 }
364
365 //=======================================================================
366 //function : endElement
367 //purpose  : virtual hook on 'EndElement' event for descendant classes
368 //=======================================================================
369
370 Standard_Boolean LDOMParser::endElement ()
371 {
372   return Standard_False;
373 }
374
375 //=======================================================================
376 //function : getCurrentElement
377 //purpose  : 
378 //=======================================================================
379
380 LDOM_Element LDOMParser::getCurrentElement () const
381 {
382   return LDOM_Element (myReader -> GetElement(), myDocument);
383 }
384
385 //=======================================================================
386 //function : getDocument
387 //purpose  : 
388 //=======================================================================
389
390 LDOM_Document LDOMParser::getDocument ()
391 {
392   return myDocument -> Self();
393 }
394