/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

package com.sun.star.comp.loader;


import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

import com.sun.star.uno.XComponentContext;
import com.sun.star.lang.XInitialization;
import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.lang.XServiceInfo;
import com.sun.star.lang.XSingleServiceFactory;
import com.sun.star.lang.XSingleComponentFactory;
import com.sun.star.lang.XTypeProvider;

import com.sun.star.registry.XRegistryKey;

import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.Type;


/**
 * The purpose of this class to help component implementation.
 * This class has default implementations for <code>getServiceFactory</code>
 * and <code>writeRegistryServiceInfo</code>.
 * <p>
 * @version     $Revision: 1.9 $ $ $Date: 2008-04-11 11:10:09 $
 * @author      Kay Ramme
 * @see         com.sun.star.lang.XMultiServiceFactory
 * @see         com.sun.star.lang.XServiceInfo
 * @see         com.sun.star.lang.XSingleServiceFactory
 * @see         com.sun.star.registry.XRegistryKey
 * @since       UDK1.0
 */
public class FactoryHelper {

    private static final boolean DEBUG = false;
    // the factory
    static protected class Factory
        implements XSingleServiceFactory, XSingleComponentFactory, XServiceInfo,
        XTypeProvider {
        protected static Class __objectArray;

        static {
            try {
                __objectArray = Class.forName("[Ljava.lang.Object;");
            }
            catch(ClassNotFoundException classNotFoundException) {
                System.err.println(FactoryHelper.class.getName() + " exception occurred - " + classNotFoundException);
            }
        }

//        private static final boolean DEBUG = false;

        protected XMultiServiceFactory _xMultiServiceFactory;
        protected XRegistryKey         _xRegistryKey;
        protected int                  _nCode;
        protected Constructor          _constructor;
        protected String               _implName;
        protected String               _serviceName;
        // keeps the Id for XTypeProvider
        protected static Object _mutex= new Object();
        private static byte[] _implementationId;

        protected Factory(Class implClass,
                          String serviceName,
                          XMultiServiceFactory xMultiServiceFactory,
                          XRegistryKey xRegistryKey)
        {
            _xMultiServiceFactory = xMultiServiceFactory;
            _xRegistryKey         = xRegistryKey;
            _implName             = implClass.getName();
            _serviceName          = serviceName;

            Constructor constructors[] = implClass.getConstructors();
            for(int i = 0; i < constructors.length && _constructor == null; ++i) {
                Class parameters[] = constructors[i].getParameterTypes();

                if(parameters.length == 3
                   && parameters[0].equals(XComponentContext.class)
                     && parameters[1].equals(XRegistryKey.class)
                     && parameters[2].equals(__objectArray)) {
                    _nCode = 0;
                    _constructor = constructors[i];
                }
                else if(parameters.length == 2
                     && parameters[0].equals(XComponentContext.class)
                     && parameters[1].equals(XRegistryKey.class)) {
                    _nCode = 1;
                    _constructor = constructors[i];
                }
                else if(parameters.length == 2
                     && parameters[0].equals(XComponentContext.class)
                     && parameters[1].equals(__objectArray)) {
                    _nCode = 2;
                    _constructor = constructors[i];
                }
                else if(parameters.length == 1
                     && parameters[0].equals(XComponentContext.class)) {
                    _nCode = 3;
                    _constructor = constructors[i];
                }
                // depr
                else if(parameters.length == 3
                     && parameters[0].equals(XMultiServiceFactory.class)
                     && parameters[1].equals(XRegistryKey.class)
                     && parameters[2].equals(__objectArray)) {
                    _nCode = 4;
                    _constructor = constructors[i];
                }
                else if(parameters.length == 2
                     && parameters[0].equals(XMultiServiceFactory.class)
                     && parameters[1].equals(XRegistryKey.class)) {
                    _nCode = 5;
                    _constructor = constructors[i];
                }
                else if(parameters.length == 2
                     && parameters[0].equals(XMultiServiceFactory.class)
                     && parameters[1].equals(__objectArray)) {
                    _nCode = 6;
                    _constructor = constructors[i];
                }
                else if(parameters.length == 1
                     && parameters[0].equals(XMultiServiceFactory.class)) {
                    _nCode = 7;
                    _constructor = constructors[i];
                }
                else if(parameters.length == 1
                     && parameters[0].equals(__objectArray)) {
                    _nCode = 8;
                    _constructor = constructors[i];
                }
                else if(parameters.length == 0) {
                    _nCode = 9;
                    _constructor = constructors[i];
                }
            }

            if(_constructor == null) // have not found a useable constructor
                throw new com.sun.star.uno.RuntimeException(getClass().getName() + " can not find a useable constructor");
        }

        private final XMultiServiceFactory getSMgr( XComponentContext xContext )
        {
            if (xContext != null)
            {
                return UnoRuntime.queryInterface(
                    XMultiServiceFactory.class, xContext.getServiceManager() );
            }
            else
            {
                return _xMultiServiceFactory;
            }
        }

        // XComponentContext impl
        //______________________________________________________________________________________________
        public Object createInstanceWithContext(
            XComponentContext xContext )
            throws com.sun.star.uno.Exception
        {
            Object args[];
            switch (_nCode)
            {
            case 0:
                args = new Object [] { xContext, _xRegistryKey, new Object[ 0 ] };
                break;
            case 1:
                args = new Object [] { xContext, _xRegistryKey };
                break;
            case 2:
                args = new Object [] { xContext, new Object[ 0 ] };
                break;
            case 3:
                args = new Object [] { xContext };
                break;
            case 4:
                args = new Object [] { getSMgr( xContext ), _xRegistryKey, new Object[ 0 ] };
                break;
            case 5:
                args = new Object [] { getSMgr( xContext ), _xRegistryKey };
                break;
            case 6:
                args = new Object [] { getSMgr( xContext ), new Object[ 0 ] };
                break;
            case 7:
                args = new Object [] { getSMgr( xContext ) };
                break;
            case 8:
                args = new Object [] { new Object[ 0 ] };
                break;
            default:
                args = new Object [ 0 ];
                break;
            }

            try
            {
                return _constructor.newInstance( args );
            }
            catch (InvocationTargetException invocationTargetException)
            {
                Throwable targetException = invocationTargetException.getTargetException();

                if (targetException instanceof java.lang.RuntimeException)
                    throw (java.lang.RuntimeException)targetException;
                else if (targetException instanceof com.sun.star.uno.Exception)
                    throw (com.sun.star.uno.Exception)targetException;
                else if (targetException instanceof com.sun.star.uno.RuntimeException)
                    throw (com.sun.star.uno.RuntimeException)targetException;
                else
                    throw new com.sun.star.uno.Exception( targetException.toString() );
            }
            catch (IllegalAccessException illegalAccessException)
            {
                throw new com.sun.star.uno.Exception( illegalAccessException.toString() );
            }
            catch (InstantiationException instantiationException)
            {
                throw new com.sun.star.uno.Exception( instantiationException.toString() );
            }
        }
        //______________________________________________________________________________________________
        public Object createInstanceWithArgumentsAndContext(
            Object rArguments[], XComponentContext xContext )
            throws com.sun.star.uno.Exception
        {
            Object args[];

            boolean bInitCall = true;
            switch (_nCode)
            {
            case 0:
                args = new Object [] { xContext, _xRegistryKey, rArguments };
                bInitCall = false;
                break;
            case 1:
                args = new Object [] { xContext, _xRegistryKey };
                break;
            case 2:
                args = new Object [] { xContext, rArguments };
                bInitCall = false;
                break;
            case 3:
                args = new Object [] { xContext };
                break;
            case 4:
                args = new Object [] { getSMgr( xContext ), _xRegistryKey, rArguments };
                bInitCall = false;
                break;
            case 5:
                args = new Object [] { getSMgr( xContext ), _xRegistryKey };
                break;
            case 6:
                args = new Object [] { getSMgr( xContext ), rArguments };
                bInitCall = false;
                break;
            case 7:
                args = new Object [] { getSMgr( xContext ) };
                break;
            case 8:
                args = new Object [] { rArguments };
                bInitCall = false;
                break;
            default:
                args = new Object [ 0 ];
                break;
            }

            try
            {
                Object instance = _constructor.newInstance( args );
                if (bInitCall)
                {
                    XInitialization xInitialization = UnoRuntime.queryInterface(
                        XInitialization.class, instance );
                    if (xInitialization != null)
                    {
                        xInitialization.initialize( rArguments );
                    }
                }
                return instance;
            }
            catch (InvocationTargetException invocationTargetException)
            {
                Throwable targetException = invocationTargetException.getTargetException();

                if (targetException instanceof java.lang.RuntimeException)
                    throw (java.lang.RuntimeException)targetException;
                else if (targetException instanceof com.sun.star.uno.Exception)
                    throw (com.sun.star.uno.Exception)targetException;
                else if (targetException instanceof com.sun.star.uno.RuntimeException)
                    throw (com.sun.star.uno.RuntimeException)targetException;
                else
                    throw new com.sun.star.uno.Exception( targetException.toString() );
            }
            catch (IllegalAccessException illegalAccessException)
            {
                throw new com.sun.star.uno.Exception( illegalAccessException.toString() );
            }
            catch (InstantiationException instantiationException)
            {
                throw new com.sun.star.uno.Exception( instantiationException.toString() );
            }
        }

        /**
         * Creates an instance of the desired service.
         * <p>
         * @return  returns an instance of the desired service
         * @see                  com.sun.star.lang.XSingleServiceFactory
         */
        public Object createInstance()
            throws com.sun.star.uno.Exception,
                   com.sun.star.uno.RuntimeException
        {
            return createInstanceWithContext( null );
        }

         /**
         * Creates an instance of the desired service.
         * <p>
         * @return  returns an instance of the desired service
         * @param   args     the args given to the constructor of the service
         * @see              com.sun.star.lang.XSingleServiceFactory
         */
        public Object createInstanceWithArguments(Object[] args)
            throws com.sun.star.uno.Exception,
                   com.sun.star.uno.RuntimeException
        {
            return createInstanceWithArgumentsAndContext( args, null );
        }

         /**
         * Gives the supported services
         * <p>
         * @return  returns an array of supported services
         * @see             com.sun.star.lang.XServiceInfo
         */
        public String[] getSupportedServiceNames() throws com.sun.star.uno.RuntimeException {
            return new String[]{_serviceName};
        }

         /**
         * Gives the implementation name
         * <p>
         * @return  returns the implementation name
         * @see             com.sun.star.lang.XServiceInfo
         */
        public String getImplementationName() throws com.sun.star.uno.RuntimeException {
            return _implName;
        }

         /**
         * Indicates if the given service is supported.
         * <p>
         * @return  returns true if the given service is supported
         * @see              com.sun.star.lang.XServiceInfo
         */
        public boolean supportsService(String serviceName) throws com.sun.star.uno.RuntimeException {
            String services[] = getSupportedServiceNames();

            boolean found = false;

            for(int i = 0; i < services.length && !found; ++i)
                found = services[i].equals(serviceName);

            return found;
        }

        //XTypeProvider
        public byte[] getImplementationId()
        {
            synchronized (_mutex)
            {
                if (_implementationId == null)
                {
                    int hash = hashCode();
                    String sName= getClass().getName();
                    byte[] arName= sName.getBytes();
                    int nNameLength= arName.length;

                    _implementationId= new byte[ 4 + nNameLength];
                    _implementationId[0]= (byte)(hash & 0xff);
                    _implementationId[1]= (byte)((hash >>> 8) & 0xff);
                    _implementationId[2]= (byte)((hash >>> 16) & 0xff);
                    _implementationId[3]= (byte)((hash >>>24) & 0xff);

                    for (int i= 0; i < nNameLength; i++)
                    {
                        _implementationId[4 + i]= arName[i];
                    }
                }
            }
            return _implementationId;
        }
        //XTypeProvider
        public com.sun.star.uno.Type[] getTypes()
        {
            Type[] t = new Type[] {
                new Type(XSingleServiceFactory.class),
                new Type(XSingleComponentFactory.class),
                new Type(XServiceInfo.class),
                new Type(XTypeProvider.class)
            };
            return t;
        }

    }

