/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   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 .
 */

#include <sal/config.h>
#include <sal/log.hxx>

#include <cassert>
#include <memory>

#include <elements.hxx>
#include <osl/diagnose.h>
#include <osl/mutex.hxx>
#include <osl/file.hxx>
#include <fwkutil.hxx>
#include <fwkbase.hxx>
#include "framework.hxx"
#include <libxmlutil.hxx>
#include <algorithm>
#include <libxml/parser.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <optional>
#include <string.h>

// For backwards compatibility, the nRequirements flag word is
// read/written as potentially signed hexadecimal number (though that has no
// practical relevance given that it has only one flag with value 0x01
// defined).

using namespace osl;
namespace jfw
{

static OString getElement(OString const & docPath,
                        xmlChar const * pathExpression)
{
    //Prepare the xml document and context
    OSL_ASSERT(!docPath.isEmpty());
    jfw::CXmlDocPtr doc(xmlParseFile(docPath.getStr()));
    if (doc == nullptr)
        throw FrameworkException(
            JFW_E_ERROR,
            "[Java framework] Error in function getElement (elements.cxx)"_ostr);

    jfw::CXPathContextPtr context(xmlXPathNewContext(doc));
    if (xmlXPathRegisterNs(context, reinterpret_cast<xmlChar const *>("jf"),
        reinterpret_cast<xmlChar const *>(NS_JAVA_FRAMEWORK)) == -1)
        throw FrameworkException(
            JFW_E_ERROR,
            "[Java framework] Error in function getElement (elements.cxx)"_ostr);

    CXPathObjectPtr pathObj = xmlXPathEvalExpression(pathExpression, context);
    OString sValue;
    if (xmlXPathNodeSetIsEmpty(pathObj->nodesetval))
    {
        throw FrameworkException(
            JFW_E_ERROR,
            "[Java framework] Error in function getElement (elements.cxx)"_ostr);
    }
    sValue = reinterpret_cast<char*>(pathObj->nodesetval->nodeTab[0]->content);
    return sValue;
}

OString getElementUpdated()
{
    return getElement(jfw::getVendorSettingsPath(),
                      reinterpret_cast<xmlChar const *>("/jf:javaSelection/jf:updated/text()"));
}

void createSettingsStructure(xmlDoc * document, bool * bNeedsSave)
{
    OString sExcMsg("[Java framework] Error in function createSettingsStructure "
                         "(elements.cxx)."_ostr);
    xmlNode * root = xmlDocGetRootElement(document);
    if (root == nullptr)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);
    bool bFound = false;
    xmlNode * cur = root->children;
    while (cur != nullptr)
    {
        if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("enabled")) == 0)
        {
            bFound = true;
            break;
        }
        cur = cur->next;
    }
    if (bFound)
    {
        *bNeedsSave = false;
        return;
    }
    //We will modify this document
    *bNeedsSave = true;
    // Now we create the child elements ------------------
    //Get xsi:nil namespace
    xmlNs* nsXsi = xmlSearchNsByHref(
        document, root, reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE));

    //<enabled xsi:nil="true"
    xmlNode  * nodeEn = xmlNewTextChild(
        root, nullptr, reinterpret_cast<xmlChar const *>("enabled"), reinterpret_cast<xmlChar const *>(""));
    if (nodeEn == nullptr)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);
    xmlSetNsProp(nodeEn, nsXsi, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>("true"));
    //add a new line
    xmlNode * nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n"));
    xmlAddChild(root, nodeCrLf);

    //<userClassPath xsi:nil="true">
    xmlNode  * nodeUs = xmlNewTextChild(
        root, nullptr, reinterpret_cast<xmlChar const *>("userClassPath"), reinterpret_cast<xmlChar const *>(""));
    if (nodeUs == nullptr)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);
    xmlSetNsProp(nodeUs, nsXsi, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>("true"));
    //add a new line
    nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n"));
    xmlAddChild(root, nodeCrLf);

    //<vmParameters xsi:nil="true">
    xmlNode  * nodeVm = xmlNewTextChild(
        root, nullptr, reinterpret_cast<xmlChar const *>("vmParameters"), reinterpret_cast<xmlChar const *>(""));
    if (nodeVm == nullptr)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);
    xmlSetNsProp(nodeVm, nsXsi, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>("true"));
    //add a new line
    nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n"));
    xmlAddChild(root, nodeCrLf);

    //<jreLocations xsi:nil="true">
    xmlNode  * nodeJre = xmlNewTextChild(
        root, nullptr, reinterpret_cast<xmlChar const *>("jreLocations"), reinterpret_cast<xmlChar const *>(""));
    if (nodeJre == nullptr)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);
    xmlSetNsProp(nodeJre, nsXsi, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>("true"));
    //add a new line
    nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n"));
    xmlAddChild(root, nodeCrLf);

    //<javaInfo xsi:nil="true">
    xmlNode  * nodeJava = xmlNewTextChild(
        root, nullptr, reinterpret_cast<xmlChar const *>("javaInfo"), reinterpret_cast<xmlChar const *>(""));
    if (nodeJava == nullptr)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);
    xmlSetNsProp(nodeJava, nsXsi, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>("true"));
    //add a new line
    nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n"));
    xmlAddChild(root, nodeCrLf);
}

