//
// $Id: DOMCommon.cpp,v 1.1 2004/06/01 17:05:18 bakerj Exp $
//
//************************** Property of the MITRE Corporation ***************************//
//
// Copyright (c) 2003 - The MITRE Corporation
//
// This file is part of the Query-based Network Assessment project.
//
// The Query-based Network Assessment is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License.
//
// The Query-based Network Assessment is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with the
// Query-based Network Assessment; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
//****************************************************************************************//
//
//  file:	DOMCommon.cpp
//  author:	Jon Baker	
//  date:	15 December 2003
//
//****************************************************************************************//

#include "DOMCommon.h"


//****************************************************************************************//
//										DOMCommon Class									  //	
//****************************************************************************************//
void DOMCommon::AddAttribute(DOMElement *node, string attName, string attValue)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Add a new attribute to the node 
	// -----------------------------------------------------------------------

	const XMLCh *name = XMLString::transcode(attName.c_str());
	const XMLCh *value = XMLString::transcode(attValue.c_str());
	node->setAttribute(name, value);

}

NodeVector* DOMCommon::FindAllNodes(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *doc, string nodeName, string attribute, string attValue)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the all nodes that match the node name and attribute value
	//
	//	All inputs are required
	// -----------------------------------------------------------------------

	NodeVector *nodes		= new NodeVector();
	DOMNode *tmpNode		= NULL;
	DOMNodeList *nodeList	= NULL;
	int listLen				= 0;
	int index				= 0;

	nodeList = doc->getElementsByTagName(XMLString::transcode(nodeName.c_str()));

	listLen = nodeList->getLength();
	while(index < listLen)
	{
		tmpNode = nodeList->item(index++);	
		if(tmpNode != NULL)
		{
			//	Check for attribute if desired
			if(attribute.compare("") == 0)
			{
				nodes->push_back(tmpNode);
				continue;
			}else if(((DOMElement*)tmpNode)->hasAttribute(XMLString::transcode(attribute.c_str())))
			{
				//	Check for attribute value if desired
				if(attValue.compare("") == 0)
				{
					nodes->push_back(tmpNode);
					continue;
				}else if((GetAttributeByName(tmpNode, attribute)).compare(attValue) == 0)
				{
					nodes->push_back(tmpNode);
					continue;
				}
			}
		}
	}

	return nodes;
}


DOMNode* DOMCommon::FindNode(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *doc, string nodeName, string attribute, string attValue)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the first node found that has the corresponding name with
	//	the attribute and attribute value specified.
	//
	//	Requires input of at least a node name and a DOMDocument.
	// -----------------------------------------------------------------------

	DOMNode *tmpNode		= NULL;
	DOMNode *result			= NULL;
	DOMNodeList *nodeList	= NULL;
	int listLen				= 0;
	int index				= 0;


	//	Get a list of all the nodes in the document with the nodeName and loop through them
	nodeList = doc->getElementsByTagName(XMLString::transcode(nodeName.c_str()));
	listLen = nodeList->getLength();
	while(index < listLen)
	{
		tmpNode = nodeList->item(index++);	
		if(tmpNode != NULL)
		{
			//	Check for attribute if desired
			if(attribute.compare("") == 0)
			{
				result = tmpNode;
				break;
			}else if(((DOMElement*)tmpNode)->hasAttribute(XMLString::transcode(attribute.c_str())))
			{
				//	Check for attribute value if desired
				if(attValue.compare("") == 0)
				{
					result = tmpNode;
					break;
				}else if((GetAttributeByName(tmpNode, attribute)).compare(attValue) == 0)
				{
					result = tmpNode;
					break;
				}
			}
		}
	}

	return result;
}

DOMNode* DOMCommon::FindNode(DOMNode *element, string nodeName, string attribute, string attValue)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the first node found that has the corresponding name with
	//	the attribute and attribute value specified.
	//
	//	Requires input of at least a node name and a DOMNode.
	// -----------------------------------------------------------------------

	DOMNode *tmpNode		= NULL;
	DOMNode *result			= NULL;
	DOMNodeList *nodeList	= NULL;
	int listLen				= 0;
	int index				= 0;


	nodeList = ((DOMElement*)element)->getElementsByTagName(XMLString::transcode(nodeName.c_str()));

	listLen = nodeList->getLength();
	while(index < listLen)
	{
		tmpNode = nodeList->item(index++);	
		if(tmpNode != NULL)
		{
			//	Check for attribute if desired
			if(attribute.compare("") == 0)
			{
				result = tmpNode;
				break;
			}else if(((DOMElement*)tmpNode)->hasAttribute(XMLString::transcode(attribute.c_str())))
			{
				//	Check for attribute value if desired
				if(attValue.compare("") == 0)
				{
					result = tmpNode;
					break;
				}else if((GetAttributeByName(tmpNode, attribute)).compare(attValue) == 0)
				{
					result = tmpNode;
					break;
				}
			}
		}
	}

	return result;
}

