You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pp...@apache.org on 2009/04/23 18:55:47 UTC
svn commit: r767973 [2/2] - in /openjpa/trunk:
openjpa-kernel/src/main/java/org/apache/openjpa/enhance/
openjpa-kernel/src/main/java/org/apache/openjpa/kernel/
openjpa-kernel/src/main/java/org/apache/openjpa/meta/
openjpa-kernel/src/main/resources/org/...
Modified: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataDefaults.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataDefaults.java?rev=767973&r1=767972&r2=767973&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataDefaults.java (original)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceMetaDataDefaults.java Thu Apr 23 16:55:45 2009
@@ -26,12 +26,17 @@
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.List;
import java.util.ArrayList;
+
+import javax.persistence.Access;
+import javax.persistence.AccessType;
import javax.persistence.Basic;
import javax.persistence.ElementCollection;
import javax.persistence.Embeddable;
@@ -55,19 +60,26 @@
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.log.Log;
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.ValueMetaData;
+
+import static javax.persistence.AccessType.FIELD;
+import static javax.persistence.AccessType.PROPERTY;
import static org.apache.openjpa.persistence.PersistenceStrategy.*;
import org.apache.openjpa.util.MetaDataException;
+import org.apache.openjpa.util.UserException;
import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.enhance.Reflection;
/**
* JPA-based metadata defaults.
*
* @author Patrick Linskey
* @author Abe White
+ * @author Pinaki Poddar
* @nojavadoc
*/
public class PersistenceMetaDataDefaults
@@ -76,8 +88,8 @@
private static final Localizer _loc = Localizer.forPackage
(PersistenceMetaDataDefaults.class);
- private static final Map<Class, PersistenceStrategy> _strats =
- new HashMap<Class, PersistenceStrategy>();
+ private static final Map<Class<?>, PersistenceStrategy> _strats =
+ new HashMap<Class<?>, PersistenceStrategy>();
private static final Set<String> _ignoredAnnos = new HashSet<String>();
static {
@@ -103,6 +115,20 @@
_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();
+ protected AnnotatedFilter annotatedFilter = new AnnotatedFilter();
+ protected GetterFilter getterFilter = new GetterFilter();
+ protected SetterFilter setterFilter = new SetterFilter();
+
public PersistenceMetaDataDefaults() {
setCallbackMode(CALLBACK_RETHROW | CALLBACK_ROLLBACK |
CALLBACK_FAIL_FAST);
@@ -198,16 +224,22 @@
* with ACCESS_UNKNOWN
*/
public void setDefaultAccessType(String type) {
- if (type == null)
- return;
if ("PROPERTY".equals(type.toUpperCase()))
setDefaultAccessType(ClassMetaData.ACCESS_PROPERTY);
- else
+ else if ("FIELD".equals(type.toUpperCase()))
setDefaultAccessType(ClassMetaData.ACCESS_FIELD);
+ else
+ throw new IllegalArgumentException(_loc.get("access-invalid",
+ type).toString());
}
@Override
public void populate(ClassMetaData meta, int access) {
+ if (!AccessCode.isSet(access)) {
+ meta.setAccessType(determineAccessType(meta));
+ } else {
+ meta.setAccessType(access);
+ }
super.populate(meta, access);
meta.setDetachable(true);
// do not call get*Fields as it will lock down the fields.
@@ -229,63 +261,194 @@
vmd.setCascadeAttach(ValueMetaData.CASCADE_NONE);
vmd.setCascadeDetach(ValueMetaData.CASCADE_NONE);
}
-
- @Override
- protected int getAccessType(ClassMetaData meta) {
- return getAccessType(meta.getDescribedType());
+
+ ClassMetaData getCachedSuperclassMetaData(ClassMetaData meta) {
+ if (meta == null)
+ return null;
+ Class<?> sup = meta.getDescribedType().getSuperclass();
+ if (sup == null)
+ return null;
+ return meta.getRepository().getCachedMetaData(sup);
}
/**
- * Recursive helper to determine access type based on annotation placement.
+ * Recursive helper to determine access type based on annotation placement
+ * on members for the given class without an explicit access annotation.
*/
- private int getAccessType(Class cls) {
- // traversed entire hierarchy without finding annotations
- if (cls == null || cls == Object.class)
- return ClassMetaData.ACCESS_UNKNOWN;
-
- int access = 0;
- if (annotated((Field[]) AccessController.doPrivileged(
- J2DoPrivHelper.getDeclaredFieldsAction(cls))).size() > 0)
- access |= ClassMetaData.ACCESS_FIELD;
- if (annotated((Method[]) AccessController.doPrivileged(
- J2DoPrivHelper.getDeclaredMethodsAction(cls))).size() > 0
- || cls.isInterface()) // OpenJPA managed ifaces must use prop access
- access |= ClassMetaData.ACCESS_PROPERTY;
- return getAccessType(cls.getSuperclass()) | access;
+ private int determineAccessType(ClassMetaData meta) {
+ ClassMetaData sup = getCachedSuperclassMetaData(meta);
+ while (sup != null && sup.isExplicitAccess())
+ sup = sup.getPCSuperclassMetaData();
+ if (sup != null && AccessCode.isSet(sup.getAccessType()))
+ return sup.getAccessType();
+
+ // traversed entire hierarchy without finding access type
+ if (meta.getDescribedType().isInterface()) // managed interfaces
+ return ClassMetaData.ACCESS_PROPERTY;
+ Field[] allFields = AccessController.doPrivileged(J2DoPrivHelper.
+ getDeclaredFieldsAction(meta.getDescribedType()));
+ List<Field> fields = filter(allFields, nonTransientFilter,
+ annotatedFilter);
+ Method[] publicMethods = meta.getDescribedType().getMethods();
+
+ List<Method> getters = filter(publicMethods, getterFilter,
+ nonTransientFilter, annotatedFilter);
+ List<Method> setters = filter(publicMethods, setterFilter);
+ getters = matchGetterAndSetter(getters, setters);
+ if (!fields.isEmpty()) {
+ if (!getters.isEmpty())
+ throw new UserException(getters.toString());
+ return ClassMetaData.ACCESS_FIELD;
+ } else if (!getters.isEmpty()) {
+ return ClassMetaData.ACCESS_PROPERTY;
+ }
+ return getDefaultAccessType();
}
+
+ /**
+ * 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<Method>();
+
+ 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) {
+ System.err.println(_loc.get("getter-unmatched", getter)
+ .toString());
+ unmatched.add(getter);
+ }
- @Override
- protected List getFieldAccessNames(ClassMetaData meta) {
- return annotated((Field[]) AccessController.doPrivileged(
- J2DoPrivHelper.getDeclaredFieldsAction(meta.getDescribedType())));
+ }
+ 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 explicit = meta.isExplicitAccess();
+ boolean unknown = !AccessCode.isSet(meta.getAccessType());
+ boolean isField = AccessCode.isField(meta.getAccessType());
+
+ if (explicit || unknown || isField) {
+ Field[] fields = AccessController.doPrivileged(J2DoPrivHelper.
+ getDeclaredFieldsAction(meta.getDescribedType()));
+
+ return filter(fields, fieldFilter, 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 explicit = meta.isExplicitAccess();
+ boolean unknown = !AccessCode.isSet(meta.getAccessType());
+ boolean isProperty = AccessCode.isProperty(meta.getAccessType());
+
+ if (explicit || unknown || isProperty) {
+ Method[] publicMethods = meta.getDescribedType().getMethods();
+
+ List<Method> getters = filter(publicMethods, methodFilter,
+ getterFilter, 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) {
+ List<Member> members = new ArrayList<Member>();
+ List<Field> fields = getPersistentFields(meta);
+ List<Method> getters = getPersistentMethods(meta);
+
+ boolean isMixed = !fields.isEmpty() && !getters.isEmpty();
+ boolean isEmpty = fields.isEmpty() && getters.isEmpty();
+
+ boolean explicit = meta.isExplicitAccess();
+ boolean unknown = !AccessCode.isSet(meta.getAccessType());
+
+ if (isEmpty) {
+ if (unknown)
+ warn(_loc.get("access-unknown", 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(_loc.get("access-mixed", meta, fields, getters));
+ if (fields.isEmpty()) {
+ meta.setAccessType(ClassMetaData.ACCESS_PROPERTY);
+ members.addAll(getters);
+ } else {
+ meta.setAccessType(ClassMetaData.ACCESS_FIELD);
+ members.addAll(fields);
+ }
+ }
+ return members;
+ }
+
+ void assertNoDuplicate(List<Field> fields, List<Method> getters) {
+
+ }
+
+ void error(Localizer.Message message) {
+ System.err.println(message.toString());
+ }
+ void warn(Localizer.Message message) {
+ System.err.println(message.toString());
+ }
+
@Override
- protected List getPropertyAccessNames(ClassMetaData meta) {
- return annotated((Method[]) AccessController.doPrivileged(
- J2DoPrivHelper.getDeclaredMethodsAction(meta.getDescribedType())));
+ protected List<String> getFieldAccessNames(ClassMetaData meta) {
+ return toNames(getPersistentFields(meta));
}
- /**
- * Return the members of <code>members</code> that have persistence
- * annotations.
- */
- private static List annotated(AnnotatedElement[] members) {
- Annotation[] annos;
- String name;
- List annotated = new ArrayList(members.length);
- for (int i = 0; i < members.length; i++) {
- annos = (Annotation[]) AccessController.doPrivileged(J2DoPrivHelper
- .getAnnotationsAction(members[i]));
- for (int j = 0; j < annos.length; j++) {
- name = annos[j].annotationType().getName();
- if ((name.startsWith("javax.persistence.")
- || name.startsWith("org.apache.openjpa.persistence."))
- && !_ignoredAnnos.contains(name))
- annotated.add(members[i]);
- }
- }
- return annotated;
+ @Override
+ protected List<String> getPropertyAccessNames(ClassMetaData meta) {
+ return toNames(getPersistentMethods(meta));
}
protected boolean isDefaultPersistent(ClassMetaData meta, Member member,
@@ -293,8 +456,23 @@
int mods = member.getModifiers();
if (Modifier.isTransient(mods))
return false;
-
- if (member instanceof Method) {
+ 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 {
// check for setters for methods
Method setter = AccessController.doPrivileged(
@@ -327,6 +505,215 @@
Transient.class))).booleanValue();
}
+ /**
+ * 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);
+ }
+
+ /**
+ * Gets either the instance field or the getter method depending upon the
+ * access style of the given meta-data.
+ * <br>
+ * Defining class is used instead of declaring class because this method
+ * may be invoked during parsing phase when declaring meta-data may not be
+ * available.
+ */
+ @Override
+ protected Member getMemberByProperty(ClassMetaData meta, String property) {
+ Class<?> cls = meta.getDescribedType();
+ int classAccessCode = meta.getAccessType();
+ Field field = Reflection.findField(cls, property, false);;
+ Method getter = Reflection.findGetter(cls, property, false);
+ if (isAnnotated(field) && isAnnotated(getter))
+ throw new IllegalStateException(_loc.get("access-duplicate",
+ field, getter).toString());
+
+ if (AccessCode.isField(classAccessCode)) {
+ if (isAnnotatedAccess(getter, AccessType.PROPERTY)) {
+ meta.setAccessType(AccessCode.MIXED | meta.getAccessType());
+ return getter;
+ }
+ return field;
+ } else if (AccessCode.isProperty(classAccessCode)) {
+ if (isAnnotatedAccess(field, AccessType.FIELD)) {
+ meta.setAccessType(AccessCode.MIXED | meta.getAccessType());
+ return field;
+ }
+ return getter;
+ } else if (!AccessCode.isSet(classAccessCode)) {
+ if (isAnnotated(field)) {
+ meta.setAccessType(AccessCode.FIELD);
+ return field;
+ }
+ if (isAnnotated(getter)) {
+ meta.setAccessType(AccessCode.PROPERTY);
+ return getter;
+ }
+ throw new IllegalStateException("access-none " + property);
+ } else {
+ throw new IllegalStateException(meta + " " +
+ AccessCode.toString(classAccessCode));
+ }
+ }
+
+ // ========================================================================
+ // Selection Filters select specific elements from a collection.
+ // Used to determine the persistent members of a given class.
+ // ========================================================================
+
+ /**
+ * Inclusive element filtering predicate.
+ *
+ */
+ private static 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<T>();
+ 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;
+ }
+
+ /**
+ * 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> {
+ public boolean includes(Method method) {
+ return isGetter(method);
+ }
+ }
+
+ /**
+ * Selects setter method. A setter method name starts with 'set', returns a
+ * void and has single argument.
+ *
+ */
+ static class SetterFilter implements InclusiveFilter<Method> {
+ 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;
+ }
+
+ public boolean includes(AnnotatedElement obj) {
+ Access access = obj.getAnnotation(Access.class);
+ return access != null && access.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;
+ }
+
+ 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 all non-transient element.
+ */
+ static class TransientFilter implements InclusiveFilter<AnnotatedElement> {
+ public boolean includes(AnnotatedElement obj) {
+ 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> {
+ 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);
@@ -338,4 +725,9 @@
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);
+ }
}
Modified: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/XMLPersistenceMetaDataParser.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/XMLPersistenceMetaDataParser.java?rev=767973&r1=767972&r2=767973&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/XMLPersistenceMetaDataParser.java (original)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/XMLPersistenceMetaDataParser.java Thu Apr 23 16:55:45 2009
@@ -62,6 +62,7 @@
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.LifecycleMetaData;
+import org.apache.openjpa.meta.MetaDataContext;
import org.apache.openjpa.meta.MetaDataDefaults;
import org.apache.openjpa.meta.MetaDataFactory;
import static org.apache.openjpa.meta.MetaDataModes.*;
@@ -105,6 +106,11 @@
private static final Map<String, Object> _elems =
new HashMap<String, Object>();
+ // Map for storing deferred metadata which needs to be populated
+ // after embeddedables are loaded.
+ private static final Map<Class, ArrayList<MetaDataContext>> _embeddables =
+ new HashMap<Class, ArrayList<MetaDataContext>>();
+
static {
_elems.put(ELEM_PKG, ELEM_PKG);
_elems.put(ELEM_ACCESS, ELEM_ACCESS);
@@ -836,8 +842,8 @@
return false;
}
- // if we don't know the access type, check to see if a superclass
- // has already defined the access type
+ // if we don't know the default access type, check to see if a
+ // superclass has already defined the access type
int defaultAccess = _access;
if (defaultAccess == ClassMetaData.ACCESS_UNKNOWN) {
ClassMetaData sup = repos.getCachedMetaData(_cls.getSuperclass());
@@ -845,10 +851,11 @@
defaultAccess = sup.getAccessType();
}
+ int access = ClassMetaData.ACCESS_UNKNOWN;
if (meta == null) {
// add metadata for this type
- int access = toAccessType(attrs.getValue("access"), defaultAccess);
- meta = repos.addMetaData(_cls, access);
+ access = toAccessType(attrs.getValue("access"), defaultAccess);
+ meta = repos.addMetaData(_cls);
meta.setEnvClassLoader(_envLoader);
meta.setSourceMode(MODE_NONE);
@@ -856,9 +863,10 @@
if (_parser != null)
_parser.parse(_cls);
}
-
+ access = meta.getAccessType();
+
boolean mappedSuper = "mapped-superclass".equals(elem);
- meta.setAbstract(mappedSuper);
+ boolean embeddable = "embeddable".equals(elem);
if (isMetaDataMode()) {
meta.setSource(getSourceFile(), meta.SRC_XML);
meta.setSourceMode(MODE_META, true);
@@ -871,9 +879,14 @@
String name = attrs.getValue("name");
if (!StringUtils.isEmpty(name))
meta.setTypeAlias(name);
- meta.setEmbeddedOnly(mappedSuper || "embeddable".equals(elem));
- if (mappedSuper)
- meta.setIdentityType(meta.ID_UNKNOWN);
+ meta.setAbstract(mappedSuper);
+ meta.setEmbeddedOnly(mappedSuper || embeddable);
+// if (mappedSuper)
+// meta.setIdentityType(meta.ID_UNKNOWN);
+
+ if (embeddable) {
+ addDeferredEmbeddableMetaData(_cls);
+ }
}
if (isMappingMode())
meta.setSourceMode(MODE_MAPPING, true);
@@ -1030,7 +1043,8 @@
fmd.setPrimaryKey(true);
fmd.setEmbedded(true);
if (fmd.getEmbeddedMetaData() == null)
- fmd.addEmbeddedMetaData();
+// fmd.addEmbeddedMetaData();
+ deferEmbeddable(fmd.getDeclaredType(), fmd);
return true;
}
@@ -1181,16 +1195,19 @@
ClassMetaData meta = (ClassMetaData) currentElement();
String name = attrs.getValue("name");
FieldMetaData field = meta.getDeclaredField(name);
- if ((field == null || field.getDeclaredType() == Object.class)
+ int fldAccess = getFieldAccess(field, attrs);
+ // If the access defined in XML is not the same as what was defined
+ // by default or annotation, find the appropriate backing member and
+ // replace what is currently defined in metadata.
+ if ((field == null || field.getDeclaredType() == Object.class ||
+ field.getAccessType() != fldAccess)
&& meta.getDescribedType() != Object.class) {
Member member = null;
Class type = null;
- int def = _repos.getMetaDataFactory().getDefaults().
- getDefaultAccessType();
try {
- if (meta.getAccessType() == ClassMetaData.ACCESS_PROPERTY
- || (meta.getAccessType() == ClassMetaData.ACCESS_UNKNOWN
- && def == ClassMetaData.ACCESS_PROPERTY)) {
+ if ((field != null &&
+ field.getAccessType() == ClassMetaData.ACCESS_PROPERTY) ||
+ (fldAccess == ClassMetaData.ACCESS_PROPERTY)) {
String cap = StringUtils.capitalize(name);
type = meta.getDescribedType();
try {
@@ -1259,6 +1276,34 @@
}
/**
+ * Determines access for field based upon existing metadata and XML
+ * attributes.
+ *
+ * @param field FieldMetaData current metadata for field
+ * @param attrs XML Attributes defined on this field
+ * @return
+ */
+ private int getFieldAccess(FieldMetaData field, Attributes attrs) {
+ if (attrs != null) {
+ String access = attrs.getValue("access");
+ if ("PROPERTY".equals(access))
+ return ClassMetaData.ACCESS_PROPERTY;
+ if ("FIELD".equals(access))
+ return ClassMetaData.ACCESS_FIELD;
+ }
+ // Check access defined on field, if provided
+ if (field != null) {
+ return field.getAccessType();
+ }
+ // Otherwise, get the default access type of the declaring class
+ ClassMetaData meta = (ClassMetaData) currentElement();
+ if (meta != null) {
+ return (meta.getAccessType() & ~ClassMetaData.ACCESS_EXPLICIT);
+ }
+ return ClassMetaData.ACCESS_UNKNOWN;
+ }
+
+ /**
* Implement to add field mapping data. Does nothing by default.
*/
protected void startFieldMapping(FieldMetaData field, Attributes attrs)
@@ -1372,8 +1417,10 @@
assertPC(fmd, "Embedded");
fmd.setEmbedded(true);
fmd.setSerialized(false); // override any Lob annotation
+
if (fmd.getEmbeddedMetaData() == null)
- fmd.addEmbeddedMetaData();
+// fmd.addEmbeddedMetaData();
+ deferEmbeddable(fmd.getDeclaredType(), fmd);
}
/**
@@ -1485,7 +1532,9 @@
if (JavaTypes.maybePC(fmd.getElement())) {
fmd.getElement().setEmbedded(true);
if (fmd.getElement().getEmbeddedMetaData() == null)
- fmd.getElement().addEmbeddedMetaData();
+// fmd.getElement().addEmbeddedMetaData();
+ deferEmbeddable(fmd.getElement().getDeclaredType(),
+ fmd.getElement());
}
}
@@ -1869,4 +1918,41 @@
return PersistenceCapable.class;
return super.classForName(name, isRuntime());
}
+
+ /**
+ * Process all deferred embeddables for a given class. This should only
+ * happen after the access type of the embeddable is known.
+ *
+ * @param embedType embeddable class
+ * @param access class level access for embeddable
+ */
+ protected void addDeferredEmbeddableMetaData(Class embedType) {
+ ArrayList<MetaDataContext> fmds = _embeddables.get(embedType);
+ if (fmds != null && fmds.size() > 0) {
+ for (int i = fmds.size() -1 ; i >= 0; i--) {
+ MetaDataContext md = fmds.get(i);
+ if (md instanceof FieldMetaData) {
+ ((FieldMetaData)md).addEmbeddedMetaData();
+ }
+ else if (md instanceof ValueMetaData) {
+ ((ValueMetaData)md).addEmbeddedMetaData();
+ }
+ fmds.remove(i);
+ }
+ // If all mds in the list were processed, remove the item
+ // from the map.
+ if (fmds.size() == 0) {
+ _embeddables.remove(embedType);
+ }
+ }
+ }
+
+ protected void deferEmbeddable(Class embedType, MetaDataContext fmd) {
+ ArrayList<MetaDataContext> fmds = _embeddables.get(embedType);
+ if (fmds == null) {
+ fmds = new ArrayList<MetaDataContext>();
+ _embeddables.put(embedType, fmds);
+ }
+ fmds.add(fmd);
+ }
}
Modified: openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties?rev=767973&r1=767972&r2=767973&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties (original)
+++ openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties Thu Apr 23 16:55:45 2009
@@ -174,3 +174,7 @@
"optimistic-force-increment"(25), \
"pessimistic-read"(30), "pessimistic-write"(40) or \
"pessimistic-force-increment"(50). Specified value: {0}.
+access-invalid: "{0}" is not a valid access style. Valid access styles are \
+ "PROPERTY" and "FIELD".
+getter-unmatched: Getter method "{0}" has no matching setter method.
+
\ No newline at end of file
Modified: openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/meta/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/meta/localizer.properties?rev=767973&r1=767972&r2=767973&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/meta/localizer.properties (original)
+++ openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/meta/localizer.properties Thu Apr 23 16:55:45 2009
@@ -35,5 +35,5 @@
match with the expected key type "{2}"
getter-unmatched: Getter method "{0}" in "{1}" has no matching setter method.
mmg-tool-banner: Starting OpenJPA Annotation Processor for Metamodel Generation
-mmg-process: Generating source code for meta-model class "{0}" at "{1}"
+mmg-process: Processing source code "{0}" to generate metamodel source code
mmg-too-sign: Generated by OpenJPA MetaModel Generator Tool.
\ No newline at end of file