NodeJava::NodeJava(Layer layer):
    m_layer(layer)
{
    //This class reads and write to files which should only be done in
    //application mode
    if (getMode() == JFW_MODE_DIRECT)
        throw FrameworkException(
            JFW_E_DIRECT_MODE,
            "[Java framework] Trying to access settings files in direct mode."_ostr);
}


void NodeJava::load()
{
    static constexpr OString sExcMsg("[Java framework] Error in function NodeJava::load"
                             "(elements.cxx)."_ostr);
    if (SHARED == m_layer)
    {
        //we do not support yet to write into the shared installation

        //check if shared settings exist at all.
        OUString sURL(BootParams::getSharedData());
        jfw::FileStatus s = sURL.isEmpty()
            ? FILE_DOES_NOT_EXIST : checkFileURL(sURL);
        if (s == FILE_INVALID)
            throw FrameworkException(
                JFW_E_ERROR,
                "[Java framework] Invalid file for shared Java settings."_ostr);
        else if (s == FILE_DOES_NOT_EXIST)
            //Writing shared data is not supported yet.
            return;
    }
    else if (USER == m_layer)
    {
        if (!prepareSettingsDocument())
        {
            SAL_INFO("jfw.level1", "no path to load user settings document from");
            return;
        }
    }
    else
    {
        OSL_FAIL("[Java framework] Unknown enum used.");
    }


    //Read the user elements
    OString sSettingsPath = getSettingsPath();
    //There must not be a share settings file
    CXmlDocPtr docUser(xmlParseFile(sSettingsPath.getStr()));
    if (docUser == nullptr)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);

    xmlNode * cur = xmlDocGetRootElement(docUser);
    if (cur == nullptr || cur->children == nullptr)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);

    CXmlCharPtr sNil;
    cur = cur->children;
    while (cur != nullptr)
    {
        if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("enabled")) == 0)
        {
            //only overwrite share settings if xsi:nil="false"
            sNil = xmlGetNsProp(
                cur, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE));
            if (sNil == nullptr)
                throw FrameworkException(JFW_E_ERROR, sExcMsg);
            if (xmlStrcmp(sNil, reinterpret_cast<xmlChar const *>("false")) == 0)
            {
                CXmlCharPtr sEnabled( xmlNodeListGetString(
                    docUser, cur->children, 1));
                if (xmlStrcmp(sEnabled, reinterpret_cast<xmlChar const *>("true")) == 0)
                    m_enabled = std::optional<sal_Bool>(true);
                else if (xmlStrcmp(sEnabled, reinterpret_cast<xmlChar const *>("false")) == 0)
                    m_enabled = std::optional<sal_Bool>(false);
            }
        }
        else if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("userClassPath")) == 0)
        {
            sNil = xmlGetNsProp(
                cur, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE));
            if (sNil == nullptr)
                throw FrameworkException(JFW_E_ERROR, sExcMsg);
            if (xmlStrcmp(sNil, reinterpret_cast<xmlChar const *>("false")) == 0)
            {
                CXmlCharPtr sUser(xmlNodeListGetString(
                    docUser, cur->children, 1));
                m_userClassPath = std::optional<OUString>(OUString(sUser));
            }
        }
        else if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("javaInfo")) == 0)
        {
            sNil = xmlGetNsProp(
                cur, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE));
            if (sNil == nullptr)
                throw FrameworkException(JFW_E_ERROR, sExcMsg);

            if (xmlStrcmp(sNil, reinterpret_cast<xmlChar const *>("false")) == 0)
            {
                if (! m_javaInfo)
                    m_javaInfo = std::optional<CNodeJavaInfo>(CNodeJavaInfo());
                m_javaInfo->loadFromNode(docUser, cur);
            }
        }
        else if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("vmParameters")) == 0)
        {
            sNil = xmlGetNsProp(
                cur, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE));
            if (sNil == nullptr)
                throw FrameworkException(JFW_E_ERROR, sExcMsg);
            if (xmlStrcmp(sNil, reinterpret_cast<xmlChar const *>("false")) == 0)
            {
                if ( ! m_vmParameters)
                    m_vmParameters = std::optional<std::vector<OUString> >(
                        std::vector<OUString> ());

                xmlNode * pOpt = cur->children;
                while (pOpt != nullptr)
                {
                    if (xmlStrcmp(pOpt->name, reinterpret_cast<xmlChar const *>("param")) == 0)
                    {
                        CXmlCharPtr sOpt = xmlNodeListGetString(
                            docUser, pOpt->children, 1);
                        m_vmParameters->push_back(sOpt);
                    }
                    pOpt = pOpt->next;
                }
            }
        }
        else if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("jreLocations")) == 0)
        {
            sNil = xmlGetNsProp(
                cur, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE));
            if (sNil == nullptr)
                throw FrameworkException(JFW_E_ERROR, sExcMsg);
            if (xmlStrcmp(sNil, reinterpret_cast<xmlChar const *>("false")) == 0)
            {
                if (! m_JRELocations)
                    m_JRELocations = std::optional<std::vector<OUString> >(
                        std::vector<OUString>());

                xmlNode * pLoc = cur->children;
                while (pLoc != nullptr)
                {
                    if (xmlStrcmp(pLoc->name, reinterpret_cast<xmlChar const *>("location")) == 0)
                    {
                        CXmlCharPtr sLoc = xmlNodeListGetString(
                            docUser, pLoc->children, 1);
                        m_JRELocations->push_back(sLoc);
                    }
                    pLoc = pLoc->next;
                }
            }
        }
        cur = cur->next;
    }
}

