| /* |
| * 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; |
| } |