/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF 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.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_cli_ure.hxx"

#include <stdio.h>
#include <vector>
#include <memory>

#include "climaker_share.h"

#include "sal/main.h"
#include "osl/process.h"
#include "osl/file.hxx"
#include "osl/thread.h"
#include "rtl/ustrbuf.hxx"
#include "cppuhelper/shlib.hxx"
#include "cppuhelper/bootstrap.hxx"
#include "com/sun/star/lang/XInitialization.hpp"
#include "com/sun/star/lang/XSingleComponentFactory.hpp"
#include "com/sun/star/lang/XComponent.hpp"
#include "com/sun/star/container/XHierarchicalNameAccess.hpp"
#include "com/sun/star/container/XSet.hpp"
#include "com/sun/star/reflection/XTypeDescriptionEnumerationAccess.hpp"
#include "com/sun/star/registry/XSimpleRegistry.hpp"

using namespace ::std;
using namespace ::System::Reflection;


using namespace ::rtl;
using namespace ::osl;
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;

namespace climaker
{

//------------------------------------------------------------------------------
static char const s_usingText [] =
"\n"
"using: climaker <switches> [registry-file-1 registry-file-2 ...]\n"
"\n"
"switches:\n"
" -O, --out <output-file>       output assembly file;\n"
"                               defaults to cli_unotypes.dll if more than one\n"
"                               registry-file is given, else <registry-file>.dll\n"
" -T, --types                   types to be generated (if none is given,\n"
"   <type1[;type2;...]>         then all types of given registries are emitted\n"
" -X, --extra <rdb-file>        additional rdb to saturate referenced types in\n"
"                               given registry file(s); these types will not be\n"
"                               emitted into the output assembly file\n"
" -r, --reference               reference metadata from assembly file\n"
"   <assembly-file>\n"
" -k, --keyfile                 keyfile needed for strong name\n"
" --assembly-version <version>  sets assembly version\n"
" --assembly-description <text> sets assembly description text\n"
" --assembly-product <text>     sets assembly product name\n"
" --assembly-company <text>     sets assembly company\n"
" --assembly-copyright <text>   sets assembly copyright\n"
" --assembly-trademark <text>   sets assembly trademark\n"
" -v, --verbose                 verbose output to stdout\n"
" -h, --help                    this message\n"
"\n"
"example: climaker --out cli_mytypes.dll \\\n"
"                  --reference cli_uretypes.dll \\\n"
"                  --extra types.rdb \\\n"
"                  mytypes.rdb\n"
"\n";

struct OptionInfo
{
    char const * m_name;
    sal_uInt32 m_name_length;
    sal_Unicode m_short_option;
    bool m_has_argument;
};

bool g_verbose = false;

//------------------------------------------------------------------------------
static const OptionInfo s_option_infos [] = {
    { RTL_CONSTASCII_STRINGPARAM("out"), 'O', true },
    { RTL_CONSTASCII_STRINGPARAM("types"), 'T', true },
    { RTL_CONSTASCII_STRINGPARAM("extra"), 'X', true },
    { RTL_CONSTASCII_STRINGPARAM("reference"), 'r', true },
    { RTL_CONSTASCII_STRINGPARAM("keyfile"), 'k', true },
    { RTL_CONSTASCII_STRINGPARAM("delaySign"), 'd', true },
    { RTL_CONSTASCII_STRINGPARAM("assembly-version"), '\0', true },
    { RTL_CONSTASCII_STRINGPARAM("assembly-description"), '\0', true },
    { RTL_CONSTASCII_STRINGPARAM("assembly-product"), '\0', true },
    { RTL_CONSTASCII_STRINGPARAM("assembly-company"), '\0', true },
    { RTL_CONSTASCII_STRINGPARAM("assembly-copyright"), '\0', true },
    { RTL_CONSTASCII_STRINGPARAM("assembly-trademark"), '\0', true },
    { RTL_CONSTASCII_STRINGPARAM("verbose"), 'v', false },
    { RTL_CONSTASCII_STRINGPARAM("help"), 'h', false }
};

//==============================================================================
static OptionInfo const * get_option_info(
    OUString const & opt, sal_Unicode copt = '\0' )
{
    for ( sal_Int32 pos = 0;
          pos < (sizeof (s_option_infos) / sizeof (OptionInfo));
          ++pos )
    {
        OptionInfo const & option_info = s_option_infos[ pos ];
        
        if (opt.getLength() > 0)
        {
            if (opt.equalsAsciiL(
                    option_info.m_name, option_info.m_name_length ) &&
                (copt == '\0' || copt == option_info.m_short_option))
            {
                return &option_info;
            }
        }
        else
        {
            OSL_ASSERT( copt != '\0' );
            if (copt == option_info.m_short_option)
            {
                return &option_info;
            }
        }
    }
    OSL_ENSURE(
        0, OUStringToOString( opt, osl_getThreadTextEncoding() ).getStr() );
    return 0;
}

//==============================================================================
static bool is_option(
    OptionInfo const * option_info, sal_uInt32 * pIndex )
{
    OSL_ASSERT( option_info != 0 );
    if (osl_getCommandArgCount() <= *pIndex)
        return false;
    
    OUString arg;
    osl_getCommandArg( *pIndex, &arg.pData );
    sal_Int32 len = arg.getLength();
    
    if (len < 2 || arg[ 0 ] != '-')
        return false;
    
    if (len == 2 && arg[ 1 ] == option_info->m_short_option)
    {
        ++(*pIndex);
#if OSL_DEBUG_LEVEL > 1
        OSL_TRACE(
            __FILE__": identified option \'%c\'", option_info->m_short_option );
#endif
        return true;
    }
    if (arg[ 1 ] == '-' && rtl_ustr_ascii_compare(
            arg.pData->buffer + 2, option_info->m_name ) == 0)
    {
        ++(*pIndex);
#if OSL_DEBUG_LEVEL > 1
        OSL_TRACE( __FILE__": identified option \'%s\'", option_info->m_name );
#endif
        return true;
    }
    return false;
}

//==============================================================================
static inline bool read_option(
    bool * flag, OptionInfo const * option_info, sal_uInt32 * pIndex )
{
    bool ret = is_option( option_info, pIndex );
    if (ret)
        *flag = true;
    return ret;
}

//==============================================================================
static bool read_argument(
    OUString * pValue, OptionInfo const * option_info, sal_uInt32 * pIndex )
{
    if (is_option( option_info, pIndex ))
    {
        if (*pIndex < osl_getCommandArgCount())
        {
            osl_getCommandArg( *pIndex, &pValue->pData );
            ++(*pIndex);
#if OSL_DEBUG_LEVEL > 1
            OString cstr_val(
                OUStringToOString( *pValue, osl_getThreadTextEncoding() ) );
            OSL_TRACE( __FILE__": argument value: %s\n", cstr_val.getStr() );
#endif
            return true;
        }
        --(*pIndex);
    }
    return false;
}

//==============================================================================
static OUString const & path_get_working_dir()
{
    static OUString s_workingDir;
    if (! s_workingDir.getLength())
        osl_getProcessWorkingDir( &s_workingDir.pData );
    return s_workingDir;
}

//==============================================================================
static OUString path_make_absolute_file_url( OUString const & path )
{
    OUString file_url;
    oslFileError rc = osl_getFileURLFromSystemPath(
        path.pData, &file_url.pData );
    if (osl_File_E_None == rc)
    {
        OUString abs;
        rc = osl_getAbsoluteFileURL(
            path_get_working_dir().pData, file_url.pData, &abs.pData );
        if (osl_File_E_None == rc)
        {
            return abs;
        }
        else
        {
            throw RuntimeException(
                OUSTR("cannot make absolute: ") + file_url,
                Reference< XInterface >() );
        }
    }
    else
    {
        throw RuntimeException(
            OUSTR("cannot get file url from system path: ") + path,
            Reference< XInterface >() );
    }
}

//==============================================================================
Reference< registry::XSimpleRegistry > open_registries(
    vector< OUString > const & registries,
    Reference< XComponentContext > xContext )
{
    if (registries.empty())
    {
        throw RuntimeException(
            OUSTR("no registries given!"),
            Reference< XInterface >() );
    }
    
    Reference< registry::XSimpleRegistry > xSimReg;
    for ( size_t nPos = registries.size(); nPos--; )
    {
        Reference< registry::XSimpleRegistry > xReg(
            xContext->getServiceManager()->createInstanceWithContext(
                OUSTR("com.sun.star.registry.SimpleRegistry"), xContext ),
            UNO_QUERY_THROW );
        xReg->open( registries[ nPos ], sal_True, sal_False );
        if (! xReg->isValid())
        {
            throw RuntimeException(
                OUSTR("invalid registry: ") + registries[ nPos ],
                Reference< XInterface >() );
        }
        
        if (xSimReg.is()) // nest?
        {
            Reference< registry::XSimpleRegistry > xNested(
                xContext->getServiceManager()->createInstanceWithContext(
                    OUSTR("com.sun.star.registry.NestedRegistry"), xContext ),
                UNO_QUERY_THROW );
            Reference< lang::XInitialization > xInit(
                xNested, UNO_QUERY_THROW );
            Sequence< Any > args( 2 );
            args[ 0 ] <<= xReg;
            args[ 1 ] <<= xSimReg;
            xInit->initialize( args );
            xSimReg = xNested;
        }
        else
        {
            xSimReg = xReg;
        }
    }
    
    return xSimReg;
}

}

