/*
* Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  Filters an XML-report generated by HeaderAnalyser or OrdinalChecker,
*                using an XML-file with statuses of known issues.
*
*/


#include <bcfilter.h>
#include "xmlprocessor.hpp"
#include "xmldomainmap.hpp"
#include "xmlsaxparser.hpp"
#include "xmlnode.hpp"
#include "xmlsaxhandler.hpp"
#include "xmlsaxerrorhandler.hpp"


#include <vector>
//#ifdef __WINDOWS__
#if defined(_WIN32) || defined(WIN32)
#include <io.h>
#include <windows.h>
#else
 #include <unistd.h>
#include <glob.h>
#include <fcntl.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <unistd.h>
#define _MAX_PATH MAXPATHLEN
#endif

#define HEADER_ISSUE_LIM1T 100
#define LIBRARY_ISSUE_LIMIT 1000

#if defined(_WIN32) || defined(WIN32)
  #define SLEEP_IN_MILLISECONDS 5
#endif

using namespace std;
//using namespace XML;
// Let's get the ugly stuff out of the way first
typedef XMLSAXHandler<XMLDomainMap<XMLNode> > DOCHANDLER;
typedef XMLSAXErrorHandler ERRORHANDLER;
typedef XMLSAXParser<DOCHANDLER, ERRORHANDLER> PARSER;
typedef XMLProcessor<PARSER> XMLEngine;

// ----------------------------------------------------------------------------
// ReportFilterException::ReportFilterException
// ----------------------------------------------------------------------------
//
ReportFilterException::ReportFilterException(string aMessage)
{
    iErrorMessage = aMessage;
}

// ----------------------------------------------------------------------------
// ReportFilterException::errorMessage
// ----------------------------------------------------------------------------
//
string ReportFilterException::errorMessage()
{
    return iErrorMessage;
}

bool fileExists( const string& aPath )
{    
    if (!aPath.empty())
    {
        struct stat stat_p;
        if (stat(aPath.c_str(), &stat_p) == 0)
            return !S_ISDIR(stat_p.st_mode);  // return true if not a directory
        else
            return false;  // cannot find entry
    }
    else
        return false;
}

bool directoryExists(const string& aPath)
{
    if (!aPath.empty())
    {
        struct stat stat_p;
        if (stat(aPath.c_str(), &stat_p) == 0)
            return S_ISDIR(stat_p.st_mode);  // return true if a directory
        else
            return false;  // cannot find entry
    }
    else
        return false;
}

void mkdirAll(const string& aPath)
{
    if (!aPath.empty())
    {
        string target_path = aPath;
        
        // loop through each character in the string and try to find directory delimeters
        for (unsigned int i=0; i<target_path.length(); i++)
        {
            string::size_type pos = target_path.find(DIR_SEPARATOR, i);
           
            if (pos != string::npos)
            {
                // construct the base directory name
                string base_directory = target_path.substr(0, pos+1);
               
                if (!directoryExists(base_directory))
                #ifdef __WINDOWS__
                    mkdir(base_directory.c_str());
                #else
                    mkdir(base_directory.c_str(), ACCESSPERMS);
                #endif
                i=pos;           
            }  
        }
    }
}
#ifdef __UNIX__

// ----------------------------------------------------------------------------
// splitString 
// Split string
// ----------------------------------------------------------------------------
//
vector<string> splitString(const string& str, char separator)
{
    vector<string> ret;
    string::size_type pos = str.find(separator);
    unsigned int lastpos = 0;
    while(pos != string::npos)
    {
        ret.push_back(str.substr(lastpos, pos - lastpos));
        lastpos = (unsigned int)pos + 1;
        pos = str.find(separator, lastpos);
    }
    if (!str.empty())
    {
        ret.push_back(str.substr(lastpos, pos));
    }
    return ret;
}

// ----------------------------------------------------------------------------
// StripPath
// Strip off path
// ----------------------------------------------------------------------------
//
string StripPath(const string& aFilename) 
{
    size_t pos = aFilename.find_last_of("\\/");

    if (pos != string::npos) 
    {
        return string(aFilename.begin()+pos+1, aFilename.end());
    }
    return aFilename;
}

// ----------------------------------------------------------------------------
// absolutePath
// Get full path
// ----------------------------------------------------------------------------
//
bool absolutePath(const string& aRelpath, string& aAbspath)
{
    char* base = new char[_MAX_PATH+1];
    char* home;
    getcwd(base, _MAX_PATH);
    home = getenv("HOME");
    aAbspath = base;
    if (aRelpath.length() == 0)
    {
        aAbspath = "";
        return false;
    }
    vector<string> splittedabspath = splitString(aAbspath, '/');
    vector<string> splittedrelpath = splitString(aRelpath, '/');
    vector<string> splittedhomepath = splitString(home, '/');
    int longest;
    if (splittedabspath.size() > splittedhomepath.size())
    {
        longest = splittedabspath.size();
    } else
    {
        longest = splittedhomepath.size();
    }
    splittedabspath.reserve(longest + splittedrelpath.size());
    if (splittedrelpath.at(0) == "")
    {
        splittedabspath.clear();
        if (splittedrelpath.at(1) == "")
        {
            splittedabspath.push_back("");
            splittedabspath.push_back("");
        }
    }
    if (splittedrelpath.at(0) == "~")
    {
        splittedabspath = splittedhomepath;
        splittedrelpath.erase(splittedrelpath.begin());
    }
    vector<string>::iterator dir = splittedrelpath.begin();
    vector<string>::iterator dirend = splittedrelpath.end();
    for(; dir != dirend; dir++)
    {
        string dirstr = *dir;
        if (dirstr == "")
        {
            // Do nothing
        } else if (dirstr == ".")
        {
            // Do nothing
        } else if (dirstr == "..")
        {
            if (splittedabspath.size() > 1)
            {
                splittedabspath.pop_back();
            }
        } else
        {
            if (dirstr.find_first_not_of('.') == string::npos)
            {
              throw ReportFilterException("Illegal directory: '" + dirstr + "'\n");
            }
            splittedabspath.push_back(dirstr);
        }
    }
    dir = splittedabspath.begin();
    dirend = splittedabspath.end();
    if (dir != dirend && *dir == "")
    {
        dir++;
    }
    aAbspath = "";
    for(; dir != dirend; dir++)
    {
        aAbspath += '/';
        aAbspath += *dir;
    }
    return true;
}
#endif

