/*
 *************************************************************************
 * Copyright (c) 2004, 2005 Actuate Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Actuate Corporation  - initial API and implementation
 *  
 *************************************************************************
 */

package org.eclipse.birt.data.oda.util.manifest;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.birt.core.framework.IConfigurationElement;
import org.eclipse.birt.core.framework.IExtension;
import org.eclipse.birt.core.framework.IExtensionPoint;
import org.eclipse.birt.core.framework.IExtensionRegistry;
import org.eclipse.birt.core.framework.Platform;
import org.eclipse.birt.data.oda.OdaException;
import org.eclipse.birt.data.oda.util.OdaResources;
import org.eclipse.birt.data.oda.util.ResourceCache;
import org.eclipse.birt.data.oda.util.ResourceManager;

/**
 * The Manifest Explorer is the entry point to explore and access
 * the BIRT ODA manifest of all the ODA plug-ins extensions that implement the 
 * <code>org.eclipse.birt.data.oda.dataSource</code> or
 * <code>org.eclipse.datatools.connectivity.oda.dataSource</code> extension point.
 * The <code>ManifestExplorer</code> singleton instance can be retrieved 
 * using the <code>getInstance()</code> method.
 * @deprecated	As of BIRT 2.0, replaced by
 * 		{@link org.eclipse.datatools.connectivity.oda.util.manifest.ManifestExplorer 
 * 				org.eclipse.datatools.connectivity.oda.util.manifest.ManifestExplorer} .
 * 		<br>The BIRT ODA run-time framework has been migrated to the 
 * 		Eclipse Data Tools Platform (DTP) project.
 */
public class ManifestExplorer
{
	private static ManifestExplorer sm_instance = null;

    // trace logging variables
	private static Logger sm_logger = null;

    // works around bug in some J2EE servers - Bugzilla #126073
    private static final String PACKAGE_NAME  = 
            "org.eclipse.birt.data.oda.util.manifest";  //$NON-NLS-1$
	
	/**
	 * Gets the <code>ManifestExplorer</code> instance to  
	 * explore the manifest of the data source extensions.
	 * @return	the <code>ManifestExplorer</code> instance.
	 */
	public static ManifestExplorer getInstance()
	{
        if( sm_instance == null )
            sm_instance = new ManifestExplorer();
		return sm_instance;
	}
    
    /**
     * Singleton instance release method.
     */
    public static void releaseInstance()
    {
        sm_instance = null;
        sm_logger = null;
    }
	
	private ManifestExplorer()
	{
	}

    private static Logger getLogger()
    {
        if( sm_logger == null )
            sm_logger = Logger.getLogger( PACKAGE_NAME );
        
        return sm_logger;
    }
	
	/**
	 * Returns a collection of identifiers of 
	 * all ODA data source extensions.
	 * The extension's data source element ID and display name
	 * are stored as the key and value in the returned Properties instance.
	 * Returns an empty <code>Properties</code> if there are 
	 * no data source extensions found.
	 * @return	a <code>Properties</code> containing the id
	 * 			and display name of all data source extensions.
	 */
	public Properties getDataSourceIdentifiers()
	{
		IExtension[] extensions = getDataSourceExtensions();
		int length = ( extensions == null ) ? 
		        		0 : extensions.length;
		Properties extensionIds = new Properties();
		for( int i = 0; i < length; i++ )
		{
			IExtension extension = extensions[i];			
			try
			{
				IConfigurationElement dsElement = getDataSourceElement( extension );
				String dataSourceId = dsElement.getAttribute( "id" );
				String dataSourceDisplayName = getElementDisplayName( dsElement );
				extensionIds.setProperty( dataSourceId, dataSourceDisplayName );
			}
			catch( OdaException ex )
			{
                getLogger().log( Level.WARNING, "Ignoring invalid extension.", ex );
			}
		}
		
		return extensionIds;
	}

	/**
	 * Returns the extension configuration information found 
	 * in the plugin manifest file
	 * of the specified data source extension.
	 * Includes extension that implements either the org.eclipse.birt.data.oda.dataSource
	 * or org.eclipse.datatools.connectivity.oda.dataSource extension points.
	 * @param extensionId	the unique id of the data source element
	 * 						in a data source extension.
	 * @return	the extension manifest information.
	 * @throws IllegalArgumentException	if no ODA driver plugin supports the data source extension ID.
	 * @throws OdaException	if the extension manifest is invalid.
	 */
	public ExtensionManifest getExtensionManifest( String extensionId ) 
		throws OdaException
	{
	    return new ExtensionManifest( findIExtension( extensionId ) );   
	}
	
