FileDocCategorySizeDatePackage
DeploymentDescriptorModel.javaAPI DocGlassfish v2 API34726Fri May 04 22:34:52 BST 2007com.sun.jdo.spi.persistence.support.ejb.model

DeploymentDescriptorModel.java

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, 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/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

/*
 * DeploymentDescriptorModel.java
 *
 * Created on December 3, 2001, 3:43 PM
 */

package com.sun.jdo.spi.persistence.support.ejb.model;

import java.io.*;
import java.util.*;
import java.lang.reflect.*;

import com.sun.enterprise.deployment.*;
import com.sun.jdo.api.persistence.model.RuntimeModel;
import com.sun.jdo.spi.persistence.support.ejb.model.util.NameMapper;
import com.sun.jdo.api.persistence.model.jdo.RelationshipElement;
import com.sun.jdo.api.persistence.model.jdo.PersistenceFieldElement;
import com.sun.jdo.spi.persistence.utility.JavaTypeHelper;

/** This is a subclass of RuntimeModel which uses the deployment descriptor 
 * to augment the java metadata for a non-existent persistence-capable
 * java/class file.  It is primarily used at ejbc time, though it could be 
 * used at any time as long as sufficient mapping and deployment descriptor 
 * information is available.  See the javadoc for individual methods below 
 * for differences and translations between persistence-capable and ejb 
 * names and behavior.  There are different answers to methods depending 
 * on whether they are called on the persistence-capable or the ejb.  These 
 * are primarily for the handling of serializable, non-primitive, non-wrapper 
 * type fields: isByteArray, getFieldType (returning byte array), 
 * isPersistentTypeAllowed and isPersistentAllowed (returning true) which 
 * return answers about the byte array when called on the persistence-capable 
 * and return answers about the serializable type when called on the ejb.
 *
 * @author Rochelle Raccah
 */
public class DeploymentDescriptorModel extends RuntimeModel
{
	private ClassLoader _classLoader;
	private NameMapper _nameMapper;
        
    /**
     * Signature with CVS keyword substitution for identifying the generated code 
     */
    public static final String SIGNATURE = "$RCSfile: DeploymentDescriptorModel.java,v $ $Revision: 1.3 $"; //NOI18N
        
	/** Creates a new instance of DeploymentDescriptorModel
	 * @param nameMapper the name mapper to be used as a helper for 
	 * translation between persistence class and ejb names.
	 * @param classLoader the class loader object which is used for the
	 * application.
	 */
	public DeploymentDescriptorModel (NameMapper nameMapper, 
		ClassLoader classLoader)
	{
		super();
		_classLoader = classLoader;
		_nameMapper = nameMapper;
	}

	private ClassLoader getClassLoader () { return _classLoader; }
	private NameMapper getNameMapper () { return _nameMapper; }

	/** Returns the input stream with the supplied resource name found with 
	 * the supplied class name.  This method overrides the one in 
	 * RuntimeModel to enforce using the class loader provided at construction 
	 * time instead of the one specified in this method.
	 * @param className the fully qualified name of the class which will be
	 * used as a base to find the resource
	 * @param classLoader the class loader used to find mapping information
	 * This parameter is ignored in this implementation - instead the 
	 * class loader supplied at construction time is used.
	 * @param resourceName the name of the resource to be found
	 * @return the input stream for the specified resource, <code>null</code>
	 * if an error occurs or none exists
	 */
	protected BufferedInputStream getInputStreamForResource (String className, 
		ClassLoader classLoader, String resourceName)
	{
		return super.getInputStreamForResource(
			className, getClassLoader(), resourceName);
	}

