/*******************************************************************************
 * Copyright (c) 2001, 2005 IBM Corporation and others.
 * 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:
 * IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jst.j2ee.model.internal.validation;


import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.jem.java.Field;
import org.eclipse.jem.java.JavaClass;
import org.eclipse.jem.java.Method;
import org.eclipse.jst.j2ee.ejb.EnterpriseBean;
import org.eclipse.wst.validation.internal.provisional.core.IMessage;


/**
 * Performs validation common to session and entity beans.
 */
public abstract class AValidateBean extends AValidateEJB {
	protected HashSet componentParents = null; // The JavaClass instances which are parents of this JavaClass instance, if this JavaClass instance is part of a component inheritance hierarchy.
	
	public AValidateBean() {
		componentParents = new HashSet();
	}
	
	public void reset() {
		super.reset();
		componentParents.clear();
	}
	
	protected void buildComponentParents(EnterpriseBean bean) {
		if (bean == null) {
			return;
		}
		EnterpriseBean supertype = getSuperType(bean);
		if (supertype == null) {
			return;
		}

		// check this CMP's supertype
		JavaClass thisEjbObject = bean.getEjbClass();
		JavaClass parentEjbObject = supertype.getEjbClass();

		// EJBObject a Xchild of parent EJBObject (X = child, grandchild, great-grandchild, etc.)
		if (ValidationRuleUtility.isAssignableFrom(thisEjbObject, parentEjbObject)) {
			componentParents.add(parentEjbObject);
		}
		buildComponentParents(supertype);
	}

	public final Object getTarget(Object parent, Object clazz) {
		if(parent == null) {
			return null;
		}

		return ((EnterpriseBean)parent).getEjbClass();
	}
	
	public final static List getRemoteHomeMethodsExtended(List[] methodsExtendedList) {
		return methodsExtendedList[0];
	}
	
	public final static List getRemoteComponentMethodsExtended(List[] methodsExtendedList) {
		return methodsExtendedList[1];
	}
	
	public final static List getBeanClassMethodsExtended(List[] methodsExtendedList) {
		return methodsExtendedList[2];
	}
	
	public final List[] getMethodsExtended(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz) {
		// A bean class needs the following classes' extended methods:
		//    1. home
		//    2. remote
		//    3. bean class (when searching for a matching ejbPostCreate method).
		// If a particular bean doesn't use a remote view or local view,
		// then the corresponding entry in the array will be null. 
		List[] result = new List[3];
		JavaClass remoteHome = bean.getHomeInterface();
		if(remoteHome != null) {
			result[0] = remoteHome.getMethodsExtended();
		}
		else {
			result[0] = null;
		}
		
		JavaClass remoteComponent = bean.getRemoteInterface();
		if(remoteComponent != null) {
			result[1] = remoteComponent.getMethodsExtended();
		}
		else {
			result[1] = null;
		}
		
		JavaClass beanClass = bean.getEjbClass();
		if(beanClass != null) {
			result[2] = beanClass.getMethodsExtended();
		}
		else {
			result[2] = null;
		}
		
		return result;
	}
	
