/**************************************************************
 * 
 * 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_jvmfwk.hxx"
#include "boost/scoped_array.hpp"
#include "rtl/ustring.hxx"
#include "rtl/bootstrap.hxx"
#include "osl/thread.hxx"
#include "osl/file.hxx"
#include "osl/module.hxx"
#include "jvmfwk/framework.h"
#include "jvmfwk/vendorplugin.h"
#include <vector>
#include <functional>
#include <algorithm>
#include "framework.hxx"
#include "fwkutil.hxx"
#include "elements.hxx"
#include "fwkbase.hxx"

#ifdef WNT
/** The existence of the file useatjava.txt decides if a Java should be used
    that supports accessibility tools.
 */
#define USE_ACCESSIBILITY_FILE "useatjava.txt"
#endif

#define UNO_JAVA_JFW_JREHOME "UNO_JAVA_JFW_JREHOME"
namespace {
JavaVM * g_pJavaVM = NULL;

bool g_bEnabledSwitchedOn = false;

sal_Bool areEqualJavaInfo(
    JavaInfo const * pInfoA,JavaInfo const * pInfoB)
{
    return jfw_areEqualJavaInfo(pInfoA, pInfoB);
}
}

javaFrameworkError SAL_CALL jfw_findAllJREs(JavaInfo ***pparInfo, sal_Int32 *pSize)
{
    javaFrameworkError retVal = JFW_E_NONE;
    try
    {
		osl::MutexGuard guard(jfw::FwkMutex::get());
		javaFrameworkError errcode = JFW_E_NONE;
		if (pparInfo == NULL || pSize == NULL)
			return JFW_E_INVALID_ARG;

        jfw::VendorSettings aVendorSettings;
		//Get a list of plugins which provide Java information
        std::vector<jfw::PluginLibrary> vecPlugins =
            aVendorSettings.getPluginData();

        //Create a vector that holds the libraries, which will be later
        //dynamically loaded;
        boost::scoped_array<osl::Module> sarModules;
        sarModules.reset(new osl::Module[vecPlugins.size()]);
        osl::Module * arModules = sarModules.get();
		//Add the JavaInfos found by jfw_plugin_getAllJavaInfos to the vector
		//Make sure that the contents are destroyed if this
		//function returns with an error
		std::vector<jfw::CJavaInfo> vecInfo;
		//Add the JavaInfos found by jfw_plugin_getJavaInfoByPath to this vector
		//Make sure that the contents are destroyed if this
		//function returns with an error
		std::vector<jfw::CJavaInfo> vecInfoManual;
		typedef std::vector<jfw::CJavaInfo>::iterator it_info;
		//get the list of paths to jre locations which have been
		//added manually
		const jfw::MergedSettings settings;
		const std::vector<rtl::OUString>& vecJRELocations =
			settings.getJRELocations();
		//Use every plug-in library to get Java installations.        
		typedef std::vector<jfw::PluginLibrary>::const_iterator ci_pl;
        int cModule = 0;
 		for (ci_pl i = vecPlugins.begin(); i != vecPlugins.end(); i++, cModule++)
 		{
			const jfw::PluginLibrary & library = *i;
			jfw::VersionInfo versionInfo =
				aVendorSettings.getVersionInformation(library.sVendor);
            arModules[cModule].load(library.sPath);
            osl::Module & pluginLib = arModules[cModule];

			if (pluginLib.is() == sal_False)
			{
				rtl::OString msg = rtl::OUStringToOString(
					library.sPath, osl_getThreadTextEncoding());
				fprintf(stderr,"[jvmfwk] Could not load plugin %s\n" \
						"Modify the javavendors.xml accordingly!\n", msg.getStr()); 
				return JFW_E_NO_PLUGIN;
			}
			jfw_plugin_getAllJavaInfos_ptr getAllJavaFunc =
				(jfw_plugin_getAllJavaInfos_ptr) pluginLib.getFunctionSymbol(
					rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("jfw_plugin_getAllJavaInfos")));

			OSL_ASSERT(getAllJavaFunc);
			if (getAllJavaFunc == NULL)
				return JFW_E_ERROR;

			//get all installations of one vendor according to minVersion,
			//maxVersion and excludeVersions
			sal_Int32 cInfos = 0;
			JavaInfo** arInfos = NULL;
			javaPluginError plerr  = (*getAllJavaFunc)(
				library.sVendor.pData,
				versionInfo.sMinVersion.pData,
				versionInfo.sMaxVersion.pData,
				versionInfo.getExcludeVersions(),
				versionInfo.getExcludeVersionSize(),
				& arInfos,
				& cInfos);                                    

			if (plerr != JFW_PLUGIN_E_NONE)
				return JFW_E_ERROR;

			for (int j = 0; j < cInfos; j++)
                vecInfo.push_back(jfw::CJavaInfo::createWrapper(arInfos[j]));

			rtl_freeMemory(arInfos);

			//Check if the current plugin can detect JREs at the location
			// of the paths added by jfw_setJRELocations or jfw_addJRELocation
			//get the function from the plugin
			jfw_plugin_getJavaInfoByPath_ptr jfw_plugin_getJavaInfoByPathFunc =
				(jfw_plugin_getJavaInfoByPath_ptr) pluginLib.getFunctionSymbol(
					rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("jfw_plugin_getJavaInfoByPath")));

			OSL_ASSERT(jfw_plugin_getJavaInfoByPathFunc);
			if (jfw_plugin_getJavaInfoByPathFunc == NULL)
				return JFW_E_ERROR;

			typedef std::vector<rtl::OUString>::const_iterator citLoc;
			//Check every manually added location
			for (citLoc ii = vecJRELocations.begin();
				ii != vecJRELocations.end(); ii++)
			{
// 				rtl::OUString sLocation =
// 					rtl::OStringToOUString(*ii, RTL_TEXTENCODING_UTF8);
				jfw::CJavaInfo aInfo;
				plerr = (*jfw_plugin_getJavaInfoByPathFunc)(
					ii->pData,
                    library.sVendor.pData,
					versionInfo.sMinVersion.pData,
					versionInfo.sMaxVersion.pData,
					versionInfo.getExcludeVersions(),
					versionInfo.getExcludeVersionSize(),
					& aInfo.pInfo);
				if (plerr == JFW_PLUGIN_E_NO_JRE)
					continue;
				if (plerr == JFW_PLUGIN_E_FAILED_VERSION)
					continue;
				else if (plerr !=JFW_PLUGIN_E_NONE)
					return JFW_E_ERROR;

				if (aInfo)
				{
					//Was this JRE already added?. Different plugins could detect
					//the same JRE
					it_info it_duplicate =
						std::find_if(vecInfoManual.begin(), vecInfoManual.end(),
								std::bind2nd(std::ptr_fun(areEqualJavaInfo), aInfo));
					if (it_duplicate == vecInfoManual.end())
						vecInfoManual.push_back(aInfo);
				}
			}
		}
		//Make sure vecInfoManual contains only JavaInfos for the vendors for which
		//there is a javaSelection/plugins/library entry in the javavendors.xml
		//To obtain the JavaInfos for the manually added JRE locations the function
		//jfw_getJavaInfoByPath is called which can return a JavaInfo of any vendor.
		std::vector<jfw::CJavaInfo> vecInfoManual2;
		for (it_info ivm = vecInfoManual.begin(); ivm != vecInfoManual.end(); ivm++)
		{
			for (ci_pl ii = vecPlugins.begin(); ii != vecPlugins.end(); ii++)
			{
				if ( ii->sVendor.equals((*ivm)->sVendor))
				{
					vecInfoManual2.push_back(*ivm);
					break;
				}
			}
		}
		//Check which JavaInfo from vector vecInfoManual2 is already
		//contained in vecInfo. If it already exists then remove it from
		//vecInfoManual2
		for (it_info j = vecInfo.begin(); j != vecInfo.end(); j++)
		{
			it_info it_duplicate = 
				std::find_if(vecInfoManual2.begin(), vecInfoManual2.end(),
							std::bind2nd(std::ptr_fun(areEqualJavaInfo), *j));
			if (it_duplicate != vecInfoManual2.end())
				vecInfoManual2.erase(it_duplicate);
		}
		//create an fill the array of JavaInfo*
		sal_Int32 nSize = vecInfo.size() + vecInfoManual2.size();
		*pparInfo = (JavaInfo**) rtl_allocateMemory(
			nSize * sizeof(JavaInfo*));
		if (*pparInfo == NULL)
			return JFW_E_ERROR;

		typedef std::vector<jfw::CJavaInfo>::iterator it;
		int index = 0;
		//Add the automatically detected JREs
		for (it k = vecInfo.begin(); k != vecInfo.end(); k++)
			(*pparInfo)[index++] = k->detach();
		//Add the manually detected JREs
		for (it l = vecInfoManual2.begin(); l != vecInfoManual2.end(); l++)
			(*pparInfo)[index++] = l->detach();
	        
		*pSize = nSize;
		return errcode;
    }
    catch (jfw::FrameworkException& e)
    {
        retVal = e.errorCode;
        fprintf(stderr, "%s\n", e.message.getStr());
        OSL_ENSURE(0, e.message.getStr());
    }
    return retVal;
}