	/** Returns the name of the second to top (top excluding java.lang.Object)
	 * superclass for the given class name.  This method overrides the one in 
	 * RuntimeModel in order to return the supplied className if it  
	 * represents a persistence-capable class.  This is because the 
	 * persistence-capable classes don't mirror the inheritance 
	 * hierarchy of the ejbs.
	 * @param className the fully qualified name of the class to be checked
	 * @return the top non-Object superclass for className,
	 * <code>className</code> if an error occurs or none exists
	 */
	protected String findPenultimateSuperclass (String className)
	{
		return (isPCClassName(className) ? className : 
			super.findPenultimateSuperclass(className));
	}

	/** Returns the name of the superclass for the given class name.  This 
	 * method overrides the one in RuntimeModel in order to return 
	 * java.lang.Object if the supplied className represents a 
	 * persistence-capable class.  This is because the persistence-capable 
	 * classes don't mirror the inheritance hierarchy of the ejbs.
	 * @param className the fully qualified name of the class to be checked
	 * @return the superclass for className, <code>null</code> if an error
	 * occurs or none exists
	 */
	protected String getSuperclass (String className)
	{
		return (isPCClassName(className) ? "java.lang.Object" :	// NOI18N
			super.getSuperclass(className));
	}

	/** Creates a file with the given base file name and extension
	 * parallel to the supplied class (if it does not yet exist).  This 
	 * method overrides the one in RuntimeModel and throws 
	 * UnsupportedOperationException in the case where the file 
	 * doesn't yet exist.  If the file exists (even if it has 
	 * just been "touched"), this method will return that output stream, 
	 * but it is not capable of using the supplied class as a sibling location.
	 * @param className the fully qualified name of the class
	 * @param baseFileName the name of the base file
	 * @param extension the file extension
	 * @return the output stream for the specified resource, <code>null</code>
	 * if an error occurs or none exists
	 * @exception IOException if there is some error creating the file
	 */
	protected BufferedOutputStream createFile (String className, String baseFileName, 
		String extension) throws IOException
	{
		BufferedOutputStream outputStream = 
			super.createFile(className, baseFileName, extension);

		if (outputStream != null)
			return outputStream;

		throw new UnsupportedOperationException();
	}

	/** Deletes the file with the given file name which is parallel
	 * to the supplied class.  This method overrides the one in RuntimeModel 
	 * and throws UnsupportedOperationException.
	 * @param className the fully qualified name of the class
	 * @param fileName the name of the file
	 * @exception IOException if there is some error deleting the file
	 */
	protected void deleteFile (String className, String fileName)
		throws IOException
	{
		throw new UnsupportedOperationException();
	}

	/** Returns the class element with the specified className.  If the 
	 * specified className represents a persistence-capable class, the 
	 * abstract bean class for the corresponding ejb is always returned 
	 * (even if there is a Class object available for the 
	 * persistence-capable).  If there is an ejb name and an abstract bean
	 * class with the same name, the abstract bean class which is associated
	 * with the ejb will be returned, not the abstract bean class which  
	 * corresponds to the supplied name (directly).  If the specified 
	 * className represents a persistence-capable key class name, the 
	 * corresponding bean's key class is returned.
	 * @param className the fully qualified name of the class to be checked
	 * @param classLoader the class loader used to find mapping information
	 * @return the class element for the specified className
	 */
	public Object getClass (final String className, 
		final ClassLoader classLoader)
	{
		String testClass = className;

		// translate the class name to corresponding ejb name's abstract 
		// bean or key class if necessary
		if (className != null)
		{
			NameMapper nameMapper = getNameMapper();
			String ejbName = 
				(isPCClassName(className) ? getEjbName(className) : className);

			if (nameMapper.isEjbName(ejbName))
				testClass = nameMapper.getAbstractBeanClassForEjbName(ejbName);
			else
			{
				String keyClass = 
					nameMapper.getKeyClassForPersistenceKeyClass(className);

				if (keyClass != null)
				{
					// if it's a pk field of type primitive, byte[], 
					// or other array, return the primitive class or a 
					// dummy class
					if (NameMapper.PRIMARY_KEY_FIELD == 
						getPersistenceKeyClassType(className))
					{
						if (isPrimitive(keyClass))
							return JavaTypeHelper.getPrimitiveClass(keyClass);
						if (isByteArray(keyClass) || keyClass.endsWith("[]"))
							return byte[].class;
					}

					testClass = keyClass;
				}
			}
		}

		return super.getClass(testClass, getClassLoader());
	}

