<commit_before>/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. The end-user documentation included with the redistribution,
*    if any, must include the following acknowledgment:
*       "This product includes software developed by the
*        Apache Software Foundation (http://www.apache.org/)."
*    Alternately, this acknowledgment may appear in the software itself,
*    if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
*    not be used to endorse or promote products derived from this
*    software without prior written permission. For written
*    permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
*    nor may "Apache" appear in their name, without prior written
*    permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
* ITS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, International
* Business Machines, Inc., http://www.ibm.com.  For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

// ---------------------------------------------------------------------------
//  Includes
// ---------------------------------------------------------------------------
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/TransService.hpp>
#include <xercesc/sax2/SAX2XMLReader.hpp>
#include <xercesc/sax2/XMLReaderFactory.hpp>
#include "MsgCreator.h"
#include "InMemHandler.hpp"
#include "ICUResHandler.hpp"
#include "NLSHandler.hpp"
#include <cassert>

#if !defined(NDEBUG) && defined(_MSC_VER)
#include <crtdbg.h>
#endif

XALAN_USING_STD(cerr)
XALAN_USING_STD(cout)
XALAN_USING_STD(endl)

// ---------------------------------------------------------------------------
//  Local helper methods
// ---------------------------------------------------------------------------
static void usage()
{
    cout << "\nUsage:\n"
		"    MsgCreator [options] <XML file>\n\n"
		"This program reads XML message file, and then creates C++ the " << endl <<
		" source / data files for message localization (for .dll/.so build) \n"

		"Options:\n"
		"   -TYPE xxx    Type of localization [ICU | NLS | InMem*]\n"
		"   -LOCAL       ex: [ fr | fr_FR ]; The default is an 'en' local \n"
		"                       If your .xlf file doesn't contain translation \n"
		"                       to 'xx_YY' local, the system will look for\n"
		"                       'xx' translation; "<< endl <<
		"   <XML file>   Can be provided optionally. If not provided, the \n"
		"                       program looks for ${XALANCROOT}/xalanc/NLS directory, \n"
		"                       and looking for XalanMsg_<given local>.xlf file. \n"
		"  * = Default if not provided explicitly.\n\n"
		"    -?          Show this help.\n\n"
		"\n"
		<<  endl;
}

class CmdLineParams
{

public:

	typedef enum{

		ICU_LOCALMSG = 0,
		INMEM_LOCALMSG,
		NLS_LOCALMSG

	}TypeOfLocalMsg;

	const char*		inXMLFileName;

	TypeOfLocalMsg  enTypeOfLocalMsg;

	const char*      encodingName;

	char*		localName;

	bool		isLocalGiven;

	CmdLineParams():
	inXMLFileName(0),
		enTypeOfLocalMsg(INMEM_LOCALMSG),
		encodingName("LATIN1"),
		localName(0),
		isLocalGiven(false){}

	~CmdLineParams() { delete[] localName; }
};

#if defined(OS390)
#include <strings.h>

int
compareNoCase(
			  const char*		str1,
			  const char*		str2)
{
	return strcasecmp(str1, str2);
}

#else

int
compareNoCase(
			  const char*		str1,
			  const char*		str2)
{
	return stricmp(str1, str2);
}

#endif

bool
getArgs(
		int				argc,
		char*			argv[],
		CmdLineParams&	p,
		const char**	pErrorMsg)
{
	bool fSuccess = true;
	char* errorMessage = 0;
	if ( argc > 7)
	{
		fSuccess = false;
		errorMessage = "Too many parameters\n";
	}
	for (int i = 1; i < argc && fSuccess == true; ++i)
	{
		if (!compareNoCase("-h", argv[i]) || !compareNoCase("-?", argv[i]))
		{
			fSuccess = false;
		}
		else if (!compareNoCase("-LOCAL", argv[i]) )
		{
			++i;
			if( i < argc && argv[i][0] != '-' )
			{
				int localLen = strlen(argv[i]);
				if ( localLen != 2 && localLen !=5)
				{
					fSuccess = false;
					errorMessage = "Unrecognized local\n";
				}

				if ( fSuccess  && localLen == 5 && argv[i][2] != '_')
				{
					fSuccess = false;
					errorMessage = "Unrecognized form of local\n";
				}
				if ( fSuccess )
				{
					p.isLocalGiven = true;

					p.localName = new char[10];

					strcpy(p.localName,argv[i]);

					p.localName[9]='\0'; // just in case
				}
			}
		}
		else if (!compareNoCase("-TYPE", argv[i]) )
		{
			++i;

			if( i < argc && argv[i][0] != '-' )
			{
				if( !compareNoCase("ICU",argv[i] ))
				{
					p.enTypeOfLocalMsg = CmdLineParams::ICU_LOCALMSG;

				}else if( !compareNoCase("NLS",argv[i] ))
				{
					p.enTypeOfLocalMsg = CmdLineParams::NLS_LOCALMSG;

				}
				else if( !compareNoCase("InMem",argv[i] ))
				{
					p.enTypeOfLocalMsg = CmdLineParams::INMEM_LOCALMSG;

				}else
				{
					fSuccess = false;
					errorMessage = "Unknown TYPE of localization\n";
				}
			}
			else
			{
				fSuccess = false;
			}
		}else if ( argv[i][0] != '-' )
		{
			p.inXMLFileName = argv[i];
		}
	}

	if ( fSuccess )
	{
		*pErrorMsg = 0;
	}
	else
	{
		*pErrorMsg = errorMessage;
	}

	return fSuccess;
}

static char* buildInputFileName(const CmdLineParams&	p)
{

	char* result = new char[85];
	result[0] = '\0';

	// if file name was given expisitly, don't build it
	if( p.inXMLFileName != 0)
	{
		assert(strlen(p.inXMLFileName) < 84);
		strcpy(result,p.inXMLFileName);
		return result;
	}

	strcat(result,s_xalanMsgFileName);

	if( p.isLocalGiven )
	{

		strncat(result,p.localName,2);

	}
	else
	{
		strcat(result,s_en);

	}
	strcat(result,s_xliffExten);

	return result;
}

static char* buildOutputFileName(const CmdLineParams&	p)
{
	char* pOutputFileName = new char[80];
	pOutputFileName[0] = '\0';

	switch(p.enTypeOfLocalMsg){
	case CmdLineParams::ICU_LOCALMSG:
		{
			if (p.isLocalGiven )
			{
				strcpy(pOutputFileName, p.localName);
			}
			else
			{
				strcpy(pOutputFileName, s_en);
			}

			strcat(pOutputFileName, s_txtExten);

			break;
		}
	case CmdLineParams::INMEM_LOCALMSG:
		{
			strcpy(pOutputFileName, DATA_FILE_NAME);
			break;
		}

	case CmdLineParams::NLS_LOCALMSG:
		{
			strcpy(pOutputFileName,s_xalanMsgFileName);

			if (p.isLocalGiven )
			{
				strncat(pOutputFileName, p.localName,6);
			}
			else
			{
				strncat(pOutputFileName, s_en,3);
			}

			strcat(pOutputFileName, s_msgExten);

			break;
		}
	default:
		{
			// in that stage the local should be defined
			assert ( 0 );
		}
	}
	return pOutputFileName;
}

// ---------------------------------------------------------------------------
//  Program entry point
// ---------------------------------------------------------------------------
int main(int argC, char* argV[])
{

#if !defined(NDEBUG) && defined(_MSC_VER)
	_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);

	_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
	_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
#endif

	CmdLineParams	theParams;
	int iReturnValue = 0;
	const char* errorMessage = 0;
	if (getArgs(argC, argV, theParams, &errorMessage) == false)
	{
		if ( errorMessage != 0)
		{
			cerr << errorMessage;
		}
		usage();
		iReturnValue = 1;

	}
	else
	{

		try
		{
			XMLPlatformUtils::Initialize();
		}

		catch (const XMLException& toCatch)
		{
			cerr << "Error during initialization! :\n"
				<< StrX(toCatch.getMessage()) << endl;
			iReturnValue = 2;
		}
		if (iReturnValue == 0)
		{

			{	//  Create a SAX parser object.
				SAX2XMLReader* parser = XMLReaderFactory::createXMLReader();

				parser->setFeature(XMLUni::fgSAX2CoreValidation, false);

				parser->setFeature(XMLUni::fgXercesDynamic, true);

				parser->setFeature(XMLUni::fgSAX2CoreNameSpaces, false);

				parser->setFeature(XMLUni::fgXercesSchema, true);

				parser->setFeature(XMLUni::fgXercesSchemaFullChecking, false);

				parser->setFeature(XMLUni::fgSAX2CoreNameSpacePrefixes, false);

				//  Create the handler object and install it as the document and error
				//  handler for the parser. Then parse the file and catch any exceptions
				//  that propogate out
				//

				int errorCount = 0;

				char* const		fileName = buildInputFileName(theParams);

				char* const		pOutputFileName = buildOutputFileName(theParams);

				SAX2Handler* handler = 0;
				try
				{
					switch(theParams.enTypeOfLocalMsg){
					case CmdLineParams::ICU_LOCALMSG:
						{
							handler = new ICUResHandler(pOutputFileName);
							break;
						}
					case CmdLineParams::INMEM_LOCALMSG:
						{
							handler = new InMemHandler(pOutputFileName);
							break;
						}
					case CmdLineParams::NLS_LOCALMSG:
						{
							handler = new NLSHandler(pOutputFileName);
							break;
						}
					default:
						{
							assert (0);
						}
					}
				}

				catch(const XalanFileOutputStream::XalanFileOutputStreamOpenException& e)
				{
					cerr << "\nAn error occurred\n  Error: ";
					if (e.m_pMessage != 0)
					{
						cerr << e.m_pMessage <<  endl;
					}
					else
					{
						cerr << "unknown (XalanFileOutputStreamOpenException) " << "\n" << endl;

					}

					iReturnValue = 4;

				}
				catch(const XalanFileOutputStream::XalanFileOutputStreamWriteException& e)
				{
					cerr << "\nAn error occurred\n  Error: ";
					if (e.m_pMessage != 0)
					{
						cerr << e.m_pMessage << endl;
					}
					else
					{
						cerr << "unknown (XalanFileOutputStreamWriteException) " << "\n" << endl;

					}

					iReturnValue = 5;

				}
				catch (...)
				{
					cerr << "Unknown error occured." << endl;
					iReturnValue = 6;
				}

				// run the parser
				if ( iReturnValue == 0)
				{
					try
					{
						if ( theParams.isLocalGiven == true )
						{
							handler->setXML_Lang(theParams.localName);
						}

						parser->setContentHandler(handler);

						parser->setErrorHandler(handler);

						parser->parse(fileName);

						errorCount = parser->getErrorCount();
					}

					catch (const XMLException& toCatch)
					{
						cerr << "\nAn error occurred\n  Error: "
							<< StrX(toCatch.getMessage())
							<< "\n" << endl;
						iReturnValue = 7;
					}
					catch (...)
					{
						cerr << endl << "General error occured." << endl;
						iReturnValue = 8;
					}
				}

				delete handler;

				delete[] pOutputFileName;

				delete[] fileName;
				//
				//  Delete the parser itself.  Must be done prior to calling Terminate, below.
				//
				delete parser;

				if (errorCount != 0)
					iReturnValue = 9;

			}
			// And call the termination method
			XMLPlatformUtils::Terminate();

		}

	}
	return iReturnValue;
}

