blob: 7346d080fd7a27e7ff91d46b1c8fbb23a5ba7bdd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.openjpa.util;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.Date;
import org.apache.openjpa.enhance.FieldManager;
import org.apache.openjpa.enhance.PCRegistry;
import org.apache.openjpa.enhance.PersistenceCapable;
import org.apache.openjpa.enhance.Reflection;
import org.apache.openjpa.kernel.ObjectIdStateManager;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.kernel.StateManagerImpl;
import org.apache.openjpa.kernel.StoreManager;
import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.AccessCode;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.ValueStrategies;
/**
* Utility class for manipulating application object ids.
*
* @author Abe White
*/
public class ApplicationIds {
private static final Localizer _loc = Localizer.forPackage
(ApplicationIds.class);
private static final Localizer _loc2 = Localizer.forPackage
(StateManagerImpl.class);
/**
* Return the primary key values for the given object id. The values
* will be returned in the same order as the metadata primary key fields.
* Values for PC primary key fields will be the primary key value or
* oid value of the related instance (depending on
* {@link FieldMetaData#isObjectIdFieldIdOfPC}).
*/
public static Object[] toPKValues(Object oid, ClassMetaData meta) {
if (meta == null)
return null;
Object[] pks;
if (meta.isOpenJPAIdentity()) {
pks = new Object[1];
if (oid != null)
pks[0] = ((OpenJPAId) oid).getIdObject();
return pks;
}
// reset owning 'meta' to the owner of the primary key fields, because
// the one passed in might be a proxy, like for embedded mappings;
// since getPrimaryKeyFields is guaranteed to return the primary
// keys in the order of inheritance, we are guaranteed that
// the last element will be the most-derived class.
FieldMetaData[] fmds = meta.getPrimaryKeyFields();
meta = fmds[fmds.length - 1].getDeclaringMetaData();
pks = new Object[fmds.length];
if (oid == null)
return pks;
if (!Modifier.isAbstract(meta.getDescribedType().getModifiers())) {
// copy fields from the oid
PrimaryKeyFieldManager consumer = new PrimaryKeyFieldManager();
consumer.setStore(pks);
oid = wrap(meta, oid);
PCRegistry.copyKeyFieldsFromObjectId(meta.getDescribedType(),
consumer, oid);
return consumer.getStore();
}
// default to reflection
if (meta.isObjectIdTypeShared())
oid = ((ObjectId) oid).getId();
Class<?> oidType = oid.getClass();
for (int i = 0; i < fmds.length; i++) {
if (AccessCode.isField(meta.getAccessType()))
pks[i] = Reflection.get(oid, Reflection.findField(oidType,
fmds[i].getName(), true));
else
pks[i] = Reflection.get(oid, Reflection.findGetter(oidType,
fmds[i].getName(), true));
}
return pks;
}
/**
* Wraps the given object for the given type into a OpenJPA specific
* application identity object wrapper instance (i.e. ObjectId) if all of
* the following is true:
* the given type is not using built-in OpenJPA identity types
* the given type is using a shared OpenJPA identity type
* the given object is not already a wrapper identity type
*/
public static Object wrap(ClassMetaData meta, Object oid) {
if (!meta.isOpenJPAIdentity()
&& meta.isObjectIdTypeShared()
&& !(oid instanceof ObjectId)) {
return new ObjectId(meta.getDescribedType(), oid);
}
return oid;
}
/**
* Return a new object id constructed from the given primary key values.
* Values for PC primary key fields should be the primary key value or
* oid value of the related instance (depending on
* {@link FieldMetaData#isObjectIdFieldIdOfPC}).
*/
public static Object fromPKValues(Object[] pks, ClassMetaData meta) {
if (meta == null || pks == null)
return null;
boolean convert = !meta.getRepository().getConfiguration().
getCompatibilityInstance().getStrictIdentityValues();
if (meta.isOpenJPAIdentity()) {
int type = meta.getPrimaryKeyFields()[0].getObjectIdFieldTypeCode();
Object val = (convert) ? JavaTypes.convert(pks[0], type) : pks[0];
switch (type) {
case JavaTypes.BYTE:
case JavaTypes.BYTE_OBJ:
if (!convert && !(val instanceof Byte))
throw new ClassCastException("!(x instanceof Byte)");
return new ByteId(meta.getDescribedType(),
val == null ? 0 : ((Number) val).byteValue());
case JavaTypes.CHAR:
case JavaTypes.CHAR_OBJ:
return new CharId(meta.getDescribedType(),
val == null ? 0 : (Character) val);
case JavaTypes.DOUBLE:
case JavaTypes.DOUBLE_OBJ:
if (!convert && !(val instanceof Double))
throw new ClassCastException("!(x instanceof Double)");
return new DoubleId(meta.getDescribedType(),
val == null ? 0 : ((Number) val).doubleValue());
case JavaTypes.FLOAT:
case JavaTypes.FLOAT_OBJ:
if (!convert && !(val instanceof Float))
throw new ClassCastException("!(x instanceof Float)");
return new FloatId(meta.getDescribedType(),
val == null ? 0 : ((Number) val).floatValue());
case JavaTypes.INT:
case JavaTypes.INT_OBJ:
if (!convert && !(val instanceof Integer))
throw new ClassCastException("!(x instanceof Integer)");
return new IntId(meta.getDescribedType(),
val == null ? 0 : ((Number) val).intValue());
case JavaTypes.LONG:
case JavaTypes.LONG_OBJ:
if (!convert && !(val instanceof Long))
throw new ClassCastException("!(x instanceof Long)");
return new LongId(meta.getDescribedType(),
val == null ? 0 : ((Number) val).longValue());
case JavaTypes.SHORT:
case JavaTypes.SHORT_OBJ:
if (!convert && !(val instanceof Short))
throw new ClassCastException("!(x instanceof Short)");
return new ShortId(meta.getDescribedType(),
val == null ? 0 : ((Number) val).shortValue());
case JavaTypes.STRING:
return new StringId(meta.getDescribedType(), (String) val);
case JavaTypes.DATE:
return new DateId(meta.getDescribedType(), (Date) val);
case JavaTypes.OID:
case JavaTypes.OBJECT:
return new ObjectId(meta.getDescribedType(), val);
case JavaTypes.BIGDECIMAL:
if (!convert && !(val instanceof BigDecimal))
throw new ClassCastException(
"!(x instanceof BigDecimal)");
return new BigDecimalId(meta.getDescribedType(),
(BigDecimal)val);
case JavaTypes.BIGINTEGER:
if (!convert && !(val instanceof BigInteger))
throw new ClassCastException(
"!(x instanceof BigInteger)");
return new BigIntegerId(meta.getDescribedType(),
(BigInteger)val);
case JavaTypes.BOOLEAN:
case JavaTypes.BOOLEAN_OBJ:
if (!convert && !(val instanceof Boolean))
throw new ClassCastException("!(x instanceof Boolean)");
return new BooleanId(meta.getDescribedType(),
val == null ? false : (Boolean)val);
default:
throw new InternalException();
}
}
// copy pks to oid
if (!Modifier.isAbstract(meta.getDescribedType().getModifiers())) {
Object oid = PCRegistry.newObjectId(meta.getDescribedType());
PrimaryKeyFieldManager producer = new PrimaryKeyFieldManager();
producer.setStore(pks);
if (convert)
producer.setMetaData(meta);
PCRegistry.copyKeyFieldsToObjectId(meta.getDescribedType(),
producer, oid);
return ApplicationIds.wrap(meta, oid);
}
// default to reflection
Class<?> oidType = meta.getObjectIdType();
if (Modifier.isAbstract(oidType.getModifiers()))
throw new UserException(_loc.get("objectid-abstract", meta));
Object copy = null;
try {
copy = AccessController.doPrivileged(
J2DoPrivHelper.newInstanceAction(oidType));
} catch (Throwable t) {
if (t instanceof PrivilegedActionException)
t = ((PrivilegedActionException) t).getException();
throw new GeneralException(t);
}
FieldMetaData[] fmds = meta.getPrimaryKeyFields();
Object val;
for (int i = 0; i < fmds.length; i++) {
val = (convert) ? JavaTypes.convert(pks[i],
fmds[i].getObjectIdFieldTypeCode()) : pks[i];
if (AccessCode.isField(meta.getAccessType()))
Reflection.set(copy, Reflection.findField(oidType,
fmds[i].getName(), true), val);
else
Reflection.set(copy, Reflection.findSetter(oidType,
fmds[i].getName(), fmds[i].getDeclaredType(), true), val);
}
if (meta.isObjectIdTypeShared())
copy = new ObjectId(meta.getDescribedType(), copy);
return copy;
}
/**
* Copy the given oid value.
*/
public static Object copy(Object oid, ClassMetaData meta) {
if (meta == null || oid == null)
return null;
if (meta.isOpenJPAIdentity()) {
// use meta type instead of oid type in case it's a subclass
Class<?> cls = meta.getDescribedType();
OpenJPAId koid = (OpenJPAId) oid;
FieldMetaData pk = meta.getPrimaryKeyFields()[0];
switch (pk.getObjectIdFieldTypeCode()) {
case JavaTypes.BYTE:
case JavaTypes.BYTE_OBJ:
return new ByteId(cls, ((ByteId) oid).getId(),
koid.hasSubclasses());
case JavaTypes.CHAR:
case JavaTypes.CHAR_OBJ:
return new CharId(cls, ((CharId) oid).getId(),
koid.hasSubclasses());
case JavaTypes.DOUBLE:
case JavaTypes.DOUBLE_OBJ:
return new DoubleId(cls, ((DoubleId) oid).getId(),
koid.hasSubclasses());
case JavaTypes.FLOAT:
case JavaTypes.FLOAT_OBJ:
return new FloatId(cls, ((FloatId) oid).getId(),
koid.hasSubclasses());
case JavaTypes.INT:
case JavaTypes.INT_OBJ:
return new IntId(cls, ((IntId) oid).getId(),
koid.hasSubclasses());
case JavaTypes.LONG:
case JavaTypes.LONG_OBJ:
return new LongId(cls, ((LongId) oid).getId(),
koid.hasSubclasses());
case JavaTypes.SHORT:
case JavaTypes.SHORT_OBJ:
return new ShortId(cls, ((ShortId) oid).getId(),
koid.hasSubclasses());
case JavaTypes.STRING:
return new StringId(cls, oid.toString(),
koid.hasSubclasses());
case JavaTypes.OID:
ClassMetaData embed = pk.getEmbeddedMetaData();
Object inner = koid.getIdObject();
if (embed != null)
inner = copy(inner, embed, embed.getFields());
return new ObjectId(cls, inner, koid.hasSubclasses());
case JavaTypes.OBJECT:
return new ObjectId(cls, koid.getIdObject(),
koid.hasSubclasses());
case JavaTypes.DATE:
return new DateId(cls, ((DateId) oid).getId(),
koid.hasSubclasses());
case JavaTypes.BIGDECIMAL:
return new BigDecimalId(cls, ((BigDecimalId) oid).getId(),
koid.hasSubclasses());
case JavaTypes.BIGINTEGER:
return new BigIntegerId(cls, ((BigIntegerId) oid).getId(),
koid.hasSubclasses());
default:
throw new InternalException();
}
}
// create a new pc instance of the right type, set its key fields
// to the original oid values, then copy its key fields to a new
// oid instance
if (!Modifier.isAbstract(meta.getDescribedType().getModifiers())
&& !hasPCPrimaryKeyFields(meta)) {
Class<?> type = meta.getDescribedType();
PersistenceCapable pc = PCRegistry.newInstance(type, null, oid,
false);
Object copy = pc.pcNewObjectIdInstance();
pc.pcCopyKeyFieldsToObjectId(copy);
return copy;
}
Object copy = (!meta.isObjectIdTypeShared()) ? oid
: ((ObjectId) oid).getId();
copy = copy(copy, meta, meta.getPrimaryKeyFields());
if (meta.isObjectIdTypeShared())
copy = new ObjectId(meta.getDescribedType(), copy,
((OpenJPAId) oid).hasSubclasses());
return copy;
}
/**
* Return true if any of the given type's primary key fields are
* persistent objects.
*/
private static boolean hasPCPrimaryKeyFields(ClassMetaData meta) {
FieldMetaData[] fmds = meta.getPrimaryKeyFields();
for (FieldMetaData fmd : fmds)
if (fmd.getDeclaredTypeCode() == JavaTypes.PC)
return true;
return false;
}
/**
* Copy the given identity object using reflection.
*/
private static Object copy(Object oid, ClassMetaData meta,
FieldMetaData[] fmds) {
if (oid == null)
return null;
Class<?> oidType = oid.getClass();
Object copy = null;
try {
copy = AccessController.doPrivileged(
J2DoPrivHelper.newInstanceAction(oidType));
} catch (Throwable t) {
if (t instanceof PrivilegedActionException)
t = ((PrivilegedActionException) t).getException();
throw new GeneralException(t);
}
Field field;
Object val;
for (FieldMetaData fmd : fmds) {
if (fmd.getManagement() != FieldMetaData.MANAGE_PERSISTENT)
continue;
if (AccessCode.isField(meta.getAccessType())) {
field = Reflection.findField(oidType, fmd.getName(),
true);
Reflection.set(copy, field, Reflection.get(oid, field));
}
else { // property
val = Reflection.get(oid, Reflection.findGetter(oidType,
fmd.getName(), true));
Reflection.set(copy, Reflection.findSetter(oidType, fmd.
getName(), fmd.getObjectIdFieldType(), true), val);
}
}
return copy;
}
/**
* Return the given primary key field value from the given oid.
*/
public static Object get(Object oid, FieldMetaData fmd) {
if (oid == null)
return null;
if (oid instanceof OpenJPAId)
return ((OpenJPAId) oid).getIdObject();
ClassMetaData meta = fmd.getDefiningMetaData();
Class<?> oidType = oid.getClass();
if (AccessCode.isField(meta.getAccessType()))
return Reflection.get(oid, Reflection.findField(oidType,
fmd.getName(), true));
return Reflection.get(oid, Reflection.findGetter(oidType, fmd.getName(),
true));
}
/**
* Generate an application id based on the current primary key field state
* of the given instance.
*/
public static Object create(PersistenceCapable pc, ClassMetaData meta) {
if (pc == null)
return null;
Object oid = pc.pcNewObjectIdInstance();
if (oid == null)
return null;
if (!meta.isOpenJPAIdentity()) {
pc.pcCopyKeyFieldsToObjectId(oid);
return oid;
}
FieldMetaData pk = meta.getPrimaryKeyFields()[0];
if (pk.getDeclaredTypeCode() != JavaTypes.OID)
return oid;
// always copy oid object in case field value mutates or becomes
// managed
ObjectId objid = (ObjectId) oid;
ClassMetaData embed = pk.getEmbeddedMetaData();
objid.setId(copy(objid.getId(), embed, embed.getFields()));
return objid;
}
/**
* Assign an application identity object to the given state, or return
* false if determining the application identity requires a flush.
*/
public static boolean assign(OpenJPAStateManager sm, StoreManager store,
boolean preFlush) {
ClassMetaData meta = sm.getMetaData();
if (meta.getIdentityType() != ClassMetaData.ID_APPLICATION)
throw new InternalException();
boolean ret;
FieldMetaData[] pks = meta.getPrimaryKeyFields();
if (meta.isOpenJPAIdentity()
&& pks[0].getDeclaredTypeCode() == JavaTypes.OID) {
OpenJPAStateManager oidsm = new ObjectIdStateManager
(sm.fetchObjectField(pks[0].getIndex()), sm, pks[0]);
ret = assign(oidsm, store, pks[0].getEmbeddedMetaData().
getFields(), preFlush);
sm.storeObjectField(pks[0].getIndex(),
oidsm.getManagedInstance());
} else
ret = assign(sm, store, meta.getPrimaryKeyFields(), preFlush);
if (!ret)
return false;
// base oid on field values
sm.setObjectId(create(sm.getPersistenceCapable(), meta));
return true;
}
/**
* Assign generated values to given primary key fields.
*/
private static boolean assign(OpenJPAStateManager sm, StoreManager store,
FieldMetaData[] pks, boolean preFlush) {
// If we are generating values...
for (FieldMetaData pk : pks)
if (pk.getValueStrategy() != ValueStrategies.NONE) {
// If a value already exists on this field, throw exception.
// This is considered an application coding error.
if (!sm.isDefaultValue(pk.getIndex()))
throw new InvalidStateException(_loc2.get("existing-value-override-excep",
pk.getFullName(false), Exceptions.toString(sm.getPersistenceCapable()),
sm.getPCState().getClass().getSimpleName()));
// Assign the generated value
if (store.assignField(sm, pk.getIndex(), preFlush))
pk.setValueGenerated(true);
else
return false;
}
return true;
}
/**
* Check if object id is set or not.
*/
public static boolean isIdSet(Object id, ClassMetaData meta,
String mappedByIdFieldName) {
Object key = null;
if (meta.isOpenJPAIdentity())
key = ApplicationIds.getKey(id, meta);
else
key = ((ObjectId)id).getId();
Object val = null;
if (mappedByIdFieldName.length() != 0) {
if (((ObjectId)id).getId() == null)
return false;
Class<?> idClass = ((ObjectId)id).getId().getClass();
val = Reflection.get(key,
Reflection.findField(idClass, mappedByIdFieldName, true));
} else
val = key;
boolean notSet = (val == null
|| (val instanceof String && ((String)val).length() == 0)
|| (val instanceof Number && ((Number)val).longValue() == 0));
return !notSet;
}
/**
* Return the key from the given id.
*/
public static Object getKey(Object id, ClassMetaData meta) {
if (meta == null || id == null)
return null;
if (meta.isOpenJPAIdentity()) {
int type = meta.getPrimaryKeyFields()[0].getObjectIdFieldTypeCode();
switch (type) {
case JavaTypes.BYTE:
case JavaTypes.BYTE_OBJ:
return ((ByteId)id).getId();
case JavaTypes.CHAR:
case JavaTypes.CHAR_OBJ:
return ((CharId)id).getId();
case JavaTypes.DOUBLE:
case JavaTypes.DOUBLE_OBJ:
return ((DoubleId)id).getId();
case JavaTypes.FLOAT:
case JavaTypes.FLOAT_OBJ:
return ((FloatId)id).getId();
case JavaTypes.INT:
case JavaTypes.INT_OBJ:
return ((IntId)id).getId();
case JavaTypes.LONG:
case JavaTypes.LONG_OBJ:
return ((LongId)id).getId();
case JavaTypes.SHORT:
case JavaTypes.SHORT_OBJ:
return ((ShortId)id).getId();
case JavaTypes.STRING:
return ((StringId)id).getId();
case JavaTypes.DATE:
return ((DateId)id).getId();
case JavaTypes.OID:
case JavaTypes.OBJECT:
return ((ObjectId)id).getId();
case JavaTypes.BIGDECIMAL:
return ((BigDecimalId)id).getId();
case JavaTypes.BIGINTEGER:
return ((BigIntegerId)id).getId();
default:
throw new InternalException();
}
} else { // IdClass
return ((ObjectId)id).getId();
}
}
/**
* Sets the underlying id of an ObjectId. Should only
* be used with simple (idclass) types.
*/
public static void setAppId(ObjectId id, Object newId) {
id.setId(newId);
}
/**
* Helper class used to transfer pk values to/from application oids.
*/
private static class PrimaryKeyFieldManager
implements FieldManager {
private Object[] _store = null;
private int _index = 0;
private ClassMetaData _meta = null;
public void setMetaData(ClassMetaData meta) {
_meta = meta;
}
public Object[] getStore() {
return _store;
}
public void setStore(Object[] store) {
_store = store;
}
@Override
public void storeBooleanField(int field, boolean val) {
store((val) ? Boolean.TRUE : Boolean.FALSE);
}
@Override
public void storeByteField(int field, byte val) {
store(val);
}
@Override
public void storeCharField(int field, char val) {
store(val);
}
@Override
public void storeShortField(int field, short val) {
store(val);
}
@Override
public void storeIntField(int field, int val) {
store(val);
}
@Override
public void storeLongField(int field, long val) {
store(val);
}
@Override
public void storeFloatField(int field, float val) {
store(val);
}
@Override
public void storeDoubleField(int field, double val) {
store(val);
}
@Override
public void storeStringField(int field, String val) {
store(val);
}
@Override
public void storeObjectField(int field, Object val) {
store(val);
}
@Override
public boolean fetchBooleanField(int field) {
return (retrieve(field) == Boolean.TRUE) ? true : false;
}
@Override
public char fetchCharField(int field) {
return (Character) retrieve(field);
}
@Override
public byte fetchByteField(int field) {
return ((Number) retrieve(field)).byteValue();
}
@Override
public short fetchShortField(int field) {
return ((Number) retrieve(field)).shortValue();
}
@Override
public int fetchIntField(int field) {
return ((Number) retrieve(field)).intValue();
}
@Override
public long fetchLongField(int field) {
return ((Number) retrieve(field)).longValue();
}
@Override
public float fetchFloatField(int field) {
return ((Number) retrieve(field)).floatValue();
}
@Override
public double fetchDoubleField(int field) {
return ((Number) retrieve(field)).doubleValue();
}
@Override
public String fetchStringField(int field) {
return (String) retrieve(field);
}
@Override
public Object fetchObjectField(int field) {
return retrieve(field);
}
private void store(Object val) {
_store[_index++] = val;
}
private Object retrieve(int field) {
Object val = _store[_index++];
if (_meta != null) {
FieldMetaData fmd = _meta.getField(field);
if (fmd.getDeclaredTypeCode() != JavaTypes.PC)
val = JavaTypes.convert(val, fmd.getDeclaredTypeCode());
else
val = JavaTypes.convert(val, JavaTypes.getTypeCode(fmd.
getObjectIdFieldType()));
}
return val;
}
}
}