/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */
package com.sun.enterprise.repository;

import java.util.*;
import java.lang.reflect.*;
import com.sun.enterprise.util.Utility;
import com.sun.enterprise.util.LocalStringManagerImpl;
// IASRI 4660742 START
import java.util.logging.*;
import com.sun.logging.*;
// IASRI 4660742 END

/**
 * Generic class for converting raw resource info to a 
 * J2EEResource and vice versa.  Makes following assumptions
 * about J2EEResource implementation classes.
 * 1. Has public constructor that takes a single java.lang.String arg
 * 2. Any public getX method represents a resource attribute.
 * 3. Every such getX method except getName() has a corresponding 
 *    public setX method.
 *
 * @author Kenneth Saks
 */
public class GenericConverter implements J2EEResourceConverter {

// IASRI 4660742 START
    private static Logger _logger=null;
    static{
       _logger=LogDomains.getLogger(LogDomains.ROOT_LOGGER);
        }
// IASRI 4660742 END
    private static LocalStringManagerImpl localStrings =
	new LocalStringManagerImpl(GenericConverter.class);

    private static final String RESOURCE_NAME_ATTR = "name";

    private static Hashtable resourceImplClasses;
    private static Hashtable attributeMethods;

    private int counter = 0;
    
    //
    // Resource type to concrete J2EEResource class mappings.
    //
    static {
        resourceImplClasses = new Hashtable();
        attributeMethods    = new Hashtable();

        resourceImplClasses.put
            ("jmsCnxFactory", 
             com.sun.enterprise.repository.JmsCnxFactoryResource.class);
        resourceImplClasses.put
            ("jmsDestination",
             com.sun.enterprise.repository.JmsDestinationResource.class);
        resourceImplClasses.put
            ("jdbcDataSource",
             com.sun.enterprise.repository.JdbcResource.class);
        resourceImplClasses.put
            ("jdbcXADataSource",
             com.sun.enterprise.repository.JdbcXAResource.class);
        resourceImplClasses.put
            ("jdbcDriver",
             com.sun.enterprise.repository.JdbcDriver.class);
        /**IASRI 4633236
         resourceImplClasses.put
            ("connectorCnxFactory",
            com.sun.enterprise.repository.ConnectorResource.class);
        resourceImplClasses.put
            ("resourceAdapter",
             com.sun.enterprise.repository.ResourceAdapter.class); 
        **/
    }

    public GenericConverter() {
    }   
    
    public J2EEResource rawInfoToResource(RawResourceInfo rawInfo) 
        throws J2EEResourceException {
        J2EEResource resource = null;
        try {
            Class resourceClass = 
                (Class) resourceImplClasses.get(rawInfo.getResourceType());

            if( resourceClass == null ) {
                String errMsg = localStrings.getLocalString
                    ("GenericConverter.unknown.resource.type",
                     "Unknown resource type {0}", 
                     new Object[] { rawInfo.getResourceType() }); 
                throw new J2EEResourceException(errMsg);
            }

            // Create an instance of the concrete J2EEResource class
            // by passing in the name.
            Class paramTypes[] = { java.lang.String.class };
            Constructor nameConstructor =
                resourceClass.getConstructor(paramTypes);

            String name = (String) 
                rawInfo.getAttributes().get(RESOURCE_NAME_ATTR);
            
            if( name == null ) {
                String errMsg = localStrings.getLocalString
                    ("GenericConverter.name.attribute.required",
                     "Resource type {0} with index {1} is " +
                     "missing a 'name' attribute",
                     new Object[] { rawInfo.getResourceType(), 
                                    new Integer(rawInfo.getIndex()) });
                throw new J2EEResourceException(errMsg);
            }

            Object initArgs[] = { name };
            resource = (J2EEResource) nameConstructor.newInstance(initArgs);

            //
            // Call the mutator for each resource attribute.
            //
            Set attributes = rawInfo.getAttributes().entrySet();

            for(Iterator iter = attributes.iterator(); iter.hasNext(); ) {
                Map.Entry next = (Map.Entry) iter.next();
                String attrName = (String) next.getKey();
                String attrValue = (String) next.getValue();
                try {
                    if( !attrName.equals(RESOURCE_NAME_ATTR) ) {
                        Utility.invokeSetMethod(resource, attrName, attrValue);
                    }
                } catch(NoSuchMethodException nsme) {
                    String errMsg = localStrings.getLocalString
                        ("GenericConverter.invalid.attribute",
                         "Attribute {0} of resource type {1} with index {2} "
                         + "is invalid",
                         new Object[] { attrName, rawInfo.getResourceType(), 
                                        new Integer(rawInfo.getIndex()) });
                    throw new J2EEResourceException(errMsg);
                }
            }

            //
            // Add each resource property.
            //
            Set properties = rawInfo.getProperties().entrySet();

            for(Iterator iter = properties.iterator(); iter.hasNext(); ) {
                Map.Entry next = (Map.Entry) iter.next();
                String propName = (String) next.getKey();

                ResourceProperty property = new ResourcePropertyImpl(propName);
                property.setValue(next.getValue());
                resource.addProperty(property);
            }
        } catch(J2EEResourceException jre) {
            throw jre;
        } catch(Exception e) {
//IASRI 4660742            e.printStackTrace();
// START OF IASRI 4660742
                _logger.log(Level.SEVERE,"enterprise.rawinfotoresource_exception",e);
// END OF IASRI 4660742
            throw new J2EEResourceException(e);
        }
        return resource;
    }
    