// ----------------------------------------------------------------------------
// setInterval
// sets the interval and round off value to dispaly percentage of progress
// ----------------------------------------------------------------------------

void setInterval(int atot,int* ainterval)
{
	if (atot < 5)
	{
		ainterval[0] = atot/1;
		ainterval[1] = 100;
	}
	else if (atot < 20)
	{
		ainterval[0] = atot/2;
		ainterval[1] = 50;
	}
	else if (atot < 100)
	{
		ainterval[0] = atot/4;
		ainterval[1] = 25;
	}
	else if (atot < 300)
	{
		ainterval[0] = atot/5;
		ainterval[1] = 20;
	}
	else
	{
		ainterval[0] = atot/10; 
		ainterval[1] = 10;
	}        
}

// ----------------------------------------------------------------------------
// getFullPath
// Check if the given path is valid directory
// ----------------------------------------------------------------------------

string getFullPath(const string& aPath)
{
    string ret;

#ifdef __WINDOWS__
    char temppath[_MAX_PATH+2];
    char * filepart = NULL;
    GetFullPathName(aPath.c_str(), _MAX_PATH, temppath, &filepart);
    ret = temppath;
#else
    string path = aPath;
    string filename;
    struct stat statinfo;

    if ( 0 != stat(aPath.c_str(),&statinfo) )
    {
    filename = StripPath(aPath.c_str());
    path = aPath.substr(0,aPath.size()-filename.size());
    }

    if (path.size() == 0)
    {
        path = ".";
    }

    if ( !absolutePath(path,ret) )
    {
        throw ReportFilterException("Internal error: Error parsing path.");
    }

    if (filename.size())
    {
        ret += DIR_SEPARATOR;
        ret += filename;
    }
#endif
    
    return ret;
}

int main(int argc, char *argv[]) 
{
	XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* iIssuesDoc = NULL;
	XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* iReportDoc = NULL;

	int headerissueCount = 0;
	int libissueCount = 0;

	bool lSAXParser = false;
    
    if ( XmlTools::initialiseDOM() == -1 )
        return 0;
        
    if ( !(argc == 3 | argc == 4) )
    {
        cout << "BCFilter v1.3.1 - 1st December 2009\n"
                "Copyright (c) 2001-2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.\n"
                "\n"
                "Usage: bcfilter reportfile issuefile [outputfile]\n"
                "\n"
                "Where:\n"
                "  reportfile               XML report generated by HeaderAnalyser/OrdinalChecker\n"
                "  issuefile                XML file containing known issues\n"
                "  outputfile               XML report filtered by bcfilter\n"
                "\n";
    
        return 0;
    }

    bool fileFound = false;
    string err;

	string reportFile = getFullPath(string(argv[1]));
	string issueFile = getFullPath(string(argv[2]));
	string outputFile = reportFile ;

	fileFound = fileExists(reportFile);
	if (!fileFound) 
	{
		err = "Report file not found\n";
	}

	fileFound = fileExists(issueFile);
	if (!fileFound)
	{
		err = err + "Issue file not found\n";
	}

	if ( err.size() >0) 
	{
		cout << err;
		return -1; 
	}
	if( argc == 4 )
	{
		string s;	
		unsigned int i = 0;
		outputFile = getFullPath(string(argv[3]));

		//check if the path exists. if not mkdir structure.
		mkdirAll(outputFile);

		//check if output file name specified. if not use the input file name.
		i = (unsigned int)outputFile.rfind("\\");
		s.assign(outputFile,i+1,outputFile.length());
		if( s.empty())
		{
			i = (unsigned int)reportFile.rfind("\\");
			s.assign(reportFile,i+1,reportFile.length());
			outputFile = outputFile + s ;
		}
	}

	// read report document
	iReportDoc = XmlTools::readFile( reportFile.c_str() );
	if ( iReportDoc == NULL )
	{
		return 0;
	}
    
    // get baseline and current directories
    XmlTools::getReportDirs( iReportDoc );
    
    // update report with status + comment for known header file issues
    DOMNodeList* reportHeaderList = iReportDoc->getElementsByTagName( _X("headerfile") );

	DOMNodeList* reportLibraryList = iReportDoc->getElementsByTagName( _X("library") );

	if(reportHeaderList != NULL)
		headerissueCount = reportHeaderList->getLength();
	if(reportLibraryList != NULL)
		libissueCount = reportLibraryList->getLength();
	if(libissueCount>0)
		libissueCount = iReportDoc->getElementsByTagName(_X("issue"))->getLength();

	if (libissueCount != 0)
		cout << "Total Issues: " << libissueCount << endl ;
	else
		cout << "Total Issues: " << headerissueCount << endl ;
		
	// If issue count is 0, then stop execution.
	if ( headerissueCount == 0 && libissueCount == 0 )
	{
		cout << "No Issues to filter." << endl ;
		XmlTools::writeFile( iReportDoc, outputFile.c_str() );
		XmlTools::uninitialiseDOM();
		return 0;
	}

	if(headerissueCount > HEADER_ISSUE_LIM1T  || libissueCount > LIBRARY_ISSUE_LIMIT  )
		lSAXParser = true; // Go with SAX Parser , else DOM for known issue file parsing.

	if(lSAXParser == true)
	{
		XmlTools::parseFilesWithSAX(iReportDoc,issueFile);
	} //END OF SAX PARSER

	else //WITH DOM PARSER
	{
		// read issues document
		cout<<"Parsing known issues file... ";
		iIssuesDoc = XmlTools::readFile( issueFile.c_str() );
		cout<<"Done"<<endl;
		if ( iIssuesDoc == NULL )
		{
			return 0;
		}

		XmlTools::parseFilesWithDOM(iReportDoc,iIssuesDoc);

	}//END OF DOM PARSER


	XmlTools::writeFile( iReportDoc, outputFile.c_str() );
	XmlTools::uninitialiseDOM();
return 0;
}