using namespace ::climaker;

//##############################################################################
SAL_IMPLEMENT_MAIN()
{
    sal_uInt32 nCount = osl_getCommandArgCount();
    if (0 == nCount)
    {
        printf( s_usingText );
        return 0;
    }
    
	int ret = 0;
    Reference< XComponentContext > xContext;
    
	try
	{    
        OptionInfo const * info_help =
            get_option_info( OUSTR("help") );
        OptionInfo const * info_verbose =
            get_option_info( OUSTR("verbose") );
        OptionInfo const * info_out =
            get_option_info( OUSTR("out") );
        OptionInfo const * info_types =
            get_option_info( OUSTR("types") );
        OptionInfo const * info_reference =
            get_option_info( OUSTR("reference") );
        OptionInfo const * info_extra =
            get_option_info( OUSTR("extra") );
        OptionInfo const * info_keyfile =
            get_option_info( OUSTR("keyfile") );
        OptionInfo const * info_delaySign =
            get_option_info( OUSTR("delaySign") );
        OptionInfo const * info_version =
            get_option_info( OUSTR("assembly-version") );
        OptionInfo const * info_product =
            get_option_info( OUSTR("assembly-product") );
        OptionInfo const * info_description =
            get_option_info( OUSTR("assembly-description") );
        OptionInfo const * info_company =
            get_option_info( OUSTR("assembly-company") );
        OptionInfo const * info_copyright =
            get_option_info( OUSTR("assembly-copyright") );
        OptionInfo const * info_trademark =
            get_option_info( OUSTR("assembly-trademark") );
        
        OUString output;
        vector< OUString > mandatory_registries;
        vector< OUString > extra_registries;
        vector< OUString > extra_assemblies;
        vector< OUString > explicit_types;
        OUString version, product, description, company, copyright, trademark,
            keyfile, delaySign;
        
        OUString cmd_arg;
		for ( sal_uInt32 nPos = 0; nPos < nCount; )
		{
            // options
			if (is_option( info_help, &nPos ))
			{
                printf( s_usingText );
                return 0;
			}
			else if (read_argument( &cmd_arg, info_types, &nPos ))
			{
                sal_Int32 index = 0;
                do
                {
                    explicit_types.push_back(
                        cmd_arg.getToken( 0, ';', index ) );
                }
                while (index >= 0);
			}
            else if (read_argument( &cmd_arg, info_extra, &nPos ))
            {
                extra_registries.push_back(
                    path_make_absolute_file_url( cmd_arg ) );
            }
            else if (read_argument( &cmd_arg, info_reference, &nPos ))
            {
                extra_assemblies.push_back(
                    path_make_absolute_file_url( cmd_arg ) );
            }
            else if (!read_option( &g_verbose, info_verbose, &nPos ) &&
                     !read_argument( &output, info_out, &nPos ) &&
                     !read_argument( &version, info_version, &nPos ) &&
                     !read_argument( &description, info_description, &nPos ) &&
                     !read_argument( &product, info_product, &nPos ) &&
                     !read_argument( &company, info_company, &nPos ) &&
                     !read_argument( &copyright, info_copyright, &nPos ) &&
                     !read_argument( &trademark, info_trademark, &nPos ) &&
                     !read_argument( &keyfile, info_keyfile, &nPos ) &&
                     !read_argument( &delaySign, info_delaySign, &nPos ))
            {
                if ( osl_getCommandArg( nPos, &cmd_arg.pData ) !=
                     osl_Process_E_None )
                {
                    OSL_ASSERT( false );
                }
                ++nPos;
                cmd_arg = cmd_arg.trim();
                if (cmd_arg.getLength() > 0)
                {
                    if (cmd_arg[ 0 ] == '-') // is option
                    {
                        OptionInfo const * option_info = 0;
                        if (cmd_arg.getLength() > 2 &&
                            cmd_arg[ 1 ] == '-')
                        {
                            // long option
                            option_info = get_option_info(
                                cmd_arg.copy( 2 ), '\0' );
                        }
                        else if (cmd_arg.getLength() == 2 &&
                                 cmd_arg[ 1 ] != '-')
                        {
                            // short option
                            option_info = get_option_info(
                                OUString(), cmd_arg[ 1 ] );
                        }
                        if (option_info == 0)
                        {
                            OUStringBuffer buf;
                            buf.appendAscii(
                                RTL_CONSTASCII_STRINGPARAM("unknown option ") );
                            buf.append( cmd_arg );
                            buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(
                                                 "!  Use climaker --help "
                                                 "to print all options.") );
                            throw RuntimeException(
                                buf.makeStringAndClear(),
                                Reference< XInterface >() );
                        }
                        else
                        {
                            OSL_ENSURE( 0, "unhandled valid option?!" );
                            if (option_info->m_has_argument)
                                ++nPos;
                        }
                    }
                    else
                    {
                        mandatory_registries.push_back(
                            path_make_absolute_file_url( cmd_arg ) );
                    }
                }
            }
        }
        
        // bootstrap uno
        xContext = ::cppu::bootstrap_InitialComponentContext(
            Reference< registry::XSimpleRegistry >() );
        Reference< container::XHierarchicalNameAccess > xTDmgr(
            xContext->getValueByName(
                OUSTR("/singletons/com.sun.star.reflection."
                      "theTypeDescriptionManager") ),
            UNO_QUERY_THROW );
        
        // get rdb tdprovider factory
        Reference< lang::XSingleComponentFactory > xTDprov_factory(
            ::cppu::loadSharedLibComponentFactory(
                OUSTR("bootstrap.uno" SAL_DLLEXTENSION), OUString(),
                OUSTR("com.sun.star.comp.stoc.RegistryTypeDescriptionProvider"),
                Reference< lang::XMultiServiceFactory >(
                    xContext->getServiceManager(), UNO_QUERY ),
                Reference< registry::XRegistryKey >() ), UNO_QUERY );
        if (! xTDprov_factory.is())
        {
            throw RuntimeException(
                OUSTR("cannot get registry typedescription provider: "
                      "bootstrap.uno" SAL_DLLEXTENSION "!"),
                Reference< XInterface >() );
        }
        
        // create registry td provider for mandatory registry files
        Any arg( makeAny( open_registries( mandatory_registries, xContext ) ) );
        Reference< XInterface > xTD_provider(
            xTDprov_factory->createInstanceWithArgumentsAndContext(
                Sequence< Any >( &arg, 1 ), xContext ) );
        // insert provider to tdmgr
        Reference< container::XSet > xSet( xTDmgr, UNO_QUERY_THROW );
        Any provider( makeAny( xTD_provider ) );
        xSet->insert( provider );
        OSL_ASSERT( xSet->has( provider ) );
        if (! extra_registries.empty())
        {
            arg = makeAny( open_registries( extra_registries, xContext ) );
            provider = makeAny(
                xTDprov_factory->createInstanceWithArgumentsAndContext(
                    Sequence< Any >( &arg, 1 ), xContext ) );
            xSet->insert( provider );
            OSL_ASSERT( xSet->has( provider ) );
        }
        
        if (0 == output.getLength()) // no output file specified
        {
            // if only one rdb has been given, then take rdb name
            if (1 == mandatory_registries.size())
            {
                output = mandatory_registries[ 0 ];
                output = output.copy( output.lastIndexOf( '/' ) +1 );
                sal_Int32 dot = output.lastIndexOf( '.' );
                if (dot > 0)
                    output = output.copy( 0, dot );
            }
            else
            {
                output = OUSTR("cli_unotypes");
            }
        }
        output = path_make_absolute_file_url( output );
        sal_Int32 slash = output.lastIndexOf( '/' );
        OUString sys_output_dir;
        if (FileBase::E_None != FileBase::getSystemPathFromFileURL(
                output.copy( 0, slash ), sys_output_dir ))
        {
            throw RuntimeException(
                OUSTR("cannot get system path from file url ") +
                output.copy( 0, slash ),
                Reference< XInterface >() );
        }
        OUString filename( output.copy( slash +1 ) );
        sal_Int32 dot = filename.lastIndexOf( '.' );
        OUString name( filename );
        if (dot < 0) // has no extension
            filename += OUSTR(".dll");
        else
            name = name.copy( 0, dot );
        ::System::String * output_dir = ustring_to_String( sys_output_dir );
        ::System::String * output_file = ustring_to_String( filename );

        //Get the key pair for making a strong name
        StrongNameKeyPair* kp = NULL;
        if (keyfile.getLength() > 0)
        {
            ::System::String * sKeyFile = ustring_to_String(keyfile);
            try {
                System::IO::FileStream* fs = new System::IO::FileStream(
                    sKeyFile, System::IO::FileMode::Open);
                kp = new StrongNameKeyPair(fs);
                fs->Close();
            }
            catch (System::IO::FileNotFoundException * )
            {
                throw Exception(OUSTR("Could not find the keyfile. Verify the --keyfile argument!"), 0);
            }
        }
        else
        {
            if (g_verbose)
            {
                ::System::Console::Write(
                    S"> no key file specified. Cannot create strong name!\n");
            }
        }
        // setup assembly info: xxx todo set more? e.g. avoid strong versioning
        AssemblyName * assembly_name = new AssemblyName();
        assembly_name->set_CodeBase( output_dir );
        assembly_name->set_Name( name );
        if (kp != NULL)
            assembly_name->set_KeyPair(kp);
        
        if (version.getLength() != 0)
        {
            assembly_name->set_Version(
                new ::System::Version( ustring_to_String( version ) ) );
        }
        
        // app domain
        ::System::AppDomain * current_appdomain =
              ::System::AppDomain::get_CurrentDomain();
        // target assembly
        Emit::AssemblyBuilder * assembly_builder =
            current_appdomain->DefineDynamicAssembly(
                assembly_name, Emit::AssemblyBuilderAccess::Save, output_dir );
        if (product.getLength() != 0)
        {
            ::System::Type * params __gc [] = new ::System::Type * __gc [ 1 ];
            ::System::Object * args __gc [] = new ::System::Object * __gc [ 1 ];
            params[ 0 ] = __typeof (::System::String);
            args[ 0 ] = ustring_to_String( product );
            assembly_builder->SetCustomAttribute(
                new Emit::CustomAttributeBuilder(
                    __typeof (AssemblyProductAttribute)->GetConstructor(
                        params ), args ) );
        }
        if (description.getLength() != 0)
        {
            ::System::Type * params __gc [] = new ::System::Type * __gc [ 1 ];
            ::System::Object * args __gc [] = new ::System::Object * __gc [ 1 ];
            params[ 0 ] = __typeof (::System::String);
            args[ 0 ] = ustring_to_String( description );
            assembly_builder->SetCustomAttribute(
                new Emit::CustomAttributeBuilder(
                    __typeof (AssemblyDescriptionAttribute)->GetConstructor(
                        params ), args ) );
        }
        if (company.getLength() != 0)
        {
            ::System::Type * params __gc [] = new ::System::Type * __gc [ 1 ];
            ::System::Object * args __gc [] = new ::System::Object * __gc [ 1 ];
            params[ 0 ] = __typeof (::System::String);
            args[ 0 ] = ustring_to_String( company );
            assembly_builder->SetCustomAttribute(
                new Emit::CustomAttributeBuilder(
                    __typeof (AssemblyCompanyAttribute)->GetConstructor(
                        params ), args ) );
        }
        if (copyright.getLength() != 0)
        {
            ::System::Type * params __gc [] = new ::System::Type * __gc [ 1 ];
            ::System::Object * args __gc [] = new ::System::Object * __gc [ 1 ];
            params[ 0 ] = __typeof (::System::String);
            args[ 0 ] = ustring_to_String( copyright );
            assembly_builder->SetCustomAttribute(
                new Emit::CustomAttributeBuilder(
                    __typeof (AssemblyCopyrightAttribute)->GetConstructor(
                        params ), args ) );
        }
        if (trademark.getLength() != 0)
        {
            ::System::Type * params __gc [] = new ::System::Type * __gc [ 1 ];
            ::System::Object * args __gc [] = new ::System::Object * __gc [ 1 ];
            params[ 0 ] = __typeof (::System::String);
            args[ 0 ] = ustring_to_String( trademark );
            assembly_builder->SetCustomAttribute(
                new Emit::CustomAttributeBuilder(
                    __typeof (AssemblyTrademarkAttribute)->GetConstructor(
                        params ), args ) );
        }
        
        // load extra assemblies
        Assembly * assemblies __gc [] =
            new Assembly * __gc [ extra_assemblies.size() ];
        for ( size_t pos = 0; pos < extra_assemblies.size(); ++pos )
        {
            assemblies[ pos ] = Assembly::LoadFrom(
                ustring_to_String( extra_assemblies[ pos ] ) );
        }
        
        // type emitter
        TypeEmitter * type_emitter = new TypeEmitter(
            assembly_builder->DefineDynamicModule( output_file ), assemblies );
        // add handler resolving assembly's types
        ::System::ResolveEventHandler * type_resolver =
              new ::System::ResolveEventHandler(
                  type_emitter, &TypeEmitter::type_resolve );
        current_appdomain->add_TypeResolve( type_resolver );
        
        // and emit types to it
        if (explicit_types.empty())
        {
            Reference< reflection::XTypeDescriptionEnumeration > xTD_enum(
                Reference< reflection::XTypeDescriptionEnumerationAccess >(
                    xTD_provider, UNO_QUERY_THROW )
                  ->createTypeDescriptionEnumeration(
                      OUString() /* all IDL modules */,
                      Sequence< TypeClass >() /* all classes of types */,
                      reflection::TypeDescriptionSearchDepth_INFINITE ) );
            while (xTD_enum->hasMoreElements())
            {
                type_emitter->get_type( xTD_enum->nextTypeDescription() );
            }
        }
        else
        {
            Reference< container::XHierarchicalNameAccess > xHNA(
                xTD_provider, UNO_QUERY_THROW );
            for ( size_t nPos = explicit_types.size(); nPos--; )
            {
                type_emitter->get_type(
                    Reference< reflection::XTypeDescription >(
                        xHNA->getByHierarchicalName( explicit_types[ nPos ] ),
                        UNO_QUERY_THROW ) );
            }
        }
        type_emitter->Dispose();
        
        if (g_verbose)
        {
            ::System::Console::Write(
                S"> saving assembly {0}{1}{2}...",
                output_dir,
                new ::System::String(
                    ::System::IO::Path::DirectorySeparatorChar, 1 ),
                output_file );
        }
        assembly_builder->Save( output_file );
        if (g_verbose)
        {
            ::System::Console::WriteLine( S"ok." );
        }
        current_appdomain->remove_TypeResolve( type_resolver );
	}
	catch (Exception & exc)
	{
        OString msg(
            OUStringToOString( exc.Message, osl_getThreadTextEncoding() ) );
        fprintf(
            stderr, "\n> error: %s\n> dying abnormally...\n", msg.getStr() );
        ret = 1;
	}
	catch (::System::Exception * exc)
	{
        OString msg( OUStringToOString(
                         String_to_ustring( exc->ToString() ),
                         osl_getThreadTextEncoding() ) );
        fprintf(
            stderr,
            "\n> error: .NET exception occured: %s\n> dying abnormally...",
            msg.getStr() );
        ret = 1;
	}
    
    try
    {
        Reference< lang::XComponent > xComp( xContext, UNO_QUERY );
        if (xComp.is())
            xComp->dispose();
    }
	catch (Exception & exc)
	{
        OString msg(
            OUStringToOString( exc.Message, osl_getThreadTextEncoding() ) );
        fprintf(
            stderr,
            "\n> error disposing component context: %s\n"
            "> dying abnormally...\n",
            msg.getStr() );
		ret = 1;
	}
    
	return ret;
}
