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 <xml-utils.h>
32 #include <dpl/log/log.h>
37 Parser::Parser(const std::string &XML_filename)
40 m_XMLfile = XML_filename;
41 memset(&m_saxHandler, 0, sizeof(m_saxHandler));
42 m_saxHandler.startElement = &Parser::StartElement;
43 m_saxHandler.endElement = &Parser::EndElement;
44 m_saxHandler.characters = &Parser::Characters;
45 m_saxHandler.error = &Parser::Error;
46 m_saxHandler.warning = &Parser::Warning;
53 int Parser::Validate(const std::string &XSD_schema)
55 if(XSD_schema.empty()) {
56 LogError("no XSD file path given");
57 return ERROR_INVALID_ARGUMENT;
61 std::unique_ptr<xmlSchemaParserCtxt, void(*)(xmlSchemaParserCtxtPtr)>
62 parserCtxt(xmlSchemaNewParserCtxt(XSD_schema.c_str()),
63 [](xmlSchemaParserCtxtPtr ctx){ xmlSchemaFreeParserCtxt(ctx); });
65 LogError("XSD file path is invalid");
66 return ERROR_INVALID_ARGUMENT;
69 std::unique_ptr<xmlSchema, void(*)(xmlSchemaPtr)>
70 schema(xmlSchemaParse(parserCtxt.get()),
71 [](xmlSchemaPtr schemaPtr){ xmlSchemaFree(schemaPtr); });
73 LogError("Parsing XSD file failed");
74 return ERROR_XSD_PARSE_FAILED;
78 std::unique_ptr<xmlSchemaValidCtxt, void(*)(xmlSchemaValidCtxtPtr)>
79 validCtxt(xmlSchemaNewValidCtxt(schema.get()),
80 [](xmlSchemaValidCtxtPtr validCtxPtr){ xmlSchemaFreeValidCtxt(validCtxPtr); });
82 LogError("Internal parser error");
83 return ERROR_INTERNAL;
86 xmlSetStructuredErrorFunc(NULL, NULL);
87 xmlSetGenericErrorFunc(this, &Parser::ErrorValidate);
88 xmlThrDefSetStructuredErrorFunc(NULL, NULL);
89 xmlThrDefSetGenericErrorFunc(this, &Parser::ErrorValidate);
91 retCode = xmlSchemaValidateFile(validCtxt.get(), m_XMLfile.c_str(), 0);
93 LogWarning("Validating XML file failed, ec: " << retCode);
94 retCode = ERROR_XML_VALIDATION_FAILED;
97 retCode = PARSE_SUCCESS;
104 if(m_elementListenerMap.empty()) {
105 LogError("Can not parse XML file: no registered element callbacks.");
106 return ERROR_INVALID_ARGUMENT;
108 int retCode = xmlSAXUserParseFile(&m_saxHandler, this, m_XMLfile.c_str());
110 LogWarning("Parsing XML file failed, ec: " << retCode);
111 return ERROR_XML_PARSE_FAILED;
113 // if error detected while parsing
114 if(m_elementListenerMap.empty()) {
115 LogError("Critical error detected while parsing.");
116 return ERROR_INTERNAL;
118 return PARSE_SUCCESS;
121 int Parser::RegisterErrorCb(const ErrorCb newCb)
124 LogError("Callback already registered!");
125 return ERROR_CALLBACK_PRESENT;
128 return PARSE_SUCCESS;
131 int Parser::RegisterElementCb(const char * elementName,
132 const StartCb startCb,
136 return ERROR_INVALID_ARGUMENT;
138 std::string key(elementName);
140 if(m_elementListenerMap.find(elementName) != m_elementListenerMap.end()) {
141 LogError("Callback for element " << elementName << " already registered!");
142 return ERROR_CALLBACK_PRESENT;
145 m_elementListenerMap[key] = {startCb, endCb};
146 return PARSE_SUCCESS;
149 void Parser::StartElement(const xmlChar *name,
150 const xmlChar **attrs)
152 std::string key(reinterpret_cast<const char*>(name));
153 if(m_elementListenerMap.find(key) == m_elementListenerMap.end())
156 ElementHandlerPtr newHandler;
157 const ElementListener & current = m_elementListenerMap[key];
164 while(attrs && attrs[numAttrs])
166 const char *attrChr = reinterpret_cast<const char*>(attrs[numAttrs]);
168 key = std::string(attrChr);
170 attribs[key] = std::string(attrChr);
175 newHandler = current.startCb();
177 newHandler->Start(attribs);
179 // always put a handler, even if it's empty. This will not break
180 // the sequence of queued elements when popping from the queue.
181 m_elementHandlerStack.push(newHandler);
184 void Parser::EndElement(const xmlChar *name)
186 std::string key(reinterpret_cast<const char*>(name));
187 if(m_elementListenerMap.find(key) == m_elementListenerMap.end())
190 // this should never ever happen
191 if( m_elementHandlerStack.empty() )
192 throw std::runtime_error("internal error: element queue desynchronized!");
194 ElementHandlerPtr ¤tHandler = m_elementHandlerStack.top();
196 currentHandler->End();
198 const ElementListener & current = m_elementListenerMap[key];
200 current.endCb(currentHandler);
202 m_elementHandlerStack.pop();
205 void Parser::Characters(const xmlChar *ch, size_t chLen)
207 std::string chars = trim(std::string(reinterpret_cast<const char*>(ch), chLen));
211 if( !m_elementHandlerStack.empty() )
213 ElementHandlerPtr ¤tHandler = m_elementHandlerStack.top();
215 currentHandler->Characters(chars);
219 void Parser::Error(const ErrorType errorType, const char *msg, va_list &args)
227 va_copy(args2, args);
228 std::vector<char> buf(1 + std::vsnprintf(NULL, 0, msg, args));
229 std::vsnprintf(buf.data(), buf.size(), msg, args2);
230 m_errorCb(errorType, trim(std::string(buf.begin(), buf.end())));
233 LogError("Error callback throwed an exception.");
234 // if an error handler throwed exception,
235 // do not call further callbacks
236 m_elementListenerMap.clear();
242 // -------------------------- start of static wrappers --------------------------
244 void Parser::CallbackHelper(std::function<void (void)> func)
251 catch(const std::exception &e) {
252 LogError("parser error: " << e.what());
254 m_errorCb(PARSE_ERROR, e.what());
257 LogError("unknown parser error");
259 m_errorCb(PARSE_ERROR, "unknown parser error");
261 // raise error flag - unregister listeners
262 m_elementListenerMap.clear();
264 void Parser::StartElement(void *userData,
266 const xmlChar **attrs)
268 Parser *parser = static_cast<Parser *>(userData);
269 parser->CallbackHelper([&parser, &name, &attrs] { parser->StartElement(name, attrs); });
271 void Parser::EndElement(void *userData,
274 Parser *parser = static_cast<Parser *>(userData);
275 parser->CallbackHelper([&parser, &name] { parser->EndElement(name); });
277 void Parser::Characters(void *userData,
281 Parser *parser = static_cast<Parser *>(userData);
282 parser->CallbackHelper([&parser, &ch, &len] { parser->Characters(ch, static_cast<size_t>(len)); });
285 void Parser::ErrorValidate(void *userData,
291 Parser *parser = static_cast<Parser *>(userData);
292 parser->Error(VALIDATION_ERROR, msg, args);
296 void Parser::Error(void *userData,
302 Parser *parser = static_cast<Parser *>(userData);
303 parser->Error(PARSE_ERROR, msg, args);
307 void Parser::Warning(void *userData,
313 Parser &parser = *(static_cast<Parser *>(userData));
314 parser.Error(PARSE_WARNING, msg, args);
318 // -------------------------- end of static wrappers --------------------------