int XmlTools::getHeaderAttributes( const DOMElement* aHeaderNode, char* aFilename, char* aChecksum )
{
	char filename[1024];
	char checksum[14];
	char *directory;

	// get filename; first try comparefilename, then filename
	if ( getTagValue( aHeaderNode, "comparefilename", filename, 1, 1023 ) == 0 )
	{
		directory = iCurrentDir;
	}
	else
	{
		if ( getTagValue( aHeaderNode, "filename", filename, 1, 1023 ) != 0 )
			return -2;
		directory = iBaselineDir;
	}

	// change filename to lower case
	for ( int i = 0; filename[i] != '\0'; ++i )
		if ( filename[i] >= 'A' && filename[i] <= 'Z' )
			filename[i] += 'a' - 'A';

	// remove baseline dir part of the filename
	int pos;
	for ( pos = 0; filename[pos] == directory[pos] || filename[pos] == directory[pos] + 'a' - 'A' ||
		( filename[pos] == '\\' || filename[pos] == '/' ) &&
		( directory[pos] == '\\' || directory[pos] == '/' ); ++pos ) ;

	while ( filename[pos] == '\\' || filename[pos] == '/' )
		++pos;
	if ( strlen(filename) - pos > 255 )
		return -2;
	strcpy( aFilename, &filename[pos] );

	// get checksum
	if ( getTagValue( aHeaderNode, "checksum", checksum, 13, 13 ) != 0 )
	{
		return -1;
	}

	if ( strcmp( checksum, "*************" ) == 0 )
		return -1;

	strcpy( aChecksum, checksum );

	return 0;
}

int XmlTools::getTagValue( const DOMElement* aParentNode, const char* aTagName, char* aTagValue, int minLen, int maxLen )
{
    DOMNodeList* list = aParentNode->getElementsByTagName( _X( aTagName ) );

    strcpy( aTagValue, "" );
    if ( list == NULL || list->getLength() == 0 )
        return -1;
    else
    {
        DOMNode* textNode = list->item( 0 )->getFirstChild();
        if ( textNode == NULL )
            return -1;
            
        char* text = _X( textNode->getNodeValue() );
        if ( text == NULL )
            return -1;
            
		int len =(int) strlen( text );
        if ( len < minLen || len > maxLen )
            return -1;
            
        strcpy( aTagValue, text );
        _XX( text );
        
        return 0;
    }
}


DOMElement* XmlTools::getTagNode( const DOMElement* aParentNode, const char* aTagName )
{
    DOMNodeList* list = aParentNode->getElementsByTagName( _X( aTagName ) );
    if ( list == NULL || list->getLength() == 0 )
        return NULL;
    else
        return (DOMElement*) list->item( 0 );
}


XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* XmlTools::getIssueNode( const XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* aIssuesDoc, const char* aFilename, const char* aChecksum, bool &exactMatch )
{
	XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* versionNode;
	XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* returnVersionNode;
	unsigned long i, j,k;
	char* headerVersion = NULL;
	int issueCheckSumLength;
	int misMatchedPos = 13;
	DOMNodeList* issueHeaderList = aIssuesDoc->getElementsByTagName( _X("headerfile") );

	for ( i = 0; i < issueHeaderList->getLength(); ++i )
	{
		XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* issueHeader = (DOMElement*) issueHeaderList->item( i );
		if ( issueHeader == NULL )
			continue;
		char* issueFilename = _X( issueHeader->getAttribute( _X( "name" ) ) );
		if ( stricmp( issueFilename, aFilename ) == 0 )
		{
			DOMNodeList* versionList = issueHeader->getElementsByTagName( _X( "version" ) );
			for ( j = 0; j < versionList->getLength(); ++j )
			{
				versionNode = (DOMElement*) versionList->item( j );
				headerVersion = _X( versionNode->getAttribute( _X( "checksum" ) ) );
				issueCheckSumLength = (int)strlen(headerVersion);

				if(issueCheckSumLength == strlen(aChecksum))
				{
					for ( k = issueCheckSumLength-1; k >= 0&&
						( headerVersion[k] == aChecksum[k] || headerVersion[k] == '*' ); --k ) ;
						_XX( headerVersion );
					if ( k == -1 )
						break;
					else
					{
						if( k < misMatchedPos )
						{
							misMatchedPos=k;
							returnVersionNode = versionNode;	
						}
					}
				}
			}

			if ( j < versionList->getLength() /* || checksumMatched */ )
			{
				_XX( issueFilename );
				exactMatch=true;
				return versionNode;
			}
		}	
		_XX( issueFilename );
	}
	
	if( misMatchedPos < 7 )
	{
		exactMatch=false;
		return returnVersionNode;
	}
		
	return NULL;
}

DOMElement* XmlTools::getIssueNode( const XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* aIssuesDoc, const char* aLibname, const DOMElement* aReportIssueNode )
{
    DOMElement* knownIssueNode;
    unsigned long i, j, k;
    char kval[256], rval[5][256];
    char attribs[5][16] = { "typeid", "funcname", "funcpos", "newfuncname", "newfuncpos" };
    
    DOMNodeList* libraryList = aIssuesDoc->getElementsByTagName( _X("library") );
    
    for ( i = 0; i < libraryList->getLength(); ++i )
    {
        DOMElement* libraryName = (DOMElement*) libraryList->item( i );
        if ( libraryName == NULL )
            continue;
        char* issueLibname = _X( libraryName->getAttribute( _X( "name" ) ) );
        if ( stricmp( issueLibname, aLibname ) == 0 )
        {
            for ( k = 0; k < 5; ++ k )
                getTagValue( aReportIssueNode, attribs[k], rval[k], 1, 255 );
                
            DOMNodeList* issueList = libraryName->getElementsByTagName( _X( "issue" ) );
            for ( j = 0; j < issueList->getLength(); ++j )
            {
                knownIssueNode = (DOMElement*) issueList->item( j );
                
                for ( k = 0; k < 5; ++ k )
                {
                    getTagValue( knownIssueNode, attribs[k], kval, 1, 255 );
                    if ( strcmp( kval, rval[k] ) != 0 && ( kval[0] != '*' ) )
                        break;
                }
                
                // all attributes match, so ok
                if ( k == 5 )
                {
                    _XX( issueLibname );
                    return knownIssueNode;
                }
            }
            _XX( issueLibname );
            return NULL;
        }
        _XX( issueLibname );
    }
    return NULL;
}

