//
// $Id: DOMProcessor.cpp,v 1.2 2004/07/08 15:25:37 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
//
//****************************************************************************************//

#include "DOMProcessor.h"

//****************************************************************************************//
//								DOMProcessor Class										  //	
//****************************************************************************************//

DOMProcessor::DOMProcessor()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Init the DOMProcessor
	//	Throw an exception if there is an error.
	//
	// -----------------------------------------------------------------------

    try 
	{
        XMLPlatformUtils::Initialize();
    }
    catch (const XMLException& toCatch) 
	{
        string errMsg = "Error:  An error occured durring initialization of the xml utilities:\n";
        errMsg.append(DOMCommon::ToString(toCatch.getMessage()));
		errMsg.append("\n");

		throw DOMProcessorException(errMsg);
    }	
}

DOMProcessor::~DOMProcessor()
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Clean up after the DOMOvalXMLAnalyzer. 
	//
	// -----------------------------------------------------------------------

	//  Delete the parser itself.  Must be done prior to calling Terminate, below.
	if(parser != NULL)
		parser->release();

	XMLPlatformUtils::Terminate();

}


XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* DOMProcessor::ParseFile(string filePathIn, bool validate)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Parse the specified file and return a DOMDocument. 
	//	'filePathIn' should be the complete path to the desired file.
	//
	//	2/25/2004 - Added validate paramater. 
	//		The validate flag is used to indicate whether the xml file 
	//		should be checked with a schema file. 
	//			- validate == (false)	- <DEFAULT> never validate the xml
	//			- validate == (true)	- always validate the xml
	//		When validating and xml file the schema must be in the same 
	//		directory as the file. 
	//
	// -----------------------------------------------------------------------
	
    XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *resultDocument = NULL;

    // Instantiate the DOM parser.
    static const XMLCh gLS[] = { chLatin_L, chLatin_S, chNull };
    DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation(gLS);
    parser = ((DOMImplementationLS*)impl)->createDOMBuilder(DOMImplementationLS::MODE_SYNCHRONOUS, 0);

	///////////////////////////////////////////////////////
    //	Set fetuares on the builder
	///////////////////////////////////////////////////////

	//	If validating the document namespaces and Schema must be set to true
	if(validate)
	{
		parser->setFeature(XMLUni::fgDOMNamespaces, true);
		parser->setFeature(XMLUni::fgXercesSchema, true);
		parser->setFeature(XMLUni::fgXercesSchemaFullChecking, true);
		//	Treat validation errors as fatal - default is false
		//	The parser, by default will exit after the first fatal error.
		parser->setFeature(XMLUni::fgXercesValidationErrorAsFatal, true); 
		parser->setFeature(XMLUni::fgDOMValidation, true);

	}else
	{
		//	Set all validation features to false
		parser->setFeature(XMLUni::fgDOMNamespaces, false);
		parser->setFeature(XMLUni::fgXercesSchema, false);
		parser->setFeature(XMLUni::fgXercesSchemaFullChecking, false);
		parser->setFeature(XMLUni::fgDOMValidation, false);
	}
	
	//	Don't read in comments 
	parser->setFeature(XMLUni::fgDOMComments, false);
    //	Enable DataType normalization - default is off
    parser->setFeature(XMLUni::fgDOMDatatypeNormalization, true);

	// Create a new DOMErrorHandler
	// and set it to the builder
	DOMProcessorErrorHandler *errHandler = new DOMProcessorErrorHandler();
	parser->setErrorHandler(errHandler);

    try 
	{
		// reset document pool
		parser->resetDocumentPool();
        resultDocument = parser->parseURI(filePathIn.c_str());
    }
    catch (const XMLException& toCatch) 
	{
		string error = "Error while parsing xml file:";
		error.append(filePathIn);
		error.append("\n\tMessage: \n\t");
		error.append(DOMCommon::ToString(toCatch.getMessage()));

		throw DOMProcessorException(error);
    }
    catch (const DOMException& toCatch) 
	{
		string error = "Error while parsing xml file:";
		error.append(filePathIn);
		error.append("\n\tMessage: \n\t");
		error.append(DOMCommon::ToString(toCatch.msg));

		throw DOMProcessorException(error);
    }
    catch (...) 
	{
        string error = "Error while parsing xml file:";
		error.append(filePathIn);
		error.append("\n\tMessage: \n\tUnknown message");

		throw DOMProcessorException(error);
    }

	if(errHandler->getSawErrors())
	{
		string error = "Error while parsing xml file:";
		error.append(errHandler->getErrorMessages());
		throw DOMProcessorException(error);
	}

	return resultDocument;
}

XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* DOMProcessor::CreateDOMDocument(string root)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Create a new DOMDocument.
	//
	// -----------------------------------------------------------------------

	const XMLCh *xmlRoot = XMLString::transcode(root.c_str());


	DOMImplementation* impl =  DOMImplementationRegistry::getDOMImplementation(XMLString::transcode ("Core"));
	XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* doc = impl->createDocument(0, xmlRoot, 0);

	return(doc);

}

void DOMProcessor::WriteDOMDocument(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* doc,  string filePath, bool writeToFile)
{
	// -----------------------------------------------------------------------
	//	Abstract
	//
	//	Write the DOMDocument to the specified XML file.
	//	filePath is the filename and path to the file that will be written
	//
	// -----------------------------------------------------------------------

	try
	{
		// get a serializer, an instance of DOMWriter
		XMLCh tempStr[100];
		XMLString::transcode("LS", tempStr, 99);
		DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation(tempStr);
		DOMWriter *theSerializer = ((DOMImplementationLS*)impl)->createDOMWriter();

		// set feature if the serializer supports the feature/mode
		if (theSerializer->canSetFeature(XMLUni::fgDOMWRTSplitCdataSections, true))
			theSerializer->setFeature(XMLUni::fgDOMWRTSplitCdataSections, true);

		if (theSerializer->canSetFeature(XMLUni::fgDOMWRTDiscardDefaultContent, true))
			theSerializer->setFeature(XMLUni::fgDOMWRTDiscardDefaultContent, true);

		if (theSerializer->canSetFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true))
			theSerializer->setFeature(XMLUni::fgDOMWRTFormatPrettyPrint, true);

		if (theSerializer->canSetFeature(XMLUni::fgDOMWRTBOM, false))
			theSerializer->setFeature(XMLUni::fgDOMWRTBOM, false);

		//
		// Plug in a format target to receive the resultant
		// XML stream from the serializer.
		//
		// StdOutFormatTarget prints the resultant XML stream
		// to stdout once it receives any thing from the serializer.
		//
		XMLFormatTarget *myFormTarget;
		if (writeToFile)
			myFormTarget = new LocalFileFormatTarget(filePath.c_str());
		else
			myFormTarget = new StdOutFormatTarget();

		//
		// do the serialization through DOMWriter::writeNode();
		//
		theSerializer->writeNode(myFormTarget, *doc);

		delete theSerializer;
		delete myFormTarget;
	}
	catch(...)
	{
		string error;
		if(writeToFile)
		{
			error.append("Error while writing Document to XML file: ");
			error.append(filePath);
		}else
		{
			error.append("Error while writing Document to screen");
		}

		throw DOMProcessorException(error);
	}

}

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

}

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

}

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

}

//****************************************************************************************//
//								DOMProcessorErrorHandler Class							  //	
//****************************************************************************************//
DOMProcessorErrorHandler::DOMProcessorErrorHandler() : fSawErrors(false)
{
	errorMessages = "";
}

DOMProcessorErrorHandler::~DOMProcessorErrorHandler()
{
}


// ---------------------------------------------------------------------------
//  DOMProcessorErrorHandler: Overrides of the DOM ErrorHandler interface
// ---------------------------------------------------------------------------
bool DOMProcessorErrorHandler::handleError(const DOMError& domError)
{
    fSawErrors = true;
    if (domError.getSeverity() == DOMError::DOM_SEVERITY_WARNING)
        errorMessages.append("\n\tSeverity: Warning");
    else if (domError.getSeverity() == DOMError::DOM_SEVERITY_ERROR)
        errorMessages.append("\n\tSeverity: Error");
    else
        errorMessages.append("\n\tSeverity: Fatal Error");

	string msg =  DOMCommon::ToString(domError.getMessage());
	string file = DOMCommon::ToString(domError.getLocation()->getURI());
	long line = domError.getLocation()->getLineNumber();
	long at = domError.getLocation()->getColumnNumber();


	errorMessages.append("\n\tMessage: " + msg);
	errorMessages.append("\n\tFile: " + file);
	errorMessages.append("\n\tLine " + Common::ToString(line));
	errorMessages.append("\n\tAt char " + Common::ToString(at));
	

    return true;
}

void DOMProcessorErrorHandler::resetErrors()
{
    fSawErrors = false;
}

bool DOMProcessorErrorHandler::getSawErrors() const
{
    return fSawErrors;
}

string DOMProcessorErrorHandler::getErrorMessages() const
{
    return errorMessages;
}