<commit_msg>Added const qualifier.<commit_after>/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. The end-user documentation included with the redistribution,
*    if any, must include the following acknowledgment:
*       "This product includes software developed by the
*        Apache Software Foundation (http://www.apache.org/)."
*    Alternately, this acknowledgment may appear in the software itself,
*    if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
*    not be used to endorse or promote products derived from this
*    software without prior written permission. For written
*    permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
*    nor may "Apache" appear in their name, without prior written
*    permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
* ITS 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, International
* Business Machines, Inc., http://www.ibm.com.  For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

// ---------------------------------------------------------------------------
//  Includes
// ---------------------------------------------------------------------------
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/TransService.hpp>
#include <xercesc/sax2/SAX2XMLReader.hpp>
#include <xercesc/sax2/XMLReaderFactory.hpp>
#include "MsgCreator.h"
#include "InMemHandler.hpp"
#include "ICUResHandler.hpp"
#include "NLSHandler.hpp"
#include <cassert>

#if !defined(NDEBUG) && defined(_MSC_VER)
#include <crtdbg.h>
#endif

XALAN_USING_STD(cerr)
XALAN_USING_STD(cout)
XALAN_USING_STD(endl)

// ---------------------------------------------------------------------------
//  Local helper methods
// ---------------------------------------------------------------------------
static void usage()
{
    cout << "\nUsage:\n"
		"    MsgCreator [options] <XML file>\n\n"
		"This program reads XML message file, and then creates C++ the " << endl <<
		" source / data files for message localization (for .dll/.so build) \n"

		"Options:\n"
		"   -TYPE xxx    Type of localization [ICU | NLS | InMem*]\n"
		"   -LOCAL       ex: [ fr | fr_FR ]; The default is an 'en' local \n"
		"                       If your .xlf file doesn't contain translation \n"
		"                       to 'xx_YY' local, the system will look for\n"
		"                       'xx' translation; "<< endl <<
		"   <XML file>   Can be provided optionally. If not provided, the \n"
		"                       program looks for ${XALANCROOT}/xalanc/NLS directory, \n"
		"                       and looking for XalanMsg_<given local>.xlf file. \n"
		"  * = Default if not provided explicitly.\n\n"
		"    -?          Show this help.\n\n"
		"\n"
		<<  endl;
}

class CmdLineParams
{

public:

	typedef enum{

		ICU_LOCALMSG = 0,
		INMEM_LOCALMSG,
		NLS_LOCALMSG

	}TypeOfLocalMsg;

	const char*		inXMLFileName;

	TypeOfLocalMsg  enTypeOfLocalMsg;

	const char*      encodingName;

	char*		localName;

	bool		isLocalGiven;

	CmdLineParams():
	inXMLFileName(0),
		enTypeOfLocalMsg(INMEM_LOCALMSG),
		encodingName("LATIN1"),
		localName(0),
		isLocalGiven(false){}

	~CmdLineParams() { delete[] localName; }
};

#if defined(OS390)
#include <strings.h>

int
compareNoCase(
			  const char*		str1,
			  const char*		str2)
{
	return strcasecmp(str1, str2);
}

#else

int
compareNoCase(
			  const char*		str1,
			  const char*		str2)
{
	return stricmp(str1, str2);
}

#endif

bool
getArgs(
		int				argc,
		char*			argv[],
		CmdLineParams&	p,
		const char**	pErrorMsg)
{
	bool fSuccess = true;
	const char*		errorMessage = 0;
	if ( argc > 7)
	{
		fSuccess = false;
		errorMessage = "Too many parameters\n";
	}
	for (int i = 1; i < argc && fSuccess == true; ++i)
	{
		if (!compareNoCase("-h", argv[i]) || !compareNoCase("-?", argv[i]))
		{
			fSuccess = false;
		}
		else if (!compareNoCase("-LOCAL", argv[i]) )
		{
			++i;
			if( i < argc && argv[i][0] != '-' )
			{
				int localLen = strlen(argv[i]);
				if ( localLen != 2 && localLen !=5)
				{
					fSuccess = false;
					errorMessage = "Unrecognized local\n";
				}

				if ( fSuccess  && localLen == 5 && argv[i][2] != '_')
				{
					fSuccess = false;
					errorMessage = "Unrecognized form of local\n";
				}
				if ( fSuccess )
				{
					p.isLocalGiven = true;

					p.localName = new char[10];

					strcpy(p.localName,argv[i]);

					p.localName[9]='\0'; // just in case
				}
			}
		}
		else if (!compareNoCase("-TYPE", argv[i]) )
		{
			++i;

			if( i < argc && argv[i][0] != '-' )
			{
				if( !compareNoCase("ICU",argv[i] ))
				{
					p.enTypeOfLocalMsg = CmdLineParams::ICU_LOCALMSG;

				}else if( !compareNoCase("NLS",argv[i] ))
				{
					p.enTypeOfLocalMsg = CmdLineParams::NLS_LOCALMSG;

				}
				else if( !compareNoCase("InMem",argv[i] ))
				{
					p.enTypeOfLocalMsg = CmdLineParams::INMEM_LOCALMSG;

				}else
				{
					fSuccess = false;
					errorMessage = "Unknown TYPE of localization\n";
				}
			}
			else
			{
				fSuccess = false;
			}
		}else if ( argv[i][0] != '-' )
		{
			p.inXMLFileName = argv[i];
		}
	}

	if ( fSuccess )
	{
		*pErrorMsg = 0;
	}
	else
	{
		*pErrorMsg = errorMessage;
	}

	return fSuccess;
}

static char* buildInputFileName(const CmdLineParams&	p)
{

	char* result = new char[85];
	result[0] = '\0';

	// if file name was given expisitly, don't build it
	if( p.inXMLFileName != 0)
	{
		assert(strlen(p.inXMLFileName) < 84);
		strcpy(result,p.inXMLFileName);
		return result;
	}

	strcat(result,s_xalanMsgFileName);

	if( p.isLocalGiven )
	{

		strncat(result,p.localName,2);

	}
	else
	{
		strcat(result,s_en);

	}
	strcat(result,s_xliffExten);

	return result;
}

static char* buildOutputFileName(const CmdLineParams&	p)
{
	char* pOutputFileName = new char[80];
	pOutputFileName[0] = '\0';

	switch(p.enTypeOfLocalMsg){
	case CmdLineParams::ICU_LOCALMSG:
		{
			if (p.isLocalGiven )
			{
				strcpy(pOutputFileName, p.localName);
			}
			else
			{
				strcpy(pOutputFileName, s_en);
			}

			strcat(pOutputFileName, s_txtExten);

			break;
		}
	case CmdLineParams::INMEM_LOCALMSG:
		{
			strcpy(pOutputFileName, DATA_FILE_NAME);
			break;
		}

	case CmdLineParams::NLS_LOCALMSG:
		{
			strcpy(pOutputFileName,s_xalanMsgFileName);

			if (p.isLocalGiven )
			{
				strncat(pOutputFileName, p.localName,6);
			}
			else
			{
				strncat(pOutputFileName, s_en,3);
			}

			strcat(pOutputFileName, s_msgExten);

			break;
		}
	default:
		{
			// in that stage the local should be defined
			assert ( 0 );
		}
	}
	return pOutputFileName;
}

// ---------------------------------------------------------------------------
//  Program entry point
// ---------------------------------------------------------------------------
int main(int argC, char* argV[])
{

#if !defined(NDEBUG) && defined(_MSC_VER)
	_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);

	_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
	_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
#endif

	CmdLineParams	theParams;
	int iReturnValue = 0;
	const char* errorMessage = 0;
	if (getArgs(argC, argV, theParams, &errorMessage) == false)
	{
		if ( errorMessage != 0)
		{
			cerr << errorMessage;
		}
		usage();
		iReturnValue = 1;

	}
	else
	{

		try
		{
			XMLPlatformUtils::Initialize();
		}

		catch (const XMLException& toCatch)
		{
			cerr << "Error during initialization! :\n"
				<< StrX(toCatch.getMessage()) << endl;
			iReturnValue = 2;
		}
		if (iReturnValue == 0)
		{

			{	//  Create a SAX parser object.
				SAX2XMLReader* parser = XMLReaderFactory::createXMLReader();

				parser->setFeature(XMLUni::fgSAX2CoreValidation, false);

				parser->setFeature(XMLUni::fgXercesDynamic, true);

				parser->setFeature(XMLUni::fgSAX2CoreNameSpaces, false);

				parser->setFeature(XMLUni::fgXercesSchema, true);

				parser->setFeature(XMLUni::fgXercesSchemaFullChecking, false);

				parser->setFeature(XMLUni::fgSAX2CoreNameSpacePrefixes, false);

				//  Create the handler object and install it as the document and error
				//  handler for the parser. Then parse the file and catch any exceptions
				//  that propogate out
				//

				int errorCount = 0;

				char* const		fileName = buildInputFileName(theParams);

				char* const		pOutputFileName = buildOutputFileName(theParams);

				SAX2Handler* handler = 0;
				try
				{
					switch(theParams.enTypeOfLocalMsg){
					case CmdLineParams::ICU_LOCALMSG:
						{
							handler = new ICUResHandler(pOutputFileName);
							break;
						}
					case CmdLineParams::INMEM_LOCALMSG:
						{
							handler = new InMemHandler(pOutputFileName);
							break;
						}
					case CmdLineParams::NLS_LOCALMSG:
						{
							handler = new NLSHandler(pOutputFileName);
							break;
						}
					default:
						{
							assert (0);
						}
					}
				}

				catch(const XalanFileOutputStream::XalanFileOutputStreamOpenException& e)
				{
					cerr << "\nAn error occurred\n  Error: ";
					if (e.m_pMessage != 0)
					{
						cerr << e.m_pMessage <<  endl;
					}
					else
					{
						cerr << "unknown (XalanFileOutputStreamOpenException) " << "\n" << endl;

					}

					iReturnValue = 4;

				}
				catch(const XalanFileOutputStream::XalanFileOutputStreamWriteException& e)
				{
					cerr << "\nAn error occurred\n  Error: ";
					if (e.m_pMessage != 0)
					{
						cerr << e.m_pMessage << endl;
					}
					else
					{
						cerr << "unknown (XalanFileOutputStreamWriteException) " << "\n" << endl;

					}

					iReturnValue = 5;

				}
				catch (...)
				{
					cerr << "Unknown error occured." << endl;
					iReturnValue = 6;
				}

				// run the parser
				if ( iReturnValue == 0)
				{
					try
					{
						if ( theParams.isLocalGiven == true )
						{
							handler->setXML_Lang(theParams.localName);
						}

						parser->setContentHandler(handler);

						parser->setErrorHandler(handler);

						parser->parse(fileName);

						errorCount = parser->getErrorCount();
					}

					catch (const XMLException& toCatch)
					{
						cerr << "\nAn error occurred\n  Error: "
							<< StrX(toCatch.getMessage())
							<< "\n" << endl;
						iReturnValue = 7;
					}
					catch (...)
					{
						cerr << endl << "General error occured." << endl;
						iReturnValue = 8;
					}
				}

				delete handler;

				delete[] pOutputFileName;

				delete[] fileName;
				//
				//  Delete the parser itself.  Must be done prior to calling Terminate, below.
				//
				delete parser;

				if (errorCount != 0)
					iReturnValue = 9;

			}
			// And call the termination method
			XMLPlatformUtils::Terminate();

		}

	}
	return iReturnValue;
}

