Click here to Skip to main content
15,867,686 members
Articles / Programming Languages / C++

A STL based XML Config Tool

Rate me:
Please Sign up or sign in to vote.
3.19/5 (9 votes)
19 Jun 20051 min read 69.1K   886   38  
Writing multiple enumerated configuration entries.
#include "XML_Node.h"
#include "ParamIO.h"
#include <cassert>

XML_Node::XML_Node() :
_parent(0)
{
}

XML_Node::XML_Node(const std::string &name, const std::string &attributes, XML_Node *parent) :
_name(name),
_attributes(attributes),
_parent(parent)
{
}

void XML_Node::set(const std::string &name, const std::string &attributes, XML_Node *parent)
{
	_name = name;
	_attributes = attributes;
	_parent = parent;
}


void XML_Node::addElement(const std::string &name, const std::string &value, const std::string &attributes )
{
   Element aEl( value, attributes );
   elements_iterator it = std::find_if(_elements.begin(), _elements.end(), FindElement( name, aEl ));
   if(it == _elements.end())
   {
      _elements.push_back( std::make_pair( name, aEl ) );
	  //int iSize = _elements.size();
	  //int dummy = 0;
   }
   else
   {
      it->second = aEl;
   }
}

XML_Node *XML_Node::addNode(const std::string &name, const std::string &attributes)
{
	XML_Node newNode(name, attributes, this);
	_nodes.push_back(newNode);
	return &(_nodes.back());
}

bool XML_Node::getElement(const std::string &name, std::string &value, std::string &attributes)
{
   elements_iterator it = std::find_if(_elements.begin(), _elements.end(), FindElement(name));
	
	if(it != _elements.end())
	{
		value      = it->second.value;
		attributes = it->second.attributes;
		return true;
	}

	return false;
}

bool XML_Node::setElementValue(const std::string &name, const std::string &value)
{
   elements_iterator it = std::find_if(_elements.begin(), _elements.end(), FindElement(name));
	
	if(it != _elements.end())
	{
   	it->second.value = value;
		return true;
	}

	return false;
}

XML_Node::elements_const_iterator XML_Node::beginElements() const
{
	return _elements.begin();
}

XML_Node::elements_const_iterator XML_Node::endElements() const
{
	return _elements.end();
}

XML_Node::nodes_const_iterator XML_Node::beginNodesConst() const
{
	int iSize = _nodes.size();
	return _nodes.begin();
}

XML_Node::nodes_const_iterator XML_Node::endNodesConst() const
{
	return _nodes.end();
}

XML_Node::nodes_iterator XML_Node::beginNodes()
{
	return _nodes.begin();
}

XML_Node::nodes_iterator XML_Node::endNodes()
{
	return _nodes.end();
}

const std::string &XML_Node::getName() const
{
	return _name;
}

bool XML_Node::extractValue(const std::string &str, std::string &value) const
{
	elements_const_iterator it = std::find_if(_elements.begin(), _elements.end(), FindElement(str));
	if(it == endElements())
	{
		return false;
	}
	value = it->second.value;	
	return true;
}

XML_Node *XML_Node::getParent()
{
	return _parent;
}

void XML_Node::print(std::ostream &os) const
{
   static int nTabCount = 1;
   int i;

   for(i=1; i < nTabCount; i++)
   {
      os << "\t";
   }

   nTabCount++;

   os << "<" << _name << ">" << std::endl;

   for(elements_const_iterator it = beginElements(); it != endElements(); it++)
   {
      for(i=1; i < nTabCount; i++)
      {
         os << "\t";
      }
	  os << "<" << it->first;
	  if(!it->second.attributes.empty())
		os << " " << it->second.attributes;//TBW : the Attributes shall be saved too !!
	  os << ">";
	  os << it->second.value;
	  os << "</" << it->first << ">" << std::endl;
   }

   for(nodes_const_iterator it2 = beginNodesConst(); it2 != endNodesConst(); it2++)
   {
      it2->print(os);
   }

   for(i=1; i < (nTabCount-1); i++)
   {
      os << "\t";
   }

   os << "</" << _name << ">" << std::endl;

   nTabCount--;
}

void XML_Node::clear()
{
	_name = std::string();
	_attributes = std::string();
	_parent = 0;
	_elements.clear();
	_nodes.clear();
}


XML_Node::~XML_Node()
{
	clear();
}

XML_Node::nodes_const_iterator XML_Node::findNode(const std::string &name) const
{
   return std::find_if(beginNodesConst(), endNodesConst(), FindNode(name));
}

