/*
* 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 "Element.h"
#include "XmlElementSerializingContext.h"
#include "ElementLibrary.h"
#include "ErrorContext.hpp"
#include <algorithm>
#include <assert.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
using std::string;
const std::string CElement::gDescriptionPropertyName = "Description";
CElement::CElement(const string &strName) : _strName(strName)
{
}
CElement::~CElement()
{
removeChildren();
}
void CElement::setDescription(const string &strDescription)
{
_strDescription = strDescription;
}
const string &CElement::getDescription() const
{
return _strDescription;
}
bool CElement::childrenAreDynamic() const
{
// By default, children are searched and not created during xml parsing
return false;
}
bool CElement::init(string &strError)
{
for (CElement *child : _childArray) {
if (!child->init(strError)) {
return false;
}
}
return true;
}
string CElement::dumpContent(utility::ErrorContext &errorContext, const size_t depth) const
{
string output;
string strIndent;
// Level
size_t indents = depth;
while (indents--) {
strIndent += " ";
}
// Type
output += strIndent + "- " + getKind();
// Name
if (!_strName.empty()) {
output += ": " + getName();
}
// Value
string strValue = logValue(errorContext);
if (!strValue.empty()) {
output += " = " + strValue;
}
output += "\n";
for (CElement *pChild : _childArray) {
output += pChild->dumpContent(errorContext, depth + 1);
}
return output;
}
// Element properties
void CElement::showProperties(string &strResult) const
{
strResult += "Kind: " + getKind() + "\n";
showDescriptionProperty(strResult);
}
void CElement::showDescriptionProperty(std::string &strResult) const
{
if (!getDescription().empty()) {
strResult += gDescriptionPropertyName + ": " + getDescription() + "\n";
}
}
// Content dumping
string CElement::logValue(utility::ErrorContext & /*ctx*/) const
{
return "";
}
// From IXmlSink
bool CElement::fromXml(const CXmlElement &xmlElement, CXmlSerializingContext &serializingContext)
{
xmlElement.getAttribute(gDescriptionPropertyName, _strDescription);
// Propagate through children
CXmlElement::CChildIterator childIterator(xmlElement);
CXmlElement childElement;
while (childIterator.next(childElement)) {
CElement *pChild;
if (!childrenAreDynamic()) {
pChild = findChildOfKind(childElement.getType());
if (!pChild) {
serializingContext.setError("Unable to handle XML element: " +
childElement.getPath());
return false;
}
} else {
// Child needs creation
pChild = createChild(childElement, serializingContext);
if (!pChild) {
return false;
}
}
// Dig
if (!pChild->fromXml(childElement, serializingContext)) {
return false;
}
}
return true;
}
void CElement::childrenToXml(CXmlElement &xmlElement,
CXmlSerializingContext &serializingContext) const
{
// Browse children and propagate
for (CElement *pChild : _childArray) {
// Create corresponding child element
CXmlElement xmlChildElement;
xmlElement.createChild(xmlChildElement, pChild->getXmlElementName());
// Propagate
pChild->toXml(xmlChildElement, serializingContext);
}
}
void CElement::toXml(CXmlElement &xmlElement, CXmlSerializingContext &serializingContext) const
{
setXmlNameAttribute(xmlElement);
setXmlDescriptionAttribute(xmlElement);
childrenToXml(xmlElement, serializingContext);
}
void CElement::setXmlDescriptionAttribute(CXmlElement &xmlElement) const
{
const string &description = getDescription();
if (!description.empty()) {
xmlElement.setAttribute(gDescriptionPropertyName, description);
}
}
void CElement::setXmlNameAttribute(CXmlElement &xmlElement) const
{
// By default, set Name attribute if any
string strName = getName();
if (!strName.empty()) {
xmlElement.setNameAttribute(strName);
}
}
// Name
void CElement::setName(const string &strName)
{
_strName = strName;
}
const string &CElement::getName() const
{
return _strName;
}
bool CElement::rename(const string &strName, string &strError)
{
// Check for conflict with brotherhood if relevant
if (_pParent && _pParent->childrenAreDynamic()) {
for (CElement *pParentChild : _pParent->_childArray) {
if (pParentChild != this && pParentChild->getName() == strName) {
// Conflict
strError = "Name conflicts with brother element";
return false;
}
}
}
// Change name
setName(strName);
return true;
}
string CElement::getPathName() const
{
if (!_strName.empty()) {
return _strName;
} else {
return getKind();
}
}
// Hierarchy
void CElement::addChild(CElement *pChild)
{
_childArray.push_back(pChild);
pChild->_pParent = this;
}
CElement *CElement::getChild(size_t index)
{
assert(index <= _childArray.size());
return _childArray[index];
}
const CElement *CElement::getChild(size_t index) const
{
assert(index <= _childArray.size());
return _childArray[index];
}
CElement *CElement::createChild(const CXmlElement &childElement,
CXmlSerializingContext &serializingContext)
{
// Context
CXmlElementSerializingContext &elementSerializingContext =
static_cast<CXmlElementSerializingContext &>(serializingContext);
// Child needs creation
CElement *pChild = elementSerializingContext.getElementLibrary()->createElement(childElement);
if (!pChild) {
elementSerializingContext.setError("Unable to create XML element " +
childElement.getPath());
return NULL;
}
// Store created child!
addChild(pChild);
return pChild;
}
bool CElement::removeChild(CElement *pChild)
{
auto childIt = find(begin(_childArray), end(_childArray), pChild);
if (childIt != end(_childArray)) {
_childArray.erase(childIt);
return true;
}
return false;
}
void CElement::listChildren(string &strChildList) const
{
// Get list of children names
for (CElement *pChild : _childArray) {
strChildList += pChild->getName() + "\n";
}
}
string CElement::listQualifiedPaths(bool bDive, size_t level) const
{
string strResult;
// Dive Will cause only leaf nodes to be printed
if (!bDive || !getNbChildren()) {
strResult = getQualifiedPath() + "\n";
}
if (bDive || !level) {
// Get list of children paths
for (CElement *pChild : _childArray) {
strResult += pChild->listQualifiedPaths(bDive, level + 1);
}
}
return strResult;
}
void CElement::listChildrenPaths(string &strChildList) const
{
// Get list of children paths
for (CElement *pChild : _childArray) {
strChildList += pChild->getPath() + "\n";
}
}
size_t CElement::getNbChildren() const
{
return _childArray.size();
}
const CElement *CElement::getParent() const
{
return _pParent;
}
CElement *CElement::getParent()
{
return _pParent;
}
void CElement::clean()
{
if (childrenAreDynamic()) {
removeChildren();
} else {
// Just propagate
for (CElement *pChild : _childArray) {
pChild->clean();
}
}
}
void CElement::removeChildren()
{
// Delete in reverse order
ChildArrayReverseIterator it;
for (it = _childArray.rbegin(); it != _childArray.rend(); ++it) {
delete *it;
}
_childArray.clear();
}
const CElement *CElement::findDescendant(CPathNavigator &pathNavigator) const
{
string *pStrChildName = pathNavigator.next();
if (!pStrChildName) {
return this;
}
const CElement *pChild = findChild(*pStrChildName);
if (!pChild) {
return NULL;
}
return pChild->findDescendant(pathNavigator);
}
CElement *CElement::findDescendant(CPathNavigator &pathNavigator)
{
string *pStrChildName = pathNavigator.next();
if (!pStrChildName) {
return this;
}
CElement *pChild = findChild(*pStrChildName);
if (!pChild) {
return NULL;
}
return pChild->findDescendant(pathNavigator);
}
bool CElement::isDescendantOf(const CElement *pCandidateAscendant) const
{
if (!_pParent) {
return false;
}
if (_pParent == pCandidateAscendant) {
return true;
}
return _pParent->isDescendantOf(pCandidateAscendant);
}
CElement *CElement::findChild(const string &strName)
{
for (CElement *pChild : _childArray) {
if (pChild->getPathName() == strName) {
return pChild;
}
}
return NULL;
}
const CElement *CElement::findChild(const string &strName) const
{
for (CElement *pChild : _childArray) {
if (pChild->getPathName() == strName) {
return pChild;
}
}
return NULL;
}
CElement *CElement::findChildOfKind(const string &strKind)
{
for (CElement *pChild : _childArray) {
if (pChild->getKind() == strKind) {
return pChild;
}
}
return NULL;
}
const CElement *CElement::findChildOfKind(const string &strKind) const
{
for (CElement *pChild : _childArray) {
if (pChild->getKind() == strKind) {
return pChild;
}
}
return NULL;
}
string CElement::getPath() const
{
// Take out root element from the path
if (_pParent && _pParent->_pParent) {
return _pParent->getPath() + "/" + getPathName();
}
return "/" + getPathName();
}
string CElement::getQualifiedPath() const
{
return getPath() + " [" + getKind() + "]";
}
string CElement::getXmlElementName() const
{
// Default to element kind
return getKind();
}