	/**
	 * Returns the birt core platform extension configuration information found 
	 * for the specified data source extension.
	 * Includes extension that implements either the org.eclipse.birt.data.oda.dataSource
	 * or org.eclipse.datatools.connectivity.oda.dataSource extension points.
	 * @param extensionId	the unique id of the data source element
	 * 						in a data source extension.
	 * @return	the birt platform extension manifest information.
	 * @throws IllegalArgumentException	if no ODA driver plugin supports the data source extension ID.
	 */
	IExtension findIExtension( String extensionId )
	{
	    if ( extensionId == null || extensionId.length() == 0 )
			throw new IllegalArgumentException( extensionId );

	    IExtension[] extensions = getDataSourceExtensions();
		int length = ( extensions == null ) ? 
        				0 : extensions.length;
		
		for( int i = 0; i < length; i++ )
		{
			IExtension extension = extensions[i];
			
			String dataSourceId = null;
			try
			{
				IConfigurationElement dataSourceElement = 
				    		getDataSourceElement( extension );
				dataSourceId = dataSourceElement.getAttribute( "id" );
			}
			catch( OdaException ex )
			{
                getLogger().log( Level.WARNING, "Ignoring invalid extension.", ex );
				continue;
			}
			
			// check if this is a match of given extensionId
			if( dataSourceId != null &&
			    dataSourceId.equals( extensionId ) )
				return extension;
		}

		throw new IllegalArgumentException( extensionId );
	}

	/**
	 * Returns an array of ODA extension configuration information  
	 * found in the plugin manifest file.  Returns 
	 * an empty array if there are no data source extensions found.
	 * Invalid data source extension definitions are ignored.
	 * Includes extensions that implement either the org.eclipse.birt.data.oda.dataSource
	 * or org.eclipse.datatools.connectivity.oda.dataSource extension points.
	 * @return	an <code>ExtensionManifest</code> array containing 
	 * 			the definition of all valid ODA data source extensions.
	 */
	public ExtensionManifest[] getExtensionManifests()
	{
		IExtension[] extensions = getDataSourceExtensions();
		int length = ( extensions == null ) ? 
						0 : extensions.length;
		ArrayList manifestList = new ArrayList( length );
		for( int i = 0; i < length; i++ )
		{
			IExtension extension = extensions[i];	
			try
			{
				// validate and create extension manifest
				manifestList.add( new ExtensionManifest( extension ) );
			}
			catch( OdaException ex )
			{
                getLogger().log( Level.WARNING, "Ignoring invalid extension.", ex );
			}
		}
		
		int numOfValidExtensions = manifestList.size();
		return (ExtensionManifest[]) 
			manifestList.toArray( new ExtensionManifest[ numOfValidExtensions ] );
	}

	/*
	 * returns extensions that implement either the org.eclipse.birt.data.oda.dataSource
	 * or org.eclipse.datatools.connectivity.oda.dataSource extension points
	 */
	IExtension[] getDataSourceExtensions()
	{
		IExtensionRegistry pluginRegistry = Platform.getExtensionRegistry();
		IExtension[] birtExtensions = null, dtpExtensions = null;
		int birtExtLength = 0, dtpExtLength = 0;
		
		// find birt datasource extensions
		IExtensionPoint birtExtensionPoint = 
			pluginRegistry.getExtensionPoint( "org.eclipse.birt.data.oda.dataSource" );
		if ( birtExtensionPoint != null )
		    birtExtensions = birtExtensionPoint.getExtensions();
		
		// find dtp datasource extensions
		IExtensionPoint dtpExtensionPoint = 
			pluginRegistry.getExtensionPoint( "org.eclipse.datatools.connectivity.oda.dataSource" );
		if ( dtpExtensionPoint != null )
		    dtpExtensions = dtpExtensionPoint.getExtensions();
		
		// merge the birt and dtp extensions into a single array, 
		// excluding the dtp-oda adapter from the returned array
		
		if ( birtExtensions != null )
		    birtExtLength = birtExtensions.length;
		if ( dtpExtensions != null )
		    dtpExtLength = dtpExtensions.length;
		
		List combinedList = new LinkedList();
        
		for( int i = 0; i < birtExtLength; i += 1 )
            combinedList.add( birtExtensions[i] );
        
        for( int i = 0; i < dtpExtLength; i += 1 )
            if ( ! dtpExtensions[i].getNamespace().equals( "org.eclipse.birt.data.oda.adapter.dtp" ) )
                combinedList.add( dtpExtensions[i] );
        
        IExtension[] combinedArray = new IExtension[combinedList.size()];
        combinedList.toArray( combinedArray );
        return combinedArray;
	}
	