int XmlTools::initialiseDOM()
{
    try
    {
        XMLPlatformUtils::Initialize();
    } catch (const XMLException& e )
    {
        char* message = _X( e.getMessage() );
        cout << "Error during initialization! :\n" << message << endl ;
        _XX(message);
		    return -1;
    }

    DOMImplementation* impl = DOMImplementationRegistry::getDOMImplementation( _X("Core") );
    iParser = ((DOMImplementationLS*)impl)->createDOMBuilder( DOMImplementationLS::MODE_SYNCHRONOUS, 0 );
    if ( iParser->canSetFeature( XMLUni::fgDOMValidation, true ) )
        iParser->setFeature( XMLUni::fgDOMValidation, true );
    if ( iParser->canSetFeature( XMLUni::fgDOMNamespaces, true ) )
        iParser->setFeature( XMLUni::fgDOMNamespaces, true );
    if ( iParser->canSetFeature( XMLUni::fgDOMDatatypeNormalization, true ) )
        iParser->setFeature( XMLUni::fgDOMDatatypeNormalization, true );
        
    return 0;
}

void XmlTools::uninitialiseDOM()
{
    iParser->release();
    XMLPlatformUtils::Terminate();
}

XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* XmlTools::readFile( const char* aFilename )
{
    XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* doc;
    DOMErrorHandler* myErrorHandler;

    fstream ifile;
    bool validate = false;
    //char filedoctype[10];
    string filedoctype;
    //char doctype[] = "<!DOCTYPE";
    string doctype("<!DOCTYPE");
    
    //check if a document type definition is provided for the xml file
    ifile.open(aFilename);  
    ifile >> filedoctype ;
    ifile.close();
    
     //  if( strcmp(doctype,filedoctype)== 0 )
    if(doctype.compare(filedoctype) == 0)
        validate = true;
        
    //if document type definition present,create and set error handler to parser, to report any parse errors in issues document.
    if(validate)
    {
        myErrorHandler = new DOMPrintErrorHandler();
        XmlTools::iParser->setErrorHandler(myErrorHandler);	
    }
    
    try
    {
        doc = iParser->parseURI( aFilename );
    }
    catch ( const XMLException& e )
    {
        char* message = _X( e.getMessage() );
        cout << "Failed to read " << aFilename << ". \"" << message << "\"" << endl ;
        _XX( message );
        return NULL;
    }
    catch ( const DOMException& e )
    {
        char* message = _X( e.getMessage() );
        cout << "Failed to read " << aFilename << ". \"" << message << "\"" << endl ;
        _XX( message );
        return NULL;
    }
    catch (...) 
    {
        cout << "Failed to read " << aFilename << ". Unexpected error." << endl ;
    return NULL;
    }
    
    //delete the error handler, if set
    if(validate)
        delete myErrorHandler;
        
    return doc;
}

int XmlTools::writeFile( const XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* aDocument, const char* aFilename )
{
    DOMImplementation* impl =  DOMImplementationRegistry::getDOMImplementation( _X("Core") );

    DOMWriter* writer = ( (DOMImplementationLS*) impl )->createDOMWriter();
    writer->setEncoding( _X("ASCII") );
    writer->setFeature( XMLUni::fgDOMWRTFormatPrettyPrint, true );
    XMLFormatTarget* ft;
    try 
    {
	       ft = new LocalFileFormatTarget( _X( aFilename ) ); 
    }
  	catch(...)
    {
  	    cout << "Failed to write " << aFilename << ". Directory File name conflict." << endl ;
		    return -1;
    }
    writer->writeNode( ft, *aDocument );
    return 0;
}

void XmlTools::getReportDirs( const XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* aReport )
{
    char paramName[256];
    DOMNodeList* paramList = aReport->getElementsByTagName( _X("parm") );
    for ( unsigned long i = 0; i < paramList->getLength(); ++i )
    {
        DOMElement* param = (DOMElement*) paramList->item( i );
        if ( param == NULL )
            continue;
            
        if ( getTagValue( param, "pname", paramName, 1, 255 ) == 0 )
        {
            if ( strcmp( paramName, "baselinedir" ) == 0 )
            {
                if ( getTagValue( param, "pvalue", iBaselineDir, 1, 1024 ) != 0 )
                    strcpy( iBaselineDir, "" );
            }
            else if ( strcmp( paramName, "currentdir" ) == 0 )
            {
                if ( getTagValue( param, "pvalue", iCurrentDir, 1, 1024 ) != 0 )
                    strcpy( iCurrentDir, "" );
            }
        }
    }
}