XML_Node::nodes_iterator XML_Node::findNode(const std::string &name)
{
   return std::find_if(beginNodes(), endNodes(), FindNode(name));
}


// XML_Param_Notify
XML_Param_Notify::XML_Param_Notify():
	_currentNode(0)
{
}

XML_Param_Notify::~XML_Param_Notify()
{
	clear();
}

void XML_Param_Notify::clear()
{
	_node.clear();
}

XML_Node::nodes_iterator XML_Param_Notify::top()
{
	return static_cast<XML_Node::nodes_iterator>(&_node);
}

XML_Node::nodes_const_iterator XML_Param_Notify::getNodeConst(std::vector<std::string> &strs)const
{
	// First we need to find the final node
	XML_Node::nodes_const_iterator res, end, begin;
	int iNodeCount = _node.getNodeCount();
	begin = _node.beginNodesConst();
	end   = _node.endNodesConst();
	res = begin;
	int iSize = strs.size();//TBW : better for Debugging in VC
	if( (begin != 0) && (begin != end) )
	{	
		for(int i=0; i<iSize; i++)
		{
			std::string strSearchKey = strs[i];
			res = std::find_if(begin, end, FindNode(strSearchKey));
			if(res == end)
			{
				// Couldn't find the proper parameter
				return static_cast<XML_Node::nodes_const_iterator>(0); 
			}
			begin = res->beginNodesConst();
			end   = res->endNodesConst();
			#ifdef _DEBUG
				int iResSize = (*res).getElementCount();//TBW : yust for Debugging
			#endif
		}
	}
	else if( _node.getName() == strs[0] )
	{
		XML_Node* pNode = const_cast<XML_Node*>(&_node);
		res = static_cast<XML_Node::nodes_const_iterator>(pNode);
	}
	return res;
}

XML_Node::nodes_iterator XML_Param_Notify::getNode(std::vector<std::string> &strs)
{
	// First we need to find the final node
	XML_Node::nodes_iterator res, end, begin;

	begin = static_cast<XML_Node::nodes_iterator>(&_node);
	end   = static_cast<XML_Node::nodes_iterator>(&(_node) + 1);	
   res = begin;
   int iSize = strs.size();
   for(int i=0; i<iSize; i++)
	{
		res = std::find_if(begin, end, FindNode(strs[i]));
		if(res == end)
		{
			// Couldn't find the proper parameter
			return static_cast<XML_Node::nodes_iterator>(0); 
		}
		begin = res->beginNodes();
		end   = res->endNodes();
	}

	return res;
}

void XML_Param_Notify::print(std::ostream &os) const
{
	_node.print(os);	
}

// notify methods
void XML_Param_Notify::foundNode( std::string & name, std::string & attributes )
{
	if(_currentNode != 0)
	{
		_currentNode = _currentNode->addNode(name, attributes);
	}
	else
	{
		_node.set(name, attributes, 0);
		_currentNode = &_node;
		//int iNumNodes = _node.getNodeCount();
	}
}

void XML_Param_Notify::endNode( std::string & name, std::string & attributes )
{
	_currentNode = _currentNode->getParent();
}

void XML_Param_Notify::foundElement( std::string & name, std::string & value, std::string & attributes )
{
	assert( _currentNode );
	if( _currentNode )
		_currentNode->addElement(name, value, attributes);
}

bool XML_Param_Notify::compare( const ParamIO &old, std::vector<std::string> &strs ) const
{
	// First we need to check if it's a subtree or an element
	XML_Node::nodes_const_iterator  res = getNodeConst(strs);

	if(res == static_cast<XML_Node::nodes_const_iterator>(0))
	{
      // Not an existing node, it's either an element, either a wrong access
		return compareElement(old, strs);
	}
	else
	{
		return compareSubTree(old, strs);
	}
	// Useless
	return false;
}

bool XML_Param_Notify::compareElement(const ParamIO &old, std::vector<std::string> &strs) const
{
	// First we need to find the final node
	std::string paramName = strs.back();
	strs.pop_back();
	XML_Node::nodes_const_iterator  it    = getNodeConst(strs);
	XML_Node::nodes_const_iterator  itOld = old.getTree().getNodeConst(strs);

	if(it    == static_cast<XML_Node::nodes_const_iterator>(0) && 
      itOld == static_cast<XML_Node::nodes_const_iterator>(0))
	{
      // The element doesn't exist in both trees = >they are identical
		return true;
	}

   if(it    == static_cast<XML_Node::nodes_const_iterator>(0) || 
      itOld == static_cast<XML_Node::nodes_const_iterator>(0))
   {
      // The element exists only in one tree => they are different
      return false;
   }

   std::string value, oldValue;
	bool ret = it->extractValue(paramName, value);
	bool retOld = itOld->extractValue(paramName, oldValue);

   if(ret == false && retOld == false)
   {
      // The node doesn't exist in both cases
      return true;
   }

   if(ret == false || retOld == false)
   {
      // Only one of them doesn't have this node
      return false;
   }

   if(value.compare(oldValue) == 0)
   {
      return true;
   }
   return false;
}

