/*
* Copyright (c) 2011-2014, 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.h"
#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), _pParent(NULL)
{
}
CElement::~CElement()
{
removeChildren();
}
// Logging
void CElement::log_info(const char* strMessage, ...) const
{
char *pacBuffer;
va_list listPointer;
va_start(listPointer, strMessage);
vasprintf(&pacBuffer, strMessage, listPointer);
va_end(listPointer);
if (pacBuffer != NULL) {
doLog(false, pacBuffer);
}
free(pacBuffer);
}
void CElement::log_warning(const char* strMessage, ...) const
{
char *pacBuffer;
va_list listPointer;
va_start(listPointer, strMessage);
vasprintf(&pacBuffer, strMessage, listPointer);
va_end(listPointer);
if (pacBuffer != NULL) {
doLog(true, pacBuffer);
}
free(pacBuffer);
}
// Log each element of the string list
void CElement::log_table(bool bIsWarning, const std::list<string> lstrMessage) const
{
std::list<string>::const_iterator iterator(lstrMessage.begin());
std::list<string>::const_iterator end(lstrMessage.end());
while (iterator != end) {
// Log current list element
doLog(bIsWarning, iterator->c_str());
++iterator;
}
}
void CElement::doLog(bool bIsWarning, const string& strLog) const
{
assert(_pParent);
// Propagate till root
_pParent->doLog(bIsWarning, strLog);
}
void CElement::nestLog() const
{
assert(_pParent);
// Propagate till root
_pParent->nestLog();
}
void CElement::unnestLog() const
{
assert(_pParent);
// Propagate till root
_pParent->unnestLog();
}
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)
{
uint32_t uiIndex;
for (uiIndex = 0; uiIndex < _childArray.size(); uiIndex++) {
CElement* pElement = _childArray[uiIndex];;
if (!pElement->init(strError)) {
return false;
}
}
return true;
}
void CElement::dumpContent(string& strContent, CErrorContext& errorContext, const uint32_t uiDepth) const
{
string strIndent;
// Level
uint32_t uiNbIndents = uiDepth;
while (uiNbIndents--) {
strIndent += " ";
}
// Type
strContent += strIndent + "- " + getKind();
// Name
if (!_strName.empty()) {
strContent += ": " + getName();
}
// Value
string strValue;
logValue(strValue, errorContext);
if (!strValue.empty()) {
strContent += " = " + strValue;
}
strContent += "\n";
uint32_t uiIndex;
for (uiIndex = 0; uiIndex < _childArray.size(); uiIndex++) {
_childArray[uiIndex]->dumpContent(strContent, errorContext, uiDepth + 1);
}
}
// Element properties
void CElement::showProperties(string& strResult) const
{
strResult = "\n";
strResult += "Kind: " + getKind() + "\n";
showDescriptionProperty(strResult);
}
void CElement::showDescriptionProperty(std::string &strResult) const
{
if (!getDescription().empty()) {
strResult += gDescriptionPropertyName + ": " + getDescription() + "\n";
}
}
// Content dumping
void CElement::logValue(string& strValue, CErrorContext& errorContext) const
{
(void)strValue;
(void)errorContext;
}
// From IXmlSink
bool CElement::fromXml(const CXmlElement& xmlElement, CXmlSerializingContext& serializingContext)
{
setDescription(getXmlDescriptionAttribute(xmlElement));
// Propagate through children
CXmlElement::CChildIterator childIterator(xmlElement);
// Context
CXmlElementSerializingContext& elementSerializingContext = static_cast<CXmlElementSerializingContext&>(serializingContext);
CXmlElement childElement;
while (childIterator.next(childElement)) {
CElement* pChild;
if (!childrenAreDynamic()) {
pChild = findChildOfKind(childElement.getType());
if (!pChild) {
elementSerializingContext.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, elementSerializingContext)) {
return false;
}
}
return true;
}
void CElement::childrenToXml(CXmlElement& xmlElement,
CXmlSerializingContext& serializingContext) const
{
// Browse children and propagate
size_t uiNbChildren = getNbChildren();
size_t uiChild;
for (uiChild = 0; uiChild < uiNbChildren; uiChild++) {
const CElement* pChild = _childArray[uiChild];
// Create corresponding child element
CXmlElement xmlChildElement;
xmlElement.createChild(xmlChildElement, pChild->getKind());
// 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.setAttributeString(gDescriptionPropertyName, description);
}
}
string CElement::getXmlDescriptionAttribute(const CXmlElement& xmlElement) const
{
return xmlElement.getAttributeString(gDescriptionPropertyName);
}
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()) {
size_t uiParentChild;
size_t uiParentNbChildren = _pParent->getNbChildren();
for (uiParentChild = 0; uiParentChild < uiParentNbChildren; uiParentChild++) {
const CElement* pParentChild = _pParent->getChild(uiParentChild);
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 uiIndex)
{
assert(uiIndex <= _childArray.size());
return _childArray[uiIndex];
}
const CElement* CElement::getChild(size_t uiIndex) const
{
assert(uiIndex <= _childArray.size());
return _childArray[uiIndex];
}
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)
{
ChildArrayIterator it;
for (it = _childArray.begin(); it != _childArray.end(); ++it) {
CElement* pElement = *it;
if (pElement == pChild) {
_childArray.erase(it);
return true;
}
}
return false;
}
void CElement::listChildren(string& strChildList) const
{
strChildList = "\n";
// Get list of children names
size_t uiNbChildren = getNbChildren();
size_t uiChild;
for (uiChild = 0; uiChild < uiNbChildren; uiChild++) {
const CElement* pChild = _childArray[uiChild];
strChildList += pChild->getName() + "\n";
}
}
string CElement::listQualifiedPaths(bool bDive, uint32_t uiLevel) const
{
size_t uiNbChildren = getNbChildren();
string strResult;
// Dive Will cause only leaf nodes to be printed
if (!bDive || !uiNbChildren) {
strResult = getQualifiedPath() + "\n";
}
if (bDive || !uiLevel) {
// Get list of children paths
size_t uiChild;
for (uiChild = 0; uiChild < uiNbChildren; uiChild++) {
const CElement* pChild = _childArray[uiChild];
strResult += pChild->listQualifiedPaths(bDive, uiLevel + 1);
}
}
return strResult;
}
void CElement::listChildrenPaths(string& strChildList) const
{
// Get list of children paths
size_t uiNbChildren = getNbChildren();
size_t uiChild;
for (uiChild = 0; uiChild < uiNbChildren; uiChild++) {
const CElement* pChild = _childArray[uiChild];
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
uint32_t uiIndex;
for (uiIndex = 0; uiIndex < _childArray.size(); uiIndex++) {
_childArray[uiIndex]->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)
{
uint32_t uiIndex;
for (uiIndex = 0; uiIndex < _childArray.size(); uiIndex++) {
CElement* pElement = _childArray[uiIndex];
if (pElement->getPathName() == strName) {
return pElement;
}
}
return NULL;
}
const CElement* CElement::findChild(const string& strName) const
{
uint32_t uiIndex;
for (uiIndex = 0; uiIndex < _childArray.size(); uiIndex++) {
const CElement* pElement = _childArray[uiIndex];
if (pElement->getPathName() == strName) {
return pElement;
}
}
return NULL;
}
CElement* CElement::findChildOfKind(const string& strKind)
{
uint32_t uiIndex;
for (uiIndex = 0; uiIndex < _childArray.size(); uiIndex++) {
CElement* pElement = _childArray[uiIndex];
if (pElement->getKind() == strKind) {
return pElement;
}
}
return NULL;
}
const CElement* CElement::findChildOfKind(const string& strKind) const
{
uint32_t uiIndex;
for (uiIndex = 0; uiIndex < _childArray.size(); uiIndex++) {
const CElement* pElement = _childArray[uiIndex];;
if (pElement->getKind() == strKind) {
return pElement;
}
}
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() + "]";
}
uint32_t CElement::getDepth() const
{
if (_pParent) {
return _pParent->getDepth() + 1;
}
return 0;
}
// Checksum for integrity checks
uint8_t CElement::computeStructureChecksum() const
{
// Base checksum computation on element kind
string strKind = getKind();
// Get element kind
const char* pcData = strKind.c_str();
// Cumulate
uint8_t uiChecksum = 0;
while (*pcData) {
uiChecksum += *pcData++;
}
// Propagate
uint32_t uiIndex;
for (uiIndex = 0; uiIndex < _childArray.size(); uiIndex++) {
const CElement* pChild = _childArray[uiIndex];
uiChecksum += pChild->computeStructureChecksum();
}
return uiChecksum;
}