javaFrameworkError SAL_CALL jfw_startVM(JavaVMOption *arOptions, sal_Int32 cOptions,
                                 JavaVM **ppVM, JNIEnv **ppEnv)
{
#ifndef SOLAR_JAVA
    return JFW_E_ERROR;
#else
    javaFrameworkError errcode = JFW_E_NONE;
    if (cOptions > 0 && arOptions == NULL)
        return JFW_E_INVALID_ARG;

    try
    {
		osl::MutexGuard guard(jfw::FwkMutex::get());

		//We keep this pointer so we can determine if a VM has already
		//been created.
		if (g_pJavaVM != NULL)
			return JFW_E_RUNNING_JVM;
        
		if (ppVM == NULL)
			return JFW_E_INVALID_ARG;

        std::vector<rtl::OString> vmParams;
        rtl::OString sUserClassPath;
		jfw::CJavaInfo aInfo;
		jfw::JFW_MODE mode = jfw::getMode();
		if (mode == jfw::JFW_MODE_APPLICATION)
		{
            const jfw::MergedSettings settings;
            if (sal_False == settings.getEnabled())
                return JFW_E_JAVA_DISABLED;
            aInfo.attach(settings.createJavaInfo());
            //check if a Java has ever been selected
			if (aInfo == NULL)
				return JFW_E_NO_SELECT;

#ifdef WNT
			//Because on Windows there is no system setting that we can use to determine
			//if Assistive Technology Tool support is needed, we ship a .reg file that the
			//user can use to create a registry setting. When the user forgets to set
			//the key before he starts the office then a JRE may be selected without access bridge.
			//When he later sets the key then we select a JRE with accessibility support but
			//only if the user has not manually changed the selected JRE in the options dialog.
			if (jfw::isAccessibilitySupportDesired())
			{
				// If no JRE has been selected then we do not select one. This function shall then
				//return JFW_E_NO_SELECT
				if (aInfo != NULL &&
					(aInfo->nFeatures & JFW_FEATURE_ACCESSBRIDGE) == 0)
				{
					//has the user manually selected a JRE?
					if (settings.getJavaInfoAttrAutoSelect() == true)
					{
                        // if not then the automatism has previously selected a JRE
                        //without accessibility support. We return JFW_E_NO_SELECT
                        //to cause that we search for another JRE. The search code will
                        //then prefer a JRE with accessibility support.
                        return JFW_E_NO_SELECT;                        
					}
				}
			}
#endif
			//check if the javavendors.xml has changed after a Java was selected
			rtl::OString sVendorUpdate = jfw::getElementUpdated();

			if (sVendorUpdate != settings.getJavaInfoAttrVendorUpdate())
				return JFW_E_INVALID_SETTINGS;
	        
			//check if JAVA is disabled
			//If Java is enabled, but it was disabled when this process was started
			// then no preparational work, such as setting the LD_LIBRARY_PATH, was
			//done. Therefore if a JRE needs it it must not be started.
			if (g_bEnabledSwitchedOn &&
					(aInfo->nRequirements & JFW_REQUIRE_NEEDRESTART))
				return JFW_E_NEED_RESTART;
	        
			//Check if the selected Java was set in this process. If so it
			//must not have the requirments flag JFW_REQUIRE_NEEDRESTART
			if ((aInfo->nRequirements & JFW_REQUIRE_NEEDRESTART)
				&&
				(jfw::wasJavaSelectedInSameProcess() == true))
				return JFW_E_NEED_RESTART;

            vmParams = settings.getVmParametersUtf8();
            sUserClassPath = jfw::makeClassPathOption(settings.getUserClassPath());
		} // end mode FWK_MODE_OFFICE
		else if (mode == jfw::JFW_MODE_DIRECT)
		{
            errcode = jfw_getSelectedJRE(&aInfo.pInfo);
            if (errcode != JFW_E_NONE)
                return errcode;
            //In direct mode the options are specified by bootstrap variables
            //of the form UNO_JAVA_JFW_PARAMETER_1 .. UNO_JAVA_JFW_PARAMETER_n
            vmParams = jfw::BootParams::getVMParameters();
            sUserClassPath = 
                "-Djava.class.path=" + jfw::BootParams::getClasspath();
		}
		else
            OSL_ASSERT(0);
	        
		//get the function jfw_plugin_startJavaVirtualMachine
        jfw::VendorSettings aVendorSettings;
		rtl::OUString sLibPath = aVendorSettings.getPluginLibrary(aInfo.getVendor());

		osl::Module modulePlugin(sLibPath);
		if ( ! modulePlugin)
			return JFW_E_NO_PLUGIN;

		rtl::OUString sFunctionName(
			RTL_CONSTASCII_USTRINGPARAM("jfw_plugin_startJavaVirtualMachine"));
		jfw_plugin_startJavaVirtualMachine_ptr pFunc =
			(jfw_plugin_startJavaVirtualMachine_ptr)
			osl_getFunctionSymbol(modulePlugin, sFunctionName.pData);
		if (pFunc == NULL)
			return JFW_E_ERROR;

		// create JavaVMOptions array that is passed to the plugin
		// it contains the classpath and all options set in the
		//options dialog
		boost::scoped_array<JavaVMOption> sarJOptions(
            new JavaVMOption[cOptions + 2 + vmParams.size()]);
		JavaVMOption * arOpt = sarJOptions.get();
		if (! arOpt)
			return JFW_E_ERROR;
	    
		//The first argument is the classpath
		arOpt[0].optionString= (char*) sUserClassPath.getStr();
		arOpt[0].extraInfo = NULL;
		// Set a flag that this JVM has been created via the JNI Invocation API
		// (used, for example, by UNO remote bridges to share a common thread pool
		// factory among Java and native bridge implementations):
		arOpt[1].optionString = (char *) "-Dorg.openoffice.native=";
		arOpt[1].extraInfo = 0;

        //add the options set by options dialog
        int index = 2;
        typedef std::vector<rtl::OString>::const_iterator cit;
        for (cit i = vmParams.begin(); i != vmParams.end(); i ++)
        {
            arOpt[index].optionString = const_cast<sal_Char*>(i->getStr());
            arOpt[index].extraInfo = 0;
            index ++;
        }
        //add all options of the arOptions argument
        for (int ii = 0; ii < cOptions; ii++)
        {
            arOpt[index].optionString = arOptions[ii].optionString;
            arOpt[index].extraInfo = arOptions[ii].extraInfo;
            index++;
        }
    
		//start Java
		JavaVM *pVm = NULL;
		javaPluginError plerr = (*pFunc)(aInfo, arOpt, index, & pVm, ppEnv);
		if (plerr == JFW_PLUGIN_E_VM_CREATION_FAILED)
		{
			errcode = JFW_E_VM_CREATION_FAILED;
		}
		else if (plerr != JFW_PLUGIN_E_NONE )
		{
			errcode = JFW_E_ERROR;
		}
		else
		{
			g_pJavaVM = pVm;
			*ppVM = pVm;
		}
		OSL_ASSERT(plerr != JFW_PLUGIN_E_WRONG_VENDOR);
    }
    catch (jfw::FrameworkException& e)
    {
        errcode = e.errorCode;
        fprintf(stderr, "%s\n", e.message.getStr());
        OSL_ENSURE(0, e.message.getStr());
    }
 
    return errcode;
#endif
}

