/*
 * 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 
 * glassfish/bootstrap/legal/CDDLv1.0.txt or 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html. 
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * HEADER in each file and include the License file at 
 * glassfish/bootstrap/legal/CDDLv1.0.txt.  If applicable, 
 * add the following below this CDDL HEADER, with the 
 * fields enclosed by brackets "[]" replaced with your 
 * own identifying information: Portions Copyright [yyyy] 
 * [name of copyright owner]
 */
// Copyright (c) 1998, 2006, Oracle. All rights reserved.  
package oracle.toplink.essentials.internal.ejb.cmp3.xml;

import java.util.HashMap;
import java.util.Iterator;

import java.lang.reflect.Method;

import oracle.toplink.essentials.exceptions.ValidationException;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataHelper;
import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataEntityListener;

/**
 * This class facilitates collection of entity/listener class names and any
 * callback method names that are to be applied.  This is useful in two-phase 
 * processing of the XML Descriptor, where the classloader may change between 
 * phases.  Here, phase one would involve gathering class/method name info and 
 * phase two would involve loading the classes, and setting the callback methods 
 * accordingly.
 * 
 * @author Dave McCann, Guy Pelletier
 * @since TopLink EJB 3.0 Reference Implementation
 */
public class XMLCBListener extends MetadataEntityListener {
    protected String entityClassName;
    protected String listenerClassName;
    protected HashMap callbackMethodNames;
    
    /**
     * INTERNAL:
     */
    public XMLCBListener(String listenerClassName, String entityClassName) { 
    	super();
        this.listenerClassName = listenerClassName;
        this.entityClassName = entityClassName;
    }

    /**
     * INTERNAL:
     * Adds a callback method name to the list of method names that will be 
     * applied to the listener when init() is called.
     */
    public void addEventMethodName(String event, String methodName) {
        getEventMethodNames().put(event, methodName);
    }

    /**
     * INTERNAL:
     * Return the candidate callback methods for this entity listener.
     */
    protected Method[] getCandidateMethods() {
        return MetadataHelper.getCandidateCallbackMethodsForEntityListener(this);
    }
    
    /**
     * INTERNAL:
     * Lazily initialize and return the list of callback method names.
     */
    public HashMap getEventMethodNames() {
        if (callbackMethodNames == null) {
            callbackMethodNames = new HashMap();
        }
        
        return callbackMethodNames;
    }

    /**
     * INTERNAL:
     * Initialize the listener. This involves loading the entity and listener
     * classes, creating a listener instance, and setting the methods (listed 
     * as names in the callbackMethodNames list) on the listener class.
     */
    public void initializeCallbackMethods(ClassLoader classLoader) {
        setEntityClass(MetadataHelper.getClassForName(entityClassName, classLoader));
        Class listenerClass = MetadataHelper.getClassForName(listenerClassName, classLoader);
        
        try {
            setListener(listenerClass.newInstance());
        } catch (Exception ex) {
            ValidationException.errorInstantiatingEntityListener(listenerClass, ex);
        }
    
        setMethodsOnListener();
    }

    /**
     * INTERNAL:
     * Each method name in the callbackMethodNames list will be searched for 
     * in the listener class' list of methods - if a method exists where 
     * method.getName() == methodName this method is set as a callback method.  
     * Otherwise, a validation exception will be thrown.
     */
    protected void setMethodsOnListener() {
        Method[] methods = getCandidateMethods();
        
        for (Iterator iterator = getEventMethodNames().keySet().iterator(); iterator.hasNext();) {
            String event = (String) iterator.next();
            String methodName = (String) getEventMethodNames().get(event);            
            Method method = MetadataHelper.getMethodForName(methods, methodName);
            
            if (method == null) {
                throw ValidationException.invalidCallbackMethod(getListenerClass(), methodName);
            } else if (event.equals(XMLConstants.PRE_PERSIST)) {
                setPrePersistMethod(method);
            } else if (event.equals(XMLConstants.POST_PERSIST)) {
                setPostInsertMethod(method);
            } else if (event.equals(XMLConstants.PRE_REMOVE)) {
                setPreRemoveMethod(method);
            } else if (event.equals(XMLConstants.POST_REMOVE)) {
                setPostDeleteMethod(method);
            } else if (event.equals(XMLConstants.PRE_UPDATE)) {
                setPreUpdateWithChangesMethod(method);
            } else if (event.equals(XMLConstants.POST_UPDATE)) {
                setPostUpdateMethod(method);
            } else if (event.equals(XMLConstants.POST_LOAD)) {
                setPostCloneMethod(method);
            }
        }
    }
}
