You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
262 lines
7.9 KiB
262 lines
7.9 KiB
/*
|
|
* Copyright (c) 2011-2015, Intel Corporation
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation and/or
|
|
* other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "XmlDocSource.h"
|
|
#include "AlwaysAssert.hpp"
|
|
#include <libxml/tree.h>
|
|
#include <libxml/xmlschemas.h>
|
|
#include <libxml/parser.h>
|
|
#include <libxml/xinclude.h>
|
|
#include <libxml/uri.h>
|
|
#include <memory>
|
|
#include <stdexcept>
|
|
|
|
using std::string;
|
|
using xml_unique_ptr = std::unique_ptr<xmlChar, decltype(xmlFree)>;
|
|
|
|
CXmlDocSource::CXmlDocSource(_xmlDoc *pDoc, bool bValidateWithSchema, _xmlNode *pRootNode)
|
|
: _pDoc(pDoc), _pRootNode(pRootNode), _strRootElementType(""), _strRootElementName(""),
|
|
_strNameAttributeName(""), _bValidateWithSchema(bValidateWithSchema)
|
|
{
|
|
}
|
|
|
|
CXmlDocSource::CXmlDocSource(_xmlDoc *pDoc, bool bValidateWithSchema,
|
|
const string &strRootElementType, const string &strRootElementName,
|
|
const string &strNameAttributeName)
|
|
: _pDoc(pDoc), _pRootNode(xmlDocGetRootElement(pDoc)), _strRootElementType(strRootElementType),
|
|
_strRootElementName(strRootElementName), _strNameAttributeName(strNameAttributeName),
|
|
_bValidateWithSchema(bValidateWithSchema)
|
|
{
|
|
}
|
|
|
|
CXmlDocSource::~CXmlDocSource()
|
|
{
|
|
if (_pDoc) {
|
|
// Free XML doc
|
|
xmlFreeDoc(_pDoc);
|
|
_pDoc = nullptr;
|
|
}
|
|
}
|
|
|
|
void CXmlDocSource::getRootElement(CXmlElement &xmlRootElement) const
|
|
{
|
|
xmlRootElement.setXmlElement(_pRootNode);
|
|
}
|
|
|
|
string CXmlDocSource::getRootElementName() const
|
|
{
|
|
return (const char *)_pRootNode->name;
|
|
}
|
|
|
|
string CXmlDocSource::getRootElementAttributeString(const string &strAttributeName) const
|
|
{
|
|
CXmlElement topMostElement(_pRootNode);
|
|
|
|
string attribute;
|
|
topMostElement.getAttribute(strAttributeName, attribute);
|
|
return attribute;
|
|
}
|
|
|
|
void CXmlDocSource::setSchemaBaseUri(const string &uri)
|
|
{
|
|
_schemaBaseUri = uri;
|
|
}
|
|
|
|
string CXmlDocSource::getSchemaBaseUri()
|
|
{
|
|
return _schemaBaseUri;
|
|
}
|
|
|
|
string CXmlDocSource::getSchemaUri() const
|
|
{
|
|
// Adding a trailing '/' is a bit dirty but works fine on both Linux and
|
|
// Windows in order to make sure that libxml2's URI handling methods
|
|
// interpret the base URI as a folder.
|
|
return mkUri(_schemaBaseUri + "/", getRootElementName() + ".xsd");
|
|
}
|
|
|
|
_xmlDoc *CXmlDocSource::getDoc() const
|
|
{
|
|
return _pDoc;
|
|
}
|
|
|
|
bool CXmlDocSource::isParsable() const
|
|
{
|
|
// Check that the doc has been created
|
|
return _pDoc != nullptr;
|
|
}
|
|
|
|
bool CXmlDocSource::populate(CXmlSerializingContext &serializingContext)
|
|
{
|
|
// Check that the doc has been created
|
|
if (!_pDoc) {
|
|
|
|
serializingContext.setError("Could not parse document ");
|
|
|
|
return false;
|
|
}
|
|
|
|
// Validate if necessary
|
|
if (_bValidateWithSchema) {
|
|
if (!isInstanceDocumentValid()) {
|
|
|
|
serializingContext.setError("Document is not valid");
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check Root element type
|
|
if (getRootElementName() != _strRootElementType) {
|
|
|
|
serializingContext.setError("Error: Wrong XML structure document ");
|
|
serializingContext.appendLineToError("Root Element " + getRootElementName() +
|
|
" mismatches expected type " + _strRootElementType);
|
|
|
|
return false;
|
|
}
|
|
|
|
if (!_strNameAttributeName.empty()) {
|
|
|
|
string strRootElementNameCheck = getRootElementAttributeString(_strNameAttributeName);
|
|
|
|
// Check Root element name attribute (if any)
|
|
if (!_strRootElementName.empty() && strRootElementNameCheck != _strRootElementName) {
|
|
|
|
serializingContext.setError("Error: Wrong XML structure document ");
|
|
serializingContext.appendLineToError(
|
|
_strRootElementType + " element " + _strRootElementName + " mismatches expected " +
|
|
_strRootElementType + " type " + strRootElementNameCheck);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CXmlDocSource::isInstanceDocumentValid()
|
|
{
|
|
#ifdef LIBXML_SCHEMAS_ENABLED
|
|
string schemaUri = getSchemaUri();
|
|
|
|
xmlDocPtr pSchemaDoc = xmlReadFile(schemaUri.c_str(), nullptr, XML_PARSE_NONET);
|
|
|
|
if (!pSchemaDoc) {
|
|
// Unable to load Schema
|
|
return false;
|
|
}
|
|
|
|
xmlSchemaParserCtxtPtr pParserCtxt = xmlSchemaNewDocParserCtxt(pSchemaDoc);
|
|
|
|
if (!pParserCtxt) {
|
|
|
|
// Unable to create schema context
|
|
xmlFreeDoc(pSchemaDoc);
|
|
return false;
|
|
}
|
|
|
|
// Get Schema
|
|
xmlSchemaPtr pSchema = xmlSchemaParse(pParserCtxt);
|
|
|
|
if (!pSchema) {
|
|
|
|
// Invalid Schema
|
|
xmlSchemaFreeParserCtxt(pParserCtxt);
|
|
xmlFreeDoc(pSchemaDoc);
|
|
return false;
|
|
}
|
|
xmlSchemaValidCtxtPtr pValidationCtxt = xmlSchemaNewValidCtxt(pSchema);
|
|
|
|
if (!pValidationCtxt) {
|
|
|
|
// Unable to create validation context
|
|
xmlSchemaFree(pSchema);
|
|
xmlSchemaFreeParserCtxt(pParserCtxt);
|
|
xmlFreeDoc(pSchemaDoc);
|
|
return false;
|
|
}
|
|
|
|
bool isDocValid = xmlSchemaValidateDoc(pValidationCtxt, _pDoc) == 0;
|
|
|
|
xmlSchemaFreeValidCtxt(pValidationCtxt);
|
|
xmlSchemaFree(pSchema);
|
|
xmlSchemaFreeParserCtxt(pParserCtxt);
|
|
xmlFreeDoc(pSchemaDoc);
|
|
|
|
return isDocValid;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
std::string CXmlDocSource::mkUri(const std::string &base, const std::string &relative)
|
|
{
|
|
xml_unique_ptr baseUri(xmlPathToURI((const xmlChar *)base.c_str()), xmlFree);
|
|
xml_unique_ptr relativeUri(xmlPathToURI((const xmlChar *)relative.c_str()), xmlFree);
|
|
/* return null pointer if baseUri or relativeUri are null pointer */
|
|
xml_unique_ptr xmlUri(xmlBuildURI(relativeUri.get(), baseUri.get()), xmlFree);
|
|
|
|
ALWAYS_ASSERT(xmlUri != nullptr, "unable to make URI from: \"" << base << "\" and \""
|
|
<< relative << "\"");
|
|
|
|
return (const char *)xmlUri.get();
|
|
}
|
|
|
|
_xmlDoc *CXmlDocSource::mkXmlDoc(const string &source, bool fromFile, bool xincludes,
|
|
CXmlSerializingContext &serializingContext)
|
|
{
|
|
_xmlDoc *doc = nullptr;
|
|
if (fromFile) {
|
|
doc = xmlReadFile(source.c_str(), nullptr, 0);
|
|
} else {
|
|
doc = xmlReadMemory(source.c_str(), (int)source.size(), "", nullptr, 0);
|
|
}
|
|
|
|
if (doc == nullptr) {
|
|
string errorMsg = "libxml failed to read";
|
|
if (fromFile) {
|
|
errorMsg += " \"" + source + "\"";
|
|
}
|
|
serializingContext.appendLineToError(errorMsg);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
if (xincludes and (xmlXIncludeProcess(doc) < 0)) {
|
|
serializingContext.appendLineToError("libxml failed to resolve XIncludes");
|
|
|
|
xmlFreeDoc(doc);
|
|
doc = nullptr;
|
|
}
|
|
|
|
return doc;
|
|
}
|