void XmlTools::parseFilesWithSAX(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* aReportDoc,const string& aissuefile)
{
	char filename[256];
	char checksum[14];
	char libname[256];

	int headerissueCount = 0;
	int libissueCount = 0;

	int next;
	int perct;
	int rem;
	int interval[2];

	char osver[16] = {0};
	char bldweek[32] = {0};
	DOMNodeList* rChild;
	bool exists = false;
	DOMElement* osVersionNode;
	DOMElement* buildWeekNode;
	DOMElement* osNode;

	int headIndx = -1;
	int libIndx = -1;
	int osIndx = -1;

	DOMNodeList* reportHeaderList = aReportDoc->getElementsByTagName( _X("headerfile") );

	DOMNodeList* reportLibraryList = aReportDoc->getElementsByTagName( _X("library") );

	DOMNodeList* osList = aReportDoc->getElementsByTagName( _X("os") );

	if(reportHeaderList != NULL)
		headerissueCount = reportHeaderList->getLength();
	if(reportLibraryList != NULL)
		libissueCount = reportLibraryList->getLength();
	if(libissueCount>0)
		libissueCount = aReportDoc->getElementsByTagName(_X("issue"))->getLength();

	//With SAX parser , parse the known issue doc
	// Create an instance of our XMLFactory
	XMLEngine parser(aissuefile);
	// Turn off validation
	parser.doValidation(false);

	// Parse file
	cout << "Parsing known issues file... " ;
	// Parse our XML file
	parser.parse();
	cout << "Done" <<endl;
	// Get the tree root
	XMLNode root = parser.getRootElement();

	int totKnownIssueHeaders = (int)root.childCount(); // FisrtOne is the list version.	

	// add version with which file has been filtered
	string version("Corrupted");
	DOMElement* headNode = NULL;
	DOMNodeList* headList = aReportDoc->getElementsByTagName( _X("header") );
	if ( headList != NULL && headList->getLength() >= 1 )
		headNode = (DOMElement*) headList->item( 0 );
	string ws( root.child("listversion").value() );
	if(ws.length()>0)
	{
		version.erase();
		version.append(ws);
	}
	if ( headNode != NULL )
	{
		DOMElement* issuesVersionNode;
		if ( ( issuesVersionNode = XmlTools::getTagNode( headNode, "knownissuesversion" ) ) == NULL )
		{
			issuesVersionNode = aReportDoc->createElement( _X( "knownissuesversion" ) );
			issuesVersionNode->appendChild( aReportDoc->createTextNode( _X(version.data()) ) );
			headNode->appendChild( issuesVersionNode );
		}
		else
		{
			issuesVersionNode->setTextContent( _X(version.data()) );
		}
	}

	//Parse Header file issues
	XMLNode iheadNorode = root.child("headerfile",headIndx);
	if (reportHeaderList->getLength() >0 && headIndx>=0)
	{
		setInterval(headerissueCount,interval); 
		next = interval[0] ;

		// update report with status + comment for known header file issues
		for (unsigned long i = 0; i < reportHeaderList->getLength(); ++i )
		{
			DOMElement* reportHeader = (DOMElement*) reportHeaderList->item( i );
			if ( reportHeader == NULL )
				continue;

			int foundChecksum = XmlTools::getHeaderAttributes( reportHeader, filename, checksum );
			if ( foundChecksum < -1 )
				continue;

			// if checksum not valid, we're done
			if ( foundChecksum != 0 )
			{
				continue;
			}

			DOMElement* shortnameNode = XmlTools::getTagNode( reportHeader, "shortname"  );

			if(shortnameNode == NULL)
			{
				shortnameNode = aReportDoc->createElement( _X( "shortname" ) );
				shortnameNode->appendChild( aReportDoc->createTextNode(_X( filename)) );
				reportHeader->appendChild(shortnameNode);
			}

			bool checksumMatched = false;
			int misMatchedPos = 13;
			string comment;
			string issFilename;
			for ( int issueIter = headIndx; issueIter < totKnownIssueHeaders; ++issueIter)
			{			
				if(strcmp (root.children_[issueIter].name().c_str(), "headerfile") !=0)
				{								
					break;
				}

				issFilename = root.children_[issueIter].value();
				string rfilename(filename);

				if(issFilename.compare(rfilename) == 0)
				{
					int checksumCount = (int)root.children_[issueIter].childCount();
					string iChechsumString;
					XMLNode versionList = root.children_[issueIter];
					for(int innerIter = 0; innerIter < checksumCount; innerIter++)
					{
						iChechsumString = versionList.children_[innerIter].value();
						const char* issueChechsum = iChechsumString.c_str();							

						int length, k;
						length = (int)iChechsumString.length();
						if( length == strlen(checksum) )
						{
							for ( k = length-1; k >= 0 &&
								( issueChechsum[k] == checksum[k] || issueChechsum[k] == '*' ); --k ) ;

							if( k == -1 )
							{
								XMLNode version = versionList.children_[innerIter];

								DOMElement* statusNode = XmlTools::getTagNode( reportHeader, "status"  );
								DOMElement* commentNode = XmlTools::getTagNode( reportHeader, "comment" );
								if ( statusNode != NULL )
									statusNode->setTextContent( _X(version.children_[0].value().data()) );
								if ( commentNode != NULL )
									commentNode->setTextContent( _X(version.children_[1].value().data()) );
								checksumMatched = true;
								break;
							}
							else
							{
								if( k < misMatchedPos )
								{
									misMatchedPos=k;
									XMLNode version = versionList.children_[innerIter];	
									comment = version.children_[1].value().data();	
								}
							}
						}
					}
					if(checksumMatched)
						break; //as chechsum of header file matched, no need to loop through rest of the  issue files.
				}	
			}//loop thru know issue header files
			if( !checksumMatched && misMatchedPos < 7 )
			{
				DOMElement* statusNode = XmlTools::getTagNode( reportHeader, "status"  );
				DOMElement* commentNode = XmlTools::getTagNode( reportHeader, "comment" );
				if ( statusNode != NULL )
					statusNode->setTextContent( _X("IOK") );
				if ( commentNode != NULL )
					commentNode->setTextContent( _X(comment.c_str()) );
			}
			
			if( (i+1)==next )
			{
				rem=0;
				perct = ((i+1)*100)/headerissueCount; 

				//round off the percentage
				rem = perct%interval[1];
				if(rem)
					perct = perct - rem + interval[1] ;

				cout << perct << "% complete" << endl ;
				next = next + interval[0];
				
				//Interrupt the parsing for 5 milliSeconds, so that any external cancel or so works out here.
        #if defined(_WIN32) || defined(WIN32)
				      Sleep(SLEEP_IN_MILLISECONDS );
        #endif
			}

		}//end of reportlist

	} //end of if for header issue()

	//Parse library issues
	XMLNode ilibNorode = root.child("library",libIndx);
	if(reportLibraryList->getLength() > 0 && libIndx >= 0)
	{
		int tot = reportLibraryList->getLength();
		setInterval(tot,interval);
		next = interval[0];

		for (unsigned long rlibiter = 0; rlibiter < reportLibraryList->getLength(); ++rlibiter )
		{
			DOMElement* reportLibrary = (DOMElement*) reportLibraryList->item( rlibiter );
			if ( reportLibrary == NULL )
				continue;

			// get name of library
			if ( XmlTools::getTagValue( reportLibrary, "shortname", libname, 1, 255 ) != 0 )
				if ( XmlTools::getTagValue( reportLibrary, "name", libname, 1, 255 ) != 0 )    
					continue;
			string islibname;
			for ( int issueLibiter =libIndx; issueLibiter < totKnownIssueHeaders;issueLibiter++)
			{
				if(strcmp(root.children_[issueLibiter].name().c_str(), "library") != 0)
					break;
				islibname = root.children_[issueLibiter].value();
				string rlibname(libname);
				if(stricmp(islibname.c_str(),rlibname.c_str()) == 0)
				{
					int l;
					char rval[5][256];
					char attribs[5][16] = { "typeid", "funcname", "funcpos", "newfuncname", "newfuncpos" };

					// iterate through all issues in the report
					DOMNodeList* rissueList = reportLibrary->getElementsByTagName( _X("issue") );
					for ( unsigned long j = 0; j < rissueList->getLength(); ++j )
					{
						// get next issue
						DOMElement* rissue = (DOMElement*) rissueList->item( j );
						if ( rissue == NULL )
							continue;

						for ( l = 0; l < 5; ++ l )
							getTagValue( rissue, attribs[l], rval[l], 1, 255 );


						XMLNode liblist = root.children_[issueLibiter];
						int totissue = (int)liblist.childCount();
						for(int k=0;k<totissue;k++)
						{

							for ( l = 0; l < 5; ++ l )
							{
								//getTagValue( knownIssueNode, attribs[k], kval, 1, 255 );
								string itp = liblist.children_[k].child(attribs[l]).value();
								if((itp.compare(rval[l])!= 0)&& (itp != "*"))
									break;
							}							
							if(l == 5)
							{
								// ...and if it is, update its status in the report
								DOMElement* statusNode = XmlTools::getTagNode( rissue, "status"  );

								if ( statusNode != NULL )
									statusNode->setTextContent( _X(liblist.children_[k].child("status").value().data() ));
								else
								{
									DOMElement* issueStatusNode = aReportDoc->createElement( _X( "status" ) );
									issueStatusNode->appendChild( aReportDoc->createTextNode( _X(liblist.children_[k].child("status").value().data()) ));
									rissue->appendChild( issueStatusNode );
								}

								DOMElement* commentNode = XmlTools::getTagNode( rissue, "comment" );
								if ( commentNode != NULL )						
									commentNode->setTextContent( _X(liblist.children_[k].child("comment").value().data()));
								else
								{
									DOMElement* issueCommentNode = aReportDoc->createElement( _X( "comment" ) );
									issueCommentNode->appendChild( aReportDoc->createTextNode( _X(liblist.children_[k].child("comment").value().data()) ));
									rissue->appendChild( issueCommentNode );
								} 
								break;
							}

						}// known lib issue list
					}//report library issues							
				}// filename matched
			}// end of knowissue library list 

			////at regular interval:get the percentage of library issues filtered and display the progress info

			if(( rlibiter +1) == next )
			{
				rem=0;
				perct = (rlibiter*100)/tot; 

				//round off the percentage
				rem = perct%interval[1];
				if(rem)
					perct = perct - rem + interval[1] ;

				cout << perct << "% complete" << endl ;
				next = next + interval[0]; 
				
				//Interrupt the parsing for 5 milliSeconds, so that any external cancel or so works out here.
#if defined(_WIN32) || defined(WIN32)
				Sleep(SLEEP_IN_MILLISECONDS );
#endif
			}
		}// report lib list		

	}//end of else if library
	XMLNode iOsNorode = root.child("os",osIndx);
	if(osIndx >= 0)
	{
		//Then get a list of <os>...</os> tags from report
		DOMNodeList* rList = aReportDoc->getElementsByTagName( _X("os") );

		for ( int osIter =osIndx; osIter < totKnownIssueHeaders; osIter++)
		{
			if(strcmp(root.children_[osIter].name().c_str(), "os") != 0)
				break;
			exists = false;
			for(unsigned long j=0; j < rList->getLength(); j++)
			{
				rChild = rList->item(j)->getChildNodes();
				// get the platfrom version from the <os> tag in report file
				// the index is 1, because of the additional 'newline's introduced in to the report
				string rver = _X((rChild->item(1))->getFirstChild()->getNodeValue());

				//if the platform versions are the same only update the buildweek values
				if(rver == root.children_[osIter].child("osversion").value() )
				{
					exists = true;
					string srbld = _X( rChild->item(3)->getFirstChild()->getNodeValue() );
					if ( srbld < (root.children_[osIter].child("buildweek").value()) )
						rChild->item(3)->getFirstChild()->setNodeValue( _X(root.children_[osIter].child("buildweek").value().data()) );

					break;
				}
			}
			// the tag does not exist in report file, create new one and append to file
			if(!exists)
			{
				osVersionNode = aReportDoc->createElement( _X( "version") );
				osVersionNode->appendChild( aReportDoc->createTextNode( _X(root.children_[osIter].child("osversion").value().data()) ));
				buildWeekNode = aReportDoc->createElement( _X( "buildweek" ) );
				buildWeekNode->appendChild( aReportDoc->createTextNode( _X(root.children_[osIter].child("buildweek").value().data()) ));

				//create the complete <os>...</os> node
				osNode = aReportDoc->createElement( _X( "os" ) );
				osNode->appendChild( osVersionNode );
				osNode->appendChild( buildWeekNode );

				//now add the new node to <header>
				headNode->appendChild( osNode );
			}
		}// end of os issuelist

	}//end of "os"
}

