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