	/** Returns a wrapped constructor element for the specified argument types 
	 * in the class with the specified name.  If the specified class name is 
	 * a persistence-capable key class name which corresponds to a bean 
	 * with an unknown primary key class a dummy constructor will also be 
	 * returned.  Types are specified as type names for primitive type 
	 * such as int, float or as fully qualified class names.
	 * @param className the name of the class which contains the constructor 
	 * to be checked
	 * @param argTypeNames the fully qualified names of the argument types
	 * @return the constructor element
	 * @see #getClass
	 */
	public Object getConstructor (final String className, String[] argTypeNames)
	{
		Object returnObject = null;

		if ((NameMapper.PRIMARY_KEY_FIELD == 
			getPersistenceKeyClassType(className)) && 
			argTypeNames.equals(NO_ARGS))
		{
			returnObject = new MemberWrapper(className, null, Modifier.PUBLIC, 
				(Class)getClass(className));
		}

		if (returnObject == null)
		{
			returnObject = super.getConstructor(className, argTypeNames);

			if (returnObject instanceof Constructor)		// wrap it
				returnObject = new MemberWrapper((Constructor)returnObject);
		}

		return returnObject;
	}

	/** Returns a wrapped method element for the specified method name and 
	 * argument types in the class with the specified name.  If the 
	 * specified className represents a persistence-capable class and 
	 * the requested methodName is readObject or writeObject, a dummy 
	 * method will be returned.  Similarly, if the specified class name is 
	 * a persistence-capable key class name which corresponds to a bean 
	 * with an unknown primary key class or a primary key field (in both 
	 * cases there is no user defined primary key class) and the requested 
	 * method is equals or hashCode, a dummy method will also be returned.  
	 * Types are specified as type names for primitive type such as int, 
	 * float or as fully qualified class names.  Note, the method does not 
	 * return inherited methods.
	 * @param className the name of the class which contains the method 
	 * to be checked
	 * @param methodName the name of the method to be checked
	 * @param argTypeNames the fully qualified names of the argument types
	 * @return the method element
	 * @see #getClass
	 */
	public Object getMethod (final String className, final String methodName,
		String[] argTypeNames)
	{
		int keyClassType = getPersistenceKeyClassType(className);
		Object returnObject = null;

		if (isPCClassName(className))
		{
			if ((methodName.equals("readObject") && 	// NOI18N
				argTypeNames.equals(READ_OBJECT_ARGS)) || 
				(methodName.equals("writeObject") && 	// NOI18N
				argTypeNames.equals(WRITE_OBJECT_ARGS)))
			{
				returnObject = new MemberWrapper(methodName, 
					Void.TYPE, Modifier.PRIVATE, (Class)getClass(className));
			}
		}
		if ((NameMapper.UNKNOWN_KEY_CLASS == keyClassType) || 
			(NameMapper.PRIMARY_KEY_FIELD == keyClassType))
		{
			if (methodName.equals("equals") && 	// NOI18N
				argTypeNames.equals(EQUALS_ARGS))
			{
				returnObject = new MemberWrapper(methodName, 
					Boolean.TYPE, Modifier.PUBLIC, (Class)getClass(className));
			}
			else if (methodName.equals("hashCode") && 	// NOI18N
				argTypeNames.equals(NO_ARGS))
			{
				returnObject = new MemberWrapper(methodName, 
					Integer.TYPE, Modifier.PUBLIC, (Class)getClass(className));
			}
		}

		if (returnObject == null)
		{
			returnObject = super.getMethod(className, methodName, argTypeNames);

			if (returnObject instanceof Method)		// wrap it
				returnObject = new MemberWrapper((Method)returnObject);
		}

		return returnObject;
	}

