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

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

import com.sun.star.uno.XComponentContext;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.lang.XSingleComponentFactory;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XEventListener;
import com.sun.star.lang.EventObject;

import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Vector;


//==================================================================================================
class Disposer implements XEventListener
{
    private XComponent m_xComp;
    
    //----------------------------------------------------------------------------------------------
    Disposer( XComponent xComp )
    {
        m_xComp = xComp;
    }
    //______________________________________________________________________________________________
    public void disposing( EventObject Source )
    {
        m_xComp.dispose();
    }
}

/** Component context implementation.
*/
public class ComponentContext implements XComponentContext, XComponent
{
    private static final boolean DEBUG = false;
    private static final String SMGR_NAME = "/singletons/com.sun.star.lang.theServiceManager";
    private static final String TDMGR_NAME = "/singletons/com.sun.star.reflection.theTypeDescriptionManager";
    
    private Hashtable m_table;
    private XComponentContext m_xDelegate;
    
    private XMultiComponentFactory m_xSMgr;
    private boolean m_bDisposeSMgr;
    
    private Vector m_eventListener;

    /** Ctor to create a component context passing a hashtable for values and a delegator
        reference. Entries of the passed hashtable are either direct values or
        ComponentContextEntry objects.
        
        @param table
               entries
        @param xDelegate
               if values are not found, request is delegated to this object
    */
    public ComponentContext( Hashtable table, XComponentContext xDelegate )
    {
        m_eventListener = new Vector();
        m_table = table;
        m_xDelegate = xDelegate;
        m_xSMgr = null;
        m_bDisposeSMgr = false;

        Object o = table.get( SMGR_NAME );
        if (o != null)
        {
            if (o instanceof ComponentContextEntry)
            {
                o = ((ComponentContextEntry)o).m_value;
            }
            m_xSMgr = UnoRuntime.queryInterface(
                XMultiComponentFactory.class, o );
        }
        if (m_xSMgr != null)
        {
            m_bDisposeSMgr = true;
        }
        else if (m_xDelegate != null)
        {
            m_xSMgr = m_xDelegate.getServiceManager();
        }
        
        // listen for delegate
        XComponent xComp = UnoRuntime.queryInterface(
            XComponent.class, m_xDelegate );
        if (xComp != null)
        {
            xComp.addEventListener( new Disposer( this ) );
        }
    }
    
    // XComponentContext impl
    //______________________________________________________________________________________________
    public Object getValueByName( String rName )
    {
        Object o = m_table.get( rName );
        if (o != null)
        {
            if (o instanceof ComponentContextEntry)
            {
                ComponentContextEntry entry = (ComponentContextEntry)o;
                if (entry.m_lateInit != null)
                {
                    Object xInstance = null;

                    try
                    {
                        String serviceName = (String)entry.m_lateInit;
                        if (serviceName != null)
                        {
                            if (m_xSMgr != null)
                            {
                                xInstance = m_xSMgr.createInstanceWithContext( serviceName, this );
                            }
                            else
                            {
                                if (DEBUG)
                                    System.err.println( "### no service manager instance for late init of singleton instance \"" + rName + "\"!" );
                            }
                        }
                        else
                        {
                            XSingleComponentFactory xCompFac =
                                UnoRuntime.queryInterface(
                                    XSingleComponentFactory.class, entry.m_lateInit );
                            if (xCompFac != null)
                            {
                                xInstance = xCompFac.createInstanceWithContext( this );
                            }
                            else
                            {
                                if (DEBUG)
                                    System.err.println( "### neither service name nor service factory given for late init of singleton instance \"" + rName + "\"!" );
                            }
                        }
                    }
                    catch (com.sun.star.uno.Exception exc)
                    {
                        if (DEBUG)
                            System.err.println( "### exception occurred on late init of singleton instance \"" + rName + "\": " + exc.getMessage() );
                    }
                
                    if (xInstance != null)
                    {
                        synchronized (entry)
                        {
                            if (entry.m_lateInit != null)
                            {
                                entry.m_value = xInstance;
                                entry.m_lateInit = null;
                            }
                            else // inited in the meantime
                            {
                                // dispose fresh service instance
                                XComponent xComp = UnoRuntime.queryInterface(
                                    XComponent.class, xInstance );
                                if (xComp != null)
                                {
                                    xComp.dispose();
                                }
                            }
                        }
                    }
                    else
                    {
                        if (DEBUG)
                            System.err.println( "### failed late init of singleton instance \"" + rName + "\"!" );
                    }
                }
                return entry.m_value;
            }
            else // direct value in map
            {
                return o;
            }
        }
        else if (m_xDelegate != null)
        {
            return m_xDelegate.getValueByName( rName );
        }
        else
        {
            return Any.VOID;
        }
    }
    //______________________________________________________________________________________________
    public XMultiComponentFactory getServiceManager()
    {
        return m_xSMgr;
    }
    
    // XComponent impl
    //______________________________________________________________________________________________
    public void dispose()
    {
        if (DEBUG)
            System.err.print( "> disposing context " + this );
        
        // fire events
        EventObject evt = new EventObject( this );
        Enumeration eventListener = m_eventListener.elements();
        while (eventListener.hasMoreElements())
        {
            XEventListener listener = (XEventListener)eventListener.nextElement();
            listener.disposing( evt );
        }
        m_eventListener.removeAllElements();
        
        XComponent tdmgr = null;
        // dispose values, then service manager, then typdescription manager
        Enumeration keys = m_table.keys();
        while (keys.hasMoreElements())
        {
            String name = (String)keys.nextElement();
            if (! name.equals( SMGR_NAME ))
            {
                Object o = m_table.get( name );
                if (o instanceof ComponentContextEntry)
                {
                    o = ((ComponentContextEntry)o).m_value;
                }
                
                XComponent xComp = UnoRuntime.queryInterface( XComponent.class, o );
                if (xComp != null)
                {
                    if (name.equals( TDMGR_NAME ))
                    {
                        tdmgr = xComp;
                    }
                    else
                    {
                        xComp.dispose();
                    }
                }
            }
        }
        m_table.clear();
        
        // smgr
        if (m_bDisposeSMgr)
        {
            XComponent xComp = UnoRuntime.queryInterface(
                XComponent.class, m_xSMgr );
            if (xComp != null)
            {
                xComp.dispose();
            }
        }
        m_xSMgr = null;
        
        // tdmgr
        if (tdmgr != null)
        {
            tdmgr.dispose();
        }
        
        if (DEBUG)
            System.err.println( "... finished" );
    }
    //______________________________________________________________________________________________
    public void addEventListener( XEventListener xListener )
    {
        if (xListener == null)
            throw new com.sun.star.uno.RuntimeException( "Listener must not be null" );
          if (m_eventListener.contains( xListener ))
              throw new com.sun.star.uno.RuntimeException( "Listener already registred." );      	
        
           m_eventListener.addElement( xListener );
    }
    //______________________________________________________________________________________________
    public void removeEventListener( XEventListener xListener )
    {
        if (xListener == null)
            throw new com.sun.star.uno.RuntimeException( "Listener must not be null" );
          if (! m_eventListener.contains( xListener ))
              throw new com.sun.star.uno.RuntimeException( "Listener is not registered." );      	
        
        m_eventListener.removeElement( xListener );	
    }
}
