/**********************************************************************
Copyright (c) 2004 Kikuchi Kousuke and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Contributors:
2004 Andy Jefferson - separated from JDOFieldMetaData (ExtendGenerator)
2004 Andy Jefferson - changed to extend FieldMetaData
2004 Andy Jefferson - removed fix() methods, using FieldMetaData.populate()
2005 Andy Jefferson - renamed
...
**********************************************************************/
package org.jpox.enhancer.bcel.metadata;
import java.lang.reflect.Method;
import javax.jdo.JDOFatalException;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.Type;
import org.jpox.ClassLoaderResolver;
import org.jpox.enhancer.ClassEnhancer;
import org.jpox.enhancer.bcel.BCELClassEnhancer;
import org.jpox.enhancer.bcel.BCELUtils;
import org.jpox.metadata.AbstractMemberMetaData;
import org.jpox.metadata.ClassMetaData;
import org.jpox.metadata.FieldMetaData;
import org.jpox.metadata.InvalidMetaDataException;
import org.jpox.metadata.MetaData;
import org.jpox.util.JPOXLogger;
import org.jpox.util.Localiser;
/**
* Extension of FieldMetaData providing hooks for BCEL enhancement process.
*
* @version $Revision: 1.16 $
*/
class BCELFieldMetaData extends FieldMetaData implements BCELFieldPropertyMetaData
{
protected static final Localiser LOCALISER_ENH=Localiser.getInstance("org.jpox.enhancer.Localisation",ClassEnhancer.class.getClassLoader());
/**
* BCEL Field for use in enhancement process.
*/
protected final BCELMember enhanceField;
/**
* Convenience constructor.
* @param parent Parent component
* @param name Name of the field
*/
public BCELFieldMetaData(MetaData parent, final String name)
{
this(parent, name, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null);
}
/**
* Convenience constructor to copy the specification from the passed field.
* This is used when we have an overriding field and we make a copy of the baseline field as a starting point.
* @param parent The parent
* @param fmd The field to copy
*/
public BCELFieldMetaData(MetaData parent, AbstractMemberMetaData fmd)
{
super(parent, fmd);
if (parent instanceof BCELClassMetaData && fieldBelongsToClass())
{
// If the field is for this class, save the field for use in BCEL enhancing later
Field field = BCELUtils.getFieldByName(name,((BCELClassMetaData)parent).getClassGen());
if (field == null)
{
throw new InvalidMetaDataException(LOCALISER_ENH, "Enhancer.FieldNotFound",
((parent instanceof ClassMetaData ) ? ((ClassMetaData)parent).getFullClassName() + "." : "" ) + name);
}
enhanceField = new BCELMember(field);
}
else
{
enhanceField = null;
}
}
/**
* Constructor
* @param parent parent MetaData instance
* @param name field name
* @param pk attribute primary-key value
* @param modifier attribute persistence-modifier value
* @param defaultFetchGroup attribute default-fetch-group value
* @param nullValue attribute null-value value
* @param embedded attribute embedded value
* @param serialized attribute serialized value
* @param dependent attribute dependent value
* @param mappedBy attribute mapped-by value
* @param column attribute column value
* @param table attribute table value
* @param catalog attribute catalog value
* @param schema attribute schema value
* @param indexed attribute indexed value
* @param unique attribute unique value
* @param fetchDepth The depth of fetch to use
* @param loadFetchGroup Name of the additional fetch group to use when loading
* @param valueStrategy attribute value-strategy value
* @param sequence attribute sequence value
* @param fieldType Implementation type of field (when reference type)
*/
public BCELFieldMetaData(MetaData parent,
final String name,
final String pk,
final String modifier,
final String defaultFetchGroup,
final String nullValue,
final String embedded,
final String serialized,
final String dependent,
final String mappedBy,
final String column,
final String table,
final String catalog,
final String schema,
final String defaultAction,
final String indexed,
final String unique,
final String fetchDepth,
final String loadFetchGroup,
final String valueStrategy,
final String sequence,
final String fieldType)
{
super(parent, name, pk, modifier, defaultFetchGroup, nullValue, embedded, serialized, dependent, mappedBy, column,
table, catalog, schema,
defaultAction, indexed, unique, fetchDepth, loadFetchGroup, valueStrategy, sequence, fieldType);
if (parent instanceof BCELClassMetaData && fieldBelongsToClass())
{
// If the field is for this class, save the field for use in BCEL enhancing later
Field field = BCELUtils.getFieldByName(name,((BCELClassMetaData)parent).getClassGen());
if (field == null)
{
throw new InvalidMetaDataException(LOCALISER_ENH, "Enhancer.FieldNotFound",
((parent instanceof ClassMetaData ) ? ((ClassMetaData)parent).getFullClassName() + "." : "" ) + name);
}
enhanceField = new BCELMember(field);
}
else
{
enhanceField = null;
}
}
/**
* Utility to return if this field is persistence capable.
* Overrides method in FieldMetaData because we don't have classes already
* enhanced - we're going to enhance them.
* @return Whether the field type is persistence capable.
*/
public boolean isFieldTypePersistable()
{
// Embedded field so just return false
if (enhanceField == null)
{
return false;
}
ObjectType objectType = (enhanceField.getType() instanceof ObjectType) ?
(ObjectType)enhanceField.getType() : null;
return isTypePersistenceCapable(objectType);
}
/**
* Utility to return if this array field has elements that are persistence capable.
* Overrides method in FieldMetaData because we don't have classes already
* enhanced - we're going to enhance them.
* @return Whether the array field element type is persistence capable.
*/
public boolean isFieldArrayTypePersistable()
{
// Embedded field so just return false
if (enhanceField == null)
{
return false;
}
ObjectType objectType = null;
if (enhanceField.getType() instanceof ArrayType)
{
Type elementType = ((ArrayType)enhanceField.getType()).getElementType();
if (elementType instanceof ObjectType)
{
objectType = (ObjectType)elementType;
}
else
{
// Not an object array so not PC
return false;
}
}
else if (enhanceField.getType() instanceof ObjectType)
{
objectType = (ObjectType)enhanceField.getType();
}
return isTypePersistenceCapable(objectType);
}
/**
* Utility to return if this type is persistence capable.
* @param type The object type to check
* @return Whether the type is persistence capable.
*/
private boolean isTypePersistenceCapable(ObjectType type)
{
// No input so just return false
if (type == null)
{
return false;
}
String fieldClassName = type.getClassName();
boolean capable = getMetaDataManager().isClassPersistable(fieldClassName);
if (!capable)
{
try
{
try
{
if (type.isCastableTo(BCELClassEnhancer.OT_PersistenceCapable))
{
capable = true;
}
}
catch (ClassCastException ex)
{
// do nothing
}
if (!capable)
{
if (type.subclassOf(BCELClassEnhancer.OT_PersistenceCapable))
{
capable = true;
}
else if (BCELClassEnhancer.OT_PersistenceCapable.subclassOf(type))
{
capable = true;
}
}
}
catch (Throwable ex)
{
//catch Throwable, so it is compatible with latest BCEL changes in methods signature.
//It nows raises ClassNotFoundException. In order to be able to compible this code
//with bcel-5.1 or bcel-5.1+, we catch as throwable
String msg = LOCALISER_ENH.msg("Enhancer.ClassNotFound",fieldClassName,ex);
JPOXLogger.ENHANCER.error(msg);
throw new JDOFatalException(msg);
}
}
return capable;
}
/**
* Method to populate the JDOConfigField. Overrides the method in
* FieldMetaData since we want to initialise some enhancement flags.
* Must pass in either field or method
* @param loader ClassLoader to use for any class loading
* @param field Field we are representing
* @param method Method we are representing
* @param primary the primary ClassLoader to use (or null)
*/
public synchronized void populate(ClassLoaderResolver loader, java.lang.reflect.Field field, Method method, ClassLoader primary)
{
// Populate as per the FieldMetaData method
super.populate(loader, field, method, primary);
// Override jdoFieldFlag in case of fields not enhanced or synthetic
if (enhanceField == null || this.enhanceField.isSynthetic())
{
jdoFieldFlag = 0;
}
}
/**
* Accessor for the byte-code enhancer field.
* @return The field
*/
public BCELMember getEnhanceField()
{
return enhanceField;
}
}