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