/** We do not use here jfw_findAllJREs and then check if a JavaInfo
    meets the requirements, because that means using all plug-ins, which
    may take quite a while. The implementation uses one plug-in and if
    it already finds a suitable JRE then it is done and does not need to
    load another plug-in
 */
javaFrameworkError SAL_CALL jfw_findAndSelectJRE(JavaInfo **pInfo)
{
    javaFrameworkError errcode = JFW_E_NONE;
    try
    {
        osl::MutexGuard guard(jfw::FwkMutex::get());
        if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
            return JFW_E_DIRECT_MODE;
		sal_uInt64 nFeatureFlags = 0;
		jfw::CJavaInfo aCurrentInfo;
//Determine if accessibility support is needed
		bool bSupportAccessibility = jfw::isAccessibilitySupportDesired();
		nFeatureFlags = bSupportAccessibility ?
			JFW_FEATURE_ACCESSBRIDGE : 0L;
	    
		//Get a list of services which provide Java information
        jfw::VendorSettings aVendorSettings;
        std::vector<jfw::PluginLibrary> vecPlugins =
 			aVendorSettings.getPluginData();
        //Create a vector that holds the libraries, which will be later
        //dynamically loaded;
        boost::scoped_array<osl::Module> sarModules;
        sarModules.reset(new osl::Module[vecPlugins.size()]);
        osl::Module * arModules = sarModules.get();
        
		//Use every plug-in library to get Java installations. At the first usable
		//Java the loop will break
		typedef std::vector<jfw::PluginLibrary>::const_iterator ci_pl;
        int cModule = 0;
		for (ci_pl i = vecPlugins.begin(); i != vecPlugins.end(); i++, cModule++)
		{
			const jfw::PluginLibrary & library = *i;
			jfw::VersionInfo versionInfo =
				aVendorSettings.getVersionInformation(library.sVendor);

            arModules[cModule].load(library.sPath);
            osl::Module & pluginLib = arModules[cModule];
			if (pluginLib.is() == sal_False)
				return JFW_E_NO_PLUGIN;

			jfw_plugin_getAllJavaInfos_ptr getAllJavaFunc =
				(jfw_plugin_getAllJavaInfos_ptr) pluginLib.getFunctionSymbol(
					rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("jfw_plugin_getAllJavaInfos")));

			OSL_ASSERT(getAllJavaFunc);
			if (getAllJavaFunc == NULL)
				continue;

			//get all installations of one vendor according to minVersion,
			//maxVersion and excludeVersions
			sal_Int32 cInfos = 0;
			JavaInfo** arInfos = NULL;
			javaPluginError plerr  = (*getAllJavaFunc)(
				library.sVendor.pData,
				versionInfo.sMinVersion.pData,
				versionInfo.sMaxVersion.pData,
				versionInfo.getExcludeVersions(),
				versionInfo.getExcludeVersionSize(),
				& arInfos,
				& cInfos);                                    

			if (plerr != JFW_PLUGIN_E_NONE)
				continue;
			//iterate over all installations to find the best which has
			//all features
			if (cInfos == 0)
			{
				rtl_freeMemory(arInfos);
				continue;
			}
			bool bInfoFound = false;
			for (int ii = 0; ii < cInfos; ii++)
			{
				JavaInfo* pJInfo = arInfos[ii];

				//We remember the very first installation in aCurrentInfo
				if (aCurrentInfo.getLocation().getLength() == 0)
						aCurrentInfo = pJInfo;
				// compare features
				// If the user does not require any features (nFeatureFlags = 0)
				// then the first installation is used
				if ((pJInfo->nFeatures & nFeatureFlags) == nFeatureFlags) 
				{
					//the just found Java implements all required features
					//currently there is only accessibility!!!
					aCurrentInfo = pJInfo;
					bInfoFound = true;
					break;
				}    
			}
			//The array returned by jfw_plugin_getAllJavaInfos must be freed as well as
			//its contents
			for (int j = 0; j < cInfos; j++)
				jfw_freeJavaInfo(arInfos[j]);
			rtl_freeMemory(arInfos);

			if (bInfoFound == true)
				break;
			//All Java installations found by the current plug-in lib
			//do not provide the required features. Try the next plug-in
		}
		if ((JavaInfo*) aCurrentInfo == NULL)
		{//The plug-ins did not find a suitable Java. Now try the paths which have been
		//added manually.
			//get the list of paths to jre locations which have been added manually
			const jfw::MergedSettings settings;
			//node.loadFromSettings();
			const std::vector<rtl::OUString> & vecJRELocations =
				settings.getJRELocations();
			//use every plug-in to determine the JavaInfo objects
			bool bInfoFound = false;
			for (ci_pl i = vecPlugins.begin(); i != vecPlugins.end(); i++)
			{
				const jfw::PluginLibrary & library = *i;
				jfw::VersionInfo versionInfo =
					aVendorSettings.getVersionInformation(library.sVendor);

				osl::Module pluginLib(library.sPath);
				if (pluginLib.is() == sal_False)
					return JFW_E_NO_PLUGIN;
				//Check if the current plugin can detect JREs at the location
				// of the paths added by jfw_setJRELocations or jfw_addJRELocation
				//get the function from the plugin
				jfw_plugin_getJavaInfoByPath_ptr jfw_plugin_getJavaInfoByPathFunc =
					(jfw_plugin_getJavaInfoByPath_ptr) pluginLib.getFunctionSymbol(
						rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("jfw_plugin_getJavaInfoByPath")));

				OSL_ASSERT(jfw_plugin_getJavaInfoByPathFunc);
				if (jfw_plugin_getJavaInfoByPathFunc == NULL)
					return JFW_E_ERROR;
	        
				typedef std::vector<rtl::OUString>::const_iterator citLoc;
				for (citLoc it = vecJRELocations.begin();
					it != vecJRELocations.end(); it++)
				{
					jfw::CJavaInfo aInfo;
					javaPluginError err = (*jfw_plugin_getJavaInfoByPathFunc)(
						it->pData,
                        library.sVendor.pData,
						versionInfo.sMinVersion.pData,
						versionInfo.sMaxVersion.pData,
						versionInfo.getExcludeVersions(),
						versionInfo.getExcludeVersionSize(),
						& aInfo.pInfo);
					if (err == JFW_PLUGIN_E_NO_JRE)
						continue;
					if (err == JFW_PLUGIN_E_FAILED_VERSION)
						continue;
					else if (err !=JFW_PLUGIN_E_NONE)
						return JFW_E_ERROR;

					if (aInfo)
					{
						//We remember the very first installation in aCurrentInfo
						if (aCurrentInfo.getLocation().getLength() == 0)
							aCurrentInfo = aInfo;
						// compare features
						// If the user does not require any features (nFeatureFlags = 0)
						// then the first installation is used
						if ((aInfo.getFeatures() & nFeatureFlags) == nFeatureFlags) 
						{
							//the just found Java implements all required features
							//currently there is only accessibility!!!
							aCurrentInfo = aInfo;
							bInfoFound = true;
							break;
						}
					}
				}//end iterate over paths
				if (bInfoFound == true)
					break;
			}// end iterate plug-ins
		}
		if ((JavaInfo*) aCurrentInfo)
		{
			jfw::NodeJava javaNode;
			javaNode.setJavaInfo(aCurrentInfo,true);
			javaNode.write();

			if (pInfo !=NULL)
			{
				//copy to out param
				*pInfo = aCurrentInfo.cloneJavaInfo();
				//remember that this JRE was selected in this process
				jfw::setJavaSelected();
			}   
		}
		else
		{
			errcode = JFW_E_NO_JAVA_FOUND;
		}
    }
    catch (jfw::FrameworkException& e)
    {
        errcode = e.errorCode;
        fprintf(stderr, "%s\n", e.message.getStr());
        OSL_ENSURE(0, e.message.getStr());
    }
 
    return errcode;
}
sal_Bool SAL_CALL jfw_areEqualJavaInfo(
    JavaInfo const * pInfoA,JavaInfo const * pInfoB)
{
    if (pInfoA == pInfoB)
        return sal_True;
    if (pInfoA == NULL || pInfoB == NULL)
        return sal_False;
    rtl::OUString sVendor(pInfoA->sVendor);
    rtl::OUString sLocation(pInfoA->sLocation);
    rtl::OUString sVersion(pInfoA->sVersion);
    rtl::ByteSequence sData(pInfoA->arVendorData);
    if (sVendor.equals(pInfoB->sVendor) == sal_True
        && sLocation.equals(pInfoB->sLocation) == sal_True
        && sVersion.equals(pInfoB->sVersion) == sal_True
        && pInfoA->nFeatures == pInfoB->nFeatures
#ifndef MACOSX
        && pInfoA->nRequirements == pInfoB->nRequirements
#endif
        && sData == pInfoB->arVendorData)
    {
        return sal_True;
    }
    return sal_False;
}