	// Package helper methods
	
	/*
	 * Returns the dataSource element of the given data source extension.
	 */
	static IConfigurationElement getDataSourceElement( IExtension extension ) 
		throws OdaException
	{
		IConfigurationElement[] configElements = extension.getConfigurationElements();
		for( int i = 0, n = configElements.length; i < n; i++ )
		{
			IConfigurationElement configElement = configElements[i];
			if( ! configElement.getName().equalsIgnoreCase( "dataSource" ) )
			    continue;

			// validate that the data source element has an id
			String dataSourceId = configElement.getAttribute( "id" );	
			if( dataSourceId == null || dataSourceId.length() == 0 )
				throw new OdaException( getLocalizedMessage( OdaResources.NO_DATA_SOURCE_EXTN_ID_DEFINED ) );

			return configElement;	// expects only one such element
		}
		
		throw new OdaException( getLocalizedMessage( OdaResources.NO_DRIVER_RUNTIME_CONFIGURATION_DEFINED ) );
	}
	
	/*
	 * Returns a collection of dataSet elements of the given data source extension.
	 */
	static Hashtable getDataSetElements( IExtension extension, String dataSourceElementId )
		throws OdaException
	{
		IConfigurationElement[] configElements = extension.getConfigurationElements();
		Hashtable dataSetElements = new Hashtable();
		for( int i = 0, size = configElements.length; i < size; i++ )
		{
			IConfigurationElement configElement = configElements[i];
			if( ! configElement.getName().equalsIgnoreCase( "dataSet" ) )
			    continue;
			
			// validate that the data set definition has an id
			String dataSetTypeId = configElement.getAttribute( "id" );
			if( dataSetTypeId == null || dataSetTypeId.length() == 0 )
				throw new OdaException( getLocalizedMessage( OdaResources.NO_DATA_SET_TYPE_ID_DEFINED,
															new Object[] { dataSourceElementId } ) );		
			// if duplicated data set type ids exist in the extension,  
			// only the last one applies
			dataSetElements.put( dataSetTypeId, new DataSetType( configElement ) );
		}

		if( dataSetElements.size() < 1 )
			throw new OdaException( getLocalizedMessage( OdaResources.NO_DATA_SET_TYPES_DEFINED,
														new Object[] { dataSourceElementId } ) );
		return dataSetElements;
	}
	
	/*
	 * Encapsulates the logic of finding the most appropriate
	 * display name to use for the given element.
	 */ 
	static String getElementDisplayName( IConfigurationElement dsElement )
	{
	    // localized displayNameId is not yet supported in Release 1;
	    // it is not necessary and removed in DTP ODA extension point

	    String displayName = dsElement.getAttribute( "defaultDisplayName" );

	    // Default to its id or name, if no display name is specified
		if ( displayName == null || displayName.length() == 0 )
		{
		    displayName = dsElement.getAttribute( "id" );
		    if ( displayName == null )  // no such attribute
			    displayName = dsElement.getAttribute( "name" );
		}
		
		return displayName;
	}

	
	static String getLocalizedMessage( int errorNumber )
	{
		ResourceManager manager = 
			ResourceCache.instance().getResources( "org.eclipse.birt.data.oda.util.OdaResources", 
												   Locale.getDefault() );
		return ( manager != null ) ? manager.getString( errorNumber ) : "";
	}
	
	static String getLocalizedMessage( int errorNumber, Object[] arguments )
	{
		return MessageFormat.format( getLocalizedMessage( errorNumber ), arguments );
	}
}