	/** Returns the inherited method element for the specified method 
	 * name and argument types in the class with the specified name.  
	 * Types are specified as type names for primitive type such as 
	 * int, float or as fully qualified class names.  Note that the class 
	 * with the specified className is not checked for this method, only
	 * superclasses are checked.  This method overrides the one in 
	 * Model in order to do special handling for a persistence-capable key
	 * class name which corresponds to a bean with a primary key 
	 * field.  In that case, we don't want to climb the inheritance 
	 * of the primary key field type.
	 * @param className the name of the class which contains the method 
	 * to be checked
	 * @param methodName the name of the method to be checked
	 * @param argTypeNames the fully qualified names of the argument types
	 * @return the method element
	 * @see #getClass
	 */
	public Object getInheritedMethod (String className, String methodName, 
		String[] argTypeNames)
	{
		// If the class name corresponds to a pk field (which means that 
		// there is no user defined primary key class, we don't want to 
		// climb the inheritance hierarchy, we only process this class.
		return ((NameMapper.PRIMARY_KEY_FIELD == 
			getPersistenceKeyClassType(className)) ? 
			getMethod(className, methodName, argTypeNames) : 
			super.getInheritedMethod(className, methodName, argTypeNames));
	}

	/** Returns a list of names of all the declared field elements in the 
	 * class with the specified name.  If the specified className represents 
	 * a persistence-capable class, the list of field names from the  
	 * corresponding ejb is returned (even if there is a Class object 
	 * available for the persistence-capable).
	 * @param className the fully qualified name of the class to be checked 
	 * @return the names of the field elements for the specified class
	 */
	public List getFields (final String className)
	{
		final EjbCMPEntityDescriptor descriptor = getCMPDescriptor(className);
		String testClass = className;

		if (descriptor != null)		// need to get names of ejb fields
		{
			Iterator iterator = descriptor.getFieldDescriptors().iterator();
			List returnList = new ArrayList();

			while (iterator.hasNext())
				returnList.add(((FieldDescriptor)iterator.next()).getName());

			return returnList;
		}
		else
		{
			NameMapper nameMapper = getNameMapper();
			String ejbName = 
				nameMapper.getEjbNameForPersistenceKeyClass(className);

			switch (getPersistenceKeyClassType(className))
			{
				// find the field names we need in the corresponding 
				// ejb key class
				case NameMapper.USER_DEFINED_KEY_CLASS:
					testClass = nameMapper.getKeyClassForEjbName(ejbName);
					break;
				// find the field name we need in the abstract bean 
				case NameMapper.PRIMARY_KEY_FIELD:
					return Arrays.asList(new String[]{
						getCMPDescriptor(ejbName).
						getPrimaryKeyFieldDesc().getName()});
				// find the field name we need in the persistence capable 
				case NameMapper.UNKNOWN_KEY_CLASS:
					String pcClassName = 
						nameMapper.getPersistenceClassForEjbName(ejbName);
					PersistenceFieldElement[] fields = 
						getPersistenceClass(pcClassName).getFields();
					int i, count = ((fields != null) ? fields.length : 0);

					for (i = 0; i < count; i++)
					{
						PersistenceFieldElement pfe = fields[i];

						if (pfe.isKey())
							return Arrays.asList(new String[]{pfe.getName()});
					}
					break;
			}
		}

		return super.getFields(testClass);
	}