OString NodeJava::getSettingsPath() const
{
    OString ret;
    switch (m_layer)
    {
    case USER: ret = getUserSettingsPath(); break;
    case SHARED: ret = getSharedSettingsPath(); break;
    default:
        OSL_FAIL("[Java framework] NodeJava::getSettingsPath()");
    }
    return ret;
}

OUString NodeJava::getSettingsURL() const
{
    OUString ret;
    switch (m_layer)
    {
    case USER: ret = BootParams::getUserData(); break;
    case SHARED: ret = BootParams::getSharedData(); break;
    default:
        OSL_FAIL("[Java framework] NodeJava::getSettingsURL()");
    }
    return ret;
}

bool NodeJava::prepareSettingsDocument() const
{
    OString sExcMsg(
        "[Java framework] Error in function prepareSettingsDocument"
        " (elements.cxx)."_ostr);
    if (!createSettingsDocument())
    {
        return false;
    }
    OString sSettings = getSettingsPath();
    CXmlDocPtr doc(xmlParseFile(sSettings.getStr()));
    if (!doc)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);

    bool bNeedsSave = false;
    createSettingsStructure(doc, & bNeedsSave);
    if (bNeedsSave)
    {
        if (xmlSaveFormatFileEnc(
                sSettings.getStr(), doc,"UTF-8", 1) == -1)
            throw FrameworkException(JFW_E_ERROR, sExcMsg);
    }
    return true;
}

