//
// $Id: VariableCollector.cpp,v 1.1 2004/06/24 13:22:31 bakerj Exp $
//
//************************** Property of the MITRE Corporation ***************************//
//
// Copyright (c) 2003 - The MITRE Corporation
//
// This file is part of the Definition Interpreter project.
//
// The Definition Interpreter 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 Definition Interpreter 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
// Definition Interpreter; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
//****************************************************************************************//


#include "VariableCollector.h"

//****************************************************************************************//
//								VariableCollector Class									  //
//****************************************************************************************//

VariableCollector::VariableCollector(DOMProcessor *myProcessor, string varFileIn, 
									 XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *ovalDoc)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Parse the specified variables file onto a DOMDocument
	//	Set the reference to the oval document
	// -----------------------------------------------------------------------
	
	//	Parse the variables file - validate it against the variables schema
	variableDocument = myProcessor->ParseFile(varFileIn, true);

	//	Set the oval document for this VariableCollector
	if(ovalDoc == NULL)
		throw VariableCollectorException("Error: A NULL oval document has been specified for use with the variable collector.");
	else
		ovalDocument = ovalDoc;

}

VariableCollector::~VariableCollector()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Delete the variables document.
	// -----------------------------------------------------------------------

	//	Call the destructor
	variableDocument->~DOMDocument();
}

// ***************************************************************************************	//
//								Public members												//
// ***************************************************************************************	//
void VariableCollector::Run()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Search the oval document for all variable references. When a reference
	//	found get teh value from the variables document. Write the new value to
	//	the oval doument and remove the variable reference.
	//	
	// -----------------------------------------------------------------------
	
	//	Get a refernce to the tests node
	DOMNode *testsNode = DOMCommon::FindNode(ovalDocument, "tests");

	//	get a list of the child nodes
	DOMNodeList *testList = testsNode->getChildNodes();

	//	Loop through all the nodes in testList
	unsigned int index = 0;
	DOMNode *test = NULL;
	int listLength = testList->getLength();
	while(index < listLength)
	{
		test = testList->item(index++);

		if(test != NULL)
		{
			//	only concerned with ELEMENT_NODEs
			if (test->getNodeType() == DOMNode::ELEMENT_NODE)
			{
				//	Get the name of the node
				string name = DOMCommon::ToString(test->getNodeName());
				
				//	Check the test type - ignore compound and unknown tests
				if(name.compare("compound_test") != 0 && name.compare("unknown_test") != 0)
					ProcessTest((DOMElement*)test);
			}
		}
	}
}

// ***************************************************************************************	//
//								Private members												//
// ***************************************************************************************	//
void VariableCollector::GetVariableValue(string varIdIn, string *opOut, string *valOut)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the value and operator of the variable in the variables 
	//	document with the corresponding variable id
	//
	//	TODO: Determine how to handle xml as part of a variable.
	//	Latest changes to the variables schema also include a type.
	//
	// -----------------------------------------------------------------------

	DOMNode *varRootNode = DOMCommon::FindNode(variableDocument, "variables");
	DOMNode *varNode = DOMCommon::FindNodeByAttribute(varRootNode, "id", varIdIn);
	
	//	Get the operator
	DOMNode *tmp = DOMCommon::FindNode(varNode, "operator");
	opOut->assign(DOMCommon::GetDataNodeValue(tmp));

	//	Get the value
	tmp = DOMCommon::FindNode(varNode, "value");
	valOut->assign(DOMCommon::GetDataNodeValue(tmp));

}
void VariableCollector::ProcessTest(DOMElement *ovalTest)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Search each element in the test for a variable reference.
	//	If one is found get the variable value from the variables document
	//
	//	NOTE: Assuming variable references only occure on
	//	test sub elements. not on their children. This means
	//	that windows file path and file versions can not have
	//	a variable reference. I have hard coded an exception if 
	//	a variable reference is found on either of these.
	//
	// -----------------------------------------------------------------------

	//	get a list of the child nodes
	DOMNodeList *testElementsList = ovalTest->getChildNodes();

	//	Get the test id
	string testId = DOMCommon::GetAttributeByName(ovalTest, "id");

	//	Loop through all the nodes in testList
	unsigned int index = 0;
	DOMNode *testElement = NULL;
	int listLength = testElementsList->getLength();
	while(index < listLength)
	{
		testElement = testElementsList->item(index++);

		if(testElement != NULL)
		{
			//	only concerned with ELEMENT_NODEs
			if (testElement->getNodeType() == DOMNode::ELEMENT_NODE)
			{
				//	Get the name of the node
				string name = DOMCommon::ToString(testElement->getNodeName());
				
				//	Look for a variable reference
				string varId = DOMCommon::GetAttributeByName(testElement, "var_ref");
				if(varId.compare("") != 0)
				{
					//	Get the variable's value and 
					//	set the value in the oval document
					string op = "";
					string val = "";
					GetVariableValue(varId, &op, &val);

					SetValue((DOMElement*)testElement, op, val);

					//	Remove the var_ref attribute on the test element.

				}else
				{
					throw VariableCollectorException("Error: A variable reference is specified with out a value in test:" + testId);
				}
			}
		}
	}

}
void VariableCollector::SetValue(DOMElement *testElm, string opIn, string valIn)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Add the value of the variable to the test element in the oval document
	//
	// -----------------------------------------------------------------------

}


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

}

VariableCollectorException::VariableCollectorException(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.
	//
	// -----------------------------------------------------------------------

}

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

}