bool XML_Param_Notify::compareSubTree(const ParamIO &old, std::vector<std::string> &strs) const
{
   XML_Node::nodes_const_iterator  it = getNodeConst(strs);
	XML_Node::nodes_const_iterator  itOld = old.getTree().getNodeConst(strs);

	if(it    == static_cast<XML_Node::nodes_const_iterator>(0) && 
      itOld == static_cast<XML_Node::nodes_const_iterator>(0))
	{
      // The subtree doesn't exist in both trees = >they are identical
		return true;
	}

   if(it    == static_cast<XML_Node::nodes_const_iterator>(0) || 
      itOld == static_cast<XML_Node::nodes_const_iterator>(0))
   {
      // The subtree exists only in one tree => they are different
      return false;
   }

   // Now compare subtrees

   // 1st compare the elements
   if(compareAllElements(it, itOld) == false)
   {
      return false;
   }
   
   // 2nd compare the children nodes
   if(compareAllChildren(it, itOld) == false)
   {
      return false;
   }

   return true;
}

XML_Node::nodes_iterator XML_Param_Notify::GetAddIterator( std::vector<std::string> &strs )
{
	// First we need to find the final node
	XML_Node::nodes_iterator end, begin;
	XML_Node::nodes_iterator res = 0, prevRes = 0;
	unsigned int i;
	begin = static_cast<XML_Node::nodes_iterator>(&_node);
	end   = static_cast<XML_Node::nodes_iterator>(&(_node) + 1);
	bool go_out = false;
	for(i=0; i<strs.size()-1 && go_out==false; i++)
	{
		prevRes = res;

		res = std::find_if(begin, end, FindNode(strs[i]));
		if(res == end)
		{
			// Couldn't find the proper parameter
			go_out = true;
		}
		else
		{
			begin = res->beginNodes();
			end   = res->endNodes();
		}
	}
	// Do we need to create any node?
	if(go_out == true)
	{
		i--;
		// We need to create new nodes
		for(;i<strs.size()-1; i++)
		{
			if(prevRes == static_cast<XML_Node::nodes_iterator>(0))
			{
				// It means the tree is empty
				_node.set(strs[i], std::string(), 0);
				prevRes = static_cast<XML_Node::nodes_iterator>(&(_node));
			}
			else
			{
				prevRes = static_cast<XML_Node::nodes_iterator>(prevRes->addNode(strs[i], std::string()));
			}
		}
	}
	else
	{
		prevRes = res;
	}
	return prevRes;
}

bool XML_Param_Notify::addElement( std::vector<std::string> &strs, const char* strAttributeString )//creates an Element setting its Attributes
{
	XML_Node::nodes_iterator prevRes = GetAddIterator( strs );
	assert( prevRes != NULL );
	prevRes->addElement( strs.back(), strAttributeString, std::string() );
	return true;	
}

bool XML_Param_Notify::compareAllElements(XML_Node::nodes_const_iterator  it, XML_Node::nodes_const_iterator  itOld) const
{
   // Check if they have the same number of elements
   int nbElem    = std::distance(it->beginElements(),    it->endElements());
   int nbElemOld = std::distance(itOld->beginElements(), itOld->endElements());

   if(nbElem != nbElemOld)
   {
      return false;
   }

   if(nbElem == 0)
   {
      // They both have no element
      return true;
   }

   // Compare the values of the elements
	for(XML_Node::elements_const_iterator elemIt = it->beginElements(); elemIt != it->endElements(); elemIt++)
   {
      std::string value = elemIt->second.value;
      std::string valueOld;

		bool res = itOld->extractValue(elemIt->first, valueOld);

      if(res == false || value.compare(valueOld)!=0)
      {
         return false;
      }
   }

   return true;
}