void NodeJava::write() const
{
    OString sExcMsg("[Java framework] Error in function NodeJava::writeSettings "
                         "(elements.cxx)."_ostr);
    CXmlDocPtr docUser;
    CXPathContextPtr contextUser;
    CXPathObjectPtr pathObj;

    if (!prepareSettingsDocument())
    {
        SAL_INFO("jfw.level1", "no path to write settings document to");
        return;
    }

    //Read the user elements
    OString sSettingsPath = getSettingsPath();
    docUser = xmlParseFile(sSettingsPath.getStr());
    if (docUser == nullptr)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);
    contextUser = xmlXPathNewContext(docUser);
    if (xmlXPathRegisterNs(contextUser, reinterpret_cast<xmlChar const *>("jf"),
        reinterpret_cast<xmlChar const *>(NS_JAVA_FRAMEWORK)) == -1)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);

    xmlNode * root = xmlDocGetRootElement(docUser);
    //Get xsi:nil namespace
    xmlNs* nsXsi = xmlSearchNsByHref(docUser,
                             root,
                             reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE));

    //set the <enabled> element
    //The element must exist
    if (m_enabled)
    {
        pathObj = xmlXPathEvalExpression(reinterpret_cast<xmlChar const *>("/jf:java/jf:enabled"),
                                         contextUser);
        if ( ! pathObj || xmlXPathNodeSetIsEmpty(pathObj->nodesetval))
            throw FrameworkException(JFW_E_ERROR, sExcMsg);

        xmlNode * nodeEnabled = pathObj->nodesetval->nodeTab[0];
        xmlSetNsProp(nodeEnabled,
                     nsXsi,
                     reinterpret_cast<xmlChar const *>("nil"),
                     reinterpret_cast<xmlChar const *>("false"));

        if (m_enabled == std::optional<sal_Bool>(true))
            xmlNodeSetContent(nodeEnabled,reinterpret_cast<xmlChar const *>("true"));
        else
            xmlNodeSetContent(nodeEnabled,reinterpret_cast<xmlChar const *>("false"));
    }

    //set the <userClassPath> element
    //The element must exist
    if (m_userClassPath)
    {
        pathObj = xmlXPathEvalExpression(reinterpret_cast<xmlChar const *>("/jf:java/jf:userClassPath"),
                                         contextUser);
        if ( ! pathObj || xmlXPathNodeSetIsEmpty(pathObj->nodesetval))
            throw FrameworkException(JFW_E_ERROR, sExcMsg);

        xmlNode * nodeEnabled = pathObj->nodesetval->nodeTab[0];
        xmlSetNsProp(nodeEnabled, nsXsi, reinterpret_cast<xmlChar const *>("nil"),reinterpret_cast<xmlChar const *>("false"));
        xmlNodeSetContent(nodeEnabled,static_cast<xmlChar*>(CXmlCharPtr(*m_userClassPath)));
    }

    //set <javaInfo> element
    if (m_javaInfo)
    {
        pathObj = xmlXPathEvalExpression(reinterpret_cast<xmlChar const *>("/jf:java/jf:javaInfo"),
                                                contextUser);
        if ( ! pathObj || xmlXPathNodeSetIsEmpty(pathObj->nodesetval))
            throw FrameworkException(JFW_E_ERROR, sExcMsg);
        m_javaInfo->writeToNode(
            docUser, pathObj->nodesetval->nodeTab[0]);
    }

    //set <vmParameters> element
    if (m_vmParameters)
    {
        pathObj = xmlXPathEvalExpression(reinterpret_cast<xmlChar const *>("/jf:java/jf:vmParameters"),
                                         contextUser);
        if ( ! pathObj || xmlXPathNodeSetIsEmpty(pathObj->nodesetval))
            throw FrameworkException(JFW_E_ERROR, sExcMsg);
        xmlNode* vmParameters = pathObj->nodesetval->nodeTab[0];
        //set xsi:nil = false;
        xmlSetNsProp(vmParameters, nsXsi,reinterpret_cast<xmlChar const *>("nil"),
                     reinterpret_cast<xmlChar const *>("false"));

        //remove option elements
        xmlNode* cur = vmParameters->children;
        while (cur != nullptr)
        {
            xmlNode* lastNode = cur;
            cur = cur->next;
            xmlUnlinkNode(lastNode);
            xmlFreeNode(lastNode);
        }
        //add a new line after <vmParameters>
        if (!m_vmParameters->empty())
        {
            xmlNode * nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n"));
            xmlAddChild(vmParameters, nodeCrLf);
        }

        for (auto const & vmParameter : *m_vmParameters)
        {
            xmlNewTextChild(vmParameters, nullptr, reinterpret_cast<xmlChar const *>("param"),
                            CXmlCharPtr(vmParameter));
            //add a new line
            xmlNode * nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n"));
            xmlAddChild(vmParameters, nodeCrLf);
        }
    }

    //set <jreLocations> element
    if (m_JRELocations)
    {
        pathObj = xmlXPathEvalExpression(reinterpret_cast<xmlChar const *>("/jf:java/jf:jreLocations"),
                                         contextUser);
        if ( ! pathObj || xmlXPathNodeSetIsEmpty(pathObj->nodesetval))
            throw FrameworkException(JFW_E_ERROR, sExcMsg);
        xmlNode* jreLocationsNode = pathObj->nodesetval->nodeTab[0];
        //set xsi:nil = false;
        xmlSetNsProp(jreLocationsNode, nsXsi,reinterpret_cast<xmlChar const *>("nil"),
                     reinterpret_cast<xmlChar const *>("false"));

        //remove option elements
        xmlNode* cur = jreLocationsNode->children;
        while (cur != nullptr)
        {
            xmlNode* lastNode = cur;
            cur = cur->next;
            xmlUnlinkNode(lastNode);
            xmlFreeNode(lastNode);
        }
        //add a new line after <vmParameters>
        if (!m_JRELocations->empty())
        {
            xmlNode * nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n"));
            xmlAddChild(jreLocationsNode, nodeCrLf);
        }

        for (auto const & JRELocation : *m_JRELocations)
        {
            xmlNewTextChild(jreLocationsNode, nullptr, reinterpret_cast<xmlChar const *>("location"),
                            CXmlCharPtr(JRELocation));
            //add a new line
            xmlNode * nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n"));
            xmlAddChild(jreLocationsNode, nodeCrLf);
        }
    }

    if (xmlSaveFormatFile(sSettingsPath.getStr(), docUser, 1) == -1)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);
}

