Salome HOME
updated copyright message
[modules/yacs.git] / src / yacsloader / xmlParserBase.cxx
1 // Copyright (C) 2006-2023  CEA, EDF
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 extern "C"
33 {
34 #include <libxml/parserInternals.h> // for xmlCreateFileParserCtxt
35 }
36 _xmlParserCtxt* xmlParserBase::_xmlParser;
37 void xmlParserBase::XML_SetUserData(_xmlParserCtxt* ctxt,
38                                     xmlParserBase* parser)
39 {
40   ctxt->userData = parser;
41 }
42
43 // --- generic part -----------------------------------------------------------
44
45 using namespace std;
46 using YACS::Exception;
47
48 std::stack<xmlParserBase*> xmlParserBase::_stackParser;
49 std::list<xmlParserBase*>  xmlParserBase::_garbage;
50
51 /*! callback usable only with libxml2
52  */
53
54 void XMLCALL xmlParserBase::start_document(void* userData)
55 {
56   //DEBTRACE("xmlParserBase::start_document");
57   xmlParserBase *currentParser = static_cast<xmlParserBase *> (userData);
58 }
59
60 /*! callback usable only with libxml2
61  */
62
63 void XMLCALL xmlParserBase::end_document  (void* userData)
64 {
65   //DEBTRACE("xmlParserBase::end_document");
66   xmlParserBase *currentParser = static_cast<xmlParserBase *> (userData);
67 }
68
69 /*! callback called on start of an xml element: \verbatim <name> \endverbatim
70  */
71
72 void XMLCALL xmlParserBase::start_element (void* userData,
73                                            const xmlChar* name,
74                                            const xmlChar** p)
75 {
76   //DEBTRACE("xmlParserBase::start_element " << name);
77   cleanGarbage();
78   xmlParserBase *currentParser = static_cast<xmlParserBase *> (userData);
79   const XML_Char *aName = tochar(name);
80   currentParser->incrCount(aName);
81   currentParser->onStart(aName, p);
82 }
83
84
85 /*! callback called on end of an xml element: \verbatim </name> \endverbatim
86  */
87
88 void XMLCALL xmlParserBase::end_element   (void* userData,
89                                            const xmlChar* name)
90 {
91   //DEBTRACE("xmlParserBase::end_element");
92   const XML_Char *aName = tochar(name);
93   xmlParserBase *childParser = static_cast<xmlParserBase *> (userData);
94   _garbage.push_back(_stackParser.top());
95   DEBTRACE("xmlParserBase::end_element " << _garbage.size());
96   _stackParser.pop();
97   XML_SetUserData(_xmlParser, _stackParser.top());
98   childParser->onEnd(aName);
99   childParser->end();
100  }
101
102
103 /*! callback called for significant characters inside tags: \verbatim <tag>content</tag> \endverbatim
104  *  or outside tags, like space or new line;
105  *  get also the CDATA tags: \verbatim <tag>![CDATA[content]]></tag> \endverbatim
106  */
107
108 void XMLCALL xmlParserBase::characters    (void* userData,
109                                            const xmlChar* ch,
110                                            int len)
111 {
112   //DEBTRACE("xmlParserBase::characters " << len);
113   xmlParserBase *currentParser = (xmlParserBase *) (userData);
114   string data((char*)ch,len);
115   currentParser->charData(data);
116 }
117
118
119 /*! callback usable only with libxml2
120  */
121
122 void XMLCALL xmlParserBase::comment       (void* userData,
123                                            const xmlChar* value)
124 {
125   //DEBTRACE("xmlParserBase::comment");
126   xmlParserBase *currentParser = static_cast<xmlParserBase *> (userData);
127 }
128
129
130 /*! callback usable only with libxml2
131  */
132
133 void XMLCALL xmlParserBase::warning       (void* userData,
134                                            const char* fmt, ...)
135 {
136   DEBTRACE("xmlParserBase::warning");
137   xmlParserBase *currentParser = static_cast<xmlParserBase *> (userData);
138   va_list args;
139   va_start(args, fmt);
140   string format = "%s";
141   if (format == fmt)
142     {
143       char* parv;
144       parv = va_arg(args, char*);
145       cerr << parv ;
146     }
147   else cerr << __FILE__ << " [" << __LINE__ << "] : " 
148             << "error format not taken into account: " << fmt << endl;
149   va_end(args);
150 }
151
152
153 /*! callback usable only with libxml2
154  */
155
156 void XMLCALL xmlParserBase::error         (void* userData,
157                                            const char* fmt, ...)
158 {
159   DEBTRACE("xmlParserBase::error");
160   xmlParserBase *currentParser = static_cast<xmlParserBase *> (userData);
161   va_list args;
162   va_start(args, fmt);
163   string format = "%s";
164   if (format == fmt)
165     {
166       char* parv;
167       parv = va_arg(args, char*);
168       cerr << parv ;
169       xmlParserBase *currentParser = (xmlParserBase *) userData;
170       //cerr << currentParser->element << endl;
171     }
172   else cerr << __FILE__ << " [" << __LINE__ << "] : " 
173             << "error format not taken into account: " << fmt << endl;
174   va_end(args);
175 }
176
177
178 /*! callback usable only with libxml2
179  */
180
181 void XMLCALL xmlParserBase::fatal_error   (void* userData,
182                                            const char* fmt, ...)
183 {
184   DEBTRACE("xmlParserBase::fatal_error");
185   xmlParserBase *currentParser = static_cast<xmlParserBase *> (userData);
186   va_list args;
187   va_start(args, fmt);
188   string format = "%s";
189   if (format == fmt)
190     {
191       char* parv;
192       parv = va_arg(args, char*);
193       cerr << parv ;
194     }
195   else cerr << __FILE__ << " [" << __LINE__ << "] : " 
196             << "error format not taken into account: " << fmt << endl;
197   va_end(args);
198 }
199
200 /*! callback called for CDATA inside tags: \verbatim <tag>![CDATA[content]]></tag> \endverbatim
201  *  used only by libxml2
202  */
203
204 void XMLCALL xmlParserBase::cdata_block   (void* userData,
205                                            const xmlChar* value,
206                                            int len)
207 {
208   //DEBTRACE("xmlParserBase::cdata_block");
209   xmlParserBase *currentParser = static_cast<xmlParserBase *> (userData);
210   string data((char*)value,len);
211   currentParser->charData(data);
212 }
213
214 void xmlParserBase::cleanGarbage()
215 {
216   while (!_garbage.empty())
217     {
218       delete (_garbage.front());
219       _garbage.pop_front();
220     }
221 }
222
223 /*! Stores the tag attributes on a map. The map is an attribute of the parser
224  *  object dedicated to the current xml tag
225  */
226
227 void xmlParserBase::getAttributes(const xmlChar** p)
228 {
229   if (p) while (*p)
230     {
231       string attributeName = (char*)*p;
232       //cerr << "attribute name " << attributeName << endl;
233       p++;
234       string attributeValue = (char*)*p;
235       //cerr << "attribute value " << attributeValue << endl;
236       p++;
237       _mapAttrib[attributeName] = attributeValue;
238     }
239 }
240
241 /*! Stores an attribute (key, value) on the map attribute.
242  *  used for attributes defined at another tag level (child tags). 
243  */
244
245 void xmlParserBase::setAttribute(std::string key, std::string value)
246 {
247   _mapAttrib[key] = value;
248 }
249
250 /*! Gets an attribute value given a string key.
251  *  If the key does not exist, throws an Exception.
252  */
253
254 std::string xmlParserBase::getAttribute(std::string key)
255 {
256   if (_mapAttrib.find(key) == _mapAttrib.end())
257     {
258       string what = "Attribute does not exist: " + key;
259       throw Exception(what);
260     }
261   return _mapAttrib[key];
262 }
263
264 /*! Add data on the data attribute of the parser object dedicated to an xml tag
265  */
266
267 void xmlParserBase::addData(std::string value)
268 {
269   //  DEBTRACE("xmlParserBase::addData()");
270   _data += value;
271 }
272
273 /*! all parsers must know their father parser (father tag), in order
274  *  to set values or attributes in father.
275  */
276
277 void xmlParserBase::init (const xmlChar** p, xmlParserBase* father)
278 {
279   _father = father;
280 }
281
282 /*! to be specialized for each kind of xml tag
283  */
284
285 void xmlParserBase::onStart  (const XML_Char* elem, const xmlChar** p)
286 {
287 }
288
289 /*! to be specialized for each kind of xml tag
290  */
291
292 void xmlParserBase::onEnd    (const XML_Char* name)
293 {
294 }
295
296 /*! to be specialized following the kind of xml tag
297  */
298
299 void xmlParserBase::charData (std::string data)
300 {
301 }
302
303 /*! May be specialized for each kind of xml tag.
304  *  Counts the number of tag occurences of a given type inside a context.
305  */
306
307 void xmlParserBase::incrCount(const XML_Char *elem)
308 {
309   if(counts.find(elem)==counts.end())
310     counts[elem]=1;
311   else
312     counts[elem]=counts[elem]+1;
313 }
314
315 /*! to be specialized for each kind of xml tag
316  */
317
318 void xmlParserBase::end()
319 {
320 }
321
322 /*! Throws an exception.
323  */
324
325 void xmlParserBase::stopParse(std::string what)
326 {
327   xmlStopParser(_xmlParser);
328 }
329
330 // ----------------------------------------------------------------------------
331
332 /*! libxml2 parser initialisation
333  * \param parser dedicated parser
334  */
335
336 xmlReader::xmlReader(xmlParserBase* parser): _rootParser(parser)
337 {
338 }
339
340 /*! libxml2 parse
341  * \param xmlFile file to parse
342  */
343
344 void xmlReader::parse(std::string xmlFile)
345 {
346   _rootParser->init(0);
347   _rootParser->_stackParser.push(_rootParser);
348
349   xmlSAXHandler baseHandler = 
350     {
351       0,  // internal_subset,
352       0,  // isStandalone
353       0,  // hasInternalSubset
354       0,  // hasExternalSubset
355       0,  // resolveEntity
356       0,  // getEntity
357       0,  // entityDecl
358       0,  // notationDecl
359       0,  // attributeDecl
360       0,  // elementDecl
361       0,  // unparsedEntityDecl
362       0,  // setDocumentLocator
363       xmlParserBase::start_document, // startDocument
364       xmlParserBase::end_document,   // endDocument
365       xmlParserBase::start_element,  // startElement
366       xmlParserBase::end_element,    // endElement
367       0,  // reference
368       xmlParserBase::characters,     // characters
369       0,  // ignorableWhitespace
370       0,  // processingInstruction
371       xmlParserBase::comment,        // comment
372       xmlParserBase::warning,        // warning
373       xmlParserBase::error,          // error
374       xmlParserBase::fatal_error,    // fatalError
375       0,  // getParameterEntity
376       xmlParserBase::cdata_block,    // cdataBlock
377       0  // externalSubset
378     };
379
380   // --- sequence from libxml++, to have a libxml context
381
382   _xmlParserCtxt* saxContext;
383   saxContext = xmlCreateFileParserCtxt(xmlFile.c_str());
384   if (!saxContext)
385     {
386       _rootParser->cleanGarbage();
387       string what = "problem while trying to open the file for parsing " + xmlFile;
388       throw Exception(what);
389     }
390   xmlSAXHandlerPtr old_sax = saxContext->sax;
391   saxContext->sax = &baseHandler;
392   _rootParser->_xmlParser = saxContext;
393   saxContext->userData = _rootParser;
394
395   xmlParseDocument(saxContext);
396   _rootParser->cleanGarbage();
397   if ( saxContext->myDoc != NULL ) {
398     xmlFreeDoc( saxContext->myDoc );
399     saxContext->myDoc = NULL;
400   }
401   if ( saxContext != NULL ) {
402     saxContext->sax = old_sax;
403     xmlFreeParserCtxt( saxContext );
404   }
405   DEBTRACE("xmlParserBase::end of parse, garbage size = " << _rootParser->getGarbageSize());
406 }
407
408 // ----------------------------------------------------------------------------