	public final List[] getFieldsExtended(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz) {
		// Never check that a bean class' field is defined on another class
		// of the bean.
		return null;
	}
	

	
	/**
	 * For the check that the ejbCreate method has a matching create method on the home,
	 * this method, given the bean method, returns the corresponding home method.
	 *
	 * 6.10.6 Session bean's home interface
	 * The following are the requirements for the session bean's home interface:
	 *   - Each create method must be named "create", and it must match one of 
	 *     the ejbCreate methods defined in the session bean class. The matching 
	 *     ejbCreate method must have the same number and types of arguments. 
	 *     (Note that the return type is different.)
	 *   - All the exceptions defined in the throws clause of an ejbCreate method 
	 *     of the session bean class must be defined in the throws clause of the 
	 *     matching create method of the home interface.
	 *...
	 *
	 * 9.2.8 Entity bean's home interface
	 * The following are the requirements for the entity bean's home interface:
	 *   - Each create method must be named "create", and it must match one of the 
	 *     ejbCreate methods defined in the enterprise Bean class. The matching 
	 *     ejbCreate method must have the same number and types of its arguments. 
	 *     (Note that the return type is different.)
	 *   - All the exceptions defined in the throws clause of the matching ejbCreate 
	 *     and ejbPostCreate methods of the enterprise Bean class must be included in 
	 *     the throws clause of the matching create method of the home interface 
	 *     (i.e the set of exceptions defined for the create method must be a superset
	 *     of the union of exceptions defined for the ejbCreate and ejbPostCreate methods)
	 */
	public final Method getMatchingHomeCreateMethod(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method, List[] methodsExtendedLists) throws InvalidInputException {
		vc.terminateIfCancelled();
		
		if (method == null) {
			return null;
		}

		if (bean == null) {
			return null;
		}

		JavaClass homeIntf = bean.getHomeInterface();
		ValidationRuleUtility.isValidTypeHierarchy(bean, homeIntf);

		// check only the current class, not the parents in this case.
		// This method is used in the component inheritance hierarchy tests.
		return ValidationRuleUtility.getMethod(method, IMethodAndFieldConstants.METHODNAME_CREATE, homeIntf.getMethods());
	}
	
	/**
	 * For the check that the ejbCreate method has a matching create method on the home,
	 * this method, given the bean method, returns the corresponding home method.
	 *
	 * 6.10.6 Session bean's home interface
	 * The following are the requirements for the session bean's home interface:
	 *   - Each create method must be named "create", and it must match one of 
	 *     the ejbCreate methods defined in the session bean class. The matching 
	 *     ejbCreate method must have the same number and types of arguments. 
	 *     (Note that the return type is different.)
	 *   - All the exceptions defined in the throws clause of an ejbCreate method 
	 *     of the session bean class must be defined in the throws clause of the 
	 *     matching create method of the home interface.
	 *...
	 *
	 * 9.2.8 Entity bean's home interface
	 * The following are the requirements for the entity bean's home interface:
	 *   - Each create method must be named "create", and it must match one of the 
	 *     ejbCreate methods defined in the enterprise Bean class. The matching 
	 *     ejbCreate method must have the same number and types of its arguments. 
	 *     (Note that the return type is different.)
	 *   - All the exceptions defined in the throws clause of the matching ejbCreate 
	 *     and ejbPostCreate methods of the enterprise Bean class must be included in 
	 *     the throws clause of the matching create method of the home interface 
	 *     (i.e the set of exceptions defined for the create method must be a superset
	 *     of the union of exceptions defined for the ejbCreate and ejbPostCreate methods)
	 */
	public Method getMatchingHomeCreateMethodExtended(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method) throws InvalidInputException {
		vc. terminateIfCancelled();
		if (method == null) {
			return null;
		}

		if (bean == null) {
			return null;
		}

		JavaClass homeIntf = bean.getHomeInterface();
		ValidationRuleUtility.isValidTypeHierarchy(bean, homeIntf);

		return ValidationRuleUtility.getMethodExtended(homeIntf, method, IMethodAndFieldConstants.METHODNAME_CREATE);
	}
	
	/**
	 * Return the name of the parent which the bean must implement.
	 * For entity beans, it's javax.ejb.EntityBean.
	 * For session beans, it's javax.ejb.SessionBean.
	 */
	protected abstract String getParentName();
	