void NodeJava::setEnabled(bool bEnabled)
{
    m_enabled =  std::optional<sal_Bool>(bEnabled);
}


void NodeJava::setUserClassPath(const OUString & sClassPath)
{
    m_userClassPath = std::optional<OUString>(sClassPath);
}

void NodeJava::setJavaInfo(const JavaInfo * pInfo, bool bAutoSelect)
{
    if (!m_javaInfo)
        m_javaInfo = std::optional<CNodeJavaInfo>(CNodeJavaInfo());
    m_javaInfo->bAutoSelect = bAutoSelect;
    m_javaInfo->bNil = false;

    if (pInfo != nullptr)
    {
        m_javaInfo->m_bEmptyNode = false;
        m_javaInfo->sVendor = pInfo->sVendor;
        m_javaInfo->sLocation = pInfo->sLocation;
        m_javaInfo->sVersion = pInfo->sVersion;
        m_javaInfo->nRequirements = pInfo->nRequirements;
        m_javaInfo->arVendorData = pInfo->arVendorData;
    }
    else
    {
        m_javaInfo->m_bEmptyNode = true;
        m_javaInfo->sVendor.clear();
        m_javaInfo->sLocation.clear();
        m_javaInfo->sVersion.clear();
        m_javaInfo->nRequirements = 0;
        m_javaInfo->arVendorData = rtl::ByteSequence();
    }
}

void NodeJava::setVmParameters(std::vector<OUString> const & arOptions)
{
    m_vmParameters = std::optional<std::vector<OUString> >(arOptions);
}

void NodeJava::addJRELocation(OUString const & sLocation)
{
    if (!m_JRELocations)
        m_JRELocations = std::optional<std::vector<OUString> >(
            std::vector<OUString> ());
     //only add the path if not already present
    std::vector<OUString>::const_iterator it =
        std::find(m_JRELocations->begin(), m_JRELocations->end(), sLocation);
    if (it == m_JRELocations->end())
        m_JRELocations->push_back(sLocation);
}

jfw::FileStatus NodeJava::checkSettingsFileStatus(OUString const & sURL)
{
    jfw::FileStatus ret = FILE_DOES_NOT_EXIST;

    //check the file time
    ::osl::DirectoryItem item;
    File::RC rc = ::osl::DirectoryItem::get(sURL, item);
    if (File::E_None == rc)
    {
        ::osl::FileStatus stat(osl_FileStatus_Mask_Validate);
        File::RC rc_stat = item.getFileStatus(stat);
        if (File::E_None == rc_stat)
        {
            ret = FILE_OK;
        }
        else if (File::E_NOENT == rc_stat)
        {
            ret = FILE_DOES_NOT_EXIST;
        }
        else
        {
            ret = FILE_INVALID;
        }
    }
    else if(File::E_NOENT == rc)
    {
        ret = FILE_DOES_NOT_EXIST;
    }
    else
    {
        ret = FILE_INVALID;
    }
    return ret;
}

