Salome HOME
8f85f1c7c845b1585a84dd52630f09ef9ce167e6
[modules/yacs.git] / src / yacsloader / xmlParserBase.cxx
1 //  Copyright (C) 2006-2008  CEA/DEN, EDF R&D
2 //
3 //  This library is free software; you can redistribute it and/or
4 //  modify it under the terms of the GNU Lesser General Public
5 //  License as published by the Free Software Foundation; either
6 //  version 2.1 of the License.
7 //
8 //  This library is distributed in the hope that it will be useful,
9 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 //  Lesser General Public License for more details.
12 //
13 //  You should have received a copy of the GNU Lesser General Public
14 //  License along with this library; if not, write to the Free Software
15 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 //  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 #include "xmlParserBase.hxx"
20 #include "Exception.hxx"
21
22 #include <stdexcept>
23 #include <iostream>
24 #include <cstdarg>
25 #include <cassert>
26
27 //#define _DEVDEBUG_
28 #include "YacsTrace.hxx"
29
30 // --- specific part for libxml2 ----------------------------------------------
31
32 #ifdef USE_LIBXML2
33 extern "C"
34 {
35 #include <libxml/parserInternals.h> // for xmlCreateFileParserCtxt
36 }
37 _xmlParserCtxt* xmlParserBase::_xmlParser;
38 void xmlParserBase::XML_SetUserData(_xmlParserCtxt* ctxt,
39                                     xmlParserBase* parser)
40 {
41   ctxt->userData = parser;
42 }
43 #endif
44
45 // --- specific part for expat ------------------------------------------------
46
47 #ifdef USE_EXPAT
48 XML_Parser xmlParserBase::_xmlParser;
49 #endif
50
51 // --- generic part -----------------------------------------------------------
52
53 using namespace std;
54 using YACS::Exception;
55
56 std::stack<xmlParserBase*> xmlParserBase::_stackParser;
57 std::list<xmlParserBase*>  xmlParserBase::_garbage;
58
59 /*! callback usable only with libxml2
60  */
61
62 void XMLCALL xmlParserBase::start_document(void* userData)
63 {
64   //DEBTRACE("xmlParserBase::start_document");
65   xmlParserBase *currentParser = static_cast<xmlParserBase *> (userData);
66 }
67
68 /*! callback usable only with libxml2
69  */
70
71 void XMLCALL xmlParserBase::end_document  (void* userData)
72 {
73   //DEBTRACE("xmlParserBase::end_document");
74   xmlParserBase *currentParser = static_cast<xmlParserBase *> (userData);
75 }
76
77 /*! callback called on start of an xml element: <name>
78  */
79
80 void XMLCALL xmlParserBase::start_element (void* userData,
81                                            const xmlChar* name,
82                                            const xmlChar** p)
83 {
84   //DEBTRACE("xmlParserBase::start_element " << name);
85   cleanGarbage();
86   xmlParserBase *currentParser = static_cast<xmlParserBase *> (userData);
87   const XML_Char *aName = tochar(name);
88   currentParser->incrCount(aName);
89   currentParser->onStart(aName, p);
90 }
91
92
93 /*! callback called on end of an xml element: </name>
94  */
95
96 void XMLCALL xmlParserBase::end_element   (void* userData,
97                                            const xmlChar* name)
98 {
99   //DEBTRACE("xmlParserBase::end_element");
100   const XML_Char *aName = tochar(name);
101   xmlParserBase *childParser = static_cast<xmlParserBase *> (userData);
102   _garbage.push_back(_stackParser.top());
103   DEBTRACE("xmlParserBase::end_element " << _garbage.size());
104   _stackParser.pop();
105   XML_SetUserData(_xmlParser, _stackParser.top());
106   childParser->onEnd(aName);
107   childParser->end();
108  }
109
110
111 /*! callback called for significant characters inside tags: <tag>content</tag>
112  *  or outside tags, like space or new line. 
113  *  with expat get also the CDATA tags: <tag>![CDATA[content]]></tag>
114  */
115
116 void XMLCALL xmlParserBase::characters    (void* userData,
117                                            const xmlChar* ch,
118                                            int len)
119 {
120   //DEBTRACE("xmlParserBase::characters " << len);
121   xmlParserBase *currentParser = (xmlParserBase *) (userData);
122   string data((char*)ch,len);
123   currentParser->charData(data);
124 }
125
126
127 /*! callback usable only with libxml2
128  */
129
130 void XMLCALL xmlParserBase::comment       (void* userData,
131                                            const xmlChar* value)
132 {
133   //DEBTRACE("xmlParserBase::comment");
134   xmlParserBase *currentParser = static_cast<xmlParserBase *> (userData);
135 }
136
137
138 /*! callback usable only with libxml2
139  */
140
141 void XMLCALL xmlParserBase::warning       (void* userData,
142                                            const char* fmt, ...)
143 {
144   DEBTRACE("xmlParserBase::warning");
145   xmlParserBase *currentParser = static_cast<xmlParserBase *> (userData);
146   va_list args;
147   va_start(args, fmt);
148   string format = "%s";
149   if (format == fmt)
150     {
151       char* parv;
152       parv = va_arg(args, char*);
153       cerr << parv ;
154     }
155   else cerr << __FILE__ << " [" << __LINE__ << "] : " 
156             << "error format not taken into account: " << fmt << endl;
157   va_end(args);
158 }
159
160
161 /*! callback usable only with libxml2
162  */
163
164 void XMLCALL xmlParserBase::error         (void* userData,
165                                            const char* fmt, ...)
166 {
167   DEBTRACE("xmlParserBase::error");
168   xmlParserBase *currentParser = static_cast<xmlParserBase *> (userData);
169   va_list args;
170   va_start(args, fmt);
171   string format = "%s";
172   if (format == fmt)
173     {
174       char* parv;
175       parv = va_arg(args, char*);
176       cerr << parv ;
177       xmlParserBase *currentParser = (xmlParserBase *) userData;
178       //cerr << currentParser->element << endl;
179     }
180   else cerr << __FILE__ << " [" << __LINE__ << "] : " 
181             << "error format not taken into account: " << fmt << endl;
182   va_end(args);
183 }
184
185
186 /*! callback usable only with libxml2
187  */
188
189 void XMLCALL xmlParserBase::fatal_error   (void* userData,
190                                            const char* fmt, ...)
191 {
192   DEBTRACE("xmlParserBase::fatal_error");
193   xmlParserBase *currentParser = static_cast<xmlParserBase *> (userData);
194   va_list args;
195   va_start(args, fmt);
196   string format = "%s";
197   if (format == fmt)
198     {
199       char* parv;
200       parv = va_arg(args, char*);
201       cerr << parv ;
202     }
203   else cerr << __FILE__ << " [" << __LINE__ << "] : " 
204             << "error format not taken into account: " << fmt << endl;
205   va_end(args);
206 }
207
208 /*! callback called for CDATA inside tags: <tag>![CDATA[content]]></tag>
209  *  used only by libxml2
210  */
211
212 void XMLCALL xmlParserBase::cdata_block   (void* userData,
213                                            const xmlChar* value,
214                                            int len)
215 {
216   //DEBTRACE("xmlParserBase::cdata_block");
217   xmlParserBase *currentParser = static_cast<xmlParserBase *> (userData);
218   string data((char*)value,len);
219   currentParser->charData(data);
220 }
221
222 void xmlParserBase::cleanGarbage()
223 {
224   while (!_garbage.empty())
225     {
226       delete (_garbage.front());
227       _garbage.pop_front();
228     }
229 }
230
231 /*! Stores the tag attributes on a map. The map is an attribute of the parser
232  *  object dedicated to the current xml tag
233  */
234
235 void xmlParserBase::getAttributes(const xmlChar** p)
236 {
237   if (p) while (*p)
238     {
239       string attributeName = (char*)*p;
240       //cerr << "attribute name " << attributeName << endl;
241       p++;
242       string attributeValue = (char*)*p;
243       //cerr << "attribute value " << attributeValue << endl;
244       p++;
245       _mapAttrib[attributeName] = attributeValue;
246     }
247 }
248
249 /*! Stores an attribute (key, value) on the map attribute.
250  *  used for attributes defined at another tag level (child tags). 
251  */
252
253 void xmlParserBase::setAttribute(std::string key, std::string value)
254 {
255   _mapAttrib[key] = value;
256 }
257
258 /*! Gets an attribute value given a string key.
259  *  If the key does not exist, throws an Exception.
260  */
261
262 std::string xmlParserBase::getAttribute(std::string key)
263 {
264   if (_mapAttrib.find(key) == _mapAttrib.end())
265     {
266       string what = "Attribute does not exist: " + key;
267       throw Exception(what);
268     }
269   return _mapAttrib[key];
270 }
271
272 /*! Add data on the data attribute of the parser object dedicated to an xml tag
273  */
274
275 void xmlParserBase::addData(std::string value)
276 {
277   //  DEBTRACE("xmlParserBase::addData()");
278   _data += value;
279 }
280
281 /*! all parsers must know their father parser (father tag), in order
282  *  to set values or attributes in father.
283  */
284
285 void xmlParserBase::init (const xmlChar** p, xmlParserBase* father)
286 {
287   _father = father;
288 }
289
290 /*! to be specialized for each kind of xml tag
291  */
292
293 void xmlParserBase::onStart  (const XML_Char* elem, const xmlChar** p)
294 {
295 }
296
297 /*! to be specialized for each kind of xml tag
298  */
299
300 void xmlParserBase::onEnd    (const XML_Char* name)
301 {
302 }
303
304 /*! to be specialized following the kind of xml tag
305  */
306
307 void xmlParserBase::charData (std::string data)
308 {
309 }
310
311 /*! May be specialized for each kind of xml tag.
312  *  Counts the number of tag occurences of a given type inside a context.
313  */
314
315 void xmlParserBase::incrCount(const XML_Char *elem)
316 {
317   if(counts.find(elem)==counts.end())
318     counts[elem]=1;
319   else
320     counts[elem]=counts[elem]+1;
321 }
322
323 /*! to be specialized for each kind of xml tag
324  */
325
326 void xmlParserBase::end()
327 {
328 }
329
330 /*! Throws an exception. different implementation with libxml2 and expat
331  */
332
333 void xmlParserBase::stopParse(std::string what)
334 {
335 #ifdef USE_LIBXML2
336   xmlStopParser(_xmlParser);
337 #endif
338 #ifdef USE_EXPAT
339   throw Exception(what);
340 #endif  
341 }
342
343 // ----------------------------------------------------------------------------
344
345 #ifdef USE_LIBXML2
346
347 /*! libxml2 parser initialisation
348  * \param parser dedicated parser
349  */
350
351 xmlReader::xmlReader(xmlParserBase* parser): _rootParser(parser)
352 {
353 }
354
355 /*! libxml2 parse
356  * \param xmlFile file to parse
357  */
358
359 void xmlReader::parse(std::string xmlFile)
360 {
361   _rootParser->init(0);
362   _rootParser->_stackParser.push(_rootParser);
363
364   xmlSAXHandler baseHandler = 
365     {
366       0,  // internal_subset,
367       0,  // isStandalone
368       0,  // hasInternalSubset
369       0,  // hasExternalSubset
370       0,  // resolveEntity
371       0,  // getEntity
372       0,  // entityDecl
373       0,  // notationDecl
374       0,  // attributeDecl
375       0,  // elementDecl
376       0,  // unparsedEntityDecl
377       0,  // setDocumentLocator
378       xmlParserBase::start_document, // startDocument
379       xmlParserBase::end_document,   // endDocument
380       xmlParserBase::start_element,  // startElement
381       xmlParserBase::end_element,    // endElement
382       0,  // reference
383       xmlParserBase::characters,     // characters
384       0,  // ignorableWhitespace
385       0,  // processingInstruction
386       xmlParserBase::comment,        // comment
387       xmlParserBase::warning,        // warning
388       xmlParserBase::error,          // error
389       xmlParserBase::fatal_error,    // fatalError
390       0,  // getParameterEntity
391       xmlParserBase::cdata_block,    // cdataBlock
392       0  // externalSubset
393     };
394
395   // --- sequence from libxml++, to have a libxml context
396
397   _xmlParserCtxt* saxContext;
398   saxContext = xmlCreateFileParserCtxt(xmlFile.c_str());
399   if (!saxContext)
400     {
401       _rootParser->cleanGarbage();
402       string what = "problem while trying to open the file for parsing " + xmlFile;
403       throw Exception(what);
404     }
405   xmlSAXHandlerPtr old_sax = saxContext->sax;
406   saxContext->sax = &baseHandler;
407   _rootParser->_xmlParser = saxContext;
408   saxContext->userData = _rootParser;
409
410   xmlParseDocument(saxContext);
411   _rootParser->cleanGarbage();
412   xmlFileClose(saxContext);
413   xmlFreeParserCtxt(saxContext);
414   DEBTRACE("xmlParserBase::end of parse, garbage size = " << _rootParser->getGarbageSize());
415 }
416 #endif
417
418 // ----------------------------------------------------------------------------
419
420 #ifdef USE_EXPAT
421
422 #define SIZEBUF        8192
423 char Buffer[SIZEBUF];
424
425 /*! expat parser initialisation
426  * \param parser dedicated parser
427  */
428
429 xmlReader::xmlReader(xmlParserBase* parser): _rootParser(parser)
430 {
431   xmlParserBase::_xmlParser= XML_ParserCreate(NULL);
432   if (! _rootParser ) 
433     {
434       cerr << "Couldn't allocate memory for parser" << endl;
435       throw Exception("Couldn't allocate memory for parser");
436     }
437 }
438
439 /*! expat parse
440  * \param xmlFile file to parse
441  */
442
443 void xmlReader::parse(std::string xmlFile)
444 {
445   FILE* fin=fopen(xmlFile.c_str(),"r");
446   if (! fin) 
447     {
448       std::cerr << "Couldn't open schema file" << std::endl;
449       throw std::invalid_argument("Couldn't open schema file");
450       //throw Exception("Couldn't open schema file");
451     }
452
453   XML_SetElementHandler(xmlParserBase::_xmlParser,
454                         xmlParserBase::start_element,
455                         xmlParserBase::end_element);
456   XML_SetCharacterDataHandler(xmlParserBase::_xmlParser,
457                               xmlParserBase::characters );
458   XML_SetUserData(xmlParserBase::_xmlParser, _rootParser);
459   _rootParser->init(0);
460   _rootParser->_stackParser.push(_rootParser);
461
462   try
463     {
464       for (;;) 
465         {
466           int done;
467           int len;
468
469           len = fread(Buffer, 1, SIZEBUF, fin);
470           if (ferror(fin)) 
471             {
472               std::cerr << "Read error" << std::endl;
473               throw Exception("Read error");
474             }
475           done = feof(fin);
476
477           if (XML_Parse(xmlParserBase::_xmlParser, Buffer, len, done) == XML_STATUS_ERROR) 
478             {
479               throw Exception(XML_ErrorString(XML_GetErrorCode(xmlParserBase::_xmlParser)));
480             }
481
482           if (done)
483             break;
484         }
485       XML_ParserFree (xmlParserBase::_xmlParser);
486       xmlParserBase::_xmlParser=0;
487       _rootParser->cleanGarbage();
488       DEBTRACE("xmlParserBase::end of parse, garbage size = " << _rootParser->getGarbageSize());
489     }
490   catch(Exception& e)
491     {
492       _rootParser->cleanGarbage();
493       //get line number from XML parser
494       cerr << "Error at line: " << XML_GetCurrentLineNumber(xmlParserBase::_xmlParser) << endl;
495       throw e;
496     }
497 }
498 #endif
499
500 // ----------------------------------------------------------------------------