	/**
	 * Returns true if the method passed in is a business method.
	 *
	 * Although the EJB 1.1 spec implies that all non-framework methods are business methods,
	 * developers may create methods on the bean which are meant for internal use only. This
	 * method (isBusinessMethod) separates the "internal helper" methods from the business
	 * methods by checking if a corresponding method exists on the remote interface. If it
	 * does, consider this a business method. 
	 */
	public boolean isBusinessMethod(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method) throws InvalidInputException {
		vc.terminateIfCancelled();

		if (method == null) {
			return false;
		}
		String name = method.getName();

		if (isFrameworkMethod(name)) {
			return false;
		}

		if (method.isConstructor()) {
			return false;
		}

		// Check if there's a matching method on the remote interface.
		if (bean == null) {
			return false;
		}

		JavaClass remoteInterface = bean.getRemoteInterface();
		ValidationRuleUtility.isValidTypeHierarchy(bean, remoteInterface);
		Method remoteInterfaceMethod = ValidationRuleUtility.getMethodExtended(remoteInterface, name, method.listParametersWithoutReturn());
		if ((remoteInterfaceMethod == null) || (ValidationRuleUtility.isEJBObjectMethod(bean, remoteInterfaceMethod))) {
			// no matching method
			// filter out methods from javax.ejb.EJBObject
			return false;
		}

		return true;
	}
	
	/**
	 * Return true if the method passed in is inherited from a parent in a
	 * component inheritance hierarchy.
	 */
	protected boolean isComponentInherited(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method meth) throws InvalidInputException {
		if (componentParents == null) {
			// Not part of a component inheritance hierarchy
			return false;
		}

		return (componentParents.contains(meth.getContainingJavaClass()));
	}
	
	public boolean isFrameworkMethod(String name) {
		if (name == null) {
			return false;
		}
		else if (name.equals(IMethodAndFieldConstants.METHODNAME_EJBACTIVATE)) {
			return true;
		}
		else if (name.equals(IMethodAndFieldConstants.METHODNAME_EJBPASSIVATE)) {
			return true;
		}
		else if (name.equals(IMethodAndFieldConstants.METHODNAME_EJBREMOVE)) {
			return true;
		}
		else if (name.startsWith(IMethodAndFieldConstants.PREFIX_EJBFIND)) {
			return true;
		}
		else if (name.equals(IMethodAndFieldConstants.METHODNAME_EJBCREATE)) {
			return true;
		}
		else if (name.equals(IMethodAndFieldConstants.METHODNAME_EJBPOSTCREATE)) {
			return true;
		}
		else if (name.equals(IMethodAndFieldConstants.METHODNAME_FINALIZE)) {
			return true;
		}
		return false;

	}
	
	/**
	 * Return true if the field can, and should, be validated.
	 * Filter out faulty fields (i.e., null), and fields which
	 * belong to the base type, whatever that is. (e.g. java.lang.Object)
	 */
	protected boolean isValid(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Field field, List[] fieldsExtendedList) throws InvalidInputException {
		if (super.isValid(vc, bean, clazz, field, fieldsExtendedList)) {
			// exclude root object methods
			if (ValidationRuleUtility.isJavaLangObjectField(bean, field)) {
				return false;
			}
			return true;
		}
		return false;
	}
	