bool NodeJava::createSettingsDocument() const
{
    const OUString sURL = getSettingsURL();
    if (sURL.isEmpty())
    {
        return false;
    }
    //make sure there is a user directory
    OString sExcMsg("[Java framework] Error in function createSettingsDocument "
                         "(elements.cxx)."_ostr);
    // check if javasettings.xml already exist
    if (FILE_OK == checkSettingsFileStatus(sURL))
        return true;

    //make sure that the directories are created in case they do not exist
    FileBase::RC rcFile = Directory::createPath(getDirFromFile(sURL));
    if (rcFile != FileBase::E_EXIST && rcFile != FileBase::E_None)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);

    //javasettings.xml does not exist yet
    CXmlDocPtr doc(xmlNewDoc(reinterpret_cast<xmlChar const *>("1.0")));
    if (! doc)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);

    //Create the root element and name spaces
    xmlNodePtr root =   xmlNewDocNode(
        doc, nullptr, reinterpret_cast<xmlChar const *>("java"), reinterpret_cast<xmlChar const *>("\n"));

    if (root == nullptr)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);

    if (xmlNewNs(root, reinterpret_cast<xmlChar const *>(NS_JAVA_FRAMEWORK),nullptr) == nullptr)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);
    if (xmlNewNs(root,reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE),reinterpret_cast<xmlChar const *>("xsi")) == nullptr)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);
    xmlDocSetRootElement(doc,  root);

    //Create a comment
    xmlNodePtr com = xmlNewComment(
        reinterpret_cast<xmlChar const *>("This is a generated file. Do not alter this file!"));
    if (com == nullptr)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);

    if (xmlAddPrevSibling(root, com) == nullptr)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);

    const OString path = getSettingsPath();
    if (xmlSaveFormatFileEnc(path.getStr(), doc,"UTF-8", 1) == -1)
         throw FrameworkException(JFW_E_ERROR, sExcMsg);
    return true;
}


CNodeJavaInfo::CNodeJavaInfo() :
    m_bEmptyNode(false), bNil(true), bAutoSelect(true),
    nRequirements(0)
{
}

void CNodeJavaInfo::loadFromNode(xmlDoc * pDoc, xmlNode * pJavaInfo)
{
    OString sExcMsg("[Java framework] Error in function NodeJavaInfo::loadFromNode "
                         "(elements.cxx)."_ostr);

    assert(pJavaInfo && pDoc);
    if (pJavaInfo->children == nullptr)
        return;
    //Get the xsi:nil attribute;
    CXmlCharPtr sNil = xmlGetNsProp(
        pJavaInfo, reinterpret_cast<xmlChar const *>("nil"), reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE));
    if ( ! sNil)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);

    if (xmlStrcmp(sNil, reinterpret_cast<xmlChar const *>("true")) == 0)
        bNil = true;
    else if (xmlStrcmp(sNil, reinterpret_cast<xmlChar const *>("false")) == 0)
        bNil = false;
    else
        throw FrameworkException(JFW_E_ERROR, sExcMsg);
    if (bNil)
        return;

    //Get javaInfo@manuallySelected attribute
    CXmlCharPtr sAutoSelect = xmlGetProp(
        pJavaInfo, reinterpret_cast<xmlChar const *>("autoSelect"));
    if ( ! sAutoSelect)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);

    if (xmlStrcmp(sAutoSelect, reinterpret_cast<xmlChar const *>("true")) == 0)
        bAutoSelect = true;
    else if (xmlStrcmp(sAutoSelect, reinterpret_cast<xmlChar const *>("false")) == 0)
        bAutoSelect = false;
    else
        throw FrameworkException(JFW_E_ERROR, sExcMsg);

    xmlNode * cur = pJavaInfo->children;

    while (cur != nullptr)
    {
        if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("vendor")) == 0)
        {
            CXmlCharPtr xmlVendor = xmlNodeListGetString(
                pDoc, cur->children, 1);
            if (! xmlVendor)
                return;
            sVendor = xmlVendor;
        }
        else if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("location")) == 0)
        {
            CXmlCharPtr xmlLocation = xmlNodeListGetString(
                pDoc, cur->children, 1);
            sLocation = xmlLocation;
        }
        else if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("version")) == 0)
        {
            CXmlCharPtr xmlVersion = xmlNodeListGetString(
                pDoc, cur->children, 1);
            sVersion = xmlVersion;
        }
        else if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("requirements")) == 0)
        {
            CXmlCharPtr xmlRequire = xmlNodeListGetString(
                pDoc, cur->children, 1);
            OUString sRequire = xmlRequire;
            nRequirements = sRequire.toInt64(16);
#ifdef MACOSX
            //javaldx is not used anymore in the mac build. In case the Java
            //corresponding to the saved settings does not exist anymore the
            //javavm services will look for an existing Java after creation of
            //the JVM failed. See stoc/source/javavm/javavm.cxx. Only if
            //nRequirements does not have the flag JFW_REQUIRE_NEEDRESTART the
            //jvm of the new selected JRE will be started. Old settings (before
            //OOo 3.3) still contain the flag which can be safely ignored.
            nRequirements &= ~JFW_REQUIRE_NEEDRESTART;
#endif
        }
        else if (xmlStrcmp(cur->name, reinterpret_cast<xmlChar const *>("vendorData")) == 0)
        {
            CXmlCharPtr xmlData = xmlNodeListGetString(
                pDoc, cur->children, 1);
            xmlChar* _data = static_cast<xmlChar*>(xmlData);
            if (_data)
            {
                rtl::ByteSequence seq(reinterpret_cast<sal_Int8*>(_data), strlen(reinterpret_cast<char*>(_data)));
                arVendorData = decodeBase16(seq);
            }
        }
        cur = cur->next;
    }

    if (sVendor.isEmpty())
        m_bEmptyNode = true;
    //Get the javainfo attributes
    CXmlCharPtr sVendorUpdate = xmlGetProp(pJavaInfo,
                               reinterpret_cast<xmlChar const *>("vendorUpdate"));
    if ( ! sVendorUpdate)
        throw FrameworkException(JFW_E_ERROR, sExcMsg);
    sAttrVendorUpdate = sVendorUpdate;
}