<|endoftext|>


<commit_before>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkottiePriv.h"

#include "SkFontMgr.h"
#include "SkMakeUnique.h"
#include "SkottieAdapter.h"
#include "SkottieJson.h"
#include "SkottieValue.h"
#include "SkSGColor.h"
#include "SkSGDraw.h"
#include "SkSGGroup.h"
#include "SkSGText.h"
#include "SkTypes.h"

#include <string.h>

namespace skottie {
namespace internal {

namespace {

SkFontStyle FontStyle(const char* style) {
    static constexpr struct {
        const char*               fName;
        const SkFontStyle::Weight fWeight;
    } gWeightMap[] = {
        { "ExtraLight", SkFontStyle::kExtraLight_Weight },
        { "Light"     , SkFontStyle::kLight_Weight      },
        { "Regular"   , SkFontStyle::kNormal_Weight     },
        { "Medium"    , SkFontStyle::kMedium_Weight     },
        { "SemiBold"  , SkFontStyle::kSemiBold_Weight   },
        { "Bold"      , SkFontStyle::kBold_Weight       },
        { "ExtraBold" , SkFontStyle::kExtraBold_Weight  },
    };

    SkFontStyle::Weight weight = SkFontStyle::kNormal_Weight;
    for (const auto& w : gWeightMap) {
        const auto name_len = strlen(w.fName);
        if (!strncmp(style, w.fName, name_len)) {
            weight = w.fWeight;
            style += name_len;
            break;
        }
    }

    static constexpr struct {
        const char*              fName;
        const SkFontStyle::Slant fSlant;
    } gSlantMap[] = {
        { "Italic" , SkFontStyle::kItalic_Slant  },
        { "Oblique", SkFontStyle::kOblique_Slant },
    };

    SkFontStyle::Slant slant = SkFontStyle::kUpright_Slant;
    if (*style != '\0') {
        for (const auto& s : gSlantMap) {
            if (!strcmp(style, s.fName)) {
                slant = s.fSlant;
                style += strlen(s.fName);
                break;
            }
        }
    }

    if (*style != '\0') {
        LOG("?? Unknown font style: %s\n", style);
    }

    return SkFontStyle(weight, SkFontStyle::kNormal_Width, slant);
}

} // namespace

bool AnimationBuilder::FontInfo::matches(const char family[], const char style[]) const {
    return 0 == strcmp(fFamily.c_str(), family)
        && 0 == strcmp(fStyle.c_str(), style);
}

void AnimationBuilder::parseFonts(const skjson::ObjectValue* jfonts,
                                  const skjson::ArrayValue* jchars) {
    // Optional array of font entries, referenced (by name) from text layer document nodes. E.g.
    // "fonts": {
    //        "list": [
    //            {
    //                "ascent": 75,
    //                "fClass": "",
    //                "fFamily": "Roboto",
    //                "fName": "Roboto-Regular",
    //                "fPath": "",
    //                "fStyle": "Regular",
    //                "fWeight": "",
    //                "origin": 1
    //            }
    //        ]
    //    },
    if (jfonts) {
        if (const skjson::ArrayValue* jlist = (*jfonts)["list"]) {
            for (const skjson::ObjectValue* jfont : *jlist) {
                if (!jfont) {
                    continue;
                }

                const skjson::StringValue* jname   = (*jfont)["fName"];
                const skjson::StringValue* jfamily = (*jfont)["fFamily"];
                const skjson::StringValue* jstyle  = (*jfont)["fStyle"];

                if (!jname   || !jname->size() ||
                    !jfamily || !jfamily->size() ||
                    !jstyle  || !jstyle->size()) {
                    LogJSON(*jfont, "!! Ignoring invalid font");
                    continue;
                }

                sk_sp<SkTypeface> tf(fFontMgr->matchFamilyStyle(jfamily->begin(),
                                                                FontStyle(jstyle->begin())));
                if (!tf) {
                    LOG("!! Could not create typeface for %s|%s\n",
                        jfamily->begin(), jstyle->begin());
                    // Last resort.
                    tf.reset(fFontMgr->matchFamilyStyle("Arial", SkFontStyle::Normal()));
                    if (!tf) {
                        continue;
                    }
                }

                fFonts.set(SkString(jname->begin(), jname->size()),
                          {
                              SkString(jfamily->begin(), jfamily->size()),
                              SkString(jstyle->begin(), jstyle->size()),
                              ParseDefault((*jfont)["ascent"] , 0.0f),
                              std::move(tf)
                          });
            }
        }
    }

    // Optional array of glyphs, to be associated with one of the declared fonts. E.g.
    // "chars": [
    //     {
    //         "ch": "t",
    //         "data": {
    //             "shapes": [...]
    //         },
    //         "fFamily": "Roboto",
    //         "size": 50,
    //         "style": "Regular",
    //         "w": 32.67
    //    }
    // ]
    if (jchars) {
        FontInfo* current_font = nullptr;

        for (const skjson::ObjectValue* jchar : *jchars) {
            if (!jchar) {
                continue;
            }

            const skjson::StringValue* jch = (*jchar)["ch"];
            if (!jch) {
                continue;
            }

            const skjson::StringValue* jfamily = (*jchar)["fFamily"];
            const skjson::StringValue* jstyle  = (*jchar)["style"]; // "style", not "fStyle"...

            const auto* ch_ptr = jch->begin();
            const auto  ch_len = jch->size();

            if (!jfamily || !jstyle || (SkUTF::CountUTF8(ch_ptr, ch_len) != 1)) {
                LogJSON(*jchar, "!! Invalid glyph");
                continue;
            }

            const auto uni = SkUTF::NextUTF8(&ch_ptr, ch_ptr + ch_len);
            SkASSERT(uni != -1);

            const auto* family = jfamily->begin();
            const auto* style  = jstyle->begin();

            // Locate (and cache) the font info. Unlike text nodes, glyphs reference the font by
            // (family, style) -- not by name :(  For now this performs a linear search over *all*
            // fonts: generally there are few of them, and glyph definitions are font-clustered.
            // If problematic, we can refactor as a two-level hashmap.
            if (!current_font || !current_font->matches(family, style)) {
                current_font = nullptr;
                fFonts.foreach([&](const SkString& name, FontInfo* finfo) {
                    if (finfo->matches(family, style)) {
                        current_font = finfo;
                        // TODO: would be nice to break early here...
                    }
                });
                if (!current_font) {
                    LOG("!! Font not found for codepoint (%d, %s, %s)\n", uni, family, style);
                    continue;
                }
            }

            // TODO: parse glyphs
        }
    }
}

sk_sp<SkTypeface> AnimationBuilder::findFont(const SkString& font_name) const {
    if (const auto* font = fFonts.find(font_name)) {
        return font->fTypeface;
    }

    LOG("!! Unknown font: \"%s\"\n", font_name.c_str());
    return nullptr;
}

sk_sp<sksg::RenderNode> AnimationBuilder::attachTextLayer(const skjson::ObjectValue& layer,
                                                          AnimatorScope* ascope) const {
    // General text node format:
    // "t": {
    //    "a": [], // animators (TODO)
    //    "d": {
    //        "k": [
    //            {
    //                "s": {
    //                    "f": "Roboto-Regular",
    //                    "fc": [
    //                        0.42,
    //                        0.15,
    //                        0.15
    //                    ],
    //                    "j": 1,
    //                    "lh": 60,
    //                    "ls": 0,
    //                    "s": 50,
    //                    "t": "text align right",
    //                    "tr": 0
    //                },
    //                "t": 0
    //            }
    //        ]
    //    },
    //    "m": {}, // "more options" (TODO)
    //    "p": {}  // "path options" (TODO)
    // },
    const skjson::ObjectValue* jt = layer["t"];
    if (!jt) {
        LogJSON(layer, "!! Missing text layer \"t\" property");
        return nullptr;
    }

    const skjson::ArrayValue* animated_props = (*jt)["a"];
    if (animated_props && animated_props->size() > 0) {
        LOG("?? Unsupported animated text properties.\n");
    }

    const skjson::ObjectValue* jd  = (*jt)["d"];
    if (!jd) {
        return nullptr;
    }

    auto text_root = sksg::Group::Make();
    auto adapter   = sk_make_sp<TextAdapter>(text_root);

    this->bindProperty<TextValue>(*jd, ascope, [adapter] (const TextValue& txt) {
        adapter->setText(txt);
    });

    return std::move(text_root);
}

} // namespace internal
} // namespace skottie
<commit_msg>[Skottie] Fallback to portable font manager<commit_after>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkottiePriv.h"

#include "SkFontMgr.h"
#include "SkMakeUnique.h"
#include "SkottieAdapter.h"
#include "SkottieJson.h"
#include "SkottieValue.h"
#include "SkSGColor.h"
#include "SkSGDraw.h"
#include "SkSGGroup.h"
#include "SkSGText.h"
#include "SkTypes.h"

#include <string.h>

