1 
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29 
30 #pragma once
31 
32 // appleseed.foundation headers.
33 #include "foundation/core/concepts/noncopyable.h"
34 #include "foundation/platform/thread.h"
35 #include "foundation/utility/foreach.h"
36 #include "foundation/utility/log.h"
37 
38 // Xerces-C++ headers.
39 #include "xercesc/sax/ErrorHandler.hpp"
40 #include "xercesc/sax/SAXParseException.hpp"
41 #include "xercesc/sax2/Attributes.hpp"
42 #include "xercesc/sax2/DefaultHandler.hpp"
43 #include "xercesc/util/XMLString.hpp"
44 
45 // Standard headers.
46 #include <cassert>
47 #include <cstddef>
48 #include <map>
49 #include <memory>
50 #include <stack>
51 #include <string>
52 
53 // Forward declarations.
54 namespace foundation    { class Logger; }
55 
56 namespace foundation
57 {
58 
59 //
60 // An utility class to initialize and terminate Xerces-C++ in a thread-safe manner.
61 //
62 
63 class XercesCManager
64   : public NonCopyable
65 {
66   public:
67     // Initialize Xerces-C++. This method is thread-safe.
68     // Returns true if Xerces-C++ is properly initialized.
69     static bool initialize();
70 
71     // Initialize Xerces-C++ and issue an error message if
72     // the initialization failed. This method is thread-safe.
73     static bool initialize(Logger& logger);
74 
75     // Terminate Xerces-C++. This method is thread-safe.
76     static void terminate();
77 
78   private:
79     static boost::mutex s_mutex;
80 };
81 
82 
83 //
84 // An utility class to automatize Xerces-C++ initialization and termination.
85 //
86 
87 class XercesCContext
88   : public NonCopyable
89 {
90   public:
91     // Constructor, initializes Xerces-C++.
92     XercesCContext();
93 
94     // Constructor, initializes Xerces-C++ and issue an
95     // error message if the initialization failed.
96     explicit XercesCContext(Logger& logger);
97 
98     // Destructor, terminates Xerces-C++.
99     ~XercesCContext();
100 
101     // Return true if Xerces-C++ was properly initialized.
102     bool is_initialized() const;
103 
104   private:
105     bool m_initialized;
106 };
107 
108 
109 //
110 // Utility functions for string transcoding.
111 //
112 
113 std::basic_string<char> transcode(const XMLCh* s);
114 std::basic_string<XMLCh> transcode(const char* s);
115 std::basic_string<XMLCh> transcode(const std::string& s);
116 
117 
118 //
119 // Element handler interface.
120 //
121 
122 template <typename ElementID>
123 class IElementHandler
124   : public NonCopyable
125 {
126   public:
127     typedef IElementHandler<ElementID> ElementHandlerType;
128 
129     // Destructor.
~IElementHandler()130     virtual ~IElementHandler() {}
131 
132     // Receive notification of the start of an element.
133     virtual void start_element(
134         const xercesc::Attributes&  attrs) = 0;
135 
136     // Receive notification of the end of an element.
137     virtual void end_element() = 0;
138 
139     // Receive notification of character data inside an element.
140     virtual void characters(
141         const XMLCh* const          chars,
142         const XMLSize_t             length) = 0;
143 
144     // Receive notification of the start of a child element.
145     virtual void start_child_element(
146         const ElementID             element,
147         ElementHandlerType*         handler) = 0;
148 
149     // Receive notification of the end of a child element.
150     virtual void end_child_element(
151         const ElementID             element,
152         ElementHandlerType*         handler) = 0;
153 };
154 
155 
156 //
157 // A convenient base class for element handlers.
158 //
159 
160 template <typename ElementID>
161 class ElementHandlerBase
162   : public IElementHandler<ElementID>
163 {
164   public:
165     typedef IElementHandler<ElementID> ElementHandlerType;
166 
167     // Receive notification of the start of an element.
168     void start_element(
169         const xercesc::Attributes&  attrs) override;
170 
171     // Receive notification of the end of an element.
172     void end_element() override;
173 
174     // Receive notification of character data inside an element.
175     void characters(
176         const XMLCh* const          chars,
177         const XMLSize_t             length) override;
178 
179     // Receive notification of the start of a child element.
180     void start_child_element(
181         const ElementID             element,
182         ElementHandlerType*         handler) override;
183 
184     // Receive notification of the end of a child element.
185     void end_child_element(
186         const ElementID             element,
187         ElementHandlerType*         handler) override;
188 
189   protected:
190     // Utility function to retrieve the value of an attribute.
191     static std::string get_value(
192         const xercesc::Attributes&  attrs,
193         const std::string&          name,
194         const std::string&          default_value = "");
195 };
196 
197 
198 //
199 // Element handler factory interface.
200 //
201 
202 template <typename ElementID>
203 class IElementHandlerFactory
204   : public NonCopyable
205 {
206   public:
207     typedef IElementHandler<ElementID> ElementHandlerType;
208 
209     // Destructor.
~IElementHandlerFactory()210     virtual ~IElementHandlerFactory() {}
211 
212     // Create a new instance of the element handler.
213     virtual std::unique_ptr<ElementHandlerType> create() = 0;
214 };
215 
216 
217 //
218 // A generic SAX2 content handler.
219 //
220 
221 template <typename ElementID>
222 class SAX2ContentHandler
223   : public xercesc::DefaultHandler
224 {
225   public:
226     typedef IElementHandlerFactory<ElementID> ElementHandlerFactoryType;
227 
228     // Constructor.
229     SAX2ContentHandler();
230 
231     // Destructor.
232     ~SAX2ContentHandler() override;
233 
234     // Register a factory for a given element.
235     void register_factory(
236         const std::string&                          name,
237         const ElementID                             id,
238         std::unique_ptr<ElementHandlerFactoryType>  handler_factory);
239 
240     // Receive notification of the start of an element.
241     void startElement(
242         const XMLCh* const                          uri,
243         const XMLCh* const                          localname,
244         const XMLCh* const                          qname,
245         const xercesc::Attributes&                  attrs) override;
246 
247     // Receive notification of the end of an element.
248     void endElement(
249         const XMLCh* const                          uri,
250         const XMLCh* const                          localname,
251         const XMLCh* const                          qname) override;
252 
253     // Receive notification of character data inside an element.
254     void characters(
255         const XMLCh* const                          chars,
256         const XMLSize_t                             length) override;
257 
258   private:
259     typedef IElementHandler<ElementID> ElementHandlerType;
260 
261     struct FactoryInfo
262     {
263         ElementID                   m_id;
264         ElementHandlerFactoryType*  m_handler_factory;
265     };
266 
267     typedef std::map<std::string, FactoryInfo> FactoryInfoMap;
268     typedef std::stack<ElementHandlerType*> ElementHandlerStack;
269 
270     FactoryInfoMap                  m_factory_info;
271     ElementHandlerStack             m_handler_stack;
272 };
273 
274 
275 //
276 // An error handler that forwards messages to a logger.
277 //
278 
279 class ErrorLogger
280   : public xercesc::ErrorHandler
281 {
282   public:
283     // Constructor.
284     ErrorLogger(
285         Logger&             logger,
286         const std::string&  input_filepath);
287 
288     // Reset the error handler object on its reuse.
289     void resetErrors() override;
290 
291     // Receive notification of a warning.
292     void warning(const xercesc::SAXParseException& e) override;
293 
294     // Receive notification of a recoverable error.
295     void error(const xercesc::SAXParseException& e) override;
296 
297     // Receive notification of a non-recoverable error.
298     void fatalError(const xercesc::SAXParseException& e) override;
299 
300     // Read the notification counters.
301     size_t get_warning_count() const;
302     size_t get_error_count() const;
303     size_t get_fatal_error_count() const;
304 
305   private:
306     Logger&             m_logger;
307     const std::string   m_input_filepath;
308 
309     size_t              m_warning_count;
310     size_t              m_error_count;
311     size_t              m_fatal_error_count;
312 
313     void print(
314         const LogMessage::Category          category,
315         const xercesc::SAXParseException&   e) const;
316 };
317 
318 
319 //
320 // Transcoding functions implementation.
321 //
322 
transcode(const XMLCh * s)323 inline std::basic_string<char> transcode(const XMLCh* s)
324 {
325     char* temp = xercesc::XMLString::transcode(s);
326     std::basic_string<char> result = temp;
327 
328     xercesc::XMLString::release(&temp);
329 
330     return result;
331 }
332 
transcode(const char * s)333 inline std::basic_string<XMLCh> transcode(const char* s)
334 {
335     XMLCh* temp = xercesc::XMLString::transcode(s);
336     std::basic_string<XMLCh> result = temp;
337 
338     xercesc::XMLString::release(&temp);
339 
340     return result;
341 }
342 
transcode(const std::string & s)343 inline std::basic_string<XMLCh> transcode(const std::string& s)
344 {
345     return transcode(s.c_str());
346 }
347 
348 
349 //
350 // ElementHandlerBase class implementation.
351 //
352 
353 template <typename ElementID>
start_element(const xercesc::Attributes & attrs)354 void ElementHandlerBase<ElementID>::start_element(
355     const xercesc::Attributes&  attrs)
356 {
357 }
358 
359 template <typename ElementID>
end_element()360 void ElementHandlerBase<ElementID>::end_element()
361 {
362 }
363 
364 template <typename ElementID>
characters(const XMLCh * const chars,const XMLSize_t length)365 void ElementHandlerBase<ElementID>::characters(
366     const XMLCh* const          chars,
367     const XMLSize_t             length)
368 {
369 }
370 
371 template <typename ElementID>
start_child_element(const ElementID element,ElementHandlerType * handler)372 void ElementHandlerBase<ElementID>::start_child_element(
373     const ElementID             element,
374     ElementHandlerType*         handler)
375 {
376 }
377 
378 template <typename ElementID>
end_child_element(const ElementID element,ElementHandlerType * handler)379 void ElementHandlerBase<ElementID>::end_child_element(
380     const ElementID             element,
381     ElementHandlerType*         handler)
382 {
383 }
384 
385 template <typename ElementID>
get_value(const xercesc::Attributes & attrs,const std::string & name,const std::string & default_value)386 inline std::string ElementHandlerBase<ElementID>::get_value(
387     const xercesc::Attributes&  attrs,
388     const std::string&          name,
389     const std::string&          default_value)
390 {
391     const XMLCh* value = attrs.getValue(transcode(name).c_str());
392     return value ? transcode(value) : default_value;
393 }
394 
395 
396 //
397 // SAX2ContentHandler class implementation.
398 //
399 
400 template <typename ElementID>
SAX2ContentHandler()401 SAX2ContentHandler<ElementID>::SAX2ContentHandler()
402 {
403     // Push a dummy element handler on the stack to avoid special-casing for an empty stack.
404     m_handler_stack.push(new ElementHandlerBase<ElementID>());
405 }
406 
407 template <typename ElementID>
~SAX2ContentHandler()408 SAX2ContentHandler<ElementID>::~SAX2ContentHandler()
409 {
410     while (!m_handler_stack.empty())
411     {
412         delete m_handler_stack.top();
413         m_handler_stack.pop();
414     }
415 
416     for (const_each<FactoryInfoMap> i = m_factory_info; i; ++i)
417         delete i->second.m_handler_factory;
418 
419     m_factory_info.clear();
420 }
421 
422 template <typename ElementID>
register_factory(const std::string & name,const ElementID id,std::unique_ptr<ElementHandlerFactoryType> handler_factory)423 void SAX2ContentHandler<ElementID>::register_factory(
424     const std::string&                          name,
425     const ElementID                             id,
426     std::unique_ptr<ElementHandlerFactoryType>  handler_factory)
427 {
428     FactoryInfo info;
429     info.m_id = id;
430     info.m_handler_factory = handler_factory.release();
431     m_factory_info[name] = info;
432 }
433 
434 template <typename ElementID>
startElement(const XMLCh * const uri,const XMLCh * const localname,const XMLCh * const qname,const xercesc::Attributes & attrs)435 void SAX2ContentHandler<ElementID>::startElement(
436     const XMLCh* const                          uri,
437     const XMLCh* const                          localname,
438     const XMLCh* const                          qname,
439     const xercesc::Attributes&                  attrs)
440 {
441     const typename FactoryInfoMap::const_iterator it =
442         m_factory_info.find(transcode(localname));
443 
444     ElementHandlerType* handler;
445 
446     if (it == m_factory_info.end())
447     {
448         handler = new ElementHandlerBase<ElementID>();
449     }
450     else
451     {
452         handler = it->second.m_handler_factory->create().release();
453 
454         m_handler_stack.top()->start_child_element(it->second.m_id, handler);
455     }
456 
457     m_handler_stack.push(handler);
458 
459     handler->start_element(attrs);
460 }
461 
462 template <typename ElementID>
endElement(const XMLCh * const uri,const XMLCh * const localname,const XMLCh * const qname)463 void SAX2ContentHandler<ElementID>::endElement(
464     const XMLCh* const                          uri,
465     const XMLCh* const                          localname,
466     const XMLCh* const                          qname)
467 {
468     ElementHandlerType* handler = m_handler_stack.top();
469 
470     handler->end_element();
471 
472     m_handler_stack.pop();
473 
474     const typename FactoryInfoMap::const_iterator it =
475         m_factory_info.find(transcode(localname));
476 
477     if (it != m_factory_info.end())
478         m_handler_stack.top()->end_child_element(it->second.m_id, handler);
479 
480     delete handler;
481 }
482 
483 template <typename ElementID>
characters(const XMLCh * const chars,const XMLSize_t length)484 void SAX2ContentHandler<ElementID>::characters(
485     const XMLCh* const                          chars,
486     const XMLSize_t                             length)
487 {
488     assert(!m_handler_stack.empty());
489 
490     m_handler_stack.top()->characters(chars, length);
491 }
492 
493 }   // namespace foundation
494