2 * Copyright (c) 2000 - 2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
18 * @author Maciej Karpiuk (m.karpiuk2@samsung.com)
20 * @brief XML parser class implementation.
27 #include <libxml/parser.h>
28 #include <libxml/valid.h>
29 #include <libxml/xmlschemas.h>
31 #include <dpl/log/log.h>
37 const char * const WHITESPACE = " \n\r\t";
38 std::string trim_left(const std::string& s)
40 size_t startpos = s.find_first_not_of(WHITESPACE);
41 return (startpos == std::string::npos) ? "" : s.substr(startpos);
44 std::string trim_right(const std::string& s)
46 size_t endpos = s.find_last_not_of(WHITESPACE);
47 return (endpos == std::string::npos) ? "" : s.substr(0, endpos+1);
49 std::string trim(const std::string& s)
51 return trim_right(trim_left(s));
55 Parser::Parser(const char *XML_filename)
59 m_XMLfile = XML_filename;
60 memset(&m_saxHandler, 0, sizeof(m_saxHandler));
61 m_saxHandler.startElement = &Parser::StartElement;
62 m_saxHandler.endElement = &Parser::EndElement;
63 m_saxHandler.characters = &Parser::Characters;
64 m_saxHandler.error = &Parser::Error;
65 m_saxHandler.warning = &Parser::Warning;
72 int Parser::Validate(const char *XSD_schema)
75 LogError("no XSD file path given");
76 return ERROR_INVALID_ARGUMENT;
80 std::unique_ptr<xmlSchemaParserCtxt, void(*)(xmlSchemaParserCtxtPtr)>
81 parserCtxt(xmlSchemaNewParserCtxt(XSD_schema),
82 [](xmlSchemaParserCtxtPtr ctx){ xmlSchemaFreeParserCtxt(ctx); });
84 LogError("XSD file path is invalid");
85 return ERROR_INVALID_ARGUMENT;
88 std::unique_ptr<xmlSchema, void(*)(xmlSchemaPtr)>
89 schema(xmlSchemaParse(parserCtxt.get()),
90 [](xmlSchemaPtr schemaPtr){ xmlSchemaFree(schemaPtr); });
92 LogError("Parsing XSD file failed");
93 return ERROR_XSD_PARSE_FAILED;
97 std::unique_ptr<xmlSchemaValidCtxt, void(*)(xmlSchemaValidCtxtPtr)>
98 validCtxt(xmlSchemaNewValidCtxt(schema.get()),
99 [](xmlSchemaValidCtxtPtr validCtxPtr){ xmlSchemaFreeValidCtxt(validCtxPtr); });
101 LogError("Internal parser error");
102 return ERROR_INTERNAL;
105 xmlSetStructuredErrorFunc(NULL, NULL);
106 xmlSetGenericErrorFunc(this, &Parser::ErrorValidate);
107 xmlThrDefSetStructuredErrorFunc(NULL, NULL);
108 xmlThrDefSetGenericErrorFunc(this, &Parser::ErrorValidate);
110 retCode = xmlSchemaValidateFile(validCtxt.get(), m_XMLfile.c_str(), 0);
112 LogWarning("Validating XML file failed, ec: " << retCode);
113 retCode = ERROR_XML_VALIDATION_FAILED;
123 if(m_elementListenerMap.empty()) {
124 LogError("Can not parse XML file: no registered element callbacks.");
125 return ERROR_INVALID_ARGUMENT;
127 int retCode = xmlSAXUserParseFile(&m_saxHandler, this, m_XMLfile.c_str());
129 LogWarning("Parsing XML file failed, ec: " << retCode);
130 return ERROR_XML_PARSE_FAILED;
132 // if error detected while parsing
133 if(m_elementListenerMap.empty()) {
134 LogError("Critical error detected while parsing.");
135 return ERROR_INTERNAL;
140 int Parser::RegisterErrorCb(const ErrorCb newCb)
143 LogError("Callback already registered!");
144 return ERROR_CALLBACK_PRESENT;
150 int Parser::RegisterElementCb(const char * elementName,
151 const StartCb startCb,
155 return ERROR_INVALID_ARGUMENT;
157 std::string key(elementName);
159 if(m_elementListenerMap.find(elementName) != m_elementListenerMap.end()) {
160 LogError("Callback for element " << elementName << " already registered!");
161 return ERROR_CALLBACK_PRESENT;
164 m_elementListenerMap[key] = {startCb, endCb};
168 void Parser::StartElement(const xmlChar *name,
169 const xmlChar **attrs)
171 std::string key(reinterpret_cast<const char*>(name));
172 if(m_elementListenerMap.find(key) == m_elementListenerMap.end())
175 ElementHandlerPtr newHandler;
176 const ElementListener & current = m_elementListenerMap[key];
183 while(attrs && attrs[numAttrs])
185 const char *attrChr = reinterpret_cast<const char*>(attrs[numAttrs]);
187 key = std::string(attrChr);
189 attribs[key] = std::string(attrChr);
194 newHandler = current.startCb();
196 newHandler->Start(attribs);
198 // always put a handler, even if it's empty. This will not break
199 // the sequence of queued elements when popping from the queue.
200 m_elementHandlerStack.push(newHandler);
203 void Parser::EndElement(const xmlChar *name)
205 std::string key(reinterpret_cast<const char*>(name));
206 if(m_elementListenerMap.find(key) == m_elementListenerMap.end())
209 // this should never ever happen
210 if( m_elementHandlerStack.empty() )
211 throw std::runtime_error("internal error: element queue desynchronized!");
213 ElementHandlerPtr ¤tHandler = m_elementHandlerStack.top();
215 currentHandler.get()->End();
217 const ElementListener & current = m_elementListenerMap[key];
219 current.endCb(currentHandler);
221 m_elementHandlerStack.pop();
224 void Parser::Characters(const xmlChar *ch, size_t chLen)
226 std::string chars = trim(std::string(reinterpret_cast<const char*>(ch), chLen));
230 if( !m_elementHandlerStack.empty() )
232 ElementHandlerPtr ¤tHandler = m_elementHandlerStack.top();
234 currentHandler.get()->Characters(chars);
238 void Parser::Error(const ErrorType errorType, const char *msg, va_list &args)
246 va_copy(args2, args);
247 std::vector<char> buf(1 + std::vsnprintf(NULL, 0, msg, args));
248 std::vsnprintf(buf.data(), buf.size(), msg, args2);
249 m_errorCb(errorType, trim(std::string(buf.begin(), buf.end())));
252 LogError("Error callback throwed an exception.");
253 // if an error handler throwed exception,
254 // do not call further callbacks
255 m_elementListenerMap.clear();
261 // -------------------------- start of static wrappers --------------------------
263 void Parser::CallbackHelper(std::function<void (void)> func)
270 catch(const std::exception &e) {
271 LogError("parser error: " << e.what());
273 m_errorCb(PARSE_ERROR, e.what());
276 LogError("unknown parser error");
278 m_errorCb(PARSE_ERROR, "unknown parser error");
280 // raise error flag - unregister listeners
281 m_elementListenerMap.clear();
283 void Parser::StartElement(void *userData,
285 const xmlChar **attrs)
287 Parser *parser = static_cast<Parser *>(userData);
288 parser->CallbackHelper([&parser, &name, &attrs] { parser->StartElement(name, attrs); });
290 void Parser::EndElement(void *userData,
293 Parser *parser = static_cast<Parser *>(userData);
294 parser->CallbackHelper([&parser, &name] { parser->EndElement(name); });
296 void Parser::Characters(void *userData,
300 Parser *parser = static_cast<Parser *>(userData);
301 parser->CallbackHelper([&parser, &ch, &len] { parser->Characters(ch, static_cast<size_t>(len)); });
304 void Parser::ErrorValidate(void *userData,
310 Parser *parser = static_cast<Parser *>(userData);
311 parser->Error(VALIDATION_ERROR, msg, args);
315 void Parser::Error(void *userData,
321 Parser *parser = static_cast<Parser *>(userData);
322 parser->Error(PARSE_ERROR, msg, args);
326 void Parser::Warning(void *userData,
332 Parser &parser = *(static_cast<Parser *>(userData));
333 parser.Error(PARSE_WARNING, msg, args);
337 // -------------------------- end of static wrappers --------------------------