bool XML_Param_Notify::compareAllChildren(XML_Node::nodes_const_iterator  it, XML_Node::nodes_const_iterator  itOld) const
{
   // Check if they have the same number of elements
   int nb    = std::distance(it->beginNodesConst(),    it->endNodesConst());
   int nbOld = std::distance(itOld->beginNodesConst(), itOld->endNodesConst());

   if(nb != nbOld)
   {
      return false;
   }

   if(nb == 0)
   {
      // They both have no element
      return true;
   }

   // Compare the children
	for(XML_Node::nodes_const_iterator nodeIt = it->beginNodesConst(); nodeIt != it->endNodesConst(); nodeIt++)
   {
      XML_Node::nodes_const_iterator nodeItOld = itOld->findNode(nodeIt->getName());

      if(nodeItOld == itOld->endNodesConst())
      {
         // One node exists in it and not itOld
         return false;
      }

      if(compareAllElements(nodeIt, nodeItOld) == false)
      {
         // Subtree are not identical
         return false;
      }

      if(compareAllChildren(nodeIt, nodeItOld) == false)
      {
         // Subtree are not identical
         return false;
      }
   }

   return true;
}

////////// Extract 
bool XML_Param_Notify::extractSubTree(std::vector<std::string> &strs, ParamIO &subtree) const
{
	// First we need to check if it's a subtree or an element
	XML_Node::nodes_const_iterator  res = getNodeConst(strs);

	if(res != static_cast<XML_Node::nodes_const_iterator>(0))
	{
      *(subtree.getTree().top()) = (*res);
      return true;
	}
	// We didn't get a node (either element or wrong access)
	return false;
}

////////// Erase 
bool XML_Param_Notify::eraseSubTree(std::vector<std::string> &strs)
{
    std::string lastAccess = strs.back();
    strs.pop_back();
    // First we need to check if it's a subtree or an element
    XML_Node::nodes_iterator	res = getNode(strs);

    if(res != static_cast<XML_Node::nodes_const_iterator>(0))
    {
        if(res->eraseElement(lastAccess) == true)
        {
            return true;
        }

        if(res->eraseNode(lastAccess) == true)
        {
            return true;
        }
    }

    return false;
}

bool XML_Node::eraseElement(const std::string &name)
{
	elements_iterator it = std::find_if(_elements.begin(), _elements.end(), FindElement(name));
	if(it != _elements.end())
	{
        _elements.erase(it);
		return true;
	}
	return false;
}

bool XML_Node::eraseNode(const std::string &name)
{
    // Find if we want to delete a node or an element
    XML_Node::nodes_iterator it = findNode(name);
    if(it != endNodes())
    {
        _nodes.erase(it);
        return true;
    }
    return false;
}

///////////////////////////////////////////////////////////////////////////
//Functions added by TBW
///////////////////////////////////////////////////////////////////////////

//TBW : added to do a specific delete, when names are equal
int XML_Param_Notify::eraseElement( std::vector<std::string> &vNodes, std::vector<std::string>& vTokensToFindInAttributes )
{
	int iDeleteCount = 0;
	std::string lastAccess = vNodes.back();
    vNodes.pop_back();
    // First we need to check if it's a subtree or an element
    XML_Node::nodes_iterator res = getNode(vNodes);
    if(res != static_cast<XML_Node::nodes_const_iterator>(0))
    {    
		iDeleteCount = res->eraseElements( lastAccess, vTokensToFindInAttributes );
	}
	return iDeleteCount;
}
//TBW : Added to modify the Attributes of Objects with equal Name
int XML_Param_Notify::addElementAttributes( std::vector<std::string> &vNodes, std::vector<std::string>& vTokensToFindInAttributes, const std::string& strAttributeVal )
{
	int iModifyCount = 0;
	// First we need to check if it's a subtree or an element
    std::string lastAccess = vNodes.back();
    vNodes.pop_back();    
	XML_Node::nodes_iterator res = getNode( vNodes );
    if(res != static_cast<XML_Node::nodes_const_iterator>(0))
    {    
		iModifyCount = res->addAttributesToElements( lastAccess, vTokensToFindInAttributes, strAttributeVal );
	}
	else
	{	
		addElement( vNodes, strAttributeVal );
	}
	return iModifyCount;
}
//TBW: added to be able to append a whole string
int XML_Param_Notify::addAttributeString( std::vector<std::string> &vNodes, std::vector<std::string>& vTokensToFindInAttributes, const char* strAttributeString )
{
	int iModifyCount = 0;
	//First we need to check if it's a subtree or an element
    std::string lastAccess = vNodes.back();
    //vNodes.pop_back();//not in this case -> last node shall be created too
	XML_Node::nodes_iterator res = getNode( vNodes );
	int iNumNodes = vNodes.size();//TBW : yust for debugging
    if(res != static_cast<XML_Node::nodes_const_iterator>(0))
    {    
		iModifyCount = res->addAttributesToElements( lastAccess, vTokensToFindInAttributes, strAttributeString );
	}
	else
	{	
		addElement( vNodes, strAttributeString );
	}
	return iModifyCount;
}