	/** Returns a list of names of all the field elements in the
	 * class with the specified name.  This list includes the inherited 
	 * fields.  If the specified className represents a 
	 * persistence-capable class, the list of field names from the  
	 * corresponding ejb is returned (even if there is a Class object 
	 * available for the persistence-capable).  This method overrides 
	 * the one in Model in order to do special handling for a 
	 * persistence-capable key class name which corresponds to a bean 
	 * with a primary key field.  In that case, we don't want to
	 * climb the inheritance hierarchy of the primary key field type.
	 * @param className the fully qualified name of the class to be checked
	 * @return the names of the field elements for the specified class
	 */
	public List getAllFields (String className)
	{
		// If the class name corresponds to a pk field (which means that 
		// there is no user defined primary key class, we don't want to 
		// climb the inheritance hierarchy, we only process this class.
		return ((NameMapper.PRIMARY_KEY_FIELD == 
			getPersistenceKeyClassType(className)) ? getFields(className) : 
			super.getAllFields(className));
	}

	/** Returns a wrapped field element for the specified fieldName in the 
	 * class with the specified className.  If the specified className 
	 * represents a persistence-capable class, a field representing the 
	 * field in the abstract bean class for the corresponding ejb is always 
	 * returned (even if there is a Field object available for the 
	 * persistence-capable).  If there is an ejb name and an abstract bean
	 * class with the same name, the abstract bean class which is associated
	 * with the ejb will be used, not the abstract bean class which  
	 * corresponds to the supplied name (directly).
	 * @param className the fully qualified name of the class which contains
	 * the field to be checked
	 * @param fieldName the name of the field to be checked
	 * @return the wrapped field element for the specified fieldName
	 */
	public Object getField (final String className, String fieldName)
	{
		String testClass = className;
		Object returnObject = null;

		if (className != null)
		{
			NameMapper nameMapper = getNameMapper();
			boolean isPCClass = isPCClassName(className);
			boolean isPKClassName = false;
			String searchClassName = className;
			String searchFieldName = fieldName;

			// translate the class name & field names to corresponding 
			// ejb name's abstract bean equivalents if necessary
			if (isPCClass)
			{
				searchFieldName = nameMapper.
					getEjbFieldForPersistenceField(className, fieldName);
				searchClassName = getEjbName(className);
			}
			else	// check if it's a pk class without a user defined key class
			{
				String ejbName = 
					nameMapper.getEjbNameForPersistenceKeyClass(className);

				switch (getPersistenceKeyClassType(className))
				{
					// find the field we need in the corresponding 
					// abstract bean (translated below from ejbName)
					case NameMapper.PRIMARY_KEY_FIELD:
						testClass = ejbName;
						searchClassName = ejbName;
						isPKClassName = true;
						break;
					// find the field we need by called updateFieldWrapper
					// below which handles the generated field for the 
					// unknown key class - need to use the
					// persistence-capable class name and flag to call that
					// code, so we configure it here
					case NameMapper.UNKNOWN_KEY_CLASS:
						testClass = nameMapper.
							getPersistenceClassForEjbName(ejbName);
						isPCClass = true;
						isPKClassName = true;
						break;
				}
			}

			if (nameMapper.isEjbName(searchClassName))
			{
				searchClassName = nameMapper.
					getAbstractBeanClassForEjbName(searchClassName);
			}

			returnObject = super.getField(searchClassName, searchFieldName);

			if (returnObject == null)	// try getting it from the descriptor
				returnObject = getFieldWrapper(testClass, searchFieldName);
			else if (returnObject instanceof Field)		// wrap it
				returnObject = new MemberWrapper((Field)returnObject);

			if (isPCClass)
			{
				returnObject = updateFieldWrapper(
					(MemberWrapper)returnObject, testClass, fieldName);
			}
			// when asking for these fields as part of the 
			// persistence-capable's key class, we need to represent the 
			// public modifier which will be generated in the inner class
			if (isPKClassName && (returnObject instanceof MemberWrapper))
				((MemberWrapper)returnObject)._modifiers = Modifier.PUBLIC;
			
		}

		return returnObject;
	}