void CNodeJavaInfo::writeToNode(xmlDoc* pDoc,
                                xmlNode* pJavaInfoNode) const

{
    assert(pJavaInfoNode && pDoc);
    //write the attribute vendorSettings

    //javaInfo@vendorUpdate
    //creates the attribute if necessary
    OString sUpdated = getElementUpdated();

    xmlSetProp(pJavaInfoNode, reinterpret_cast<xmlChar const *>("vendorUpdate"),
               reinterpret_cast<xmlChar const *>(sUpdated.getStr()));

    //javaInfo@autoSelect
    xmlSetProp(pJavaInfoNode, reinterpret_cast<xmlChar const *>("autoSelect"),
               reinterpret_cast<xmlChar const *>(bAutoSelect ? "true" : "false"));

    //Set xsi:nil in javaInfo element to false
    //the xmlNs pointer must not be destroyed
    xmlNs* nsXsi = xmlSearchNsByHref(pDoc,
                             pJavaInfoNode,
                             reinterpret_cast<xmlChar const *>(NS_SCHEMA_INSTANCE));

    xmlSetNsProp(pJavaInfoNode,
                 nsXsi,
                 reinterpret_cast<xmlChar const *>("nil"),
                 reinterpret_cast<xmlChar const *>("false"));

    //Delete the children of JavaInfo
    xmlNode* cur = pJavaInfoNode->children;
    while (cur != nullptr)
    {
        xmlNode* lastNode = cur;
        cur = cur->next;
        xmlUnlinkNode(lastNode);
        xmlFreeNode(lastNode);
    }

    //If the JavaInfo was set with an empty value,
    //then we are done.
    if (m_bEmptyNode)
        return;

    //add a new line after <javaInfo>
    xmlNode * nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n"));
    xmlAddChild(pJavaInfoNode, nodeCrLf);

    //Create the vendor element
    xmlNewTextChild(pJavaInfoNode, nullptr, reinterpret_cast<xmlChar const *>("vendor"),
                    CXmlCharPtr(sVendor));
    //add a new line for better readability
    nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n"));
    xmlAddChild(pJavaInfoNode, nodeCrLf);

    //Create the location element
    xmlNewTextChild(pJavaInfoNode, nullptr, reinterpret_cast<xmlChar const *>("location"),
                    CXmlCharPtr(sLocation));
    //add a new line for better readability
    nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n"));
    xmlAddChild(pJavaInfoNode, nodeCrLf);

    //Create the version element
    xmlNewTextChild(pJavaInfoNode, nullptr, reinterpret_cast<xmlChar const *>("version"),
                    CXmlCharPtr(sVersion));
    //add a new line for better readability
    nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n"));
    xmlAddChild(pJavaInfoNode, nodeCrLf);

    //Create the features element, for backwards compatibility (it used to support one flag
    // JFW_FEATURE_ACCESSBRIDGE = 0x01, but is ignored and always written as zero now)
    xmlNewTextChild(pJavaInfoNode, nullptr, reinterpret_cast<xmlChar const *>("features"),
                    reinterpret_cast<xmlChar const *>("0"));
    //add a new line for better readability
    nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n"));
    xmlAddChild(pJavaInfoNode, nodeCrLf);


    //Create the requirements element
    OUString sRequirements = OUString::number(
         nRequirements, 16);
    xmlNewTextChild(pJavaInfoNode, nullptr, reinterpret_cast<xmlChar const *>("requirements"),
                    CXmlCharPtr(sRequirements));
    //add a new line for better readability
    nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n"));
    xmlAddChild(pJavaInfoNode, nodeCrLf);


    //Create the vendorData element
    rtl::ByteSequence data = encodeBase16(arVendorData);
    xmlNode* dataNode = xmlNewChild(pJavaInfoNode, nullptr,
                                    reinterpret_cast<xmlChar const *>("vendorData"),
                                    reinterpret_cast<xmlChar const *>(""));
    xmlNodeSetContentLen(dataNode,
                         reinterpret_cast<xmlChar*>(data.getArray()), data.getLength());
    //add a new line for better readability
    nodeCrLf = xmlNewText(reinterpret_cast<xmlChar const *>("\n"));
    xmlAddChild(pJavaInfoNode, nodeCrLf);
}