//TBW : Added to remove the Attributes of Objects with equal Name
int XML_Param_Notify::removeElementAttributes( std::vector<std::string> &vNodes, std::vector<std::string>& vTokensToFindInAttributes, const std::string& strAttrinutesToRemove  )
{
	int iRemoveCount = 0;
	std::string lastAccess = vNodes.back();
    vNodes.pop_back();
    // First we need to check if it's a subtree or an element
    XML_Node::nodes_iterator res = getNode(vNodes);
    if(res != static_cast<XML_Node::nodes_const_iterator>(0))
    {    
		iRemoveCount = res->removeAttributesFromElements( lastAccess, vTokensToFindInAttributes, strAttrinutesToRemove );
	}
	return iRemoveCount ;
}

int XML_Node::eraseElements( const std::string &name, std::vector<std::string>& vTokensToFindInAttributes )
{
	int iDeleteCount = 0;
	std::vector< std::pair<std::string, Element > >::iterator it = std::find_if(_elements.begin(), _elements.end(), FindElementByAttribute(name, vTokensToFindInAttributes ));
	while(it != _elements.end() )
	{
		_elements.erase(it);
		iDeleteCount++;
		it = std::find_if(_elements.begin(), _elements.end(), FindElementByAttribute(name, vTokensToFindInAttributes ));
	}
	return iDeleteCount;
}

int XML_Node::removeAttributesFromElements(const std::string &name, std::vector<std::string>& vTokensToFindInAttributes, const std::string& AttributesToRemove )
{
	//first find the proper Element
	int iModified = 0;
	std::vector< std::pair<std::string, Element > > vRemovedElems;//catch the removed Eleemnts to not handle the twice..
	std::vector< std::pair<std::string, Element > >::iterator it = std::find_if(_elements.begin(), _elements.end(), FindElementByAttribute(name, vTokensToFindInAttributes ));
	while(it != _elements.end() )
	{
		std::string strName = it->first;
		Element foundEl = it->second;
		_elements.erase(it);
		int iFoundPos = foundEl.attributes.find( AttributesToRemove );
		if( iFoundPos != std::string::npos )
		{
			//modify
			int iValueBegin = foundEl.attributes.find( '"' );
			int iValueEnd = foundEl.attributes.find( '"', iValueBegin + 1 );
			if( iValueEnd != std::string::npos )
			{
				foundEl.attributes = foundEl.attributes.substr( iValueEnd+2, foundEl.attributes.size() - iValueEnd );//+2, because '" ' after each Attribute
				iModified++;
			}
		}
		vRemovedElems.push_back( std::make_pair<std::string, Element>( strName, foundEl ) );//reinsert
		it = std::find_if(_elements.begin(), _elements.end(), FindElementByAttribute(name, vTokensToFindInAttributes ));
	}
	while( !vRemovedElems.empty() )
	{
		_elements.push_back( vRemovedElems.back() );
		vRemovedElems.pop_back();
	}	
	return iModified;
}

int XML_Node::addAttributesToElements(const std::string &name, std::vector<std::string>& vTokensToFindInAttributes, const std::string &AttributesToAdd )
{
	int iModified = 0;
	std::vector< std::pair<std::string, Element > > vRemovedElems;//catch the removed Eleemnts to not handle the twice..
	std::vector< std::pair<std::string, Element > >::iterator it = std::find_if(_elements.begin(), _elements.end(), FindElementByAttribute(name, vTokensToFindInAttributes ));
	while(it != _elements.end() )
	{
		std::string strName = it->first;
		Element foundEl = it->second;
		_elements.erase(it);
		iModified++;
		//modify
		foundEl.attributes += AttributesToAdd;		
		vRemovedElems.push_back( std::make_pair<std::string, Element>( strName, foundEl ) );//reinsert
		it = std::find_if(_elements.begin(), _elements.end(), FindElementByAttribute(name, vTokensToFindInAttributes ));
	}
	//copy back modified elements
	while( !vRemovedElems.empty() )
	{
		_elements.push_back( vRemovedElems.back() );
		vRemovedElems.pop_back();
	}
	return iModified;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer
Germany Germany
Studying and having a degree of medical engineering, but decided to work as a software developer writing medical applications for image processing.

Comments and Discussions