	/** Returns the field type for the specified fieldName in the class
	 * with the specified className.  This method is overrides the one in 
	 * Model in order to do special handling for non-collection relationship 
	 * fields.  If it's a generated relationship that case, the returned 
	 * MemberWrapper from getField contains a type of the abstract bean and 
	 * it's impossible to convert that into the persistence capable class name, so here 
	 * that case is detected, and if found, the ejb name is extracted and 
	 * used to find the corresponding persistence capable class.  For a 
	 * relationship which is of type of the local interface, we do the 
	 * conversion from local interface to persistence-capable class.  In the 
	 * case of a collection relationship (generated or not), the superclass' 
	 * implementation which provides the java type is sufficient.
	 * @param className the fully qualified name of the class which contains
	 * the field to be checked
	 * @param fieldName the name of the field to be checked
	 * @return the field type for the specified fieldName
	 */
	public String getFieldType (String className, String fieldName)
	{
		String returnType = super.getFieldType(className, fieldName);

		if (!isCollection(returnType) && isPCClassName(className))
		{
			NameMapper nameMapper = getNameMapper();
			String ejbName = 
				nameMapper.getEjbNameForPersistenceClass(className);
			String ejbField = 
				nameMapper.getEjbFieldForPersistenceField(className, fieldName);

			if (nameMapper.isGeneratedEjbRelationship(ejbName, ejbField))
			{
				String[] inverse = 
					nameMapper.getEjbFieldForGeneratedField(ejbName, ejbField);
            
				returnType = nameMapper.
					getPersistenceClassForEjbName(inverse[0]);
			}

			if (nameMapper.isLocalInterface(returnType))
			{
				returnType = nameMapper.getPersistenceClassForLocalInterface(
					className, fieldName, returnType);
			}
		}

		return returnType;
	}

	/** Returns the string representation of declaring class of 
	 * the specified member element.  Note, the member element is 
	 * either a class element as returned by getClass, a field element 
	 * as returned by getField, a constructor element as returned by 
	 * getConstructor, or a method element as returned by getMethod 
	 * executed on the same model instance.  This implementation expects 
	 * the member element to be a reflection instance or a wrapped member 
	 * instance.
	 * @param memberElement the member element to be checked
	 * @return the string representation of the declaring class of 
	 * the specified memberElement
	 * @see #getClass
	 * @see #getField
	 * @see #getConstructor
	 * @see #getMethod
	 */
	public String getDeclaringClass (Object memberElement)
	{
		if ((memberElement != null) && (memberElement instanceof MemberWrapper))
		{
			Class classElement = 
				((MemberWrapper)memberElement).getDeclaringClass();

			return ((classElement != null) ? classElement.getName() : null);
		}

		return super.getDeclaringClass(memberElement);
	}

	/** Returns the modifier mask for the specified member element.
	 * Note, the member element is either a class element as returned by 
	 * getClass, a member wrapper element as returned by getField or 
	 * getMethod, a constructor element as returned by getConstructor 
	 * executed on the same model instance. 
	 * This implementation expects the member element being a reflection 
	 * instance or a wrapped member instance.
	 * @param memberElement the member element to be checked
	 * @return the modifier mask for the specified memberElement
	 * @see java.lang.reflect.Modifier
	 * @see #getClass
	 * @see #getField
	 * @see #getConstructor
	 * @see #getMethod
	 */
	public int getModifiers (Object memberElement)
	{
		if ((memberElement != null) && (memberElement instanceof MemberWrapper))
			return ((MemberWrapper)memberElement).getModifiers();

		return super.getModifiers(memberElement);
	}

	/** Returns the modifier mask for the specified className.  This method 
	 * overrides the one in Model to strip out the abstract modifier when 
	 * the persistence capable class is represented by the abstract bean.
	 * It also adds the static modifier when the specified class represents
	 * the persistence capable key class which will be generated.
	 * @param className the fully qualified name of the class to be checked
	 * @return the modifier mask for the specified class
	 * @see java.lang.reflect.Modifier
	 */
	public int getModifiersForClass (String className)
	{
		int modifiers = super.getModifiersForClass(className);

		if (isPCClassName(className))
			modifiers &= ~Modifier.ABSTRACT;
		else if (getNameMapper().
			getKeyClassForPersistenceKeyClass(className) != null)
		{
			modifiers |= Modifier.STATIC;
		}

		return modifiers;
	}