void XmlTools::parseFilesWithDOM(XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* aReportDoc,
								 const XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* aIssueDoc)
{
	char filename[256];
	char checksum[14];
	char libname[256];

	int headerissueCount = 0;
	int libissueCount = 0;

	int next;
	int perct;
	int rem;
	int interval[2];

	char osver[16] = {0};
	char bldweek[32] = {0};
	DOMNodeList* iChild;
	DOMNodeList* rChild;
	bool exists = false;
	DOMElement* osVersionNode;
	DOMElement* buildWeekNode;
	DOMElement* osNode;

	DOMNodeList* reportHeaderList = aReportDoc->getElementsByTagName( _X("headerfile") );

	DOMNodeList* reportLibraryList = aReportDoc->getElementsByTagName( _X("library") );

	DOMNodeList* osList = aReportDoc->getElementsByTagName( _X("os") );

	if(reportHeaderList != NULL)
		headerissueCount = reportHeaderList->getLength();
	if(reportLibraryList != NULL)
		libissueCount = reportLibraryList->getLength();
	if(libissueCount>0)
		libissueCount = aReportDoc->getElementsByTagName(_X("issue"))->getLength();

	//if any header issue present, display the total number issues to be filetred
	if( headerissueCount > 0 )
	{
		setInterval(headerissueCount,interval); 
		next = interval[0] ;
	}

	for (unsigned long i = 0; i < reportHeaderList->getLength(); ++i )
	{
		DOMElement* reportHeader = (DOMElement*) reportHeaderList->item( i );
		if ( reportHeader == NULL )
			continue;

		int foundChecksum = XmlTools::getHeaderAttributes( reportHeader, filename, checksum );
		if ( foundChecksum < -1 )
			continue;

		// if checksum not valid, we're done
		if ( foundChecksum != 0 )
		{
			continue;
		}
		DOMElement* shortnameNode = XmlTools::getTagNode( reportHeader, "shortname"  );

		if(shortnameNode == NULL)
		{
			shortnameNode = aReportDoc->createElement( _X( "shortname" ) );
			shortnameNode->appendChild( aReportDoc->createTextNode(_X( filename)) );
			reportHeader->appendChild(shortnameNode);
		}  


		// otherwise, try to find corresponding issue
		DOMElement* statusNode = XmlTools::getTagNode( reportHeader, "status"  );
		DOMElement* commentNode = XmlTools::getTagNode( reportHeader, "comment" );
		bool exactMatch = false;
		DOMElement* issueNode = XmlTools::getIssueNode( aIssueDoc, filename, checksum, exactMatch );

		if ( issueNode != NULL )
		{
			statusNode = XmlTools::getTagNode( reportHeader, "status"  );
			commentNode = XmlTools::getTagNode( reportHeader, "comment" );
			if ( statusNode != NULL )
				if( exactMatch )
					statusNode->setTextContent( XmlTools::getTagNode( issueNode, "status" )->getFirstChild()->getNodeValue() );
				else
					statusNode->setTextContent( _X("IOK") );

			if ( commentNode != NULL )
				commentNode->setTextContent( XmlTools::getTagNode( issueNode, "comment" )->getFirstChild()->getNodeValue() );
		}



		//at regular interval:get the percentage of header issues filtered and display the progress info
		if( (i+1)==next )
		{
			rem=0;
			perct = ((i+1)*100)/headerissueCount; 

			//round off the percentage
			rem = perct%interval[1];
			if(rem)
				perct = perct - rem + interval[1] ;

			cout << perct << "% complete" << endl ;
			next = next + interval[0]; 
			
			//Interrupt the parsing for 5 milliSeconds, so that any external cancel or so works out here.
			#if defined(_WIN32) || defined(WIN32)
			      Sleep(SLEEP_IN_MILLISECONDS );
      #endif
		}
	}

	int libissues = 0;

	//if any library issue present, get and display the total number of library issues
	if( libissueCount > 0 )
	{
		setInterval(libissueCount,interval);
		next = interval[0];
	}

	for (unsigned long i = 0; i < reportLibraryList->getLength(); ++i )
	{
		DOMElement* reportLibrary = (DOMElement*) reportLibraryList->item( i );
		if ( reportLibrary == NULL )
			continue;

		// get name of library
		if ( XmlTools::getTagValue( reportLibrary, "shortname", libname, 1, 255 ) != 0 )	
			if ( XmlTools::getTagValue( reportLibrary, "name", libname, 1, 255 ) != 0 )
				continue;

		// iterate through all issues in the report
		DOMNodeList* issueList = reportLibrary->getElementsByTagName( _X("issue") );
		for ( unsigned long j = 0; j < issueList->getLength(); ++j )
		{
			// get next issue
			DOMElement* issue = (DOMElement*) issueList->item( j );
			if ( issue == NULL )
				continue;

			// check if this is a known issue...
			DOMElement* issueNode = XmlTools::getIssueNode( aIssueDoc, libname, issue );

			// ...and if it is, update its status in the report
			if ( issueNode != NULL )
			{
				DOMElement* statusNode = XmlTools::getTagNode( issue, "status"  );
				if ( statusNode != NULL )
					statusNode->setTextContent( XmlTools::getTagNode( issueNode, "status" )->getFirstChild()->getNodeValue() );
				else
				{
					DOMElement* issueStatusNode = aReportDoc->createElement( _X( "status" ) );
					issueStatusNode->appendChild( aReportDoc->createTextNode( XmlTools::getTagNode( issueNode, "status" )->getFirstChild()->getNodeValue() ) );
					issue->appendChild( issueStatusNode );
				}

				DOMElement* commentNode = XmlTools::getTagNode( issue, "comment" );
				if ( commentNode != NULL )
					commentNode->setTextContent( XmlTools::getTagNode( issueNode, "comment" )->getFirstChild()->getNodeValue() );
				else
				{
					DOMElement* issueCommentNode = aReportDoc->createElement( _X( "comment" ) );
					issueCommentNode->appendChild( aReportDoc->createTextNode( XmlTools::getTagNode( issueNode, "comment" )->getFirstChild()->getNodeValue() ) );
					issue->appendChild( issueCommentNode );
				}
			}

			////at regular interval:get the percentage of library issues filtered and display the progress info
			++libissues ;
			if( libissues == next )
			{
				rem=0;
				perct = (libissues*100)/libissueCount; 

				//round off the percentage
				rem = perct%interval[1];
				if(rem)
					perct = perct - rem + interval[1] ;

				cout << perct << "% complete" << endl ;
				next = next + interval[0];  
				
				//Interrupt the parsing for 5 milliSeconds, so that any external cancel or so works out here.
				#if defined(_WIN32) || defined(WIN32)
   				    Sleep(SLEEP_IN_MILLISECONDS );
        #endif
			}
		}
	}    

	// add version with which file has been filtered
	char version[64];
	DOMElement* headNode = NULL;
	DOMNodeList* headList = aReportDoc->getElementsByTagName( _X("header") );
	if ( headList != NULL && headList->getLength() >= 1 )
		headNode = (DOMElement*) headList->item( 0 );
	strcpy( version, "Corrupted!" );

	DOMNodeList* versionList = aIssueDoc->getElementsByTagName( _X("listversion") );
	if ( versionList != NULL && versionList->getLength() >= 1 )
	{
		char* text = _X( versionList->item( 0 )->getFirstChild()->getNodeValue() );
		strcpy( version, text );
		_XX( text );
	}

	if ( headNode != NULL )
	{
		DOMElement* issuesVersionNode;
		if ( ( issuesVersionNode = XmlTools::getTagNode( headNode, "knownissuesversion" ) ) == NULL )
		{
			issuesVersionNode = aReportDoc->createElement( _X( "knownissuesversion" ) );
			issuesVersionNode->appendChild( aReportDoc->createTextNode( _X( version ) ) );
			headNode->appendChild( issuesVersionNode );
		}
		else
		{
			issuesVersionNode->setTextContent( _X( version ) );
		}
	}

	//The following implementation is for the global os versioning for knownissues document
	//First get a list of <os>...</os> tags from knownissues

	//First get a list of <os>...</os> tags from knownissues
	DOMNodeList* iList = aIssueDoc->getElementsByTagName( _X("os") );
	//Then get a list of <os>...</os> tags from report
	DOMNodeList* rList = aReportDoc->getElementsByTagName( _X("os") );

	for(unsigned long i=0; i < iList->getLength(); i++)
	{
		iChild = iList->item(i)->getChildNodes();
		const XMLCh* iver = (iChild->item(0))->getFirstChild()->getNodeValue();
		exists = false;

		for(unsigned long j=0; j < rList->getLength(); j++)
		{
			rChild = rList->item(j)->getChildNodes();
			// get the platfrom version from the <os> tag in report file
			// the index is 1, because of the additional 'newline's introduced in to the report
			const XMLCh* rver = (rChild->item(1))->getFirstChild()->getNodeValue();
			string siver = _X( iver );
			string srver = _X( rver );

			//if the platform versions are the same only update the buildweek values
			if(siver == srver)
			{
				exists = true;
				// get the buildweek from the <os> tag in report file and knownissues
				// the index is 3, because of the additional 'newline's introduced in to the report
				string srbld = _X( rChild->item(3)->getFirstChild()->getNodeValue() );
				string sibld = _X( iChild->item(1)->getFirstChild()->getNodeValue() );

				//update report file only if knownissues value is greater
				if ( srbld < sibld )
					rChild->item(3)->getFirstChild()->setNodeValue(iChild->item(1)->getFirstChild()->getNodeValue());

				break;
			}
		}

		// the tag does not exist in report file, create new one and append to file
		if( !exists )
		{
			osVersionNode = aReportDoc->createElement( _X( "version") );
			osVersionNode->appendChild( aReportDoc->createTextNode( iver ) );
			const XMLCh* ibld = (iChild->item(1))->getFirstChild()->getNodeValue();
			buildWeekNode = aReportDoc->createElement( _X( "buildweek" ) );
			buildWeekNode->appendChild( aReportDoc->createTextNode( ibld ) );

			//create the complete <os>...</os> node
			osNode = aReportDoc->createElement( _X( "os" ) );
			osNode->appendChild( osVersionNode );
			osNode->appendChild( buildWeekNode );

			//now add the new node to <header>
			headNode->appendChild( osNode );
		}
	}

}

bool DOMPrintErrorHandler::handleError(const DOMError &domError)
{
    // Display whatever error message passed from the parser
    if (domError.getSeverity() == DOMError::DOM_SEVERITY_WARNING)
        XERCES_STD_QUALIFIER cerr << "\nWarning Message: ";
    else if (domError.getSeverity() == DOMError::DOM_SEVERITY_ERROR)
        XERCES_STD_QUALIFIER cerr << "\nError Message: ";
    else
        XERCES_STD_QUALIFIER cerr << "\nFatal Message: ";

    char *msg = XMLString::transcode(domError.getMessage());
    DOMLocator *errorLocator = domError.getLocation();
    XERCES_STD_QUALIFIER cerr<< errorLocator->getLineNumber() << ",";
    XERCES_STD_QUALIFIER cerr<< errorLocator->getColumnNumber() << ": ";
    XERCES_STD_QUALIFIER cerr<< msg <<XERCES_STD_QUALIFIER endl;
    XMLString::release(&msg);

    // Instructs the parser to continue parsing if possible.
    return true;
}
