/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/

package helper;

import com.sun.star.uno.*;
import com.sun.star.lang.*;
import com.sun.star.container.*;
import com.sun.star.beans.*;
import com.sun.star.util.*;

/**
 * This <CODE>ConfigHelper</CODE> makes it possible to access the 
 * configuration and change their content.<P>
 * <P>
 * Example: <P>
 * Listing of the <CODE>Configuration</CODE> Views.xcu:<P>
 * &lt;oor:component-data xmlns:oor=&quot;http://openoffice.org/2001/registry&quot; xmlns:xs=&quot;http://www.w3.org/2001/XMLSchema&quot; oor:name=&quot;Views&quot; oor:package=&quot;org.openoffice.Office&quot;&gt;<p>
 *   &lt;node oor:name=&quot;Windows&quot;&gt;<P>
 *       <&lt;node oor:name=&quot;SplitWindow0&quot; oor:op=&quot;replace&quot;&gt;<P>
 *           &lt;prop oor:name=&quot;Visible&quot; oor:type=&quot;xs:boolean&quot;&gt;<P>
 *                &lt;value&gt;false&lt;/value&gt;<P>
 *           &lt;/prop&gt;<P>
 *           &lt;prop oor:name=&quot;WindowState&quot; oor:type=&quot;xs:string&quot;&gt;<P>
 *               &lt;value/&gt;<P>
 *           &lt;/prop&gt;<P>
 *            &lt;node oor:name=&quot;UserData&quot;&gt;<P>
 *               &lt;prop oor:name=&quot;UserItem&quot; oor:op=&quot;replace&quot;
 *                oor:type=&quot;xs:string&quot;&gt;<P>
 *                   &lt;value&gt;V1,2,0&lt;/value&gt;<P>
 *               &lt;/prop&gt;<P>
 *           &lt;/node&gt;<P>
 *        &lt;/node&gt;<P>
 *  &lt;/node&gt;<P>
 * <P>
 * <CODE>Definition</CODE><P>
 * <ul>
 *    <li><CODE>&lt;node oor:name=&quot;Windows&quot;&gt;</CODE> 
 *        represents a <CODE>Set</CODE> and is a <CODE>XNameContainer</CODE></LI>
 *    <li><CODE>&lt;node oor:name=&quot;SplitWindow0&quot;&gt;</CODE> 
 *        represents a <CODE>Group</CODE> and is a <CODE>XNameReplace</CODE></LI>
 *    <li><CODE>&lt;prop oor:name=&quot;Visible&quot;&gt;</CODE> 
 *        represents a pr<CODE></CODE>operty of the group</li>
 *    <li><CODE>&lt;node oor:name=&quot;UserData&quot;&gt;</CODE> 
 *        represents a <CODE>extensible group</CODE> and is a <CODE>XNameContainer</CODE></LI>
 *    <li><CODE>&lt;prop oor:name=&quot;UserItem&quot;&gt;</CODE> 
 *        represents a <CODE>property</CODE> of the extensible group</LI>
 * </UL>
 * We assume in the following examples the existance of:<P>
 * <CODE>ConfigHelper aConfig = new ConfigHelper(xMSF, "org.openoffice.Office.Views", false);</CODE>
 * <ul>
 *    <li>If you like to insert a new <CODE>Group</CODE> into the <CODE>Set</CODE> "Windows":<p>
 *        <CODE>XNameReplace xMyGroup = aConfig.getOrInsertGroup("Windows", "myGroup");</CODE><P>
 *        The method <CODE>getOrInsertGroup()</CODE> uses the 
 *        <CODE>XSingleServiceFactory</CODE> to create the skeleton of a new group.
 *        
 *    </li>
 *    <li>If you like to change the property "WindowState" of "myGroup"
 *        of the Set "Windows"<p>
 *        <CODE>aConfig.updateGroupProperty(
 *          "Windows","myGroup", "WindowState", "952,180,244,349;1;0,0,0,0;");</CODE>
 *    </li>
 *    <li>If you like to change the property "myProp" of the extensible group
 *        "myExtGroup" which is an extensible group of "my2ndGroup" of the 
 *        Set "Windows":<p>
 *        <CODE>aConfig.insertOrUpdateExtensibleGroupProperty(
 *              "Windows", "my2ndGroup", "myExtGroup", "myProp","TheValue");</CODE>
 *    </li>
 * </ul>
 */
public class ConfigHelper
{
    private XMultiServiceFactory m_xSMGR = null;
    private XHierarchicalNameAccess m_xConfig = null;