	/**
	 * Return true if the method can, and should, be validated.
	 * Filter out faulty methods (i.e., null), and methods which
	 * belong to the base type, whatever that is. (e.g. java.lang.Object)
	 */
	protected final boolean isValid(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method, List[] methodsExtendedList) throws InvalidInputException {
		if (super.isValid(vc, bean, clazz, method, methodsExtendedList)) {
			// exclude root object methods
			if (ValidationRuleUtility.isJavaLangObjectMethod(bean, method)) {
				return false;
			}
			else if (method.getName().equals(IMethodAndFieldConstants.METHODNAME_EJBCREATE)) {
				return isValidEjbCreateMethod(vc, bean, clazz, method, methodsExtendedList);
			}
			else if (method.getName().equals(IMethodAndFieldConstants.METHODNAME_EJBPOSTCREATE)) {
				return isValidEjbCreateMethod(vc, bean, clazz, method, methodsExtendedList);
			}
			else {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Return true if the ejbCreate method passed in is not inherited 
	 * from a parent in a component inheritance hierarchy, or is inherited,
	 * but is also defined on this bean's home interface.
	 *
	 * This method is used to exclude ejbCreate methods which are inherited
	 * from a parent in a component inheritance hierarchy, and are not
	 * exposed anywhere on this bean class instance itself. (i.e., inherited,
	 * but never used.) Since we generate the code for these methods, we
	 * should not flag them as warnings.
	 */
	protected final boolean isValidEjbCreateMethod(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method meth, List[] methodsExtendedLists) throws InvalidInputException {
		boolean isValid = true;
		boolean isComp = isComponentInherited(vc, bean, clazz, meth);
		if (isComp) {
			Method homeMeth = getMatchingHomeCreateMethod(vc, bean, clazz, meth, methodsExtendedLists);
			if (homeMeth == null) {
				isValid = false;
			}
		}

		return isValid;
	}
	
	/**
	 * 18.1.2 Programming restrictions
	 *...
	 *    - An enterprise Bean must not use read/write static fields. Using read-only static fields is
	 *   allowed. Therefore, it is recommended that all static fields in the enterprise bean class be
	 *   declared as final.
	 *...
	 */
	public void primValidate(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Field field) throws InvalidInputException {
		//super.primValidate(ejbMethod);

		vc.terminateIfCancelled();

		validateStaticField(vc, bean, clazz, field);
	}
	
	/**
	 * 6.10.4 Business methods
	 * The session bean class may define zero or more business methods whose signatures must follow these rules:
	 * 		- The method names can be arbitrary, but they must not start with "ejb" to avoid conflicts with the callback methods used by the EJB architecture.
	 *
	 * 9.2.6 Business methods
	 *   - The entity bean class may define zero or more business methods whose signatures 
	 *     must follow these rules:
	 *        - The method names can be arbitrary, but they must not start with 'ejb' to 
	 *          avoid conflicts with the callback methods used by the EJB architecture.
	 */
	public void validateBusinessMethod(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method) throws InvalidInputException {
		// The method names can be arbitrary, but they must not start with "ejb" to avoid conflicts with the callback methods used by the EJB architecture.
		if (method.getName().startsWith(IMethodAndFieldConstants.PREFIX_EJB)) {
			IMessage message = MessageUtility.getUtility().getMessage(vc, IEJBValidatorMessageConstants.CHKJ2411, IEJBValidationContext.ERROR, bean, clazz, method, this);
			vc.addMessage(message);
		}

		// Section 6.10.5 (session), 9.2.7 (entity), declare that all exceptions declared
		// on the ejbCreate, ejbPostCreate methods must be defined in the throws clause of
		// the matching create of the remote interface.

		JavaClass remote = bean.getRemoteInterface();
		ValidationRuleUtility.isValidTypeHierarchy(bean, remote);

		Method remoteMethod = ValidationRuleUtility.getMethodExtended(remote, method, method.getName());
		if (remoteMethod == null) {
			// Then this method shouldn't have been called; unless the method exists on the remote, this bean method isn't a business method.
			return;
		}
		
		Set exceptions = ValidationRuleUtility.getNotSubsetExceptions(bean, method, remoteMethod);
		if (exceptions.size() > 0) {
			Iterator iterator = exceptions.iterator();
			while (iterator.hasNext()) {
				JavaClass exc = (JavaClass) iterator.next();
				IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb11Constants.CHKJ2432, IEJBValidationContext.ERROR, bean, clazz, method, new String[] { remote.getQualifiedName(), exc.getQualifiedName()}, this);
				vc.addMessage(message);
			}
		}
		
		validateMatchingMethodExceptions(vc, bean, clazz, method, bean.getRemoteInterface(), remoteMethod);
	}
	
	/**
	 * 6.10.2 Session bean class
	 * The following are the requirements for session bean class:
	 * 		- The class must implement, directly or indirectly, the javax.ejb.SessionBean 
	 *        interface.
	 * 		- The class must be defined as public, must not be final, and must not 
	 *        be abstract.
	 * 		- The class may, but is not required to, implement the session bean's 
	 *        remote interface [4] .
	 * 		- If the class is a stateful session bean, it may optionally implement 
	 *        the javax.ejb.SessionSynchronization interface.
	 * 		- The session bean class may have superclasses and/or superinterfaces. If the 
	 *        session bean has superclasses, then the business methods, the ejbCreate methods, 
	 *        the methods of the SessionBean interface, and the methods of the optional 
	 *        SessionSynchronization interface may be defined in the session bean class, 
	 *        or in any of its superclasses.
	 *
	 *...
	 * 9.2.2 Enterprise bean class
	 *   - The following are the requirements for an entity bean class:
	 *      - The class must implement, directly or indirectly, the javax.ejb.EntityBean interface.
	 *      - The class must be defined as public and must not be abstract.
	 *      - The class must not be defined as final.
	 *...
	 *   - The class may, but is not required to, implement the entity bean's remote interface [9]. 
	 *     If the class implements the entity bean's remote interface, the class must provide no-op 
	 *     implementations of the methods defined in the javax.ejb.EJBObject interface. The container 
	 *     will never invoke these methods on the bean instances at runtime.
	 *   - A no-op implementation of these methods is required to avoid 
	 *     defining the entity bean class as abstract.
	 *   - The entity bean class must implement the business methods, and the 
	 *     ejbCreate, ejbPostCreate, and ejbFind<METHOD> methods as described 
	 *     later in this section.
	 *   - The entity bean class may have superclasses and/or superinterfaces. 
	 *     If the entity bean has superclasses, the business methods, the 
	 *     ejbCreate and ejbPostCreate methods, the finder methods, and the
	 *     methods of the EntityBean interface may be implemented in the 
	 *     enterprise bean class or in any of its superclasses.
	 *   - The entity bean class is allowed to implement other methods (for 
	 *     example helper methods invoked internally by the business methods) 
	 *     in addition to the methods required by the EJB specification.
	 *...
	 */
	public void validateClass(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz) throws InvalidInputException {
		vc.terminateIfCancelled();

		buildComponentParents(bean);

		// The class must implement, directly or indirectly, the javax.ejb.EntityBean (for entity beans), 
		// or the javax.ejb.SessionBean (for session beans), interface.
		if (!ValidationRuleUtility.isAssignableFrom(clazz, ValidationRuleUtility.getType(getParentName(), bean))) {
			IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb11Constants.CHKJ2013, IEJBValidationContext.ERROR, bean, clazz, new String[] {clazz.getQualifiedName(), getParentName()}, this);
			vc.addMessage(message);
		}

		// For both entity and session beans, the class must be public.
		if (!clazz.isPublic()) {
			IMessage message = MessageUtility.getUtility().getMessage(vc, IEJBValidatorMessageConstants.CHKJ2022, IEJBValidationContext.ERROR, bean, clazz, new String[] { clazz.getQualifiedName()}, this);
			vc.addMessage(message);
		}

		// For both entity and session beans, the class must not be abstract.
		if (clazz.isAbstract()) {
			IMessage message = MessageUtility.getUtility().getMessage(vc, IEJBValidatorMessageConstants.CHKJ2014, IEJBValidationContext.ERROR, bean, clazz, new String[] { clazz.getQualifiedName()}, this);
			vc.addMessage(message);
		}

		// For both entity and session beans, the class must not be final.
		if (clazz.isFinal()) {
			IMessage message = MessageUtility.getUtility().getMessage(vc, IEJBValidatorMessageConstants.CHKJ2015, IEJBValidationContext.ERROR, bean, clazz, new String[] { clazz.getQualifiedName()}, this);
			vc.addMessage(message);
		}

		if (ValidationRuleUtility.isUnnamedPackage(clazz.getJavaPackage())) {
			IMessage message = MessageUtility.getUtility().getMessage(vc, IEJBValidatorMessageConstants.CHKJ2041, IEJBValidationContext.INFO, bean, clazz, this);
			vc.addMessage(message);
		}
		
		validateAppendixB(vc, bean, clazz);
	}
	
	/**
	 * 9.2.3 ejbCreate methods
	 *   - The entity bean class may define zero or more ejbCreate(...) methods whose signatures 
	 *     must follow these rules:
	 *       - The method name must be ejbCreate.
	 *       - The method must be declared as public.
	 *       - The method must not be declared as final or static.
	 *       - The return type must be the entity bean's primary key type.
	 *       - The method argument and return value types must be legal types for RMI-IIOP.
	 *       - The throws clause may define arbitrary application specific exceptions, 
	 *         including the javax.ejb.CreateException.
	 *       - Compatibility Note: EJB 1.0 allowed the ejbCreate method to throw the 
	 *         java.rmi.RemoteException to indicate a non-application exception. This 
	 *         practice is deprecated in EJB 1.1 -- an EJB 1.1 compliant enterprise bean 
	 *         should throw the javax.ejb.EJBException or another java.lang.RuntimeException
	 *         to indicate non-application exceptions to the Container (see Section 12.2.2).
	 *   - The entity object created by the ejbCreate method must have a unique primary key. 
	 *     This means that the primary key must be different from the primary keys of all 
	 *     the existing entity objects within the same home. The ejbCreate method should 
	 *     throw the DuplicateKeyException on an attempt to create an entity object with 
	 *     a duplicate primary key. However, it is legal to reuse the primary key of a
	 *     previously removed entity object.
	 *
	 * 9.2.8 Entity bean's home interface
	 * The following are the requirements for the entity bean's home interface:
	 *   - Each create method must be named "create", and it must match one of the 
	 *     ejbCreate methods defined in the enterprise Bean class. The matching 
	 *     ejbCreate method must have the same number and types of its arguments. 
	 *     (Note that the return type is different.)
	 *   - All the exceptions defined in the throws clause of the matching ejbCreate 
	 *     and ejbPostCreate methods of the enterprise Bean class must be included in 
	 *     the throws clause of the matching create method of the home interface 
	 *     (i.e the set of exceptions defined for the create method must be a superset
	 *     of the union of exceptions defined for the ejbCreate and ejbPostCreate methods)
	 *...
	 *
	 * 9.4.2 ejbCreate, ejbPostCreate
	 *...
	 *   - The ejbCreate(...) methods must be defined to return the primary key class type. 
	 *     The implementation of the ejbCreate(...) methods should be coded to return a null. 
	 *     The returned value is ignored by the Container.
	 *...
	 */
	public void validateEjbCreateMethod_homeDep(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method) throws InvalidInputException {
		vc.terminateIfCancelled();

		// The method which calls this method must have already tested that the method name is ejbCreate.
		if (method == null) {
			return;
		}

		Method createMethod = getMatchingHomeCreateMethodExtended(vc, bean, clazz, method);
		if (createMethod == null) {
			// Can't check the exceptions of a method which doesn't exist.
			return;
		}

		// Whether this is from a component inheritance or not, if the method exists on the
		// home, check that the method follows the spec.

		// Section 6.10.6 (session), 9.2.8 (entity), declare that all exceptions declared
		// on the ejbCreate, ejbPostCreate methods must be defined in the throws clause of
		// the matching create of the home interface.
		Set exceptions = ValidationRuleUtility.getNotSubsetExceptions(bean, method, createMethod);
		if (exceptions.size() > 0) {
			Iterator iterator = exceptions.iterator();
			while (iterator.hasNext()) {
				JavaClass exc = (JavaClass) iterator.next();
				IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb11Constants.CHKJ2420, IEJBValidationContext.ERROR, bean, clazz, method, new String[] { exc.getQualifiedName(), bean.getHomeInterface().getQualifiedName()}, this);
				vc.addMessage(message);
			}
		}

	}
	