DOMNode* DOMCommon::FindNodeByAttribute(DOMNode *node, string attribute, string attValue)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Recursively search the passed DOMNode for an element 
	//	with a corresponding attribute and attribute value.
	//
	// -----------------------------------------------------------------------
	
	DOMNode *child;
	DOMNode *found = NULL;

    if (node) 
	{
        if (node->getNodeType() == DOMNode::ELEMENT_NODE)
		{
			//	test to see if the node has attributes
			if(node->hasAttributes()) 
			{
				// get all the attributes of the node
				DOMNamedNodeMap *pAttributes = node->getAttributes();
				int nSize = pAttributes->getLength();
	
				//	Loop through the attributes
		        for(int i=0;i<nSize;++i) 
				{
					DOMAttr *pAttributeNode = (DOMAttr*) pAttributes->item(i);
					// get attribute name
					 string attName = ToString(pAttributeNode->getName());

					//	Is this the attribute we are looking for
					if(attribute.compare(attName.c_str())==0)
					{   
						//	Get the attribute value
						string attVal = ToString(pAttributeNode->getValue());
						
						//	Is the value correct
						if(attValue.compare(attVal.c_str()) == 0)
						{
							//	found the match we are looking for
							//	return a ptr to the node 
							found = node;
							break;
						}
					}
				}
				//	No attributes match for this node search children
				if(found == NULL)
				{
					//	loop through all child nodes calling this function recursively
					for (child = node->getFirstChild(); child != 0; child=child->getNextSibling())
					{
						found = FindNodeByAttribute(child, attribute, attValue);
			
						//	Break out of the loop if found a node
						if(found != NULL)
							break;
					}
				}

			//	No attributes found search its children
			}else
			{
				//	loop through all child nodes calling this function recursively
				for (child = node->getFirstChild(); child != 0; child=child->getNextSibling())
				{
					found = FindNodeByAttribute(child, attribute, attValue);
		
					//	Break out of the loop if found a node
					if(found != NULL)
						break;
				}
			}


		//	not an element node ok to search its children
		}else
		{
			//	loop through all child nodes calling this function recursively
			for (child = node->getFirstChild(); child != 0; child=child->getNextSibling())
			{
				found = FindNodeByAttribute(child, attribute, attValue);
	
				//	Break out of the loop if found a node
				if(found != NULL)
					break;
			}
		}
    }

    return (found);
}

string DOMCommon::GetAttributeByName(DOMNode *node, string name)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return NULL if the attribute is not found. 
	//	value is set to the value of the attribute if it is found.
	//
	// -----------------------------------------------------------------------
	
	string value = "";

	//	Check inputs
	if(node == NULL)
		throw DOMCommonException("Error: Unable to get attribute value. NULL node supplied\n");

	if(node->getNodeType() != DOMNode::ELEMENT_NODE)
		throw DOMCommonException("Error: Unable to get attribute value. Invalid node supplied\n");

	if(name.compare("") == 0)
		throw DOMCommonException("Error: Unable to get attribute value. NULL attribute name supplied\n");


	const XMLCh *attName = XMLString::transcode(name.c_str());
	value = ToString(((DOMElement*)node)->getAttribute(attName));

	//////////   DEBUG ////////////////////
	//	cout << "***** debug *****" <<endl;
	//	cout << "GetAttributeByName()" << endl;
	//	cout << "Name: " << name << endl;
	//	cout << "Value: " << value << endl;
	//////////   DEBUG ////////////////////

	return(value);
}

void DOMCommon::RemovetAttributeByName(DOMNode *node, string name)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Remove the specified attribute
	//
	// -----------------------------------------------------------------------

	const XMLCh *attName = XMLString::transcode(name.c_str());
	((DOMElement*)node)->removeAttribute(attName);
}

string DOMCommon::GetDataNodeValue(DOMNode *node)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the value of a data node. Return an empty string if there is 
	//	no value
	// -----------------------------------------------------------------------

	string result = "";

	//	Check input
	if(node == NULL)
		throw DOMCommonException("Error: Attempted to get the value of a NULL node.");

	if(node->getNodeType() != DOMNode::ELEMENT_NODE)
		throw DOMCommonException("Error: Attempted to get the value of a Node that is not an element node.");

	if(node->hasChildNodes())
	{
		DOMNodeList *childList = node->getChildNodes();
		if(childList->getLength() == 1)
		{
			DOMNode *child = node->getFirstChild();
			if(child->getNodeType() == DOMNode::TEXT_NODE)
				result = ToString(((DOMText*)child)->getData());
			else
				throw DOMCommonException("Error: When getting the value of a node there should be exactly one child of that node and it should be a TEXT_NODE");

		}else
		{
			throw DOMCommonException("Error: When getting the value of a node there should be exactly one child of that node");
		}
	}else
	{
		throw DOMCommonException("Error: When getting the value of a node there should be exactly one child of that node");
	}

	return (result);

}

string DOMCommon::ToString(const XMLCh *xml)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Convert the XMLCh* to a string and handle memory allocation.
	// -----------------------------------------------------------------------

	string result = "";
	char *tmp;

	tmp = XMLString::transcode(xml);
	result = tmp;
	XMLString::release(&tmp);

	return(result);
}


//****************************************************************************************//
//							DOMCommonException Class									  //	
//****************************************************************************************//
DOMCommonException::DOMCommonException() : Exception()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Default constructor simply set the severity to ERROR_FATAL. This is 
	//	done with the explicit call to the Exception class default constructor.
	//
	// -----------------------------------------------------------------------

}

DOMCommonException::DOMCommonException(string errMsgIn) : Exception(errMsgIn)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Set the error message and then set the severity to ERROR_FATAL. This is 
	//	done with the explicit call to the Exception class constructor that 
	//	takes a single string param.
	//
	// -----------------------------------------------------------------------

}

DOMCommonException::~DOMCommonException()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Do nothing for now
	//
	// -----------------------------------------------------------------------

}