void SAL_CALL jfw_freeJavaInfo(JavaInfo *pInfo)
{
    if (pInfo == NULL)
        return;
    rtl_uString_release(pInfo->sVendor);
    rtl_uString_release(pInfo->sLocation);
    rtl_uString_release(pInfo->sVersion);
    rtl_byte_sequence_release(pInfo->arVendorData);
    rtl_freeMemory(pInfo);
}

javaFrameworkError SAL_CALL jfw_getSelectedJRE(JavaInfo **ppInfo)
{
    javaFrameworkError errcode = JFW_E_NONE;
    try
    {
		osl::MutexGuard guard(jfw::FwkMutex::get());
		if (ppInfo == NULL)
			return JFW_E_INVALID_ARG;

        if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
        {
            rtl::OUString sJRE = jfw::BootParams::getJREHome();

            jfw::CJavaInfo aInfo;
            if ((errcode = jfw_getJavaInfoByPath(sJRE.pData, & aInfo.pInfo))
                != JFW_E_NONE)
                throw jfw::FrameworkException(
                    JFW_E_CONFIGURATION,
                    rtl::OString(
                        "[Java framework] The JRE specified by the bootstrap "
                        "variable UNO_JAVA_JFW_JREHOME  or  UNO_JAVA_JFW_ENV_JREHOME "
                        " could not be recognized. Check the values and make sure that you "
                        "use a plug-in library that can recognize that JRE."));

            *ppInfo = aInfo.detach();
            return JFW_E_NONE;
        }

		const jfw::MergedSettings settings;		
		jfw::CJavaInfo aInfo;
		aInfo.attach(settings.createJavaInfo());
		if (! aInfo)
		{
			*ppInfo = NULL;
			return JFW_E_NONE;
		}
		//If the javavendors.xml has changed, then the current selected
		//Java is not valid anymore
		// /java/javaInfo/@vendorUpdate != javaSelection/updated (javavendors.xml)
		rtl::OString sUpdated = jfw::getElementUpdated();
	    
		if (sUpdated.equals(settings.getJavaInfoAttrVendorUpdate()) == sal_False)
			return JFW_E_INVALID_SETTINGS;
		*ppInfo = aInfo.detach();
    }
    catch (jfw::FrameworkException& e)
    {
        errcode = e.errorCode;
        fprintf(stderr, "%s\n", e.message.getStr());
        OSL_ENSURE(0, e.message.getStr());
    }
    return errcode;
}