    /**
     * Creates a factory for the given class.
     * <p>
     * @deprecated as of UDK 1.0
     * <p>
     * @return  returns a factory
     * @param   implClass     the implementing class
     * @param   multiFactory  the given multi service factory (service manager)
     * @param   regKey        the given registry key
     * @see              com.sun.star.lang.XServiceInfo
     */
    static public XSingleServiceFactory getServiceFactory(Class implClass,
                                                          XMultiServiceFactory multiFactory,
                                                          XRegistryKey regKey)
    {
        XSingleServiceFactory xSingleServiceFactory = null;

        try {
            Field serviceName = null;

            try {
                serviceName = implClass.getField("__serviceName");
            }
            catch(NoSuchFieldException noSuchFieldExceptio) {
                serviceName = implClass.getField("serviceName");  // old style
            }

            xSingleServiceFactory =  new Factory(implClass, (String)serviceName.get(null), multiFactory, regKey);
        }
        catch(NoSuchFieldException noSuchFieldException) {
            System.err.println("##### FactoryHelper.getServiceFactory - exception:" + noSuchFieldException);
        }
        catch(IllegalAccessException illegalAccessException) {
            System.err.println("##### FactoryHelper.getServiceFactory - exception:" + illegalAccessException);
        }

        return xSingleServiceFactory;
    }

