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