    public RawResourceInfo resourceToRawInfo(J2EEResource resource)
        throws J2EEResourceException {
        RawResourceInfo rawInfo = null;
        try {
            // First create an instance of raw resource info with
            // the resource type and a unique index.
            Class[] emptyArgs   = new Class[] {};
            Class resourceClass = resource.getClass();
            String resourceType = getResourceType(resourceClass.getName());
            rawInfo = new RawResourceInfo(resourceType, counter);
            counter++;

            //
            // Get the value of each storable resource attribute and
            // add it to the raw info.
            //
            List attrMethods = getAttributeMethods(resourceClass);
            for(Iterator iter = attrMethods.iterator(); iter.hasNext(); ) {
                Method method = (Method) iter.next();
                Object value  = method.invoke(resource, (Object[]) emptyArgs);
                if( value == null ) {
                    value = "";
                }
                // Skip "get" to form attribute name.
                String propName = method.getName().substring(3);
                // Convert first letter to lower case.
                propName = propName.substring(0,1).toLowerCase() +
                    propName.substring(1);
                rawInfo.getAttributes().put(propName, value);
            }

            // Add each resource property.
            Set properties = resource.getProperties();
            for(Iterator iter = properties.iterator(); iter.hasNext(); ) {
                ResourceProperty next = (ResourceProperty) iter.next();
                rawInfo.getProperties().put(next.getName(), next.getValue());
            }
        } catch(Exception e) {
//IASRI 4660742            e.printStackTrace();
// START OF IASRI 4660742
                _logger.log(Level.SEVERE,"enterprise.resourcetorawinfo_exception",e);
// END OF IASRI 4660742
            throw new J2EEResourceException(e);
        }

        return rawInfo;
    }


    /**
     * Get the corresonding resource type string for a
     * concrete J2EEResource class.
     */
    private String getResourceType(String className) {
        Set implClasses = resourceImplClasses.entrySet();
        for(Iterator iter = implClasses.iterator(); iter.hasNext(); ) {
            Map.Entry next = (Map.Entry) iter.next();
            Class nextClass = (Class) next.getValue();
            if( nextClass.getName().equals(className) ) {
                return (String) next.getKey();
            }
        }
        throw new java.lang.IllegalArgumentException();
    }

    /**
     * Get the set of attribute methods whose values should
     * be converted.
     */ 
    private static List getAttributeMethods(Class resourceClass) 
        throws Exception 
    {

        Class[] emptyArgs  = new Class[] {};

        Method[] methodsToSkip = 
        {
            // Immutable J2EEResource attribute
            resourceClass.getMethod("getType", emptyArgs),

            // java.lang.Object accessors
            resourceClass.getMethod("getProperties", emptyArgs),
            resourceClass.getMethod("getProperty", 
                                    new Class[] { String.class }),
            java.lang.Object.class.getMethod("getClass", emptyArgs)
        };

        List methodsForClass = 
            (List) attributeMethods.get(resourceClass.getName());

        // If we haven't already generated the list of attribute
        // methods for this concrete J2EEResource class.
        if( methodsForClass == null ) {
            methodsForClass = new Vector();

            Method[] allMethods = resourceClass.getMethods();
            for(int methodIndex = 0; methodIndex < allMethods.length; 
                methodIndex++) {
                Method method = allMethods[methodIndex];
                if( isStorableAttrMethod(method, methodsToSkip) ) {
                    methodsForClass.add(method);
                }
            }

            attributeMethods.put(resourceClass.getName(), 
                                 methodsForClass);
        }
        return methodsForClass;
    }

    private static boolean isStorableAttrMethod(Method method,
                                                Method[] methodsToSkip) {
        for(int methodIndex = 0; methodIndex < methodsToSkip.length; 
            methodIndex++ ) {
            if( method.equals(methodsToSkip[methodIndex]) ) {
                return false;
            }
        }
        return method.getName().startsWith("get");
    }

}
