//
// $Id: AbsXmlFileContentProbe.cpp,v 1.4 2005/04/12 18:10:28 bakerj Exp $
//
//****************************************************************************************//
// Copyright (c) 2005, 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 "AbsXmlFileContentProbe.h"

AbsXmlFileContentProbe::AbsXmlFileContentProbe()
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//	Note that this is a protected constructor
	// -----------------------------------------------------------------------
}

AbsXmlFileContentProbe::~AbsXmlFileContentProbe()
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//	Note that this is a protected constructor
	// -----------------------------------------------------------------------
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  Public Members  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
pdVector AbsXmlFileContentProbe::Run(ProbeData *probeData)
{
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//	
	//	Use a platform specifc FileFinder to find all files taht match the 
	//  spoecified pattern. If no pattern is used simply use the provided 
	//  file path. Then evaulate xpath expression on each matching file and
	//  write a XmlFileContentData object to hold the resulting data.
	//
	//  Note 1: xpath expression are treated as though they always evaluate to 
	//  a single node.
	//
	//  Note 2: regular expressions are not allowed as part of an xpath
	//
	//  Note 3: this probe should allow for a context node to be provided
	//  for now the context node will default to '/' (document root)
	// -----------------------------------------------------------------------

	// cast the probeData obj as a XmlFileContentData obj
	AbsXmlFileContentData *xfcd = (AbsXmlFileContentData*)probeData;

	pdVector results;

	string currentFile = "";
	string contextNode = "/";
	string xpath = xfcd->xpath->object;

	// Get the matching files 
	sVector files;
	try {
		if(xfcd->path->type == pattern_match) {
			files = this->GetFiles(xfcd->path->object);

			//	Add a record indicating if the pattern was successfull
			AbsXmlFileContentData *tmp = this->GetDataObj();
			tmp->path->object = xfcd->path->object;
			tmp->path->type = xfcd->path->type;
			tmp->xpath->object = xfcd->xpath->object;
			tmp->isPatternMatchObject = xfcd->isPatternMatchObject;
			if(files.size() == 0) {
				tmp->SetStatus(doesNotExist);
			} else {
				tmp->SetStatus(exists);
			}
			results.push_back(tmp);
			tmp = NULL;
			
		} else {
			files.push_back(xfcd->path->object);
		}
	} catch(Exception theException) {
		AbsXmlFileContentData *tmpXFCD = this->GetDataObj();
		tmpXFCD->path->object = currentFile;
		tmpXFCD->path->type = literal;
		tmpXFCD->xpath->object = xpath;
		tmpXFCD->xpath->type = literal;
		tmpXFCD->valueOf->dataType = stringType;
		tmpXFCD->SetMessage(theException.GetErrorMessage());
		tmpXFCD->SetStatus(error);
		results.push_back(tmpXFCD);

    } catch(...) {
		AbsXmlFileContentData *tmpXFCD = this->GetDataObj();
		tmpXFCD->path->object = currentFile;
		tmpXFCD->path->type = literal;
		tmpXFCD->xpath->object = xpath;
		tmpXFCD->xpath->type = literal;
		tmpXFCD->SetMessage("Error: AbsXmlFileContentProbe() An unknown error occured while collecting data.");
		tmpXFCD->SetStatus(error);
		results.push_back(tmpXFCD);
	}

	if(files.size() == 0) {
		// no files were found report and stop
		AbsXmlFileContentData *tmpXFCD = this->GetDataObj();
		tmpXFCD->path->object = xfcd->path->object;
		tmpXFCD->path->type = xfcd->path->type;
		tmpXFCD->xpath->object = xfcd->xpath->object;
		tmpXFCD->xpath->type = xfcd->xpath->type;
		tmpXFCD->SetStatus(doesNotExist);
		results.push_back(tmpXFCD);

	} else {

		XALAN_USING_XALAN(XSLException)

		//XALAN_USING_XERCES(XMLPlatformUtils)
		XALAN_USING_XALAN(XPathEvaluator)
		XMLPlatformUtils::Initialize();
		XPathEvaluator::initialize();

		XALAN_USING_XERCES(LocalFileInputSource)
		XALAN_USING_XALAN(XalanDocument)
		XALAN_USING_XALAN(XalanDocumentPrefixResolver)
		XALAN_USING_XALAN(XalanDOMString)
		XALAN_USING_XALAN(XalanNode)
		XALAN_USING_XALAN(XalanSourceTreeInit)
		XALAN_USING_XALAN(XalanSourceTreeDOMSupport)
		XALAN_USING_XALAN(XalanSourceTreeParserLiaison)
		XALAN_USING_XALAN(XObjectPtr)

		// Initialize the XalanSourceTree subsystem...
		XalanSourceTreeInit	theSourceTreeInit;

		// We'll use these to parse the XML file.
		XalanSourceTreeDOMSupport theDOMSupport;
		XalanSourceTreeParserLiaison theLiaison(theDOMSupport);

		// Hook the two together...
		theDOMSupport.setParserLiaison(&theLiaison);

		// Loop through all the files in the files vector
		sVector::iterator fileIterator;
		for (fileIterator=files.begin(); fileIterator!=files.end(); fileIterator++) {
			
			AbsXmlFileContentData *resultXFCD = this->GetDataObj();

			// make sure that an error on one file does not stop processing of other files
			// and report errors on individual files
			try {
				string currentFile = (*fileIterator);

				// Start creating the result object for the test.
				resultXFCD->path->object = currentFile;
				resultXFCD->path->type = literal;
				resultXFCD->xpath->object = xpath;
				resultXFCD->xpath->type = literal;

				const XalanDOMString theFileName(currentFile.c_str());

				// Create an input source that represents a local file...
				const LocalFileInputSource theInputSource(theFileName.c_str());

				// Parse the document...
				XalanDocument* theDocument;
				try {
					theDocument = theLiaison.parseXMLStream(theInputSource);

				} catch(...) {
					resultXFCD->SetStatus(doesNotExist);
					resultXFCD->SetMessage("Error: The specified document does not exist: " + currentFile);
					results.push_back(resultXFCD);
					continue;
				}

				if(theDocument == NULL) {
					resultXFCD->SetStatus(error);
					resultXFCD->SetMessage("Error: Unable to parse the current document: " + currentFile);
					results.push_back(resultXFCD);
					continue;
				}

				XalanDocumentPrefixResolver	thePrefixResolver(theDocument);

				XPathEvaluator theEvaluator;

				// OK, let's find the context node...
				XalanNode* const theContextNode =
						theEvaluator.selectSingleNode(	theDOMSupport,
														theDocument,
														XalanDOMString(contextNode.c_str()).c_str(),
														thePrefixResolver);

				if (theContextNode == 0) {

					// no nodes were found report and continue
					resultXFCD->SetStatus(error);
					resultXFCD->SetMessage("Error the specified context node, \'" + contextNode + "\' was not found.");

				} else {

					// OK, let's evaluate the expression...
					const XObjectPtr theResult = XObjectPtr(
					theEvaluator.evaluate(
							theDOMSupport,
							theContextNode,
							XalanDOMString(xpath.c_str()).c_str(),
							thePrefixResolver));

					if(theResult.null() == false) {
						// no nodes were found report and continue
						resultXFCD->SetStatus(doesNotExist);
						resultXFCD->SetMessage("The specified xpath expression did not match any nodes in the current document.");

					} else {

						// Convert the result value to a string
						ostringstream r;
						r << theResult->str();
						string value = r.str();

						// Push the results onto the result vector
						resultXFCD->valueOf->dataType = stringType;
						resultXFCD->valueOf->value = value;

					}
				}
			} catch(const XSLException& theException) {
				// Convert the XSLException message to a string
				ostringstream m;
				m << (theException.getMessage());
				string errMsg = m.str();

				resultXFCD->SetMessage(errMsg);
				resultXFCD->SetStatus(error);

			} catch(ProbeException theException) {

				resultXFCD->SetMessage(theException.GetErrorMessage());

			} catch(Exception theException) {

				resultXFCD->SetMessage(theException.GetErrorMessage());
				resultXFCD->SetStatus(error);

			} catch(...) {
				resultXFCD->SetMessage("Error: AbsXmlFileContentProbe() An unknown error occured while collecting data.");
				resultXFCD->SetStatus(error);
			}

			// Add the result obj
			results.push_back(resultXFCD);
		}

		XPathEvaluator::terminate();
		XMLPlatformUtils::Terminate();
	}

	return results;
}
