package org.jboss.ejb.plugins.cmp.jdbc.metadata;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.jboss.deployment.DeploymentException;
import org.jboss.metadata.MetaData;
import org.w3c.dom.Element;
public final class JDBCCMPFieldMetaData
{
public static final byte CHECK_DIRTY_AFTER_GET_TRUE = 1;
public static final byte CHECK_DIRTY_AFTER_GET_FALSE = 2;
public static final byte CHECK_DIRTY_AFTER_GET_NOT_PRESENT = 4;
private final JDBCEntityMetaData entity;
private final String fieldName;
private final Class fieldType;
private final String columnName;
private final int jdbcType;
private final String sqlType;
private final boolean readOnly;
private final int readTimeOut;
private final boolean primaryKeyMember;
private final boolean notNull;
private final boolean genIndex;
private final Field primaryKeyField;
private final List propertyOverrides = new ArrayList();
private final boolean unknownPkField;
private final boolean autoIncrement;
private final boolean relationTableField;
private final byte checkDirtyAfterGet;
private final String stateFactory;
private static byte readCheckDirtyAfterGet(Element element, byte defaultValue) throws DeploymentException
{
byte checkDirtyAfterGet;
String dirtyAfterGetStr = MetaData.getOptionalChildContent(element, "check-dirty-after-get");
if(dirtyAfterGetStr == null)
{
checkDirtyAfterGet = defaultValue;
}
else
{
checkDirtyAfterGet = (Boolean.valueOf(dirtyAfterGetStr).booleanValue() ?
CHECK_DIRTY_AFTER_GET_TRUE : CHECK_DIRTY_AFTER_GET_FALSE);
}
return checkDirtyAfterGet;
}
public static byte readCheckDirtyAfterGet(Element element) throws DeploymentException
{
return readCheckDirtyAfterGet(element, CHECK_DIRTY_AFTER_GET_NOT_PRESENT);
}
public JDBCCMPFieldMetaData(JDBCEntityMetaData entity)
{
this.entity = entity;
fieldName = entity.getName() + "_upk";
fieldType = entity.getPrimaryKeyClass(); columnName = entity.getName() + "_upk";
jdbcType = Integer.MIN_VALUE;
sqlType = null;
readOnly = entity.isReadOnly();
readTimeOut = entity.getReadTimeOut();
primaryKeyMember = true;
notNull = true;
primaryKeyField = null;
genIndex = false;
unknownPkField = true;
autoIncrement = false;
relationTableField = false;
checkDirtyAfterGet = CHECK_DIRTY_AFTER_GET_NOT_PRESENT;
stateFactory = null;
}
public JDBCCMPFieldMetaData(JDBCEntityMetaData entity, String fieldName)
throws DeploymentException
{
this.entity = entity;
this.fieldName = fieldName;
fieldType = loadFieldType(entity, fieldName);
columnName = fieldName;
jdbcType = Integer.MIN_VALUE;
sqlType = null;
readOnly = entity.isReadOnly();
readTimeOut = entity.getReadTimeOut();
genIndex = false;
String pkFieldName = entity.getPrimaryKeyFieldName();
if(pkFieldName != null)
{
primaryKeyField = null;
if(pkFieldName.equals(fieldName))
{
if(!entity.getPrimaryKeyClass().equals(fieldType))
{
throw new DeploymentException("primkey-field must be the same type as prim-key-class");
}
primaryKeyMember = true;
}
else
{
primaryKeyMember = false;
}
}
else
{
Field[] fields = entity.getPrimaryKeyClass().getFields();
boolean pkMember = false;
Field pkField = null;
for(int i = 0; i < fields.length; i++)
{
final Field field = fields[i];
if(field.getName().equals(fieldName))
{
if(!field.getType().equals(fieldType))
{
throw new DeploymentException("Field " + fieldName + " in prim-key-class must be of the same type.");
}
if(pkField != null)
{
if(field.getDeclaringClass().equals(entity.getPrimaryKeyClass()))
{
pkField = field;
}
org.jboss.logging.Logger.getLogger(getClass().getName() + '.' + entity.getName()).warn(
"PK field " + fieldName + " was found more than once in class hierarchy of " +
entity.getPrimaryKeyClass().getName() + ". Will use the one from " + pkField.getDeclaringClass().getName()
);
}
else
{
pkField = field;
}
pkMember = true;
}
}
primaryKeyMember = pkMember;
primaryKeyField = pkField;
}
notNull = fieldType.isPrimitive() || primaryKeyMember;
unknownPkField = false;
autoIncrement = false;
relationTableField = false;
checkDirtyAfterGet = CHECK_DIRTY_AFTER_GET_NOT_PRESENT;
stateFactory = null;
}
public JDBCCMPFieldMetaData(JDBCEntityMetaData entity,
JDBCCMPFieldMetaData defaultValues)
{
this.entity = entity;
fieldName = defaultValues.getFieldName();
fieldType = defaultValues.getFieldType();
columnName = defaultValues.getColumnName();
jdbcType = defaultValues.getJDBCType();
sqlType = defaultValues.getSQLType();
readOnly = entity.isReadOnly();
readTimeOut = entity.getReadTimeOut();
primaryKeyMember = defaultValues.isPrimaryKeyMember();
primaryKeyField = defaultValues.getPrimaryKeyField();
notNull = defaultValues.isNotNull();
unknownPkField = defaultValues.isUnknownPkField();
autoIncrement = defaultValues.isAutoIncrement();
genIndex = false; relationTableField = defaultValues.isRelationTableField();
checkDirtyAfterGet = defaultValues.getCheckDirtyAfterGet();
stateFactory = defaultValues.getStateFactory();
}
public JDBCCMPFieldMetaData(JDBCEntityMetaData entity,
Element element,
JDBCCMPFieldMetaData defaultValues)
throws DeploymentException
{
this.entity = entity;
this.unknownPkField = defaultValues.isUnknownPkField();
String unknownFieldName =
MetaData.getOptionalChildContent(element, "field-name");
if(unknownPkField && unknownFieldName != null)
{
fieldName = unknownFieldName;
}
else
{
fieldName = defaultValues.getFieldName();
}
String unknownPkClass = MetaData.getOptionalChildContent(element, "unknown-pk-class");
if(unknownPkClass == null)
{
fieldType = defaultValues.getFieldType();
}
else
{
try
{
fieldType = entity.getClassLoader().loadClass(unknownPkClass);
}
catch(ClassNotFoundException e)
{
throw new DeploymentException("could not load the class for "
+ " unknown primary key: " + unknownPkClass);
}
}
String columnStr = MetaData.getOptionalChildContent(element, "column-name");
if(columnStr != null)
{
columnName = columnStr;
}
else
{
columnName = defaultValues.getColumnName();
}
String jdbcStr = MetaData.getOptionalChildContent(element, "jdbc-type");
if(jdbcStr != null)
{
jdbcType = JDBCMappingMetaData.getJdbcTypeFromName(jdbcStr);
sqlType = MetaData.getUniqueChildContent(element, "sql-type");
}
else
{
jdbcType = defaultValues.getJDBCType();
sqlType = defaultValues.getSQLType();
}
String readOnlyStr = MetaData.getOptionalChildContent(element, "read-only");
if(readOnlyStr != null)
{
readOnly = Boolean.valueOf(readOnlyStr).booleanValue();
}
else
{
readOnly = defaultValues.isReadOnly();
}
String readTimeOutStr = MetaData.getOptionalChildContent(element, "read-time-out");
if(readTimeOutStr != null)
{
try
{
readTimeOut = Integer.parseInt(readTimeOutStr);
}
catch(NumberFormatException e)
{
throw new DeploymentException("Invalid number format in " +
"read-time-out '" + readTimeOutStr + "': " + e);
}
}
else
{
readTimeOut = defaultValues.getReadTimeOut();
}
this.primaryKeyMember = defaultValues.isPrimaryKeyMember();
primaryKeyField = defaultValues.getPrimaryKeyField();
Element notNullElement = MetaData.getOptionalChild(element, "not-null");
notNull =
fieldType.isPrimitive() ||
primaryKeyMember ||
(notNullElement != null);
Iterator iterator = MetaData.getChildrenByTagName(element, "property");
while(iterator.hasNext())
{
propertyOverrides.add(new JDBCCMPFieldPropertyMetaData(this, (Element)iterator.next()));
}
autoIncrement = MetaData.getOptionalChild(element, "auto-increment") != null;
if(MetaData.getOptionalChild(element, "dbindex") == null)
genIndex = false;
else
genIndex = true;
relationTableField = defaultValues.isRelationTableField();
checkDirtyAfterGet = readCheckDirtyAfterGet(element, defaultValues.getCheckDirtyAfterGet());
String stateFactoryStr = MetaData.getOptionalChildContent(element, "state-factory");
if(stateFactoryStr == null)
stateFactory = defaultValues.getStateFactory();
else
stateFactory = stateFactoryStr;
}
public JDBCCMPFieldMetaData(JDBCEntityMetaData entity,
Element element,
JDBCCMPFieldMetaData defaultValues,
boolean primaryKeyMember,
boolean notNull,
boolean readOnly,
int readTimeOut,
boolean relationTableField)
throws DeploymentException
{
this.entity = entity;
fieldName = defaultValues.getFieldName();
fieldType = defaultValues.getFieldType();
String columnStr = MetaData.getOptionalChildContent(element, "column-name");
if(columnStr != null)
{
columnName = columnStr;
}
else
{
columnName = defaultValues.getColumnName();
}
String jdbcStr = MetaData.getOptionalChildContent(element, "jdbc-type");
if(jdbcStr != null)
{
jdbcType = JDBCMappingMetaData.getJdbcTypeFromName(jdbcStr);
sqlType = MetaData.getUniqueChildContent(element, "sql-type");
}
else
{
jdbcType = defaultValues.getJDBCType();
sqlType = defaultValues.getSQLType();
}
this.readOnly = readOnly;
this.readTimeOut = readTimeOut;
this.primaryKeyMember = primaryKeyMember;
this.notNull = notNull;
primaryKeyField = defaultValues.getPrimaryKeyField();
Iterator iterator = MetaData.getChildrenByTagName(element, "property");
while(iterator.hasNext())
{
propertyOverrides.add(new JDBCCMPFieldPropertyMetaData(this, (Element)iterator.next()));
}
this.unknownPkField = defaultValues.isUnknownPkField();
autoIncrement = MetaData.getOptionalChild(element, "auto-increment") != null;
if(MetaData.getOptionalChild(element, "dbindex") == null)
genIndex = false;
else
genIndex = true;
this.relationTableField = relationTableField;
String dirtyAfterGetStr = MetaData.getOptionalChildContent(element, "check-dirty-after-get");
if(dirtyAfterGetStr == null)
{
checkDirtyAfterGet = defaultValues.getCheckDirtyAfterGet();
}
else
{
checkDirtyAfterGet = (Boolean.valueOf(dirtyAfterGetStr).booleanValue() ?
CHECK_DIRTY_AFTER_GET_TRUE : CHECK_DIRTY_AFTER_GET_FALSE);
}
String stateFactoryStr = MetaData.getOptionalChildContent(element, "state-factory");
if(stateFactoryStr == null)
stateFactory = defaultValues.getStateFactory();
else
stateFactory = stateFactoryStr;
}
public JDBCCMPFieldMetaData(JDBCEntityMetaData entity,
JDBCCMPFieldMetaData defaultValues,
String columnName,
boolean primaryKeyMember,
boolean notNull,
boolean readOnly,
int readTimeOut,
boolean relationTableField)
{
this.entity = entity;
fieldName = defaultValues.getFieldName();
fieldType = defaultValues.getFieldType();
this.columnName = columnName;
jdbcType = defaultValues.getJDBCType();
sqlType = defaultValues.getSQLType();
this.readOnly = readOnly;
this.readTimeOut = readTimeOut;
this.primaryKeyMember = primaryKeyMember;
primaryKeyField = defaultValues.getPrimaryKeyField();
this.notNull = notNull;
for(Iterator i = defaultValues.propertyOverrides.iterator(); i.hasNext();)
{
propertyOverrides.add(new JDBCCMPFieldPropertyMetaData(
this, (JDBCCMPFieldPropertyMetaData)i.next()));
}
this.unknownPkField = defaultValues.isUnknownPkField();
autoIncrement = false;
genIndex = false;
this.relationTableField = relationTableField;
checkDirtyAfterGet = defaultValues.getCheckDirtyAfterGet();
stateFactory = defaultValues.getStateFactory();
}
public JDBCCMPFieldMetaData(JDBCEntityMetaData entity,
String fieldName,
Class fieldType,
String columnName,
int jdbcType,
String sqlType)
throws DeploymentException
{
this.entity = entity;
this.fieldName = fieldName;
this.fieldType = fieldType;
this.columnName = columnName;
this.jdbcType = jdbcType;
this.sqlType = sqlType;
readOnly = false;
readTimeOut = -1;
primaryKeyMember = false;
notNull = true;
primaryKeyField = null;
unknownPkField = false;
autoIncrement = false;
genIndex = false;
relationTableField = false;
checkDirtyAfterGet = CHECK_DIRTY_AFTER_GET_NOT_PRESENT;
stateFactory = null;
}
public JDBCEntityMetaData getEntity()
{
return entity;
}
public String getFieldName()
{
return fieldName;
}
public Class getFieldType()
{
return fieldType;
}
public String getColumnName()
{
return columnName;
}
public int getJDBCType()
{
return jdbcType;
}
public String getSQLType()
{
return sqlType;
}
public List getPropertyOverrides()
{
return Collections.unmodifiableList(propertyOverrides);
}
public boolean isReadOnly()
{
return readOnly;
}
public int getReadTimeOut()
{
return readTimeOut;
}
public boolean isPrimaryKeyMember()
{
return primaryKeyMember;
}
public boolean isNotNull()
{
return notNull;
}
public boolean isIndexed()
{
return genIndex;
}
public Field getPrimaryKeyField()
{
return primaryKeyField;
}
public boolean isUnknownPkField()
{
return unknownPkField;
}
public boolean isAutoIncrement()
{
return autoIncrement;
}
public boolean isRelationTableField()
{
return relationTableField;
}
public byte getCheckDirtyAfterGet()
{
return checkDirtyAfterGet;
}
public String getStateFactory()
{
return stateFactory;
}
public boolean equals(Object o)
{
if(o instanceof JDBCCMPFieldMetaData)
{
JDBCCMPFieldMetaData cmpField = (JDBCCMPFieldMetaData)o;
return fieldName.equals(cmpField.fieldName) &&
entity.equals(cmpField.entity);
}
return false;
}
public int hashCode()
{
int result = 17;
result = 37 * result + entity.hashCode();
result = 37 * result + fieldName.hashCode();
return result;
}
public String toString()
{
return "[JDBCCMPFieldMetaData : fieldName=" + fieldName + ", " +
entity + "]";
}
private Class loadFieldType(JDBCEntityMetaData entity, String fieldName)
throws DeploymentException
{
if(entity.isCMP1x())
{
try
{
return entity.getEntityClass().getField(fieldName).getType();
}
catch(NoSuchFieldException e)
{
throw new DeploymentException("No field named '" + fieldName +
"' found in entity class." +
entity.getEntityClass().getName());
}
}
else
{
String baseName = Character.toUpperCase(fieldName.charAt(0)) +
fieldName.substring(1);
String getName = "get" + baseName;
String setName = "set" + baseName;
Method[] methods = entity.getEntityClass().getMethods();
for(int i = 0; i < methods.length; i++)
{
if(Modifier.isPublic(methods[i].getModifiers()) &&
Modifier.isAbstract(methods[i].getModifiers()))
{
if(getName.equals(methods[i].getName()) &&
methods[i].getParameterTypes().length == 0 &&
!methods[i].getReturnType().equals(Void.TYPE))
{
return methods[i].getReturnType();
}
if(setName.equals(methods[i].getName()) &&
methods[i].getParameterTypes().length == 1 &&
methods[i].getReturnType().equals(Void.TYPE))
{
return methods[i].getParameterTypes()[0];
}
}
}
throw new DeploymentException("No abstract accessors for field " +
"named '" + fieldName + "' found in entity class " +
entity.getEntityClass().getName());
}
}
}