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