blob: adb4bc7b5e59e61ae18210d7a0d2c12c7103b5f8 [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.persistence;
import static javax.persistence.AccessType.FIELD;
import static javax.persistence.AccessType.PROPERTY;
import static org.apache.openjpa.persistence.PersistenceStrategy.BASIC;
import static org.apache.openjpa.persistence.PersistenceStrategy.ELEM_COLL;
import static org.apache.openjpa.persistence.PersistenceStrategy.EMBEDDED;
import static org.apache.openjpa.persistence.PersistenceStrategy.MANY_MANY;
import static org.apache.openjpa.persistence.PersistenceStrategy.MANY_ONE;
import static org.apache.openjpa.persistence.PersistenceStrategy.ONE_MANY;
import static org.apache.openjpa.persistence.PersistenceStrategy.ONE_ONE;
import static org.apache.openjpa.persistence.PersistenceStrategy.PERS;
import static org.apache.openjpa.persistence.PersistenceStrategy.PERS_COLL;
import static org.apache.openjpa.persistence.PersistenceStrategy.PERS_MAP;
import static org.apache.openjpa.persistence.PersistenceStrategy.TRANSIENT;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Basic;
import javax.persistence.ElementCollection;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
import javax.persistence.Transient;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.enhance.Reflection;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.AbstractMetaDataDefaults;
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.MetaDataRepository;
import org.apache.openjpa.meta.ValueMetaData;
import org.apache.openjpa.util.InternalException;
import org.apache.openjpa.util.MetaDataException;
import org.apache.openjpa.util.UserException;
/**
* JPA-based metadata defaults.
*
* @author Patrick Linskey
* @author Abe White
* @author Pinaki Poddar
*/
public class PersistenceMetaDataDefaults
extends AbstractMetaDataDefaults {
private static final Localizer _loc = Localizer.forPackage
(PersistenceMetaDataDefaults.class);
private static final Map<Class<?>, PersistenceStrategy> _strats =
new HashMap<>();
private static final Set<String> _ignoredAnnos = new HashSet<>();
static {
_strats.put(Basic.class, BASIC);
_strats.put(ManyToOne.class, MANY_ONE);
_strats.put(OneToOne.class, ONE_ONE);
_strats.put(Embedded.class, EMBEDDED);
_strats.put(EmbeddedId.class, EMBEDDED);
_strats.put(OneToMany.class, ONE_MANY);
_strats.put(ManyToMany.class, MANY_MANY);
_strats.put(Persistent.class, PERS);
_strats.put(PersistentCollection.class, PERS_COLL);
_strats.put(ElementCollection.class, ELEM_COLL);
_strats.put(PersistentMap.class, PERS_MAP);
_ignoredAnnos.add(DetachedState.class.getName());
_ignoredAnnos.add(PostLoad.class.getName());
_ignoredAnnos.add(PostPersist.class.getName());
_ignoredAnnos.add(PostRemove.class.getName());
_ignoredAnnos.add(PostUpdate.class.getName());
_ignoredAnnos.add(PrePersist.class.getName());
_ignoredAnnos.add(PreRemove.class.getName());
_ignoredAnnos.add(PreUpdate.class.getName());
}
/**
* Set of Inclusion Filters based on member type, access type or transient
* annotations. Used to determine the persistent field/methods.
*/
protected AccessFilter propertyAccessFilter = new AccessFilter(PROPERTY);
protected AccessFilter fieldAccessFilter = new AccessFilter(FIELD);
protected MemberFilter fieldFilter = new MemberFilter(Field.class);
protected MemberFilter methodFilter = new MemberFilter(Method.class);
protected TransientFilter nonTransientFilter = new TransientFilter(false);
protected AnnotatedFilter annotatedFilter = new AnnotatedFilter();
protected GetterFilter getterFilter = new GetterFilter();
protected SetterFilter setterFilter = new SetterFilter();
private Boolean _isAbstractMappingUniDirectional = null;
private Boolean _isNonDefaultMappingAllowed = null;
private String _defaultSchema;
private Boolean _isCascadePersistPersistenceUnitDefaultEnabled = null;
public PersistenceMetaDataDefaults() {
setCallbackMode(CALLBACK_RETHROW | CALLBACK_ROLLBACK |
CALLBACK_FAIL_FAST);
setDataStoreObjectIdFieldUnwrapped(true);
}
/**
* Return the code for the strategy of the given member. Return null if
* no strategy.
*/
public static PersistenceStrategy getPersistenceStrategy
(FieldMetaData fmd, Member member) {
return getPersistenceStrategy(fmd, member, false);
}
/**
* Return the code for the strategy of the given member. Return null if
* no strategy.
*/
public static PersistenceStrategy getPersistenceStrategy
(FieldMetaData fmd, Member member, boolean ignoreTransient) {
if (member == null)
return null;
AnnotatedElement el = (AnnotatedElement) member;
if (!ignoreTransient && AccessController.doPrivileged(J2DoPrivHelper
.isAnnotationPresentAction(el, Transient.class)))
return TRANSIENT;
if (fmd != null
&& fmd.getManagement() != FieldMetaData.MANAGE_PERSISTENT)
return null;
// look for persistence strategy in annotation table
PersistenceStrategy pstrat = null;
for (Annotation anno : el.getDeclaredAnnotations()) {
if (pstrat != null && _strats.containsKey(anno.annotationType()))
throw new MetaDataException(_loc.get("already-pers", member));
if (pstrat == null)
pstrat = _strats.get(anno.annotationType());
}
if (pstrat != null)
return pstrat;
Class type;
int code;
if (fmd != null) {
type = fmd.getType();
code = fmd.getTypeCode();
} else if (member instanceof Field) {
type = ((Field) member).getType();
code = JavaTypes.getTypeCode(type);
} else {
type = ((Method) member).getReturnType();
code = JavaTypes.getTypeCode(type);
}
switch (code) {
case JavaTypes.ARRAY:
if (type == byte[].class
|| type == char[].class
|| type == Byte[].class
|| type == Character[].class)
return BASIC;
break;
case JavaTypes.BOOLEAN:
case JavaTypes.BOOLEAN_OBJ:
case JavaTypes.BYTE:
case JavaTypes.BYTE_OBJ:
case JavaTypes.CHAR:
case JavaTypes.CHAR_OBJ:
case JavaTypes.DOUBLE:
case JavaTypes.DOUBLE_OBJ:
case JavaTypes.FLOAT:
case JavaTypes.FLOAT_OBJ:
case JavaTypes.INT:
case JavaTypes.INT_OBJ:
case JavaTypes.LONG:
case JavaTypes.LONG_OBJ:
case JavaTypes.SHORT:
case JavaTypes.SHORT_OBJ:
case JavaTypes.STRING:
case JavaTypes.BIGDECIMAL:
case JavaTypes.BIGINTEGER:
case JavaTypes.DATE:
case JavaTypes.LOCAL_DATE:
case JavaTypes.LOCAL_TIME:
case JavaTypes.LOCAL_DATETIME:
case JavaTypes.OFFSET_TIME:
case JavaTypes.OFFSET_DATETIME:
return BASIC;
case JavaTypes.OBJECT:
if (Enum.class.isAssignableFrom(type))
return BASIC;
break;
}
//### EJB3: what if defined in XML?
if (AccessController.doPrivileged(J2DoPrivHelper
.isAnnotationPresentAction(type, Embeddable.class)))
return EMBEDDED;
if (Serializable.class.isAssignableFrom(type))
return BASIC;
return null;
}
/**
* Auto-configuration method for the default access type of base classes
* with ACCESS_UNKNOWN
*/
public void setDefaultAccessType(String type) {
if ("PROPERTY".equals(type.toUpperCase(Locale.ENGLISH)))
setDefaultAccessType(AccessCode.PROPERTY);
else if ("FIELD".equals(type.toUpperCase(Locale.ENGLISH)))
setDefaultAccessType(AccessCode.FIELD);
else
throw new IllegalArgumentException(_loc.get("access-invalid",
type).toString());
}
/**
* Populates the given class metadata. The access style determines which
* field and/or getter method will contribute as the persistent property
* of the given class. If the given access is unknown, then the access
* type is to be determined at first.
*
* @see #determineAccessType(ClassMetaData)
*/
@Override
public void populate(ClassMetaData meta, int access) {
populate(meta, access, false);
}
/**
* Populates the given class metadata. The access style determines which
* field and/or getter method will contribute as the persistent property
* of the given class. If the given access is unknown, then the access
* type is to be determined at first.
*
* @see #determineAccessType(ClassMetaData)
*/
@Override
public void populate(ClassMetaData meta, int access, boolean ignoreTransient) {
if (AccessCode.isUnknown(access)) {
access = determineAccessType(meta);
}
if (AccessCode.isUnknown(access)) {
error(meta, _loc.get("access-unknown", meta));
}
super.populate(meta, access, ignoreTransient);
meta.setDetachable(true);
// do not call get*Fields as it will lock down the fields.
}
@Override
protected void populate(FieldMetaData fmd) {
setCascadeNone(fmd);
setCascadeNone(fmd.getKey());
setCascadeNone(fmd.getElement());
}
/**
* Turns off auto cascading of persist, refresh, attach, detach.
*/
static void setCascadeNone(ValueMetaData vmd) {
vmd.setCascadePersist(ValueMetaData.CASCADE_NONE);
vmd.setCascadeRefresh(ValueMetaData.CASCADE_NONE);
vmd.setCascadeAttach(ValueMetaData.CASCADE_NONE);
vmd.setCascadeDetach(ValueMetaData.CASCADE_NONE);
}
ClassMetaData getCachedSuperclassMetaData(ClassMetaData meta) {
if (meta == null)
return null;
Class<?> cls = meta.getDescribedType();
Class<?> sup = cls.getSuperclass();
if (sup == null || "java.lang.Object".equals(
sup.getName()))
return null;
MetaDataRepository repos = meta.getRepository();
ClassMetaData supMeta = repos.getCachedMetaData(sup);
if (supMeta == null)
supMeta = repos.getMetaData(sup, null, false);
return supMeta;
}
/**
* Recursive helper to determine access type based on annotation placement
* on members for the given class without an explicit access annotation.
*
* @return must return a not-unknown access code
*/
private int determineAccessType(ClassMetaData meta) {
if (meta == null)
return AccessCode.UNKNOWN;
if (meta.getDescribedType().isInterface()) // managed interfaces
return AccessCode.PROPERTY;
if (!AccessCode.isUnknown(meta))
return meta.getAccessType();
int access = determineExplicitAccessType(meta.getDescribedType());
if (!AccessCode.isUnknown(access))
return access;
access = determineImplicitAccessType(meta.getDescribedType(),
meta.getRepository().getConfiguration());
if (!AccessCode.isUnknown(access))
return access;
ClassMetaData sup = getCachedSuperclassMetaData(meta);
ClassMetaData tmpSup = sup;
while (tmpSup != null && tmpSup.isExplicitAccess()) {
tmpSup = getCachedSuperclassMetaData(tmpSup);
if (tmpSup != null) {
sup = tmpSup;
}
}
if (sup != null && !AccessCode.isUnknown(sup))
return sup.getAccessType();
trace(meta, _loc.get("access-default", meta, AccessCode.toClassString(getDefaultAccessType())));
return getDefaultAccessType();
}
/**
* Determines the access type for the given class by placement of
* annotations on field or getter method. Does not consult the
* super class.
*
* Annotation can be placed on either fields or getters but not on both.
* If no field or getter is annotated then UNKNOWN access code is returned.
*/
private int determineImplicitAccessType(Class<?> cls, OpenJPAConfiguration
conf) {
if (cls.isInterface()) // Managed interfaces
return AccessCode.PROPERTY;
Field[] allFields = AccessController.doPrivileged(J2DoPrivHelper.
getDeclaredFieldsAction(cls));
Method[] methods = AccessController.doPrivileged(
J2DoPrivHelper.getDeclaredMethodsAction(cls));
List<Field> fields = filter(allFields, new TransientFilter(true));
/*
* OpenJPA 1.x permitted private properties to be persistent. This is
* contrary to the JPA 1.0 specification, which states that persistent
* properties must be public or protected. OpenJPA 2.0+ will adhere
* to the specification by default, but provides a compatibility
* option to provide pre-2.0 behavior.
*/
getterFilter.setIncludePrivate(
conf.getCompatibilityInstance().getPrivatePersistentProperties());
List<Method> getters = filter(methods, getterFilter);
if (fields.isEmpty() && getters.isEmpty())
return AccessCode.EMPTY;
fields = filter(fields, annotatedFilter);
getters = filter(getters, annotatedFilter);
List<Method> setters = filter(methods, setterFilter);
getters = matchGetterAndSetter(getters, setters);
boolean mixed = !fields.isEmpty() && !getters.isEmpty();
if (mixed)
throw new UserException(_loc.get("access-mixed",
cls, toFieldNames(fields), toMethodNames(getters)));
if (!fields.isEmpty()) {
return AccessCode.FIELD;
}
if (!getters.isEmpty()) {
return AccessCode.PROPERTY;
}
return AccessCode.UNKNOWN;
}
/**
* Explicit access type, if any, is generally detected by the parser. This
* is only used for metadata of an embeddable type which is encountered
* as a field during some other owning entity.
*
* @see ValueMetaData#addEmbeddedMetaData()
*/
private int determineExplicitAccessType(Class<?> cls) {
Access access = cls.getAnnotation(Access.class);
return access == null ? AccessCode.UNKNOWN : ((access.value() ==
AccessType.FIELD ? AccessCode.FIELD : AccessCode.PROPERTY) |
AccessCode.EXPLICIT);
}
/**
* Matches the given getters with the given setters. Removes the getters
* that do not have a corresponding setter.
*/
private List<Method> matchGetterAndSetter(List<Method> getters,
List<Method> setters) {
Collection<Method> unmatched = new ArrayList<>();
for (Method getter : getters) {
String getterName = getter.getName();
Class<?> getterReturnType = getter.getReturnType();
String expectedSetterName = "set" + getterName.substring(
(isBooleanGetter(getter) ? "is" : "get").length());
boolean matched = false;
for (Method setter : setters) {
Class<?> setterArgType = setter.getParameterTypes()[0];
String actualSetterName = setter.getName();
matched = actualSetterName.equals(expectedSetterName)
&& setterArgType == getterReturnType;
if (matched)
break;
}
if (!matched) {
unmatched.add(getter);
}
}
getters.removeAll(unmatched);
return getters;
}
/**
* Gets the fields that are possible candidate for being persisted. The
* result depends on the current access style of the given class.
*/
List<Field> getPersistentFields(ClassMetaData meta, boolean ignoreTransient) {
boolean explicit = meta.isExplicitAccess();
boolean unknown = AccessCode.isUnknown(meta);
boolean isField = AccessCode.isField(meta);
if (explicit || unknown || isField) {
Field[] fields = AccessController.doPrivileged(J2DoPrivHelper.
getDeclaredFieldsAction(meta.getDescribedType()));
return filter(fields, fieldFilter,
ignoreTransient ? null : nonTransientFilter,
unknown || isField ? null : annotatedFilter,
explicit ? (isField ? null : fieldAccessFilter) : null);
}
return Collections.EMPTY_LIST;
}
/**
* Gets the methods that are possible candidate for being persisted. The
* result depends on the current access style of the given class.
*/
List<Method> getPersistentMethods(ClassMetaData meta, boolean ignoreTransient) {
boolean explicit = meta.isExplicitAccess();
boolean unknown = AccessCode.isUnknown(meta.getAccessType());
boolean isProperty = AccessCode.isProperty(meta.getAccessType());
if (explicit || unknown || isProperty) {
Method[] publicMethods = AccessController.doPrivileged(
J2DoPrivHelper.getDeclaredMethodsAction(meta.getDescribedType()));
/*
* OpenJPA 1.x permitted private accessor properties to be persistent. This is
* contrary to the JPA 1.0 specification, which states that persistent
* properties must be public or protected. OpenJPA 2.0+ will adhere
* to the specification by default, but provides a compatibility
* option to provide pre-2.0 behavior.
*/
getterFilter.setIncludePrivate(
meta.getRepository().getConfiguration().getCompatibilityInstance().getPrivatePersistentProperties());
List<Method> getters = filter(publicMethods, methodFilter,
getterFilter,
ignoreTransient ? null : nonTransientFilter,
unknown || isProperty ? null : annotatedFilter,
explicit ? (isProperty ? null : propertyAccessFilter) : null);
List<Method> setters = filter(publicMethods, setterFilter);
return getters = matchGetterAndSetter(getters, setters);
}
return Collections.EMPTY_LIST;
}
/**
* Gets the members that are backing members for attributes being persisted.
* Unlike {@linkplain #getPersistentFields(ClassMetaData)} and
* {@linkplain #getPersistentMethods(ClassMetaData)} which returns
* <em>possible</em> candidates, the result of this method is definite.
*
* Side-effect of this method is if the given class metadata has
* no access type set, this method will set it.
*/
@Override
public List<Member> getPersistentMembers(ClassMetaData meta, boolean ignoreTransient) {
List<Member> members = new ArrayList<>();
List<Field> fields = getPersistentFields(meta, ignoreTransient);
List<Method> getters = getPersistentMethods(meta, ignoreTransient);
boolean isMixed = !fields.isEmpty() && !getters.isEmpty();
boolean isEmpty = fields.isEmpty() && getters.isEmpty();
boolean explicit = meta.isExplicitAccess();
boolean unknown = AccessCode.isUnknown(meta.getAccessType());
if (isEmpty) {
warn(meta, _loc.get("access-empty", meta));
return Collections.EMPTY_LIST;
}
if (explicit) {
if (isMixed) {
assertNoDuplicate(fields, getters);
meta.setAccessType(AccessCode.MIXED | meta.getAccessType());
members.addAll(fields);
members.addAll(getters);
} else {
members.addAll(fields.isEmpty() ? getters : fields);
}
} else {
if (isMixed)
error(meta, _loc.get("access-mixed", meta, fields, getters));
if (fields.isEmpty()) {
meta.setAccessType(AccessCode.PROPERTY);
members.addAll(getters);
} else {
meta.setAccessType(AccessCode.FIELD);
members.addAll(fields);
}
}
return members;
}
void assertNoDuplicate(List<Field> fields, List<Method> getters) {
}
void error(ClassMetaData meta, Localizer.Message message) {
Log log = meta.getRepository().getConfiguration()
.getLog(OpenJPAConfiguration.LOG_RUNTIME);
log.error(message.toString());
throw new UserException(message.toString());
}
void warn(ClassMetaData meta, Localizer.Message message) {
Log log = meta.getRepository().getConfiguration()
.getLog(OpenJPAConfiguration.LOG_RUNTIME);
log.warn(message.toString());
}
void trace(ClassMetaData meta, Localizer.Message message) {
Log log = meta.getRepository().getConfiguration()
.getLog(OpenJPAConfiguration.LOG_RUNTIME);
log.trace(message.toString());
}
@Override
protected List<String> getFieldAccessNames(ClassMetaData meta) {
return toNames(getPersistentFields(meta, false));
}
@Override
protected List<String> getPropertyAccessNames(ClassMetaData meta) {
return toNames(getPersistentMethods(meta, false));
}
protected boolean isDefaultPersistent(ClassMetaData meta, Member member,
String name) {
return isDefaultPersistent(meta, member, name, false);
}
@Override
protected boolean isDefaultPersistent(ClassMetaData meta, Member member,
String name, boolean ignoreTransient) {
int mods = member.getModifiers();
if (Modifier.isTransient(mods))
return false;
int access = meta.getAccessType();
if (member instanceof Field) {
// If mixed or unknown, default property access, keep explicit
// field members
if (AccessCode.isProperty(access)) {
if (!isAnnotatedAccess(member, AccessType.FIELD))
return false;
}
}
else if (member instanceof Method) {
// If mixed or unknown, field default access, keep explicit property
// members
if (AccessCode.isField(access)) {
if (!isAnnotatedAccess(member, AccessType.PROPERTY))
return false;
}
try {
String setterName;
if (member.getName().startsWith("is")) {
setterName = "set" + member.getName().substring(2);
} else {
setterName = "set" + member.getName().substring(3);
}
// check for setters for methods
Method setter =
(Method) AccessController.doPrivileged(J2DoPrivHelper.getDeclaredMethodAction(
meta.getDescribedType(), setterName, new Class[] { ((Method) member).getReturnType() }));
if (setter == null && !isAnnotatedTransient(member)) {
logNoSetter(meta, name, null);
return false;
}
} catch (Exception e) {
// e.g., NoSuchMethodException
if (!isAnnotatedTransient(member))
logNoSetter(meta, name, e);
return false;
}
}
PersistenceStrategy strat = getPersistenceStrategy(null, member, ignoreTransient);
if (strat == null) {
warn(meta, _loc.get("no-pers-strat", meta.getDescribedTypeString() + "." + name));
return false;
} else if (strat == PersistenceStrategy.TRANSIENT) {
return false;
} else {
return true;
}
}
private boolean isAnnotatedTransient(Member member) {
return member instanceof AnnotatedElement
&& AccessController.doPrivileged(J2DoPrivHelper
.isAnnotationPresentAction(((AnnotatedElement) member),
Transient.class));
}
/**
* May be used to determine if member is annotated with the specified
* access type.
* @param member class member
* @param type expected access type
* @return true if access is specified on member and that access
* type matches the expected type
*/
private boolean isAnnotatedAccess(Member member, AccessType type) {
if (member == null)
return false;
Access anno =
AccessController.doPrivileged(J2DoPrivHelper
.getAnnotationAction((AnnotatedElement)member,
Access.class));
return anno != null && anno.value() == type;
}
private boolean isAnnotated(Member member) {
return member != null && member instanceof AnnotatedElement
&& annotatedFilter.includes((AnnotatedElement)member);
}
private boolean isNotTransient(Member member) {
return member != null && member instanceof AnnotatedElement
&& nonTransientFilter.includes((AnnotatedElement)member);
}
/**
* Gets either the instance field or the getter method depending upon the
* access style of the given meta-data.
*/
@Override
public Member getMemberByProperty(ClassMetaData meta, String property,
int access, boolean applyDefaultRule) {
Class<?> cls = meta.getDescribedType();
Field field = Reflection.findField(cls, property, false);
Method getter = Reflection.findGetter(cls, property, false);
Method setter = Reflection.findSetter(cls, property, false);
int accessCode = AccessCode.isUnknown(access) ? meta.getAccessType() :
access;
if (field == null && getter == null)
error(meta, _loc.get("access-no-property", cls, property));
if ((isNotTransient(getter) && isAnnotated(getter)) &&
isNotTransient(field) && isAnnotated(field))
throw new IllegalStateException(_loc.get("access-duplicate",
field, getter).toString());
if (AccessCode.isField(accessCode)) {
if (isAnnotatedAccess(getter, AccessType.PROPERTY)) {
meta.setAccessType(AccessCode.MIXED | meta.getAccessType());
return getter;
}
return field == null ? getter : field;
} else if (AccessCode.isProperty(accessCode)) {
if (isAnnotatedAccess(field, AccessType.FIELD)) {
meta.setAccessType(AccessCode.MIXED | meta.getAccessType());
return field;
}
return getter == null ? field : getter;
} else if (AccessCode.isUnknown(accessCode)) {
if (isAnnotated(field)) {
meta.setAccessType(AccessCode.FIELD);
return field;
} else if (isAnnotated(getter)) {
meta.setAccessType(AccessCode.PROPERTY);
return getter;
} else {
warn(meta, _loc.get("access-none", meta, property));
throw new IllegalStateException(
_loc.get("access-none", meta, property).toString());
}
} else {
throw new InternalException(meta + " " +
AccessCode.toClassString(meta.getAccessType()));
}
}
// ========================================================================
// Selection Filters select specific elements from a collection.
// Used to determine the persistent members of a given class.
// ========================================================================
/**
* Inclusive element filtering predicate.
*
*/
private interface InclusiveFilter<T extends AnnotatedElement> {
/**
* Return true to include the given element.
*/
boolean includes(T e);
}
/**
* Filter the given collection with the conjunction of filters. The given
* collection itself is not modified.
*/
<T extends AnnotatedElement> List<T> filter(T[] array,
InclusiveFilter... filters) {
List<T> result = new ArrayList<>();
for (T e : array) {
boolean include = true;
for (InclusiveFilter f : filters) {
if (f != null && !f.includes(e)) {
include = false;
break;
}
}
if (include)
result.add(e);
}
return result;
}
<T extends AnnotatedElement> List<T> filter(List<T> list,
InclusiveFilter... filters) {
List<T> result = new ArrayList<>();
for (T e : list) {
boolean include = true;
for (InclusiveFilter f : filters) {
if (f != null && !f.includes(e)) {
include = false;
break;
}
}
if (include)
result.add(e);
}
return result;
}
/**
* Selects getter method. A getter method name starts with 'get', returns a
* non-void type and has no argument. Or starts with 'is', returns a boolean
* and has no argument.
*
*/
static class GetterFilter implements InclusiveFilter<Method> {
private boolean includePrivate;
@Override
public boolean includes(Method method) {
return isGetter(method, isIncludePrivate());
}
public void setIncludePrivate(boolean includePrivate) {
this.includePrivate = includePrivate;
}
public boolean isIncludePrivate() {
return includePrivate;
}
}
/**
* Selects setter method. A setter method name starts with 'set', returns a
* void and has single argument.
*
*/
static class SetterFilter implements InclusiveFilter<Method> {
@Override
public boolean includes(Method method) {
return isSetter(method);
}
/**
* Affirms if the given method matches the following signature
* <code> public void setXXX(T t) </code>
*/
public static boolean isSetter(Method method) {
String methodName = method.getName();
return startsWith(methodName, "set")
&& method.getParameterTypes().length == 1
&& method.getReturnType() == void.class;
}
}
/**
* Selects elements which is annotated with @Access annotation and that
* annotation has the given AccessType value.
*
*/
static class AccessFilter implements InclusiveFilter<AnnotatedElement> {
final AccessType target;
public AccessFilter(AccessType target) {
this.target = target;
}
@Override
public boolean includes(AnnotatedElement obj) {
Access access = obj.getAnnotation(Access.class);
return access != null && access.value().equals(target);
}
}
/**
* Selects elements which is annotated with @Access annotation and that
* annotation has the given AccessType value.
*
*/
static class MemberFilter implements InclusiveFilter<AnnotatedElement> {
final Class<?> target;
public MemberFilter(Class<?> target) {
this.target = target;
}
@Override
public boolean includes(AnnotatedElement obj) {
int mods = ((Member)obj).getModifiers();
return obj.getClass() == target &&
!(Modifier.isStatic(mods) || Modifier.isFinal(mods)
|| Modifier.isTransient(mods) || Modifier.isNative(mods));
}
}
/**
* Selects non-transient elements. Selectively will examine only the
* transient field modifier.
*/
static class TransientFilter implements InclusiveFilter<AnnotatedElement> {
final boolean modifierOnly;
public TransientFilter(boolean modOnly) {
modifierOnly = modOnly;
}
@Override
public boolean includes(AnnotatedElement obj) {
if (modifierOnly) {
return !Modifier.isTransient(((Member)obj).getModifiers());
}
return !obj.isAnnotationPresent(Transient.class) &&
!Modifier.isTransient(((Member)obj).getModifiers());
}
}
/**
* Selects all element annotated with <code>javax.persistence.*</code> or
* <code>org.apache.openjpa.*</code> annotation except the annotations
* marked to be ignored.
*/
static class AnnotatedFilter implements InclusiveFilter<AnnotatedElement> {
@Override
public boolean includes(AnnotatedElement obj) {
Annotation[] annos = AccessController.doPrivileged(J2DoPrivHelper
.getAnnotationsAction(obj));
for (Annotation anno : annos) {
String name = anno.annotationType().getName();
if ((name.startsWith("javax.persistence.")
|| name.startsWith("org.apache.openjpa.persistence."))
&& !_ignoredAnnos.contains(name))
return true;
}
return false;
}
}
private void logNoSetter(ClassMetaData meta, String name, Exception e) {
Log log = meta.getRepository().getConfiguration()
.getLog(OpenJPAConfiguration.LOG_METADATA);
if (log.isWarnEnabled())
log.warn(_loc.get("no-setter-for-getter", name,
meta.getDescribedType().getName()));
else if (log.isTraceEnabled())
// log the exception, if any, if we're in trace-level debugging
log.warn(_loc.get("no-setter-for-getter", name,
meta.getDescribedType().getName()), e);
}
private Log getLog(ClassMetaData meta) {
return meta.getRepository().getConfiguration()
.getLog(OpenJPAConfiguration.LOG_METADATA);
}
String toFieldNames(List<Field> fields) {
return fields.toString();
}
String toMethodNames(List<Method> methods) {
return methods.toString();
}
@Override
public boolean isAbstractMappingUniDirectional(OpenJPAConfiguration conf) {
if (_isAbstractMappingUniDirectional == null)
setAbstractMappingUniDirectional(conf);
return _isAbstractMappingUniDirectional;
}
public void setAbstractMappingUniDirectional(OpenJPAConfiguration conf) {
_isAbstractMappingUniDirectional = conf.getCompatibilityInstance().isAbstractMappingUniDirectional();
}
@Override
public boolean isNonDefaultMappingAllowed(OpenJPAConfiguration conf) {
if (_isNonDefaultMappingAllowed == null)
setNonDefaultMappingAllowed(conf);
return _isNonDefaultMappingAllowed;
}
public void setNonDefaultMappingAllowed(OpenJPAConfiguration conf) {
_isNonDefaultMappingAllowed = conf.getCompatibilityInstance().
isNonDefaultMappingAllowed();
}
@Override
public Boolean isDefaultCascadePersistEnabled() {
return _isCascadePersistPersistenceUnitDefaultEnabled;
}
@Override
public void setDefaultCascadePersistEnabled(Boolean bool) {
_isCascadePersistPersistenceUnitDefaultEnabled = bool;
}
@Override
public String getDefaultSchema() {
return _defaultSchema;
}
@Override
public void setDefaultSchema(String schema) {
_defaultSchema=schema;
}
}