//
// $Id: CollectedObject.cpp,v 1.22 2006/09/06 12:29:19 bakerj Exp $
//
//****************************************************************************************//
// Copyright (c) 2006, The MITRE 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:
//
//     * Redistributions of source code must retain the above copyright notice, this list
//       of conditions and the following disclaimer.
//     * 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.
//     * Neither the name of The MITRE Corporation 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 OWNER 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 "CollectedObject.h"

//****************************************************************************************//
//								CollectedObject Class									  //	
//****************************************************************************************//
CollectedObjectMap CollectedObject::collectedObjectsMap;


CollectedObject::CollectedObject(string id, int version, int variableInstance, OvalEnum::Flag flag) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Create a compelete CollectedObject object
	//
	// -----------------------------------------------------------------------

	this->SetId(id);
	this->SetVariableInstance(variableInstance);
	this->SetVersion(version);
	this->SetFlag(flag);
}

CollectedObject::~CollectedObject() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	clean up all the elements
	//
	// -----------------------------------------------------------------------
	
	OvalMessage* msg = NULL;
	while(this->messages.size() != 0) {
	  	msg = messages[messages.size()-1];
	  	messages.pop_back();
	  	delete msg;
	  	msg = NULL;
	}

	// Don't delete variable values they are shared and cached. There is a clear 
	// cache method in the var value class taht is called at the end of run time
}

// ***************************************************************************************	//
//								Static Public members										//
// ***************************************************************************************	//
CollectedObject* CollectedObject::Create(AbsObject* absObject) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Create a new ColelctedObject ans store a reference to it.
	//
	// -----------------------------------------------------------------------

	CollectedObject* collectedObject = new CollectedObject(absObject->GetId(), absObject->GetVersion());
	CollectedObject::Cache(collectedObject);
	return collectedObject;
}

CollectedObject* CollectedObject::CreateError(string objectId) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Create a new ColelctedObject based on the specified AbsObject
	//	Set it up as not applicable.
	//
	// -----------------------------------------------------------------------

	CollectedObject* collectedObject = new CollectedObject(objectId);
	collectedObject->SetFlag(OvalEnum::ERROR_FLAG);
	CollectedObject::Cache(collectedObject);
	return collectedObject;
}

CollectedObject* CollectedObject::CreateNotApplicable(AbsObject* absObject) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Create a new ColelctedObject based on the specified AbsObject
	//	Set it up as not applicable.
	//
	// -----------------------------------------------------------------------

	CollectedObject* collectedObject = CollectedObject::Create(absObject);
	collectedObject->SetFlag(OvalEnum::NOT_APPLICABLE_FLAG);

	OvalMessage* message = new OvalMessage("This object was determined to be not applicable by this OVAL Definition Interpreter data collector.");
	collectedObject->AppendOvalMessage(message);
	
	return collectedObject;
}

CollectedObject* CollectedObject::CreateNotSupported(AbsObject* absObject) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Create a new ColelctedObject based on the specified AbsObject
	//	Set it up as not supported.
	//
	// -----------------------------------------------------------------------

	CollectedObject* collectedObject = CollectedObject::Create(absObject);
	collectedObject->SetFlag(OvalEnum::NOT_COLLECTED_FLAG);

	OvalMessage* message = new OvalMessage("This type of object (" + absObject->GetName() + ") is not currently supported by this OVAL Definition Interpreter data collector.");
	collectedObject->AppendOvalMessage(message);
	return collectedObject;
}

void CollectedObject::WriteCollectedObjects() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Write all collected objects in the vector of collected objects.
	//	After writing delete the object to free memory.
	//
	// -----------------------------------------------------------------------

	CollectedObjectMap::iterator iterator;
	for(iterator = CollectedObject::collectedObjectsMap.begin(); iterator != CollectedObject::collectedObjectsMap.end(); iterator++) {
		
		CollectedObject* obj = iterator->second;
		obj->Write(DocumentManager::GetSystemCharacterisitcsDocument(), AbsDataCollector::Instance()->GetSCCollectedObjectsElm());
		delete obj;
	}
	
	CollectedObject::collectedObjectsMap.clear();
}