    /**
     * Creates a factory for the given class.
     * <p>
     * @return  returns a factory
     * @param   implClass     the implementing class
     * @param   serviceName   the service name of the implementing class
     * @param   multiFactory  the given multi service factory (service manager)
     * @param   regKey        the given registry key
     * @see              com.sun.star.lang.XServiceInfo
     */
    static public XSingleServiceFactory getServiceFactory(Class implClass,
                                                          String serviceName,
                                                          XMultiServiceFactory multiFactory,
                                                          XRegistryKey regKey)
    {
        return new Factory(implClass, serviceName, multiFactory, regKey);
    }

    /** Creates a factory for the given class.

        @return returns a factory object
        @param   implClass     the implementing class
    */
    static public Object createComponentFactory( Class implClass, String serviceName )
    {
        return new Factory( implClass, serviceName, null, null );
    }

    /**
     * Writes the registration data into the registry key
     * <p>
     * @return  success
     * @param   implName      the name of the implementing class
     * @param   serviceName   the service name
     * @param   regKey        the given registry key
     * @see                    com.sun.star.lang.XServiceInfo
     */
    static public boolean writeRegistryServiceInfo(String implName, String serviceName, XRegistryKey regKey) {
        boolean result = false;

          try {
            XRegistryKey newKey = regKey.createKey("/" + implName + "/UNO/SERVICES");

            newKey.createKey(serviceName);

            result = true;
          }
          catch (Exception ex) {
              System.err.println(">>>Connection_Impl.writeRegistryServiceInfo " + ex);
          }

        return result;
    }

    /** Writes the registration data into the registry key.
     * Several services are supported.
     *
     * @param impl_name name of implementation
     * @param supported_services supported services of implementation
     * @param xKey registry key to write to
     * @return success
    */
    public static boolean writeRegistryServiceInfo(
        String impl_name, String supported_services [], XRegistryKey xKey )
    {
          try
        {
            XRegistryKey xNewKey = xKey.createKey( "/" + impl_name + "/UNO/SERVICES" );
            for ( int nPos = 0; nPos < supported_services.length; ++nPos )
            {
                xNewKey.createKey( supported_services[ nPos ] );
            }
            return true;
          }
          catch (com.sun.star.registry.InvalidRegistryException exc)
        {
            if (DEBUG)
            {
                System.err.println(
                    "##### " + Factory.class.getName() + ".writeRegistryServiceInfo -- exc: " +
                    exc.toString() );
            }
          }
        return false;
    }

}