javaFrameworkError SAL_CALL jfw_isVMRunning(sal_Bool *bRunning)
{
    osl::MutexGuard guard(jfw::FwkMutex::get());
    if (bRunning == NULL)
        return JFW_E_INVALID_ARG;
    if (g_pJavaVM == NULL)
        *bRunning = sal_False;
    else
        *bRunning = sal_True;
    return JFW_E_NONE;
}

javaFrameworkError SAL_CALL jfw_getJavaInfoByPath(
    rtl_uString *pPath, JavaInfo **ppInfo)
{
    javaFrameworkError errcode = JFW_E_NONE;
    try
    {
		osl::MutexGuard guard(jfw::FwkMutex::get());
		if (pPath == NULL || ppInfo == NULL)
			return JFW_E_INVALID_ARG;

        jfw::VendorSettings aVendorSettings;
		//Get a list of plugins which provide Java information
		std::vector<jfw::PluginLibrary> vecPlugins =
			aVendorSettings.getPluginData();
        //Create a vector that holds the libraries, which will be later
        //dynamically loaded;
        boost::scoped_array<osl::Module> sarModules;
        sarModules.reset(new osl::Module[vecPlugins.size()]);
        osl::Module * arModules = sarModules.get();

		typedef std::vector<rtl::OUString>::const_iterator CIT_VENDOR;
		std::vector<rtl::OUString> vecVendors =
			aVendorSettings.getSupportedVendors();
	        
		//Use every plug-in library to determine if the path represents a
		//JRE. If a plugin recognized it then the loop will break
		typedef std::vector<jfw::PluginLibrary>::const_iterator ci_pl;
        int cModule = 0;
        for (ci_pl i = vecPlugins.begin(); i != vecPlugins.end();
             i++, cModule++)
		{
			const jfw::PluginLibrary & library = *i;
			jfw::VersionInfo versionInfo =
				aVendorSettings.getVersionInformation(library.sVendor);
	        arModules[cModule].load(library.sPath);
            osl::Module & pluginLib = arModules[cModule];
			if (pluginLib.is() == sal_False)
			{
				rtl::OString msg = rtl::OUStringToOString(
					library.sPath, osl_getThreadTextEncoding());
				fprintf(stderr,"[jvmfwk] Could not load plugin %s\n" \
						"Modify the javavendors.xml accordingly!\n", msg.getStr()); 
				return JFW_E_NO_PLUGIN;
			}

			jfw_plugin_getJavaInfoByPath_ptr jfw_plugin_getJavaInfoByPathFunc =
				(jfw_plugin_getJavaInfoByPath_ptr) pluginLib.getFunctionSymbol(
					rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("jfw_plugin_getJavaInfoByPath")));

			OSL_ASSERT(jfw_plugin_getJavaInfoByPathFunc);
			if (jfw_plugin_getJavaInfoByPathFunc == NULL)
				continue;

			//ask the plugin if this is a JRE.
			//If so check if it meets the version requirements.
			//Only if it does return a JavaInfo
			JavaInfo* pInfo = NULL;
			javaPluginError plerr = (*jfw_plugin_getJavaInfoByPathFunc)(
				pPath,
                library.sVendor.pData,
				versionInfo.sMinVersion.pData,
				versionInfo.sMaxVersion.pData,
				versionInfo.getExcludeVersions(),
				versionInfo.getExcludeVersionSize(),
				& pInfo);
	        
			if (plerr == JFW_PLUGIN_E_NONE)
			{
				//check if the vendor of the found JRE is supported
                if (vecVendors.size() == 0)
                {
                    //vendor does not matter
                    *ppInfo = pInfo;
					break;
                }
                else
                {
                    rtl::OUString sVendor(pInfo->sVendor);
                    CIT_VENDOR ivendor = std::find(vecVendors.begin(), vecVendors.end(),
                                                   sVendor);
                    if (ivendor != vecVendors.end())
                    {
                        *ppInfo = pInfo;
                    }
                    else
                    {
                        *ppInfo = NULL;
                        errcode = JFW_E_NOT_RECOGNIZED;
                    }
                    break;
                }
			}
			else if(plerr == JFW_PLUGIN_E_FAILED_VERSION)
			{//found JRE but it has the wrong version
				*ppInfo = NULL;
				errcode = JFW_E_FAILED_VERSION;
				break;
			}
			else if (plerr == JFW_PLUGIN_E_NO_JRE)
			{// plugin does not recognize this path as belonging to JRE
				continue;
			}
			OSL_ASSERT(0);
		}
		if (*ppInfo == NULL && errcode != JFW_E_FAILED_VERSION)
			errcode = JFW_E_NOT_RECOGNIZED;
    }
    catch (jfw::FrameworkException& e)
    {
        errcode = e.errorCode;
        fprintf(stderr, "%s\n", e.message.getStr());
        OSL_ENSURE(0, e.message.getStr());
    }
 
    return errcode;
}