CollectedObject* CollectedObject::GetCollectedObject(string objectId) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	return the collected object that corresponds to the object id 
	//	specified. Return NULL if the object id is not found.
	// -----------------------------------------------------------------------

	CollectedObject* colelctedObject = NULL;

	CollectedObjectMap::iterator iterator;
	iterator = CollectedObject::collectedObjectsMap.find(objectId);
	if(iterator != CollectedObject::collectedObjectsMap.end()) {
		colelctedObject = iterator->second;
	} 

	return colelctedObject;
}

// ***************************************************************************************	//
//								 Public members												//
// ***************************************************************************************	//
OvalEnum::Flag CollectedObject::GetFlag() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the flag field's value
	//
	// -----------------------------------------------------------------------

	return this->flag;
}

void CollectedObject::SetFlag(OvalEnum::Flag flag) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Set the flag field's value
	//
	// -----------------------------------------------------------------------

	this->flag = flag;
}

string CollectedObject::GetId() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the id field's value
	//
	// -----------------------------------------------------------------------

	return this->id;
}

void CollectedObject::SetId(string id) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Set the id field's value
	//
	// -----------------------------------------------------------------------

	this->id = id;
}

OvalMessageVector* CollectedObject::GetMessages() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Set the messages field's value
	//
	// -----------------------------------------------------------------------
	return &this->messages;
}
void CollectedObject::SetMessages(OvalMessageVector* messages) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Set the messages field's value
	//
	// -----------------------------------------------------------------------
	
	this->messages = (*messages);
}

ItemVector* CollectedObject::GetReferences() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the references field's value
	//
	// -----------------------------------------------------------------------

	return &this->references;
}

void CollectedObject::SetReferences(ItemVector* references) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Set the references field's value
	//
	// -----------------------------------------------------------------------

	this->references = (*references);
}

int CollectedObject::GetVariableInstance() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the variableInstance field's value
	//
	// -----------------------------------------------------------------------

	return this->variableInstance;
}

void CollectedObject::SetVariableInstance(int variableInstance) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Set the variableInstance field's value
	//
	// -----------------------------------------------------------------------

	this->variableInstance = variableInstance;
}

VariableValueVector* CollectedObject::GetVariableValues() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the variableValues field's value
	//
	// -----------------------------------------------------------------------

	return &this->variableValues;
}

void CollectedObject::SetVariableValues(VariableValueVector* variableValues) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Set the variableValues field's value
	//
	// -----------------------------------------------------------------------

	this->variableValues = (*variableValues);
}

int CollectedObject::GetVersion() {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Return the version field's value
	//
	// -----------------------------------------------------------------------

	return this->version;
}

void CollectedObject::SetVersion(int version) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Set the version field's value
	//
	// -----------------------------------------------------------------------

	this->version = version;
}

void CollectedObject::AppendOvalMessage(OvalMessage* message) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Add an message to the end of the messages vector
	//
	// -----------------------------------------------------------------------

	this->messages.push_back(message);
}

void CollectedObject::AppendReference(Item* reference) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Add an reference to the end of the references vector
	//	Assign the items id before adding to vector.
	//
	// -----------------------------------------------------------------------

	this->references.push_back(reference);
}

void CollectedObject::AppendReferences(ItemVector* references) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Add the items to the end of the references vector
	//
	// -----------------------------------------------------------------------

	ItemVector::iterator iterator;
	for(iterator = references->begin(); iterator != references->end(); iterator++) {
		Item* item = (*iterator);
		this->references.push_back(item);
	}
}

void CollectedObject::AppendVariableValue(VariableValue* variableValue) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Add a varaible value to the end of teh variable values vector
	//
	// -----------------------------------------------------------------------

	this->variableValues.push_back(variableValue);
}

void CollectedObject::AppendVariableValues(VariableValueVector* vars) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Add a variable value to the end of the variable values vector
	//
	// -----------------------------------------------------------------------

	VariableValueVector::iterator iterator;
	for(iterator = vars->begin(); iterator != vars->end(); iterator++) {
		VariableValue* var = (*iterator);
		this->variableValues.push_back(var);
	}
}