    //-----------------------------------------------
    public ConfigHelper(XMultiServiceFactory xSMGR       ,
                        String               sConfigPath ,
                        boolean              bReadOnly   )
        throws com.sun.star.uno.Exception
    {
        m_xSMGR = xSMGR;

        XMultiServiceFactory xConfigRoot = (XMultiServiceFactory)
                        UnoRuntime.queryInterface(
                        XMultiServiceFactory.class,
                        m_xSMGR.createInstance(
                        "com.sun.star.configuration.ConfigurationProvider"));

        PropertyValue[] lParams = new PropertyValue[1];
        lParams[0] = new PropertyValue();
        lParams[0].Name  = "nodepath";
        lParams[0].Value = sConfigPath;

        Object aConfig;
        if (bReadOnly)
            aConfig = xConfigRoot.createInstanceWithArguments(
                            "com.sun.star.configuration.ConfigurationAccess",
                            lParams);
        else
            aConfig = xConfigRoot.createInstanceWithArguments(
                            "com.sun.star.configuration.ConfigurationUpdateAccess",
                            lParams);

        m_xConfig = (XHierarchicalNameAccess)UnoRuntime.queryInterface(
                            XHierarchicalNameAccess.class,
                            aConfig);

        if (m_xConfig == null)
            throw new com.sun.star.uno.Exception("Could not open configuration \""+sConfigPath+"\"");
    }

    //-----------------------------------------------
    public Object readRelativeKey(String sRelPath,
                                  String sKey    )
        throws com.sun.star.container.NoSuchElementException
    {
        try
        {
            XPropertySet xPath = (XPropertySet)UnoRuntime.queryInterface(
                                    XPropertySet.class,
                                    m_xConfig.getByHierarchicalName(sRelPath));
            return xPath.getPropertyValue(sKey);
        }
        catch(com.sun.star.uno.Exception ex)
        {
            throw new com.sun.star.container.NoSuchElementException(ex.getMessage());
        }
    }

    //-----------------------------------------------
    public void writeRelativeKey(String sRelPath,
                                 String sKey    ,
                                 Object aValue  )
        throws com.sun.star.container.NoSuchElementException
    {
        try
        {
            XPropertySet xPath = (XPropertySet)UnoRuntime.queryInterface(
                                    XPropertySet.class,
                                    m_xConfig.getByHierarchicalName(sRelPath));
            xPath.setPropertyValue(sKey, aValue);
        }
        catch(com.sun.star.uno.Exception ex)
        {
            throw new com.sun.star.container.NoSuchElementException(ex.getMessage());
        }
    }

    //-----------------------------------------------
    /**
     * Updates the configuration.<p>
     * This must be called after you have changed the configuration 
     * else you changes will be lost.
     */
    public void flush()
    {
        try
        {
            XChangesBatch xBatch = (XChangesBatch)UnoRuntime.queryInterface(
                                        XChangesBatch.class,
                                        m_xConfig);
            xBatch.commitChanges();
        }
        catch(com.sun.star.uno.Exception ex)
        {}
    }

    //-----------------------------------------------
    public static Object readDirectKey(XMultiServiceFactory xSMGR      ,
                                       String               sConfigFile,
                                       String               sRelPath   ,
                                       String               sKey       )
        throws com.sun.star.uno.Exception
    {
        ConfigHelper aConfig = new ConfigHelper(xSMGR, sConfigFile, true);
        return aConfig.readRelativeKey(sRelPath, sKey);
    }

    //-----------------------------------------------
    public static void writeDirectKey(XMultiServiceFactory xSMGR      ,
                                      String               sConfigFile,
                                      String               sRelPath   ,
                                      String               sKey       ,
                                      Object               aValue     )
        throws com.sun.star.uno.Exception
    {
        ConfigHelper aConfig = new ConfigHelper(xSMGR, sConfigFile, false);
        aConfig.writeRelativeKey(sRelPath, sKey, aValue);
        aConfig.flush();
    }
    
    
    /**
     * Insert a structured node (group) in a name container (set) 
     * or else update it and retrun the <CODE>XNameReplace</CODE> of it.<P>
     * The <CODE>XSingleServiceFacttory</CODE> of the <CODE>set</CODE> will be used
     * to create a new group. This group is specific to its set and
     * creates defined entries.
     * @return The [inserted] group of the set
     * @param groupName The name of the goup which should be returned
     * @param setName The name of the set
     * @throws com.sun.star.uno.Exception throws 
     *         <CODE>com.sun.star.uno.Exeception</CODE> on any error.
     */
    public XNameReplace getOrInsertGroup(String setName, String groupName) 
        throws  com.sun.star.uno.Exception
                