javaFrameworkError SAL_CALL jfw_setSelectedJRE(JavaInfo const *pInfo)
{
    javaFrameworkError errcode = JFW_E_NONE;
    try
    {
		osl::MutexGuard guard(jfw::FwkMutex::get());
        if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
            return JFW_E_DIRECT_MODE;
		//check if pInfo is the selected JRE
		JavaInfo *currentInfo = NULL;
		errcode = jfw_getSelectedJRE( & currentInfo);
		if (errcode != JFW_E_NONE && errcode != JFW_E_INVALID_SETTINGS)
			return errcode;
	    
		if (jfw_areEqualJavaInfo(currentInfo, pInfo) == sal_False)
		{
			jfw::NodeJava node;
			node.setJavaInfo(pInfo, false);
			node.write();
			//remember that the JRE was selected in this process
			jfw::setJavaSelected();
		}
    }
    catch (jfw::FrameworkException& e)
    {
        errcode = e.errorCode;
        fprintf(stderr, "%s\n", e.message.getStr());
        OSL_ENSURE(0, e.message.getStr());
    }
    return errcode;
}
javaFrameworkError SAL_CALL jfw_setEnabled(sal_Bool bEnabled)
{
    javaFrameworkError errcode = JFW_E_NONE;
    try
    {
		osl::MutexGuard guard(jfw::FwkMutex::get());
        if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
            return JFW_E_DIRECT_MODE;
                
		if (g_bEnabledSwitchedOn == false && bEnabled == sal_True)
		{
			//When the process started then Enabled was false.
			//This is first time enabled is set to true.
			//That means, no preparational work has been done, such as setting the
			//LD_LIBRARY_PATH, etc.

			//check if Enabled is false;
            const jfw::MergedSettings settings;
			if (settings.getEnabled() == sal_False)
				g_bEnabledSwitchedOn = true;
		}
		jfw::NodeJava node;
		node.setEnabled(bEnabled);
		node.write();
    }
    catch (jfw::FrameworkException& e)
    {
        errcode = e.errorCode;
        fprintf(stderr, "%s\n", e.message.getStr());
        OSL_ENSURE(0, e.message.getStr());
    }
    return errcode;
}