void CollectedObject::Write(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* scFile, DOMElement* collectedObjectsElm) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Write this item to a sc file. Make sure the item has not already been 
	//	written to the sc file
	//
	// -----------------------------------------------------------------------

	// Create a new object element
	DOMElement *newCollectedObjectElem = XmlCommon::AddChildElement(scFile, collectedObjectsElm, "object");

	// Add the attributes
	// handling defaults in the schema
	XmlCommon::AddAttribute(newCollectedObjectElem, "id", this->GetId());	

	string strFlag = OvalEnum::FlagToString(this->GetFlag());
	XmlCommon::AddAttribute(newCollectedObjectElem, "flag", strFlag);

	string version = Common::ToString(this->GetVersion());
	XmlCommon::AddAttribute(newCollectedObjectElem, "version", version);

	// Add any messges
	if(this->GetMessages()->size() > 0) {
		OvalMessageVector::iterator messageIterator;
		for(messageIterator = this->GetMessages()->begin(); messageIterator != this->GetMessages()->end(); messageIterator++) {
			OvalMessage* message = (*messageIterator);
			message->Write(scFile, newCollectedObjectElem, "oval-sc");
		}
	}

	// Call the write method for each variable_value - ensure that each var value is only written once
	if(this->GetVariableValues()->size() > 0) {
		StringPairVector varIdValuePairs;
		VariableValueVector::iterator variableValueIterator;
		for(variableValueIterator = this->GetVariableValues()->begin(); variableValueIterator != this->GetVariableValues()->end(); variableValueIterator++) {
			VariableValue* variableValue = (*variableValueIterator);
			if(!this->IsWritten(&varIdValuePairs, variableValue->GetId(), variableValue->GetValue())) {
				StringPair* pair = new StringPair();
				pair->first = variableValue->GetId();
				pair->second = variableValue->GetValue();
				varIdValuePairs.push_back(pair);
				variableValue->Write(newCollectedObjectElem);
			}
		}
	}

	// Add each reference - ensure taht each refernec is only written once.
	if(this->GetReferences()->size() > 0) {
		IntVector referenceIds;
		ItemVector::iterator referenceIterator;
		for(referenceIterator = this->GetReferences()->begin(); referenceIterator != this->GetReferences()->end(); referenceIterator++) {
			Item* reference = (*referenceIterator);
			
			if(!this->IsWritten(&referenceIds, reference->GetId())) {
				referenceIds.push_back(reference->GetId());

				// add the item to the sc file
				reference->Write(scFile, AbsDataCollector::Instance()->GetSCSystemDataElm());

				// add the reference tot he collected obj element
				string refElementName = "reference";
				DOMElement *newReferenceElm = scFile->createElement(XMLString::transcode(refElementName.c_str()));
				newCollectedObjectElem->appendChild(newReferenceElm);
				string idStr = Common::ToString(reference->GetId());
				XmlCommon::AddAttribute(newReferenceElm, "item_ref", idStr);
			}
		}
	}
}

// ***************************************************************************************	//
//								 Private members											//
// ***************************************************************************************	//
bool CollectedObject::IsWritten(StringPairVector* varIdValuePairs, string varId, string varValue) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	return true if the specifed variable value has not alredy been written
	//  must check the pair of var id and value.
	// -----------------------------------------------------------------------

	bool result = false;

	StringPairVector::iterator iterator;
	for(iterator = varIdValuePairs->begin(); iterator != varIdValuePairs->end(); iterator++) {
		string id = (*iterator)->first;
		if(id.compare(varId) == 0) {
			string value = (*iterator)->second;
			if(value.compare(varValue) == 0) {
				result = true;
                break;
			}
		}
	}
	return result;
}

bool CollectedObject::IsWritten(IntVector* itemIds, int itemId) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	return true if the specifed item id has already been 
	//	added as a reference
	// -----------------------------------------------------------------------

	bool result = false;

	IntVector::iterator iterator;
	for(iterator = itemIds->begin(); iterator != itemIds->end(); iterator++) {
		int id = (*iterator);
		if(id == itemId) {
			result = true;
               break;
		}
	}
	return result;
}

void CollectedObject::Cache(CollectedObject* collectedObject) {
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Add the provided collected object to the cache of collected objects 
	//	if it does not already exist
	// -----------------------------------------------------------------------

	if(CollectedObject::GetCollectedObject(collectedObject->GetId()) == NULL) {
		// does not already exist so add it
		CollectedObject::collectedObjectsMap.insert(CollectedObjectPair(collectedObject->GetId(), collectedObject));
	}
}

//****************************************************************************************//
//								CollectedObjectException Class							  //	
//****************************************************************************************//
CollectedObjectException::CollectedObjectException(string errMsgIn, int severity, Exception* ex) : Exception(errMsgIn, severity, ex) {
	// -----------------------------------------------------------------------
	//	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.
	//
	// -----------------------------------------------------------------------

}

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

}

