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