namespace skottie {
namespace internal {

namespace {

SkFontStyle FontStyle(const char* style) {
    static constexpr struct {
        const char*               fName;
        const SkFontStyle::Weight fWeight;
    } gWeightMap[] = {
        { "ExtraLight", SkFontStyle::kExtraLight_Weight },
        { "Light"     , SkFontStyle::kLight_Weight      },
        { "Regular"   , SkFontStyle::kNormal_Weight     },
        { "Medium"    , SkFontStyle::kMedium_Weight     },
        { "SemiBold"  , SkFontStyle::kSemiBold_Weight   },
        { "Bold"      , SkFontStyle::kBold_Weight       },
        { "ExtraBold" , SkFontStyle::kExtraBold_Weight  },
    };

    SkFontStyle::Weight weight = SkFontStyle::kNormal_Weight;
    for (const auto& w : gWeightMap) {
        const auto name_len = strlen(w.fName);
        if (!strncmp(style, w.fName, name_len)) {
            weight = w.fWeight;
            style += name_len;
            break;
        }
    }

    static constexpr struct {
        const char*              fName;
        const SkFontStyle::Slant fSlant;
    } gSlantMap[] = {
        { "Italic" , SkFontStyle::kItalic_Slant  },
        { "Oblique", SkFontStyle::kOblique_Slant },
    };

    SkFontStyle::Slant slant = SkFontStyle::kUpright_Slant;
    if (*style != '\0') {
        for (const auto& s : gSlantMap) {
            if (!strcmp(style, s.fName)) {
                slant = s.fSlant;
                style += strlen(s.fName);
                break;
            }
        }
    }

    if (*style != '\0') {
        LOG("?? Unknown font style: %s\n", style);
    }

    return SkFontStyle(weight, SkFontStyle::kNormal_Width, slant);
}

} // namespace

bool AnimationBuilder::FontInfo::matches(const char family[], const char style[]) const {
    return 0 == strcmp(fFamily.c_str(), family)
        && 0 == strcmp(fStyle.c_str(), style);
}

void AnimationBuilder::parseFonts(const skjson::ObjectValue* jfonts,
                                  const skjson::ArrayValue* jchars) {
    // Optional array of font entries, referenced (by name) from text layer document nodes. E.g.
    // "fonts": {
    //        "list": [
    //            {
    //                "ascent": 75,
    //                "fClass": "",
    //                "fFamily": "Roboto",
    //                "fName": "Roboto-Regular",
    //                "fPath": "",
    //                "fStyle": "Regular",
    //                "fWeight": "",
    //                "origin": 1
    //            }
    //        ]
    //    },
    if (jfonts) {
        if (const skjson::ArrayValue* jlist = (*jfonts)["list"]) {
            for (const skjson::ObjectValue* jfont : *jlist) {
                if (!jfont) {
                    continue;
                }

                const skjson::StringValue* jname   = (*jfont)["fName"];
                const skjson::StringValue* jfamily = (*jfont)["fFamily"];
                const skjson::StringValue* jstyle  = (*jfont)["fStyle"];

                if (!jname   || !jname->size() ||
                    !jfamily || !jfamily->size() ||
                    !jstyle  || !jstyle->size()) {
                    LogJSON(*jfont, "!! Ignoring invalid font");
                    continue;
                }

                sk_sp<SkTypeface> tf(fFontMgr->matchFamilyStyle(jfamily->begin(),
                                                                FontStyle(jstyle->begin())));
                if (!tf) {
                    LOG("!! Could not create typeface for %s|%s\n",
                        jfamily->begin(), jstyle->begin());
                    // Last resort.
                    tf = fFontMgr->legacyMakeTypeface(nullptr, FontStyle(jstyle->begin()));
                    if (!tf) {
                        continue;
                    }
                }

                fFonts.set(SkString(jname->begin(), jname->size()),
                          {
                              SkString(jfamily->begin(), jfamily->size()),
                              SkString(jstyle->begin(), jstyle->size()),
                              ParseDefault((*jfont)["ascent"] , 0.0f),
                              std::move(tf)
                          });
            }
        }
    }

    // Optional array of glyphs, to be associated with one of the declared fonts. E.g.
    // "chars": [
    //     {
    //         "ch": "t",
    //         "data": {
    //             "shapes": [...]
    //         },
    //         "fFamily": "Roboto",
    //         "size": 50,
    //         "style": "Regular",
    //         "w": 32.67
    //    }
    // ]
    if (jchars) {
        FontInfo* current_font = nullptr;

        for (const skjson::ObjectValue* jchar : *jchars) {
            if (!jchar) {
                continue;
            }

            const skjson::StringValue* jch = (*jchar)["ch"];
            if (!jch) {
                continue;
            }

            const skjson::StringValue* jfamily = (*jchar)["fFamily"];
            const skjson::StringValue* jstyle  = (*jchar)["style"]; // "style", not "fStyle"...

            const auto* ch_ptr = jch->begin();
            const auto  ch_len = jch->size();

            if (!jfamily || !jstyle || (SkUTF::CountUTF8(ch_ptr, ch_len) != 1)) {
                LogJSON(*jchar, "!! Invalid glyph");
                continue;
            }

            const auto uni = SkUTF::NextUTF8(&ch_ptr, ch_ptr + ch_len);
            SkASSERT(uni != -1);

            const auto* family = jfamily->begin();
            const auto* style  = jstyle->begin();

            // Locate (and cache) the font info. Unlike text nodes, glyphs reference the font by
            // (family, style) -- not by name :(  For now this performs a linear search over *all*
            // fonts: generally there are few of them, and glyph definitions are font-clustered.
            // If problematic, we can refactor as a two-level hashmap.
            if (!current_font || !current_font->matches(family, style)) {
                current_font = nullptr;
                fFonts.foreach([&](const SkString& name, FontInfo* finfo) {
                    if (finfo->matches(family, style)) {
                        current_font = finfo;
                        // TODO: would be nice to break early here...
                    }
                });
                if (!current_font) {
                    LOG("!! Font not found for codepoint (%d, %s, %s)\n", uni, family, style);
                    continue;
                }
            }

            // TODO: parse glyphs
        }
    }
}

sk_sp<SkTypeface> AnimationBuilder::findFont(const SkString& font_name) const {
    if (const auto* font = fFonts.find(font_name)) {
        return font->fTypeface;
    }

    LOG("!! Unknown font: \"%s\"\n", font_name.c_str());
    return nullptr;
}

sk_sp<sksg::RenderNode> AnimationBuilder::attachTextLayer(const skjson::ObjectValue& layer,
                                                          AnimatorScope* ascope) const {
    // General text node format:
    // "t": {
    //    "a": [], // animators (TODO)
    //    "d": {
    //        "k": [
    //            {
    //                "s": {
    //                    "f": "Roboto-Regular",
    //                    "fc": [
    //                        0.42,
    //                        0.15,
    //                        0.15
    //                    ],
    //                    "j": 1,
    //                    "lh": 60,
    //                    "ls": 0,
    //                    "s": 50,
    //                    "t": "text align right",
    //                    "tr": 0
    //                },
    //                "t": 0
    //            }
    //        ]
    //    },
    //    "m": {}, // "more options" (TODO)
    //    "p": {}  // "path options" (TODO)
    // },
    const skjson::ObjectValue* jt = layer["t"];
    if (!jt) {
        LogJSON(layer, "!! Missing text layer \"t\" property");
        return nullptr;
    }

    const skjson::ArrayValue* animated_props = (*jt)["a"];
    if (animated_props && animated_props->size() > 0) {
        LOG("?? Unsupported animated text properties.\n");
    }

    const skjson::ObjectValue* jd  = (*jt)["d"];
    if (!jd) {
        return nullptr;
    }

    auto text_root = sksg::Group::Make();
    auto adapter   = sk_make_sp<TextAdapter>(text_root);

    this->bindProperty<TextValue>(*jd, ascope, [adapter] (const TextValue& txt) {
        adapter->setText(txt);
    });

    return std::move(text_root);
}

} // namespace internal
} // namespace skottie
<|endoftext|>


<commit_before>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

*******************************************************************************/

//==============================================================================
// Enhanced tree view widget
//==============================================================================

#include "treeviewwidget.h"

//==============================================================================

#include <QDrag>
#include <QHeaderView>
#include <QMouseEvent>
#include <QStandardItemModel>

//==============================================================================