javaFrameworkError SAL_CALL jfw_getEnabled(sal_Bool *pbEnabled)
{
    javaFrameworkError errcode = JFW_E_NONE;
    try
    {
        if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
            return JFW_E_DIRECT_MODE;
        osl::MutexGuard guard(jfw::FwkMutex::get());
		if (pbEnabled == NULL)
			return JFW_E_INVALID_ARG;
		jfw::MergedSettings settings;
		*pbEnabled = settings.getEnabled();
    }
    catch (jfw::FrameworkException& e)
    {
        errcode = e.errorCode;
        fprintf(stderr, "%s\n", e.message.getStr());
        OSL_ENSURE(0, e.message.getStr());
    }
    return errcode;
}


javaFrameworkError SAL_CALL jfw_setVMParameters(
    rtl_uString * * arOptions, sal_Int32 nLen)
{
    javaFrameworkError errcode = JFW_E_NONE;
    try
    {
		osl::MutexGuard guard(jfw::FwkMutex::get());
        if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
            return JFW_E_DIRECT_MODE;
        jfw::NodeJava node;
		if (arOptions == NULL && nLen != 0)
			return JFW_E_INVALID_ARG;
		node.setVmParameters(arOptions, nLen);
		node.write();
    }
    catch (jfw::FrameworkException& e)
    {
        errcode = e.errorCode;
        fprintf(stderr, "%s\n", e.message.getStr());
        OSL_ENSURE(0, e.message.getStr());
    }

    return errcode;
}

javaFrameworkError SAL_CALL jfw_getVMParameters(
    rtl_uString *** parOptions, sal_Int32 * pLen)
{
    javaFrameworkError errcode = JFW_E_NONE;
    try
    {
		osl::MutexGuard guard(jfw::FwkMutex::get());
        if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
            return JFW_E_DIRECT_MODE;

		if (parOptions == NULL || pLen == NULL)
			return JFW_E_INVALID_ARG;
		const jfw::MergedSettings settings;
		settings.getVmParametersArray(parOptions, pLen);
    }
    catch (jfw::FrameworkException& e)
    {
        errcode = e.errorCode;
        fprintf(stderr, "%s\n", e.message.getStr());
        OSL_ENSURE(0, e.message.getStr());
    }
    return errcode;
}

javaFrameworkError SAL_CALL jfw_setUserClassPath(rtl_uString * pCp)
{
    javaFrameworkError errcode = JFW_E_NONE;
    try
    {
		osl::MutexGuard guard(jfw::FwkMutex::get());
        if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
            return JFW_E_DIRECT_MODE;
		jfw::NodeJava node;
		if (pCp == NULL)
			return JFW_E_INVALID_ARG;
		node.setUserClassPath(pCp);
		node.write();
    }
    catch (jfw::FrameworkException& e)
    {
        errcode = e.errorCode;
        fprintf(stderr, "%s\n", e.message.getStr());
        OSL_ENSURE(0, e.message.getStr());
    }
    return errcode;
}

javaFrameworkError SAL_CALL jfw_getUserClassPath(rtl_uString ** ppCP)
{
    javaFrameworkError errcode = JFW_E_NONE;
    try
    {
		osl::MutexGuard guard(jfw::FwkMutex::get());
        if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
            return JFW_E_DIRECT_MODE;
		if (ppCP == NULL)
			return JFW_E_INVALID_ARG;
		const jfw::MergedSettings settings;
		*ppCP = settings.getUserClassPath().pData;
		rtl_uString_acquire(*ppCP);
    }
    catch (jfw::FrameworkException& e)
    {
        errcode = e.errorCode;
        fprintf(stderr, "%s\n", e.message.getStr());
        OSL_ENSURE(0, e.message.getStr());
    }
    return errcode;
}

javaFrameworkError SAL_CALL jfw_addJRELocation(rtl_uString * sLocation)
{
    javaFrameworkError errcode = JFW_E_NONE;
    try
    {
		osl::MutexGuard guard(jfw::FwkMutex::get());
        if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
            return JFW_E_DIRECT_MODE;
        jfw::NodeJava node;
		if (sLocation == NULL)
			return JFW_E_INVALID_ARG;
		node.load();
		node.addJRELocation(sLocation);
		node.write();
    }
    catch (jfw::FrameworkException& e)
    {
        errcode = e.errorCode;
        fprintf(stderr, "%s\n", e.message.getStr());
        OSL_ENSURE(0, e.message.getStr());
    }
 
    return errcode;
    
}

javaFrameworkError SAL_CALL jfw_setJRELocations(
    rtl_uString ** arLocations, sal_Int32 nLen)
{
    javaFrameworkError errcode = JFW_E_NONE;
    try
    {
		osl::MutexGuard guard(jfw::FwkMutex::get());
        if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
            return JFW_E_DIRECT_MODE;
		jfw::NodeJava node;
		if (arLocations == NULL && nLen != 0)
			return JFW_E_INVALID_ARG;
		node.setJRELocations(arLocations, nLen);
		node.write();
    }
    catch (jfw::FrameworkException& e)
    {
        errcode = e.errorCode;
        fprintf(stderr, "%s\n", e.message.getStr());
        OSL_ENSURE(0, e.message.getStr());
    }
    return errcode;

}