	/** Determines if the specified className and fieldName pair represent a
	 * field which has a type which is valid for key fields.  Valid key 
	 * field types include non-collection SCOs (wrappers, Date, Time, etc.) 
	 * and primitives for user defined key classes.  This method overrides 
	 * the one in Model in order to do special handling for a 
	 * persistence-capable key class name which corresponds to a bean 
	 * with a primary key field.  In that case, we want to restrict the 
	 * list of allowable types not to include primitives.
	 * @param className the fully qualified name of the class which contains
	 * the field to be checked
	 * @param fieldName the name of the field to be checked
	 * @return <code>true</code> if this field name represents a field 
	 * with a valid type for a key field; <code>false</code> otherwise.
	 */
	public boolean isValidKeyType (String className, String fieldName)
	{
		return (((NameMapper.PRIMARY_KEY_FIELD == 
			getPersistenceKeyClassType(className)) && 
			isPrimitive(className, fieldName)) ? false : 
			super.isValidKeyType(className, fieldName));
	}

	/** Returns the Class type of the specified element. 
	 * If element denotes a field, it returns the type of the field. 
	 * If element denotes a method, it returns the return type of the method. 
	 * Note, element is either a field element as returned by getField, or a 
	 * method element as returned by getMethod executed on the same model 
	 * instance.
	 * @param element the element to be checked
	 * @return the Class type of the element
	 * @see #getField
	 * @see RuntimeModel#getMethod
	 */
	protected Class getTypeObject (Object element)
	{
		Class type = super.getTypeObject(element);

		if ((element != null) && (element instanceof MemberWrapper))
			type = ((MemberWrapper)element).getType();

		return type;
	}

	/**
	 * This method returns the class loader used to find mapping information 
	 * for the specified className.  This implementation overrides the one in 
	 * RuntimeModel so that it always returns the ClassLoader provided at 
	 * construction time.
	 * @param className the fully qualified name of the class to be checked 
	 * @param classLoader the class loader used to find mapping information
	 * @return the class loader used to find mapping information for the 
	 * specified className
	 */
	protected ClassLoader findClassLoader (String className, 
		ClassLoader classLoader)
	{
		return getClassLoader();
	}

	// return true if a conversion to a ejb class is needed for 
	// java.lang.reflect metadata
	private boolean isPCClassName (String className)
	{
		return (getEjbName(className) != null);
	}

	private String getEjbName (String className)
	{
		return getNameMapper().getEjbNameForPersistenceClass(className);
	}

	private EjbCMPEntityDescriptor getCMPDescriptor (String className)
	{
		String descriptorName = (isPCClassName(className) ? 
			getEjbName(className) : className);

		return getNameMapper().getDescriptorForEjbName(descriptorName);
	}

	private int getPersistenceKeyClassType (String className)
	{
		int returnValue = -1;

		if (getCMPDescriptor(className) == null)
		{
			NameMapper nameMapper = getNameMapper();
			String ejbName = 
				nameMapper.getEjbNameForPersistenceKeyClass(className);

			if (ejbName != null)
				returnValue = nameMapper.getKeyClassTypeForEjbName(ejbName);
		}

		return returnValue;
	}

	private MemberWrapper getFieldWrapper (String className, String fieldName)
	{
		EjbCMPEntityDescriptor descriptor = getCMPDescriptor(className);
		MemberWrapper returnObject = null;

		if (descriptor != null)
		{
			PersistenceDescriptor persistenceDescriptor = 
				descriptor.getPersistenceDescriptor();

			if (persistenceDescriptor != null)
			{
				Class fieldType = null;

				try
				{
					fieldType = persistenceDescriptor.getTypeFor(fieldName);
				}
				catch (RuntimeException e)
				{
					// fieldType will be null - there is no such field
				}

				returnObject = ((fieldType == null) ? null :
					new MemberWrapper(fieldName, fieldType, 
					Modifier.PRIVATE, (Class)getClass(className)));
			}
		}

		return returnObject;
	}