namespace OpenCOR {
namespace Core {

//==============================================================================

TreeViewWidget::TreeViewWidget(QWidget *pParent) :
    QTreeView(pParent),
    CommonWidget(pParent)
{
    // Set some properties for our tree view widget

    setAllColumnsShowFocus(true);
#ifdef Q_OS_MAC
    setAttribute(Qt::WA_MacShowFocusRect, 0);
    // Note: the above removes the focus border since it messes up the look of
    //       our tree view widget...
#endif
    setFrameShape(QFrame::NoFrame);
    setSelectionMode(QAbstractItemView::ExtendedSelection);
    setUniformRowHeights(true);
}

//==============================================================================

void TreeViewWidget::resizeColumnsToContents()
{
    // Resize all our columns to their contents

    for (int i = 0, iMax = header()->count(); i < iMax; ++i)
        resizeColumnToContents(i);
}

//==============================================================================

void TreeViewWidget::selectItem(const int &pRow, const int &pColumn)
{
    // Select the requested item, if any

    QStandardItemModel *treeViewModel = qobject_cast<QStandardItemModel *>(model());

    if (treeViewModel) {
        // The tree view has a model associated with it, so we can retrieve the
        // requested item

        QStandardItem *treeViewItem = treeViewModel->invisibleRootItem()->child(pRow, pColumn);

        if (treeViewItem)
            // The requested item exists, so select it...

            setCurrentIndex(treeViewItem->index());
    }
}

//==============================================================================

void TreeViewWidget::selectFirstItem()
{
    // Select the first item

    selectItem();
}

//==============================================================================

bool TreeViewWidget::isEditing() const
{
    // Return whether our tree view widget is in editing mode

    return state() == QAbstractItemView::EditingState;
}

//==============================================================================

QSize TreeViewWidget::sizeHint() const
{
    // Suggest a default size for our tree view widget
    // Note: this is critical if we want a docked widget, with a tree view
    //       widget on it, to have a decent size when docked to the main
    //       window...

    return defaultSize(0.15);
}

//==============================================================================

void TreeViewWidget::keyPressEvent(QKeyEvent *pEvent)
{
    // Check some key combinations

    if (   !(pEvent->modifiers() & Qt::ShiftModifier)
        && !(pEvent->modifiers() & Qt::ControlModifier)
        && !(pEvent->modifiers() & Qt::AltModifier)
        && !(pEvent->modifiers() & Qt::MetaModifier)) {
        // None of the modifiers is selected

        if (pEvent->key() == Qt::Key_Left) {
            // The user wants to collapse the current item or go to its parent,
            // depending on the collapsed state of the current item

            QModelIndex currIndex = currentIndex();

            if (isExpanded(currIndex)) {
                // The current item is expanded, so collapse it

                setExpanded(currIndex, false);

                // Accept the event

                pEvent->accept();
            } else {
                // The current item is collapsed, so select its parent, but
                // only if it has a parent

                if (currIndex.parent() != QModelIndex()) {
                    setCurrentIndex(currIndex.parent());

                    // Accept the event

                    pEvent->accept();
                } else {
                    // Default handling of the event

                    QTreeView::keyPressEvent(pEvent);
                }
            }
        } else if (pEvent->key() == Qt::Key_Right) {
            // The user wants to expand the current item or go to its first
            // child, should it have children

            QModelIndex currIndex = currentIndex();

            if (model()->hasChildren(currIndex)) {
                if (!isExpanded(currIndex)) {
                    // The current item is collapsed, so expand it

                    setExpanded(currIndex, true);

                    // Accept the event

                    pEvent->accept();
                } else {
                    // The current item is expanded, so select its first child

                    setCurrentIndex(currIndex.child(0, 0));

                    // Accept the event

                    pEvent->accept();
                }
            } else {
                // The current item doesn't have any children, so do nothing
                // Note: normally, we would do the default handling of the
                //       event, but this will potentially shift the whole tree
                //       view contents to the right, so do nothing indeed...

                ;
            }
        } else {
            // Default handling of the event

            QTreeView::keyPressEvent(pEvent);
        }
    } else {
        // Default handling of the event

        QTreeView::keyPressEvent(pEvent);
    }
}

//==============================================================================

void TreeViewWidget::mousePressEvent(QMouseEvent *pEvent)
{
    // Default handling of the event

    QTreeView::mousePressEvent(pEvent);

    // Select the current item (if any), if we are right clicking
    // Note: the idea is that if we want to show a context menu, then nothing
    //       will happen if we are not over an item...

    if (pEvent->button() == Qt::RightButton)
        setCurrentIndex(indexAt(pEvent->pos()));
}

//==============================================================================

void TreeViewWidget::startDrag(Qt::DropActions pSupportedActions)
{
    // This a reimplementation of QAbstractItemView::startDrag, so that we can
    // provide OpenCOR with a better pixmap for the drag object
    // Note: indeed, on Windows, the pixmap only shows the dragged item that are
    //       visible in the QTreeView. Also, if there an item covers several
    //       columns, then the pixmap will show several 'cells' some of them
    //       empty if a column is empty, so... instead we want to provide a
    //       generic pixmap which looks 'good' on all platforms...

    // Retrieve the selected draggable items, if any
    // Note: the following code is based on
    //       QAbstractItemViewPrivate::selectedDraggableIndexes...

    QModelIndexList selectedDraggableIndexes = selectedIndexes();

    for (int i = selectedDraggableIndexes.count()-1; i >= 0; --i)
        if (   !(model()->flags(selectedDraggableIndexes[i]) & Qt::ItemIsDragEnabled)
            || selectedDraggableIndexes[i].column())
            // The current selected item is not draggable or is not in the first
            // column
            // Note: regarding the test on the column number, it is because we
            //       may have a model data that requires several columns (e.g.
            //       QFileSystemModel) in which case selectedIndexes would
            //       return a number of indexes equal to the number of rows
            //       times the number of columns while we only want a number of
            //       indexes to be equal to the number of rows (since we have a
            //       selection mode of QAbstractItemView::ExtendedSelection)

            selectedDraggableIndexes.removeAt(i);

    // Start the dragging action is there is at least one selected draggable
    // item

    if (selectedDraggableIndexes.count()) {
        // There is at least one selected draggable item, so create a QMimeData
        // object for it

        QMimeData *mimeData = model()->mimeData(selectedDraggableIndexes);

        if (!mimeData)
            return;

        // Create the pixmap that will be associated with the dragging action

        QPixmap pixmap((selectedDraggableIndexes.count() == 1)?
                           ":oxygen/mimetypes/application-x-zerosize.png":
                           ":oxygen/places/document-multiple.png");

        // Create the drag object

        QDrag *drag = new QDrag(this);

        drag->setMimeData(mimeData);
        drag->setPixmap(pixmap);
        drag->setHotSpot(QPoint(0.5*pixmap.width(), 0.5*pixmap.height()));

        // Do the dragging itself

        Qt::DropAction realDefaultDropAction = Qt::IgnoreAction;

        if (   (defaultDropAction() != Qt::IgnoreAction)
            && (pSupportedActions & defaultDropAction()))
            realDefaultDropAction = defaultDropAction();
        else if (   (pSupportedActions & Qt::CopyAction)
                 && (dragDropMode() != QAbstractItemView::InternalMove))
            realDefaultDropAction = Qt::CopyAction;

        if (drag->exec(pSupportedActions, realDefaultDropAction) == Qt::MoveAction) {
            // We want to move the items
            // Note: the following code is based on
            //       QAbstractItemViewPrivate::clearOrRemove...

            const QItemSelection selection = selectionModel()->selection();

            if (!dragDropOverwriteMode()) {
                foreach (const QItemSelectionRange &itemSelectionRange, selection) {
                    QModelIndex parent = itemSelectionRange.parent();

                    if (itemSelectionRange.left())
                        continue;

                    if (itemSelectionRange.right() != (model()->columnCount(parent)-1))
                        continue;

                    model()->removeRows(itemSelectionRange.top(),
                                        itemSelectionRange.bottom()-itemSelectionRange.top()+1,
                                        parent);
                }
            } else {
                // We can't remove the rows so reset the items (i.e. the view
                // is like a table)

                foreach (const QModelIndex &index, selection.indexes()) {
                    QMap<int, QVariant> roles = model()->itemData(index);

                    for (QMap<int, QVariant>::Iterator iter = roles.begin(),
                                                       iterEnd = roles.end();
                         iter != iterEnd; ++iter)
                        iter.value() = QVariant();

                    model()->setItemData(index, roles);
                }
            }
        }
    }
}

//==============================================================================

}   // namespace Core
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<commit_msg>Don't make rows' height uniform in TreeViewWidget. Indeed, this was causing problems with PropertyEditorWidget on Windows XP (both the top and the bottom of the icon of model parameters in the SingleCellView plugin were chopped off).<commit_after>/*******************************************************************************

Licensed to the OpenCOR team under one or more contributor license agreements.
See the NOTICE.txt file distributed with this work for additional information
regarding copyright ownership. The OpenCOR team licenses this file to you under
the Apache License, Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.

*******************************************************************************/

//==============================================================================
// Enhanced tree view widget
//==============================================================================

#include "treeviewwidget.h"

//==============================================================================

#include <QDrag>
#include <QHeaderView>
#include <QMouseEvent>
#include <QStandardItemModel>

//==============================================================================

namespace OpenCOR {
namespace Core {

//==============================================================================

TreeViewWidget::TreeViewWidget(QWidget *pParent) :
    QTreeView(pParent),
    CommonWidget(pParent)
{
    // Set some properties for our tree view widget

    setAllColumnsShowFocus(true);
#ifdef Q_OS_MAC
    setAttribute(Qt::WA_MacShowFocusRect, 0);
    // Note: the above removes the focus border since it messes up the look of
    //       our tree view widget...
#endif
    setFrameShape(QFrame::NoFrame);
    setSelectionMode(QAbstractItemView::ExtendedSelection);
}

//==============================================================================

void TreeViewWidget::resizeColumnsToContents()
{
    // Resize all our columns to their contents

    for (int i = 0, iMax = header()->count(); i < iMax; ++i)
        resizeColumnToContents(i);
}

//==============================================================================

void TreeViewWidget::selectItem(const int &pRow, const int &pColumn)
{
    // Select the requested item, if any

    QStandardItemModel *treeViewModel = qobject_cast<QStandardItemModel *>(model());

    if (treeViewModel) {
        // The tree view has a model associated with it, so we can retrieve the
        // requested item

        QStandardItem *treeViewItem = treeViewModel->invisibleRootItem()->child(pRow, pColumn);

        if (treeViewItem)
            // The requested item exists, so select it...

            setCurrentIndex(treeViewItem->index());
    }
}

//==============================================================================

void TreeViewWidget::selectFirstItem()
{
    // Select the first item

    selectItem();
}

//==============================================================================

bool TreeViewWidget::isEditing() const
{
    // Return whether our tree view widget is in editing mode

    return state() == QAbstractItemView::EditingState;
}

//==============================================================================

QSize TreeViewWidget::sizeHint() const
{
    // Suggest a default size for our tree view widget
    // Note: this is critical if we want a docked widget, with a tree view
    //       widget on it, to have a decent size when docked to the main
    //       window...

    return defaultSize(0.15);
}

//==============================================================================

void TreeViewWidget::keyPressEvent(QKeyEvent *pEvent)
{
    // Check some key combinations

    if (   !(pEvent->modifiers() & Qt::ShiftModifier)
        && !(pEvent->modifiers() & Qt::ControlModifier)
        && !(pEvent->modifiers() & Qt::AltModifier)
        && !(pEvent->modifiers() & Qt::MetaModifier)) {
        // None of the modifiers is selected

        if (pEvent->key() == Qt::Key_Left) {
            // The user wants to collapse the current item or go to its parent,
            // depending on the collapsed state of the current item

            QModelIndex currIndex = currentIndex();

            if (isExpanded(currIndex)) {
                // The current item is expanded, so collapse it

                setExpanded(currIndex, false);

                // Accept the event

                pEvent->accept();
            } else {
                // The current item is collapsed, so select its parent, but
                // only if it has a parent

                if (currIndex.parent() != QModelIndex()) {
                    setCurrentIndex(currIndex.parent());

                    // Accept the event

                    pEvent->accept();
                } else {
                    // Default handling of the event

                    QTreeView::keyPressEvent(pEvent);
                }
            }
        } else if (pEvent->key() == Qt::Key_Right) {
            // The user wants to expand the current item or go to its first
            // child, should it have children

            QModelIndex currIndex = currentIndex();

            if (model()->hasChildren(currIndex)) {
                if (!isExpanded(currIndex)) {
                    // The current item is collapsed, so expand it

                    setExpanded(currIndex, true);

                    // Accept the event

                    pEvent->accept();
                } else {
                    // The current item is expanded, so select its first child

                    setCurrentIndex(currIndex.child(0, 0));

                    // Accept the event

                    pEvent->accept();
                }
            } else {
                // The current item doesn't have any children, so do nothing
                // Note: normally, we would do the default handling of the
                //       event, but this will potentially shift the whole tree
                //       view contents to the right, so do nothing indeed...

                ;
            }
        } else {
            // Default handling of the event

            QTreeView::keyPressEvent(pEvent);
        }
    } else {
        // Default handling of the event

        QTreeView::keyPressEvent(pEvent);
    }
}

//==============================================================================

void TreeViewWidget::mousePressEvent(QMouseEvent *pEvent)
{
    // Default handling of the event

    QTreeView::mousePressEvent(pEvent);

    // Select the current item (if any), if we are right clicking
    // Note: the idea is that if we want to show a context menu, then nothing
    //       will happen if we are not over an item...

    if (pEvent->button() == Qt::RightButton)
        setCurrentIndex(indexAt(pEvent->pos()));
}

//==============================================================================

void TreeViewWidget::startDrag(Qt::DropActions pSupportedActions)
{
    // This a reimplementation of QAbstractItemView::startDrag, so that we can
    // provide OpenCOR with a better pixmap for the drag object
    // Note: indeed, on Windows, the pixmap only shows the dragged item that are
    //       visible in the QTreeView. Also, if there an item covers several
    //       columns, then the pixmap will show several 'cells' some of them
    //       empty if a column is empty, so... instead we want to provide a
    //       generic pixmap which looks 'good' on all platforms...

    // Retrieve the selected draggable items, if any
    // Note: the following code is based on
    //       QAbstractItemViewPrivate::selectedDraggableIndexes...

    QModelIndexList selectedDraggableIndexes = selectedIndexes();

    for (int i = selectedDraggableIndexes.count()-1; i >= 0; --i)
        if (   !(model()->flags(selectedDraggableIndexes[i]) & Qt::ItemIsDragEnabled)
            || selectedDraggableIndexes[i].column())
            // The current selected item is not draggable or is not in the first
            // column
            // Note: regarding the test on the column number, it is because we
            //       may have a model data that requires several columns (e.g.
            //       QFileSystemModel) in which case selectedIndexes would
            //       return a number of indexes equal to the number of rows
            //       times the number of columns while we only want a number of
            //       indexes to be equal to the number of rows (since we have a
            //       selection mode of QAbstractItemView::ExtendedSelection)

            selectedDraggableIndexes.removeAt(i);

    // Start the dragging action is there is at least one selected draggable
    // item

    if (selectedDraggableIndexes.count()) {
        // There is at least one selected draggable item, so create a QMimeData
        // object for it

        QMimeData *mimeData = model()->mimeData(selectedDraggableIndexes);

        if (!mimeData)
            return;

        // Create the pixmap that will be associated with the dragging action

        QPixmap pixmap((selectedDraggableIndexes.count() == 1)?
                           ":oxygen/mimetypes/application-x-zerosize.png":
                           ":oxygen/places/document-multiple.png");

        // Create the drag object

        QDrag *drag = new QDrag(this);

        drag->setMimeData(mimeData);
        drag->setPixmap(pixmap);
        drag->setHotSpot(QPoint(0.5*pixmap.width(), 0.5*pixmap.height()));

        // Do the dragging itself

        Qt::DropAction realDefaultDropAction = Qt::IgnoreAction;

        if (   (defaultDropAction() != Qt::IgnoreAction)
            && (pSupportedActions & defaultDropAction()))
            realDefaultDropAction = defaultDropAction();
        else if (   (pSupportedActions & Qt::CopyAction)
                 && (dragDropMode() != QAbstractItemView::InternalMove))
            realDefaultDropAction = Qt::CopyAction;

        if (drag->exec(pSupportedActions, realDefaultDropAction) == Qt::MoveAction) {
            // We want to move the items
            // Note: the following code is based on
            //       QAbstractItemViewPrivate::clearOrRemove...

            const QItemSelection selection = selectionModel()->selection();

            if (!dragDropOverwriteMode()) {
                foreach (const QItemSelectionRange &itemSelectionRange, selection) {
                    QModelIndex parent = itemSelectionRange.parent();

                    if (itemSelectionRange.left())
                        continue;

                    if (itemSelectionRange.right() != (model()->columnCount(parent)-1))
                        continue;

                    model()->removeRows(itemSelectionRange.top(),
                                        itemSelectionRange.bottom()-itemSelectionRange.top()+1,
                                        parent);
                }
            } else {
                // We can't remove the rows so reset the items (i.e. the view
                // is like a table)

                foreach (const QModelIndex &index, selection.indexes()) {
                    QMap<int, QVariant> roles = model()->itemData(index);

                    for (QMap<int, QVariant>::Iterator iter = roles.begin(),
                                                       iterEnd = roles.end();
                         iter != iterEnd; ++iter)
                        iter.value() = QVariant();

                    model()->setItemData(index, roles);
                }
            }
        }
    }
}

//==============================================================================

}   // namespace Core
}   // namespace OpenCOR

//==============================================================================
// End of file
//==============================================================================
<|endoftext|>


<commit_before>#include <thread>
#include <set>
#include "std/extension.hpp"
#include "xson/json.hpp"
#include "net/syslogstream.hpp"
#include "net/acceptor.hpp"
#include "net/uri.hpp"
#include "db/engine.hpp"

namespace db::rest {

using namespace std::string_literals;
using namespace std::chrono_literals;
using namespace net;
using namespace xson;

using std::string;
using std::thread;
using std::stringstream;
using std::chrono::system_clock;
using std::set;
using std::tuple;
using std::get;
using std::ws;

class server
{
public:

    server(db::engine& engine) :
    m_engine{engine}
    {}

    void start(const string& serice_or_port = "http"s)
    {
        slog << notice << "Starting server" << flush;
        auto endpoint = acceptor{"localhost"s, serice_or_port};
        endpoint.timeout(1h);
        slog << notice << "Started server" << flush;
        while(true)
        {
            slog << notice << "Accepting connections" << flush;
            auto client = endpoint.accept();
            slog << info << "Accepted connection" << flush;
            auto worker = thread{[&](){handle(move(client));}};
            std::this_thread::sleep_for(1s);
            worker.detach();
        }
    }

private:

    void handle(endpointstream client)
    {
        while(client)
        {
            slog << debug << "Reading HTTP request" << flush;
            auto method = ""s, uri = ""s, version = ""s;
            client >> method >> uri >> version;
            slog << info << method << ' ' << uri << ' ' << version << flush;

            if(version != "HTTP/1.1")
                break;

            slog << debug << "Read HTTP request" << flush;

            slog << debug << "Reading HTTP headers" << flush;

            client >> ws;

            while(client && client.peek() != '\r')
            {
                auto name = ""s, value = ""s;
                getline(client, name, ':');
                trim(name);
                getline(client, value);
                trim(value);
                slog << info << name << ": " << value << flush;
            }

            slog << debug << "Read HTTP headers" << flush;

            if(!methods.count(method))
            {
                slog << warning << "Method " << method << " is ignored" << flush;
                client << "HTTP/1.1 400 Bad Request"                  << crlf
                       << "Date: " << to_rfc1123(system_clock::now()) << crlf
                       << "Server: YarDB/0.1"                         << crlf
                       << "Content-Length: 0"                         << crlf
                       << crlf
                       << flush;
                continue;
            }

            if(uri == R"(/)"s || uri == R"(/favicon.ico)"s)
            {
                slog << debug << "URI " << uri << " is ignored" << flush;
                client << "HTTP/1.1 204 No Content"                   << crlf
                       << "Date: " << to_rfc1123(system_clock::now()) << crlf
                       << "Server: YarDB/0.1"                         << crlf
                       << "Content-Length: 0"                         << crlf
                       << crlf
                       << flush;
                continue;
            }

            slog << debug << "URI " << uri << " is OK" << flush;

            auto uri2 = net::uri{uri};
            auto root       = uri2.path[0];
            auto collection = uri2.path[1];
            auto key        = uri2.path[2];
            auto value      = uri2.path[3];
            auto query1     = uri2.query[0];
            auto query2     = uri2.query[1];

            auto selector = xson::object{};

            if(!key.empty() && std::numeric(key))                               //collection/123
                selector = {"id"s, stoll(key)};

            else if(key == "id"s && !value.empty() && std::numeric(value))      //collection/id/123
                selector = {"id"s, stoll(value)};

            else if(!key.empty() && !value.empty())                             //collection/field/value
                selector = {to_string(key), to_string(value)};

            else if(!key.empty() && !query1.empty())                            //collection/field?eq=value
                selector = {to_string(key), make_object(query1)};

            else if(!query1.empty())                                            //collection?top=10
                selector = {make_object(query1)};

            if(!query2.empty())                                                 //collection/field?gt=value?top=10
                selector += make_object(query2);

            m_engine.collection(to_string(collection));

            auto found = false;
            auto body = json::object{};

            if(method == "GET" || method == "HEAD")
            {
                slog << debug << "Reading " << collection << json::stringify(selector,0) << " from DB" << flush;
                found = m_engine.read(selector, body);
                slog << debug << "Read "   << collection << json::stringify(selector,0)  << " from DB" << flush;
            }
            else if(method == "DELETE")
            {
                slog << debug << "Deleting " << collection << json::stringify(selector,0) << " in DB"  << flush;
                found = m_engine.destroy(selector, body);
                slog << debug << "Deleted "  << collection << json::stringify(selector,0) << " in DB"  << flush;
            }
            else if(method == "POST" || method == "PUT" || method == "PATCH")
            {
                slog << debug << "Reading content" << flush;
                body = json::parse(client);
                slog << debug << "Read content " << json::stringify(body,0) << flush;
                slog << debug << "Updating " << collection << json::stringify(selector,0) << " in DB" << flush;
                if(method == "POST"s)
                    found = m_engine.create(body);
                else if(method == "PUT"s)
                    found = m_engine.replace(selector, body);
                else if(method == "PATCH"s)
                    found = m_engine.upsert(selector, body);
                slog << debug << "Updated "  << collection << json::stringify(selector,0) << " in DB" << flush;
            }

            slog << debug << "Sending HTTP response" << flush;

            const auto content = json::stringify(body);

            if(found)
            {
                // const auto content = json::stringify({"success"s, body});
                client << "HTTP/1.1 200 OK"                                                   << crlf
                       << "Date: " << to_rfc1123(system_clock::now())                         << crlf
                       << "Server: YarDB/0.1"                                              << crlf
                       << "Access-Control-Allow-Origin: *"                                    << crlf
                       << "Access-Control-Allow-Methods: HEAD, GET, POST, PUT, PATCH, DELETE" << crlf
                       << "Accept: application/json"                                          << crlf
                       << "Content-Type: application/json"                                    << crlf
                       << "Content-Length: " << content.length()                              << crlf
                       << crlf
                       << (method != "HEAD"s ? content : ""s) << flush;
            }
            else
            {
                // const auto content = json::stringify({"error"s, body});
                client << "HTTP/1.1 404 Not Found"                                            << crlf
                       << "Date: " << to_rfc1123(system_clock::now())                         << crlf
                       << "Server: YarDB/0.1"                                              << crlf
                       << "Access-Control-Allow-Origin: *"                                    << crlf
                       << "Access-Control-Allow-Methods: HEAD, GET, POST, PUT, PATCH, DELETE" << crlf
                       << "Accept: application/json"                                          << crlf
                       << "Content-Type: application/json"                                    << crlf
                       << "Content-Length: " << content.length()                              << crlf
                       << crlf
                       << (method != "HEAD"s ? content : ""s) << flush;
            }
            slog << debug << "Sent HTTP response" << flush;
        }
        slog << debug << "client closed" << flush;
    }

    xson::object make_object(const string_view query)
    {
        auto pos = query.find_first_of('=');
        auto name = to_string(query.substr(0,pos));
        auto value = query.substr(pos+1,query.length());
        if(numeric(value))
            return {"$"s + name, stoll(value)};
        else
            return {"$"s + name, to_string(value)};
    }

    const set<string> methods = {"HEAD", "GET", "POST", "PUT", "PATCH", "DELETE"};

    db::engine& m_engine;
};

} // namespace db::rest
<commit_msg>Clean up<commit_after>#include <thread>
#include <set>
#include "std/extension.hpp"
#include "xson/json.hpp"
#include "net/syslogstream.hpp"
#include "net/acceptor.hpp"
#include "net/uri.hpp"
#include "db/engine.hpp"

namespace db::rest {

using namespace std::string_literals;
using namespace std::chrono_literals;
using namespace net;
using namespace xson;

using std::string;
using std::thread;
using std::stringstream;
using std::chrono::system_clock;
using std::set;
using std::tuple;
using std::get;
using std::ws;

class server
{
public:

    server(db::engine& engine) :
    m_engine{engine}
    {}

    void start(const string& serice_or_port = "http"s)
    {
        slog << notice << "Starting server" << flush;
        auto endpoint = acceptor{"localhost"s, serice_or_port};
        endpoint.timeout(1h);
        slog << notice << "Started server" << flush;
        while(true)
        {
            slog << notice << "Accepting connections" << flush;
            auto client = endpoint.accept();
            slog << info << "Accepted connection" << flush;
            auto worker = thread{[&](){handle(move(client));}};
            std::this_thread::sleep_for(1s);
            worker.detach();
        }
    }

private:

    void handle(endpointstream client)
    {
        while(client)
        {
            slog << debug << "Reading HTTP request message" << flush;

            slog << debug << "Reading HTTP request line" << flush;

            auto method = ""s, request_uri = ""s, http_version = ""s;
            client >> method >> request_uri >> http_version;
            slog << info << "Received request " << method << ' ' << request_uri << ' ' << http_version << flush;

            slog << debug << "Read HTTP request line" << flush;

            slog << debug << "Reading HTTP request headers" << flush;

            client >> ws;

            while(client && client.peek() != '\r')
            {
                auto request_header = ""s, value = ""s;
                getline(client, request_header, ':');
                trim(request_header);
                getline(client, value);
                trim(value);
                slog << info << "With request header " << request_header << ": " << value << flush;
            }

            slog << debug << "Read HTTP request headers" << flush;

            slog << debug << "Read HTTP request message" << flush;

            if(!methods.count(method))
            {
                slog << notice << "HTTP requests message with method " << method << " was ignored" << flush;
                client << "HTTP/1.1 400 Bad Request"                  << crlf
                       << "Date: " << to_rfc1123(system_clock::now()) << crlf
                       << "Server: YarDB/0.1"                         << crlf
                       << "Content-Length: 0"                         << crlf
                       << crlf
                       << flush;
                continue; // ignore with continue
            }

            if(request_uri == "/" || request_uri == "/favicon.ico")
            {
                slog << info << "HTTP requests message with request URI " << request_uri << " was ignored" << flush;
                client << "HTTP/1.1 404 Not Found"                    << crlf
                       << "Date: " << to_rfc1123(system_clock::now()) << crlf
                       << "Server: YarDB/0.1"                         << crlf
                       << "Content-Length: 0"                         << crlf
                       << crlf
                       << flush;
                continue; // ignore with continue
            }

            if(http_version != "HTTP/1.1")
            {
                slog << warning << "HTTP requests message with HTTP version " << http_version << " was rejected" << flush;
                client << "HTTP/1.1 505 HTTP Version Not Supported"   << crlf
                       << "Date: " << to_rfc1123(system_clock::now()) << crlf
                       << "Server: YarDB/0.1"                         << crlf
                       << "Content-Length: 0"                         << crlf
                       << crlf
                       << flush;
                break; // reject with break
            }

            slog << debug << "HTTP request with request URI " << request_uri << " was OK" << flush;

            auto components = net::uri{request_uri};
            auto root       = components.path[0];
            auto collection = components.path[1];
            auto key        = components.path[2];
            auto value      = components.path[3];
            auto query1     = components.query[0];
            auto query2     = components.query[1];

            m_engine.collection(to_string(collection));

            auto selector = xson::object{};

            if(!key.empty() && std::numeric(key))                               //collection/123
                selector = {"id"s, stoll(key)};

            else if(key == "id"s && !value.empty() && std::numeric(value))      //collection/id/123
                selector = {"id"s, stoll(value)};

            else if(!key.empty() && !value.empty())                             //collection/field/value
                selector = {to_string(key), to_string(value)};

            else if(!key.empty() && !query1.empty())                            //collection/field?eq=value
                selector = {to_string(key), make_object(query1)};

            else if(!query1.empty())                                            //collection?top=10
                selector = {make_object(query1)};

            if(!query2.empty())                                                 //collection/field?gt=value?top=10
                selector += make_object(query2);

            auto found = false;

            auto body = json::object{};

            if(method == "GET" || method == "HEAD")
            {
                slog << debug << "Reading from collection " << collection << " with selector "<< json::stringify(selector,0) << flush;
                found = m_engine.read(selector, body);
                slog << info << "Read from collection " << collection << " with selector " << json::stringify(selector,0) << flush;
            }
            else if(method == "DELETE")
            {
                slog << debug << "Deleting from collection " << collection <<  " with selector " << json::stringify(selector,0) << flush;
                found = m_engine.destroy(selector, body);
                slog << info << "Deleted from collection "  << collection <<  " with selector " << json::stringify(selector,0) << flush;
            }
            else if(method == "POST" || method == "PUT" || method == "PATCH")
            {
                slog << debug << "Reading HTTP request body" << flush;
                body = json::parse(client);
                slog << debug << "Read  HTTP request body " << json::stringify(body,0) << flush;
                slog << debug << "Updating in collection " << collection <<  " with selector " << json::stringify(selector,0) << flush;
                if(method == "POST"s)
                    found = m_engine.create(body);
                else if(method == "PUT"s)
                    found = m_engine.replace(selector, body);
                else if(method == "PATCH"s)
                    found = m_engine.upsert(selector, body);
                slog << info << "Updated in collection "  << collection <<  " with selector " << json::stringify(selector,0) << flush;
            }

            slog << debug << "Sending HTTP response message" << flush;

            const auto content = json::stringify(body);

            if(found)
            {
                // const auto content = json::stringify({"success"s, body});
                client << "HTTP/1.1 200 OK"                                                   << crlf
                       << "Date: " << to_rfc1123(system_clock::now())                         << crlf
                       << "Server: YarDB/0.1"                                              << crlf
                       << "Access-Control-Allow-Origin: *"                                    << crlf
                       << "Access-Control-Allow-Methods: HEAD, GET, POST, PUT, PATCH, DELETE" << crlf
                       << "Accept: application/json"                                          << crlf
                       << "Content-Type: application/json"                                    << crlf
                       << "Content-Length: " << content.length()                              << crlf
                       << crlf
                       << (method != "HEAD"s ? content : ""s) << flush;
            }
            else
            {
                // const auto content = json::stringify({"error"s, body});
                client << "HTTP/1.1 404 Not Found"                                            << crlf
                       << "Date: " << to_rfc1123(system_clock::now())                         << crlf
                       << "Server: YarDB/0.1"                                              << crlf
                       << "Access-Control-Allow-Origin: *"                                    << crlf
                       << "Access-Control-Allow-Methods: HEAD, GET, POST, PUT, PATCH, DELETE" << crlf
                       << "Accept: application/json"                                          << crlf
                       << "Content-Type: application/json"                                    << crlf
                       << "Content-Length: " << content.length()                              << crlf
                       << crlf
                       << (method != "HEAD"s ? content : ""s) << flush;
            }
            slog << debug << "Sent HTTP response message" << flush;
        }

        slog << debug << "Client closed connection" << flush;
    }

    xson::object make_object(const string_view query)
    {
        auto pos = query.find_first_of('=');
        auto name = to_string(query.substr(0,pos));
        auto value = query.substr(pos+1,query.length());
        if(numeric(value))
            return {"$"s + name, stoll(value)};
        else
            return {"$"s + name, to_string(value)};
    }

    const set<string> methods = {"HEAD", "GET", "POST", "PUT", "PATCH", "DELETE"};