javaFrameworkError SAL_CALL jfw_getJRELocations(
    rtl_uString *** parLocations, sal_Int32 *pLen)
{
    javaFrameworkError errcode = JFW_E_NONE;
    try
    {
		osl::MutexGuard guard(jfw::FwkMutex::get());
        if (jfw::getMode() == jfw::JFW_MODE_DIRECT)
            return JFW_E_DIRECT_MODE;
                
		if (parLocations == NULL || pLen == NULL)
			return JFW_E_INVALID_ARG;
		const jfw::MergedSettings settings;
		settings.getJRELocations(parLocations, pLen);
    }
    catch (jfw::FrameworkException& e)
    {
        errcode = e.errorCode;
        fprintf(stderr, "%s\n", e.message.getStr());
        OSL_ENSURE(0, e.message.getStr());
    }
    
    return errcode;    
}


javaFrameworkError jfw_existJRE(const JavaInfo *pInfo, sal_Bool *exist)
{
    //get the function jfw_plugin_existJRE
    jfw::VendorSettings aVendorSettings;
    jfw::CJavaInfo aInfo;
    aInfo = (const ::JavaInfo*) pInfo; //makes a copy of pInfo
    rtl::OUString sLibPath = aVendorSettings.getPluginLibrary(aInfo.getVendor());
    osl::Module modulePlugin(sLibPath);
    if ( ! modulePlugin)
        return JFW_E_NO_PLUGIN;
    rtl::OUString sFunctionName(
        RTL_CONSTASCII_USTRINGPARAM("jfw_plugin_existJRE"));
    jfw_plugin_existJRE_ptr pFunc =
        (jfw_plugin_existJRE_ptr)
        osl_getFunctionSymbol(modulePlugin, sFunctionName.pData);
    if (pFunc == NULL)
        return JFW_E_ERROR;

    javaPluginError plerr = (*pFunc)(pInfo, exist);

    javaFrameworkError ret = JFW_E_NONE;
    switch (plerr)
    {
    case JFW_PLUGIN_E_NONE:
        ret = JFW_E_NONE;
        break;
    case JFW_PLUGIN_E_INVALID_ARG:
        ret = JFW_E_INVALID_ARG;
        break;
    case JFW_PLUGIN_E_ERROR:
        ret = JFW_E_ERROR;
        break;
    default:
        ret = JFW_E_ERROR;
    }
    return ret;
}

void SAL_CALL jfw_lock()
{
    jfw::FwkMutex::get().acquire();
}

void SAL_CALL jfw_unlock()
{
    jfw::FwkMutex::get().release();
}


namespace jfw
{
CJavaInfo::CJavaInfo(): pInfo(0)
{
}

CJavaInfo::CJavaInfo(const CJavaInfo & info)
{
    pInfo = copyJavaInfo(info.pInfo);
}

CJavaInfo::CJavaInfo(::JavaInfo * info, _transfer_ownership)
{
    pInfo = info;
}
CJavaInfo CJavaInfo::createWrapper(::JavaInfo* info)
{
    return CJavaInfo(info, TRANSFER);
}
void CJavaInfo::attach(::JavaInfo * info)
{
    jfw_freeJavaInfo(pInfo);
    pInfo = info;
}
::JavaInfo * CJavaInfo::detach()
{
    JavaInfo * tmp = pInfo;
    pInfo = NULL;
    return tmp;
}

CJavaInfo::~CJavaInfo()
{
    jfw_freeJavaInfo(pInfo);
}

CJavaInfo::operator ::JavaInfo* ()
{
    return pInfo;
}

JavaInfo * CJavaInfo::copyJavaInfo(const JavaInfo * pInfo)
{
	if (pInfo == NULL)
		return NULL;
    JavaInfo* newInfo =
          (JavaInfo*) rtl_allocateMemory(sizeof(JavaInfo));
    if (newInfo)
    {
        rtl_copyMemory(newInfo, pInfo, sizeof(JavaInfo));   
        rtl_uString_acquire(pInfo->sVendor);
        rtl_uString_acquire(pInfo->sLocation);
        rtl_uString_acquire(pInfo->sVersion);
        rtl_byte_sequence_acquire(pInfo->arVendorData);
    }
    return newInfo;
}


JavaInfo* CJavaInfo::cloneJavaInfo() const
{
    if (pInfo == NULL)
        return NULL;
    return copyJavaInfo(pInfo);
}

CJavaInfo & CJavaInfo::operator = (const CJavaInfo& info)
{
    if (&info == this)
        return *this;

    jfw_freeJavaInfo(pInfo);
    pInfo = copyJavaInfo(info.pInfo);
    return *this;
}
CJavaInfo & CJavaInfo::operator = (const ::JavaInfo* info)
{
    if (info == pInfo)
        return *this;

    jfw_freeJavaInfo(pInfo);
    pInfo = copyJavaInfo(info);
    return *this;
}

const ::JavaInfo* CJavaInfo::operator ->() const
{
    return pInfo;
}

CJavaInfo::operator JavaInfo const * () const
{
    return pInfo;
}
// ::JavaInfo** CJavaInfo::operator & ()
// {
//     return & pInfo;
// }

rtl::OUString CJavaInfo::getVendor() const
{
    if (pInfo)
        return rtl::OUString(pInfo->sVendor);
    else
        return rtl::OUString();
}

rtl::OUString CJavaInfo::getLocation() const
{
    if (pInfo)
        return rtl::OUString(pInfo->sLocation);
    else
        return rtl::OUString();
}

sal_uInt64 CJavaInfo::getFeatures() const
{
    if (pInfo)
        return pInfo->nFeatures;
    else
        return 0l;
}

}