	public void validateHelperMethod(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method) {
		//Default
	}
	
	public void validateNoRemoteException(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method, String messagePrefix) throws InvalidInputException {
		// EJB 2.0 added "throws InvalidInputException" above
		if (method == null) {
			return;
		}

		if(!ValidationRuleUtility.doesNotThrowRemoteException(bean, method)) {
			IMessage message = MessageUtility.getUtility().getMessage(vc, messagePrefix, IEJBValidationContext.WARNING, bean, clazz, method, this);
			vc.addMessage(message);
		}

	}
	
	/**
	 * 18.1.2 Programming restrictions
	 *...
	 *    - An enterprise Bean must not use read/write static fields. Using read-only static fields is
	 *   allowed. Therefore, it is recommended that all static fields in the enterprise bean class be
	 *   declared as final.
	 *...
	 */
	protected void validateStaticField(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Field field) throws InvalidInputException {
		if (field == null) {
			return;
		}

		// An enterprise Bean must not use read/write static fields. Using read-only static fields is
		// allowed. Therefore, it is recommended that all static fields in the enterprise bean class be
		// declared as final.
		if (field.isStatic() && !field.isFinal()) {
			IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb11Constants.CHKJ2200, IEJBValidationContext.WARNING, bean, clazz, field, this);
			vc.addMessage(message);
		}
	}
	
	public void validateMatchingMethodExceptions(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method, JavaClass matchingClazz, Method matchingMethod) {
		// Every exception thrown on the bean class' method must be thrown on the component/home method
		/* Don't check for this here - let the home & component do it.
		Set exceptions = ValidationRuleUtility.getNotSubsetExceptions(bean, method, matchingMethod);
		Iterator eiterator = exceptions.iterator();
		while(eiterator.hasNext()) {
		}
		*/
		
		// Every exception thrown by the component/home method should be thrown or there may be compile errors
		Set exceptions = ValidationRuleUtility.getNotSubsetSubtypeExceptions(bean, matchingMethod, method);
		Iterator eiterator = exceptions.iterator();
		while(eiterator.hasNext()) {
			JavaClass exception = (JavaClass)eiterator.next();
			IMessage message = MessageUtility.getUtility().getMessage(vc, IEJBValidatorMessageConstants.CHKJ2456, IEJBValidationContext.WARNING, bean, clazz, method, new String[]{exception.getJavaName(), matchingClazz.getJavaName()}, this);
			vc.addMessage(message);
		}
	}
	
	protected void validateAppendixB(IEJBValidationContext vc, EnterpriseBean bean, JavaClass thisEjbObject) {
		// The Java inheritance structure must match the EJB inheritance structure.
		// e.g. if EJB B is a child of EJB A, then class B must be a child of class A.
		// B could be a grandchild (or great-grandchild or ...) of A.
		if(bean == null) {
			return;
		}
		EnterpriseBean supertype = getSuperType(bean);
		JavaClass parentEjbObject = null;
		if (supertype != null) {
			parentEjbObject = supertype.getEjbClass();

			// EJBObject a Xchild of parent EJBObject (X = child, grandchild, great-grandchild, etc.)
			try {
				ValidationRuleUtility.isValidType(thisEjbObject);
				ValidationRuleUtility.isValidType(parentEjbObject);
				if (!ValidationRuleUtility.isAssignableFrom(thisEjbObject, parentEjbObject)) {
					String[] msgParm = new String[] { thisEjbObject.getQualifiedName(), parentEjbObject.getQualifiedName()};
					IMessage message = MessageUtility.getUtility().getMessage(vc, IEJBValidatorMessageConstants.CHKJ2103, IEJBValidationContext.ERROR, bean, thisEjbObject, msgParm, this);
					vc.addMessage(message);
				}
			}
			catch (InvalidInputException e) {
				String[] msgParm = { e.getJavaClass().getQualifiedName(), bean.getName()};
				IMessage message = MessageUtility.getUtility().getMessage(vc, IEJBValidatorMessageConstants.CHKJ2849, IEJBValidationContext.WARNING, bean, msgParm, this);
				vc.addMessage(message);
			}
		}
		
//		validateAppendixB(vc, supertype, parentEjbObject);
	}
}