std::unique_ptr<JavaInfo> CNodeJavaInfo::makeJavaInfo() const
{
    if (bNil || m_bEmptyNode)
        return std::unique_ptr<JavaInfo>();
    return std::unique_ptr<JavaInfo>(
        new JavaInfo{
            sVendor, sLocation, sVersion, nRequirements,
            arVendorData});
}


MergedSettings::MergedSettings():
    m_bEnabled(false)
{
    NodeJava settings(NodeJava::USER);
    settings.load();
    NodeJava sharedSettings(NodeJava::SHARED);
    sharedSettings.load();
    merge(sharedSettings, settings);
}

MergedSettings::~MergedSettings()
{
}

void MergedSettings::merge(const NodeJava & share, const NodeJava & user)
{
    if (user.getEnabled())
        m_bEnabled = * user.getEnabled();
    else if (share.getEnabled())
        m_bEnabled = * share.getEnabled();
    else
        m_bEnabled = true;

    if (user.getUserClassPath())
        m_sClassPath = * user.getUserClassPath();
    else if (share.getUserClassPath())
        m_sClassPath = * share.getUserClassPath();

    if (user.getJavaInfo())
        m_javaInfo = * user.getJavaInfo();
    else if (share.getJavaInfo())
        m_javaInfo = * share.getJavaInfo();

    if (user.getVmParameters())
        m_vmParams = * user.getVmParameters();
    else if (share.getVmParameters())
         m_vmParams = * share.getVmParameters();

    if (user.getJRELocations())
        m_JRELocations = * user.getJRELocations();
    else if (share.getJRELocations())
        m_JRELocations = * share.getJRELocations();
}


::std::vector< OString> MergedSettings::getVmParametersUtf8() const
{
    ::std::vector< OString> ret;
    for (auto const & vmParam : m_vmParams)
    {
        ret.push_back( OUStringToOString(vmParam, RTL_TEXTENCODING_UTF8));
    }
    return ret;
}


std::unique_ptr<JavaInfo> MergedSettings::createJavaInfo() const
{
    return m_javaInfo.makeJavaInfo();
}
#ifdef _WIN32
bool MergedSettings::getJavaInfoAttrAutoSelect() const
{
    return m_javaInfo.bAutoSelect;
}
#endif
void MergedSettings::getVmParametersArray(std::vector<OUString> * parParams)
    const
{
    assert(parParams != nullptr);
    osl::MutexGuard guard(FwkMutex());

    *parParams = m_vmParams;
}

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