	private MemberWrapper updateFieldWrapper (MemberWrapper returnObject, 
		String className, String fieldName)
	{
		NameMapper nameMapper = getNameMapper();

		if (returnObject == null)
		{
			// can't call isPersistent or isKey because that calls 
			// hasField which calls getField and that would end up
			// in an endless loop
			PersistenceFieldElement field =
				getPersistenceFieldInternal(className, fieldName);

			if (field != null)
			{
				String ejbName = getEjbName(className);
				String ejbFieldName = nameMapper.
					getEjbFieldForPersistenceField(className, fieldName);

				// Check if this is the auto-added field for unknown pk 
				// support.  If so, return a private field of type Long.
				if (field.isKey() && (ejbName != null) && 
					(nameMapper.getKeyClassTypeForEjbName(ejbName) == 
					NameMapper.UNKNOWN_KEY_CLASS))
				{
					returnObject = new MemberWrapper(ejbFieldName, 
						Long.class, Modifier.PRIVATE, 
						(Class)getClass(className));
				}

				// Check if this is the auto-added field for 2 way managed rels 
				// support.  If so, return a private field of type according to 
				// cardinality of the relationship.
				else if ((field instanceof RelationshipElement) && 
					nameMapper.isGeneratedEjbRelationship(ejbName, 
					ejbFieldName))
				{
					RelationshipElement rel = (RelationshipElement)field;
					Class classType = null;

					// figure out the type
					if (rel.getUpperBound() > 1)
						classType = java.util.HashSet.class;
					else
					{
						String[] inverse = nameMapper.
							getEjbFieldForGeneratedField(ejbName, ejbFieldName);

						classType = (Class)getClass(inverse[0]);
					}

					if (classType != null)
					{
						returnObject = new MemberWrapper(ejbFieldName, 
							classType, Modifier.PRIVATE, 
							(Class)getClass(className));
					}
				}
				// Check if this is the auto-added version field.
				// If so, return a private field of type long.
				else if (ejbFieldName.startsWith(
					NameMapper.GENERATED_VERSION_FIELD_PREFIX) && 
					nameMapper.isGeneratedField(ejbName, ejbFieldName))
				{
					returnObject = new MemberWrapper(ejbFieldName, 
						Long.TYPE, Modifier.PRIVATE, 
						(Class)getClass(className));
				}
			}
		}

		// if the field in the corresponding ejb is a serializable, 
		// non-primitive, non-wrapper type, convert it to byte[] here
		if (!isPersistentTypeAllowed(getType(returnObject), 
			getClassLoader()) && isSerializable(returnObject))
		{
			returnObject.setType(byte[].class);
		}

		return returnObject;
	}

	private class MemberWrapper
	{
		private String _name;
		private Class _type;
		private int _modifiers;
		private Class _declaringClass;

		private MemberWrapper (Member member)
		{
			this(member.getName(), ((member instanceof Field) ? 
				((Field)member).getType() : ((member instanceof Method) ? 
				((Method)member).getReturnType() : null)), 
				member.getModifiers(), member.getDeclaringClass());
		}

		private MemberWrapper (String name, Class type, int modifiers, 
			Class declaringClass)
		{
			_name = name;
			_type = type;
			_modifiers = modifiers;
			_declaringClass = declaringClass;
		}

		private Class getType () { return _type; }
		private void setType (Class type) { _type = type; }
		private String getName () { return _name; }
		private int getModifiers () { return _modifiers; }
		private Class getDeclaringClass () { return _declaringClass; }

		/** Returns a string representation of this object.
		 * @return a string reprentation of the member wrapper object.
		 */		
		public String toString () { return getName(); }
	}
}