    db::engine& m_engine;
};

} // namespace db::rest
<|endoftext|>


<commit_before>/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "BitMatrix.h"
#include "BitArray.h"
#include "ByteMatrix.h"

#ifndef ZX_FAST_BIT_STORAGE
#include "BitHacks.h"
#endif

#include <algorithm>
#include <stdexcept>
#include <utility>

namespace ZXing {

void
BitMatrix::getRow(int y, BitArray& row) const
{
	if (y < 0 || y >= _height) {
		throw std::out_of_range("Requested row is outside the matrix");
	}
	if (row.size() != _width)
		row = BitArray(_width);
	std::copy_n(_bits.begin() + y * _rowSize, _rowSize, row._bits.begin());
}

void
BitMatrix::setRow(int y, const BitArray& row)
{
	if (int(row._bits.size()) != _rowSize) {
		throw std::invalid_argument("BitMatrix::setRegion(): row sizes do not match");
	}
	std::copy(row._bits.begin(), row._bits.end(), _bits.begin() + y *_rowSize);
}

ByteMatrix BitMatrix::toByteMatrix(int black, int white) const
{
	ByteMatrix res(width(), height());
	for (int y = 0; y < height(); ++y)
		for (int x = 0; x < width(); ++x)
			res.set(x, y, get(x, y) ? black : white);
	return res;
}

void
BitMatrix::setRegion(int left, int top, int width, int height)
{
	if (top < 0 || left < 0) {
		throw std::invalid_argument("BitMatrix::setRegion(): Left and top must be nonnegative");
	}
	if (height < 1 || width < 1) {
		throw std::invalid_argument("BitMatrix::setRegion(): Height and width must be at least 1");
	}
	int right = left + width;
	int bottom = top + height;
	if (bottom > _height || right > _width) {
		throw std::invalid_argument("BitMatrix::setRegion(): The region must fit inside the matrix");
	}
	for (int y = top; y < bottom; y++) {
		size_t offset = y * _rowSize;
		for (int x = left; x < right; x++) {
#ifdef ZX_FAST_BIT_STORAGE
			_bits[offset + x] = 1;
#else
			_bits[offset + (x / 32)] |= 1 << (x & 0x1f);
#endif
		}
	}
}

void
BitMatrix::rotate90()
{
	BitMatrix result(height(), width());
	for (int x = 0; x < width(); ++x) {
		for (int y = 0; y < height(); ++y) {
			if (get(x, y)) {
				result.set(y, width() - x - 1);
			}
		}
	}
	*this = std::move(result);
}

void
BitMatrix::rotate180()
{
#ifdef ZX_FAST_BIT_STORAGE
	std::reverse(_bits.begin(), _bits.end());
#else
	BitHacks::Reverse(_bits, _rowSize * 32 - _width);
#endif
}

void
BitMatrix::mirror()
{
	for (int x = 0; x < _width; x++) {
		for (int y = x + 1; y < _height; y++) {
			if (get(x, y) != get(y, x)) {
				flip(y, x);
				flip(x, y);
			}
		}
	}
}

bool
BitMatrix::findBoundingBox(int &left, int& top, int& width, int& height, int minSize) const
{
	left = _width;
	top = _height;
	int right = -1;
	int bottom = -1;

	for (int y = 0; y < _height; y++)
	{
		for (int x32 = 0; x32 < _rowSize; x32++)
		{
			uint32_t theBits = _bits[y * _rowSize + x32];
			if (theBits != 0)
			{
				if (y < top) {
					top = y;
				}
				if (y > bottom) {
					bottom = y;
				}
#ifdef ZX_FAST_BIT_STORAGE
				if (x32 < left)
					left = x32;
				if (x32 > right)
					right = x32;
#else
				if (x32 * 32 < left) {
					int bit = 0;
					while ((theBits << (31 - bit)) == 0) {
						bit++;
					}
					if ((x32 * 32 + bit) < left) {
						left = x32 * 32 + bit;
					}
				}
				if (x32 * 32 + 31 > right) {
					int bit = 31;
					while ((theBits >> bit) == 0) {
						bit--;
					}
					if ((x32 * 32 + bit) > right) {
						right = x32 * 32 + bit;
					}
				}
#endif
			}
		}
	}
	width = right - left + 1;
	height = bottom - top + 1;
	return width >= minSize && height >= minSize;
}

bool
BitMatrix::getTopLeftOnBit(int& left, int& top) const
{
	int bitsOffset = std::distance(_bits.begin(), std::find_if(_bits.begin(), _bits.end(), [](auto v) { return v; }));
	if (bitsOffset == (int)_bits.size()) {
		return false;
	}
	top = bitsOffset / _rowSize;
	left = (bitsOffset % _rowSize);
#ifndef ZX_FAST_BIT_STORAGE
	left = left * 32 + BitHacks::NumberOfTrailingZeros(_bits[bitsOffset]);
#endif
	return true;
}

bool
BitMatrix::getBottomRightOnBit(int& right, int& bottom) const
{
	int bitsOffset =
		_bits.size() - 1 -
		std::distance(_bits.rbegin(), std::find_if(_bits.rbegin(), _bits.rend(), [](auto v) { return v; }));
	if (bitsOffset < 0) {
		return false;
	}

	bottom = bitsOffset / _rowSize;
	right = (bitsOffset % _rowSize);
#ifndef ZX_FAST_BIT_STORAGE
	right = right * 32 + 31 - BitHacks::NumberOfLeadingZeros(_bits[bitsOffset]);
#endif
	return true;
}

BitMatrix Inflate(BitMatrix&& input, int width, int height, int quietZone)
{
	const int codeWidth = input.width();
	const int codeHeight = input.height();
	const int outputWidth = std::max(width, codeWidth + 2 * quietZone);
	const int outputHeight = std::max(height, codeHeight + 2 * quietZone);

	if (input.width() == outputWidth && input.height() == outputHeight)
		return std::move(input);

	const int scale = std::min((outputWidth - 2*quietZone) / codeWidth, (outputHeight - 2*quietZone) / codeHeight);
	// Padding includes both the quiet zone and the extra white pixels to
	// accommodate the requested dimensions.
	const int leftPadding = (outputWidth - (codeWidth * scale)) / 2;
	const int topPadding = (outputHeight - (codeHeight * scale)) / 2;

	BitMatrix result(outputWidth, outputHeight);

	for (int inputY = 0, outputY = topPadding; inputY < input.height(); ++inputY, outputY += scale) {
		for (int inputX = 0, outputX = leftPadding; inputX < input.width(); ++inputX, outputX += scale) {
			if (input.get(inputX, inputY))
				result.setRegion(outputX, outputY, scale, scale);
		}
	}

	return result;
}

BitMatrix Deflate(const BitMatrix& input, int width, int height, int top, int left, int subSampling)
{
	BitMatrix result(width, height);
	for (int y = 0; y < result.height(); y++) {
		int yOffset = top + y * subSampling;
		for (int x = 0; x < result.width(); x++) {
			if (input.get(left + x * subSampling, yOffset))
				result.set(x, y);
		}
	}
	return result;

}

} // ZXing

<commit_msg>BitMatrix: fix most of the slight performace regression in findBoundingBox<commit_after>/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include "BitMatrix.h"
#include "BitArray.h"
#include "ByteMatrix.h"

#ifndef ZX_FAST_BIT_STORAGE
#include "BitHacks.h"
#endif

#include <algorithm>
#include <stdexcept>
#include <utility>

namespace ZXing {

void
BitMatrix::getRow(int y, BitArray& row) const
{
	if (y < 0 || y >= _height) {
		throw std::out_of_range("Requested row is outside the matrix");
	}
	if (row.size() != _width)
		row = BitArray(_width);
	std::copy_n(_bits.begin() + y * _rowSize, _rowSize, row._bits.begin());
}

void
BitMatrix::setRow(int y, const BitArray& row)
{
	if (int(row._bits.size()) != _rowSize) {
		throw std::invalid_argument("BitMatrix::setRegion(): row sizes do not match");
	}
	std::copy(row._bits.begin(), row._bits.end(), _bits.begin() + y *_rowSize);
}

ByteMatrix BitMatrix::toByteMatrix(int black, int white) const
{
	ByteMatrix res(width(), height());
	for (int y = 0; y < height(); ++y)
		for (int x = 0; x < width(); ++x)
			res.set(x, y, get(x, y) ? black : white);
	return res;
}

void
BitMatrix::setRegion(int left, int top, int width, int height)
{
	if (top < 0 || left < 0) {
		throw std::invalid_argument("BitMatrix::setRegion(): Left and top must be nonnegative");
	}
	if (height < 1 || width < 1) {
		throw std::invalid_argument("BitMatrix::setRegion(): Height and width must be at least 1");
	}
	int right = left + width;
	int bottom = top + height;
	if (bottom > _height || right > _width) {
		throw std::invalid_argument("BitMatrix::setRegion(): The region must fit inside the matrix");
	}
	for (int y = top; y < bottom; y++) {
		size_t offset = y * _rowSize;
		for (int x = left; x < right; x++) {
#ifdef ZX_FAST_BIT_STORAGE
			_bits[offset + x] = 1;
#else
			_bits[offset + (x / 32)] |= 1 << (x & 0x1f);
#endif
		}
	}
}

void
BitMatrix::rotate90()
{
	BitMatrix result(height(), width());
	for (int x = 0; x < width(); ++x) {
		for (int y = 0; y < height(); ++y) {
			if (get(x, y)) {
				result.set(y, width() - x - 1);
			}
		}
	}
	*this = std::move(result);
}

void
BitMatrix::rotate180()
{
#ifdef ZX_FAST_BIT_STORAGE
	std::reverse(_bits.begin(), _bits.end());
#else
	BitHacks::Reverse(_bits, _rowSize * 32 - _width);
#endif
}

void
BitMatrix::mirror()
{
	for (int x = 0; x < _width; x++) {
		for (int y = x + 1; y < _height; y++) {
			if (get(x, y) != get(y, x)) {
				flip(y, x);
				flip(x, y);
			}
		}
	}
}

bool
BitMatrix::findBoundingBox(int &left, int& top, int& width, int& height, int minSize) const
{
	int right, bottom;
	if (!getTopLeftOnBit(left, top) || !getBottomRightOnBit(right, bottom) || bottom - top + 1 < minSize)
		return false;

#ifdef ZX_FAST_BIT_STORAGE
	for (int y = top; y <= bottom; y++ ) {
		for (int x = 0; x < left; ++x)
			if (get(x, y)) {
				left = x;
				break;
			}
		for (int x = _width-1; x > right; x--)
			if (get(x, y)) {
				right = x;
				break;
			}
	}
#else
	for (int y = top; y <= bottom; y++)
	{
		for (int x32 = 0; x32 < _rowSize; x32++)
		{
			uint32_t theBits = _bits[y * _rowSize + x32];
			if (theBits != 0)
			{
				if (x32 * 32 < left) {
					int bit = 0;
					while ((theBits << (31 - bit)) == 0) {
						bit++;
					}
					if ((x32 * 32 + bit) < left) {
						left = x32 * 32 + bit;
					}
				}
				if (x32 * 32 + 31 > right) {
					int bit = 31;
					while ((theBits >> bit) == 0) {
						bit--;
					}
					if ((x32 * 32 + bit) > right) {
						right = x32 * 32 + bit;
					}
				}
			}
		}
	}
#endif

	width = right - left + 1;
	height = bottom - top + 1;
	return width >= minSize && height >= minSize;
}

bool
BitMatrix::getTopLeftOnBit(int& left, int& top) const
{
	int bitsOffset = std::distance(_bits.begin(), std::find_if(_bits.begin(), _bits.end(), [](auto v) { return v; }));
	if (bitsOffset == (int)_bits.size()) {
		return false;
	}
	top = bitsOffset / _rowSize;
	left = (bitsOffset % _rowSize);
#ifndef ZX_FAST_BIT_STORAGE
	left = left * 32 + BitHacks::NumberOfTrailingZeros(_bits[bitsOffset]);
#endif
	return true;
}

bool
BitMatrix::getBottomRightOnBit(int& right, int& bottom) const
{
	int bitsOffset =
		_bits.size() - 1 -
		std::distance(_bits.rbegin(), std::find_if(_bits.rbegin(), _bits.rend(), [](auto v) { return v; }));
	if (bitsOffset < 0) {
		return false;
	}

	bottom = bitsOffset / _rowSize;
	right = (bitsOffset % _rowSize);
#ifndef ZX_FAST_BIT_STORAGE
	right = right * 32 + 31 - BitHacks::NumberOfLeadingZeros(_bits[bitsOffset]);
#endif
	return true;
}

BitMatrix Inflate(BitMatrix&& input, int width, int height, int quietZone)
{
	const int codeWidth = input.width();
	const int codeHeight = input.height();
	const int outputWidth = std::max(width, codeWidth + 2 * quietZone);
	const int outputHeight = std::max(height, codeHeight + 2 * quietZone);

	if (input.width() == outputWidth && input.height() == outputHeight)
		return std::move(input);

	const int scale = std::min((outputWidth - 2*quietZone) / codeWidth, (outputHeight - 2*quietZone) / codeHeight);
	// Padding includes both the quiet zone and the extra white pixels to
	// accommodate the requested dimensions.
	const int leftPadding = (outputWidth - (codeWidth * scale)) / 2;
	const int topPadding = (outputHeight - (codeHeight * scale)) / 2;

	BitMatrix result(outputWidth, outputHeight);

	for (int inputY = 0, outputY = topPadding; inputY < input.height(); ++inputY, outputY += scale) {
		for (int inputX = 0, outputX = leftPadding; inputX < input.width(); ++inputX, outputX += scale) {
			if (input.get(inputX, inputY))
				result.setRegion(outputX, outputY, scale, scale);
		}
	}

	return result;
}

BitMatrix Deflate(const BitMatrix& input, int width, int height, int top, int left, int subSampling)
{
	BitMatrix result(width, height);
	for (int y = 0; y < result.height(); y++) {
		int yOffset = top + y * subSampling;
		for (int x = 0; x < result.width(); x++) {
			if (input.get(left + x * subSampling, yOffset))
				result.set(x, y);
		}
	}
	return result;

}

} // ZXing

<|endoftext|>