    {
        XNameContainer xSetCont = this.getSet(setName);
        
        XNameReplace xChildAccess = null;

        try {
            Object xChild=xSetCont.getByName(groupName);
            xChildAccess = (XNameReplace) UnoRuntime.queryInterface(
                            XNameReplace.class,xSetCont);
        } catch(com.sun.star.container.NoSuchElementException e) {
             // proceed with inserting
        }
        
        if (xChildAccess == null)  {
            XSingleServiceFactory xChildfactory = (XSingleServiceFactory) 
                UnoRuntime.queryInterface(XSingleServiceFactory.class,xSetCont);

            Object xNewChild = xChildfactory.createInstance();

            xSetCont.insertByName(groupName, xNewChild);

            xChildAccess = (XNameReplace) 
                UnoRuntime.queryInterface(XNameContainer.class,xNewChild);   
       }
        
        return xChildAccess;
    }

     /**
     * Update a property of a group container of a set container
     * @param setName the name of the <CODE>set</CODE> which containes the <CODE>group</CODE>
     * @param groupName the name of the <CODE>group</CODE> which property should be changed
     * @param propName the name of the property which should be changed
     * @param propValue the value the property should get
     * @throws com.sun.star.uno.Exception throws <CODE>com.sun.star.uno.Exeception</CODE> on any error.
     */
    public void updateGroupProperty(String setName,
                                    String groupName, 
                                    String propName, 
                                    Object propValue)
        throws  com.sun.star.uno.Exception
    {
        XNameContainer xSetCont = this.getSet(setName);
        
        XPropertySet xProp = null;
        try {
        xProp = (XPropertySet)UnoRuntime.queryInterface(
                                    XPropertySet.class,
                                    xSetCont.getByName(groupName));
        } catch (com.sun.star.container.NoSuchElementException e){
            throw new com.sun.star.uno.Exception(
                "could not get group '" + groupName + 
               "' from set '"+ setName +"':\n" + e.toString());
        }
        try{
            xProp.setPropertyValue(propName, propValue);
        } catch (com.sun.star.uno.Exception e) {
             throw new com.sun.star.uno.Exception(
                "could not set property '" + propName +
                "' from group '"+ groupName +
                "' from set '"+ setName +"':\n" + e.toString());
        }
    }   

   
    /**
     * Insert a property in an extensible group container or else update it
     * @param setName the name of the <CODE>set</CODE> which containes the <CODE>group</CODE>
     * @param group The name of the <CODE>group</CODE> which conatins the <CODE>extensible group</CODE>.
     * @param extGroup The name of the <CODE>extensible group</CODE> which 
     *                  [should] contain the property
     * @param propName The name of the property.
     * @param propValue The value of the property.
     * @throws com.sun.star.uno.Exception throws <CODE>com.sun.star.uno.Exeception</CODE> on any error.
     */
    public void insertOrUpdateExtensibleGroupProperty(
                    String setName,
                    String group,
                    String extGroup, 
                    String propName, 
                    Object propValue) 
        throws  com.sun.star.uno.Exception
    {
        XNameContainer xSetCont = this.getSet(setName);
        
        XNameReplace xGroupAccess = null;
        XNameContainer xExtGroupCont = null;

        try {
            Object xGroup=xSetCont.getByName(group);
            xGroupAccess = (XNameReplace) UnoRuntime.queryInterface(
                            XNameReplace.class,xGroup);
        } catch(com.sun.star.container.NoSuchElementException e) {
             throw new com.sun.star.uno.Exception(
                "could not get group '" + group + 
                "' from set '"+ setName +"':\n" + e.toString());
        }
        
        try {
            Object xGroup=xGroupAccess.getByName(extGroup);
            xExtGroupCont = (XNameContainer) UnoRuntime.queryInterface(
                            XNameContainer.class,xGroup);
        } catch(com.sun.star.container.NoSuchElementException e) {
             throw new com.sun.star.uno.Exception(
                "could not get extensilbe group '"+extGroup+
                "' from group '"+ group +
                "' from set '"+ setName +"':\n" + e.toString());
        }
        
        try {
            xExtGroupCont.insertByName(propName, propValue);
        }
        catch(com.sun.star.container.ElementExistException e) {
            xExtGroupCont .replaceByName(propName, propValue);
        }
        
    }
    
    
    /**
     * Returns a <CODE>XNameContainer</CODE> of the <CODE>Set</CODE>
     * of the <CODE>Configuration</CODE>
     * @param setName the name of the Set which sould be returned
     * @throws com.sun.star.uno.Exception on any error
     * @return A XNameContainer of the Set
     */
    public XNameContainer getSet(String setName)
        throws com.sun.star.uno.Exception
    {
        XNameReplace xCont = (XNameReplace) 
                    UnoRuntime.queryInterface(XNameReplace.class, m_xConfig);

        Object oSet = xCont.getByName(setName);
        
        if (oSet == null)
             throw new com.sun.star.uno.Exception(
                "could not get set '" + setName + ": null");
            
        return (XNameContainer) UnoRuntime.queryInterface(
                                                XNameContainer.class, oSet);
        
    }
}
