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