You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2010/11/09 04:05:31 UTC

svn commit: r1032829 - in /cayenne/main/trunk: docs/doc/src/main/resources/ framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/

Author: aadamchik
Date: Tue Nov  9 03:05:30 2010
New Revision: 1032829

URL: http://svn.apache.org/viewvc?rev=1032829&view=rev
Log:
CAY-1403 Method "readNestedProperty" Should Resolve Through Iterative Invocations onto DataObject and Not Complete Within Cayenne.readNestedProperty

* Porting 3.0 patch by Andrew Lindesay, with a 3.1 twist - a code to switch as needed between
  overridable CayenneDataObject implementation of readnestedProperty and static Cayenne implementation

Modified:
    cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/Cayenne.java
    cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/CayenneDataObject.java

Modified: cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt?rev=1032829&r1=1032828&r2=1032829&view=diff
==============================================================================
--- cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt (original)
+++ cayenne/main/trunk/docs/doc/src/main/resources/RELEASE-NOTES.txt Tue Nov  9 03:05:30 2010
@@ -75,6 +75,7 @@ CAY-1500 MySQL JDBC Batching
 
 Bug Fixes Since 3.0.1:
 
+CAY-1403 Method "readNestedProperty" Should Resolve Through Iterative Invocations onto DataObject and Not Complete Within Cayenne.readNestedProperty
 CAY-1484 Flattened attribute queries are incorrectly generated
 CAY-1485 Memory information for about dialog
 CAY-1488 OutOfMemory when selecting "Remove Foreign Keys Mapped as Object Attributes"

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/Cayenne.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/Cayenne.java?rev=1032829&r1=1032828&r2=1032829&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/Cayenne.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/Cayenne.java Tue Nov  9 03:05:30 2010
@@ -24,7 +24,6 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.StringTokenizer;
 
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.ObjEntity;
@@ -112,159 +111,125 @@ public final class Cayenne {
      * </p>
      * <ul>
      * <li>Read this object property:<br>
-     * <code>String name = (String)CayenneUtils.readNestedProperty(artist, "name");</code>
-     * <br>
+     * <code>String name = (String)Cayenne.readNestedProperty(artist, "name");</code><br>
      * <br>
      * </li>
      * <li>Read an object related to this object:<br>
-     * <code>Gallery g = (Gallery)CayenneUtils.readNestedProperty(paintingInfo, "toPainting.toGallery");</code>
+     * <code>Gallery g = (Gallery)Cayenne.readNestedProperty(paintingInfo, "toPainting.toGallery");</code>
      * <br>
      * <br>
      * </li>
      * <li>Read a property of an object related to this object: <br>
-     * <code>String name = (String)CayenneUtils.readNestedProperty(painting, "toArtist.artistName");</code>
+     * <code>String name = (String)Cayenne.readNestedProperty(painting, "toArtist.artistName");</code>
      * <br>
      * <br>
      * </li>
      * <li>Read to-many relationship list:<br>
-     * <code>List exhibits = (List)CayenneUtils.readNestedProperty(painting, "toGallery.exhibitArray");</code>
+     * <code>List exhibits = (List)Cayenne.readNestedProperty(painting, "toGallery.exhibitArray");</code>
      * <br>
      * <br>
      * </li>
      * <li>Read to-many relationship in the middle of the path:<br>
-     * <code>List<String> names = (List<String>)CayenneUtils.readNestedProperty(artist, "paintingArray.paintingName");</code>
+     * <code>List<String> names = (List<String>)Cayenne.readNestedProperty(artist, "paintingArray.paintingName");</code>
      * <br>
      * <br>
      * </li>
      * </ul>
      */
-    public static Object readNestedProperty(Persistent p, String path) {
-        return readNestedProperty(p, path, tokenizePath(path), 0, 0);
-    }
+    public static Object readNestedProperty(Object o, String path) {
 
-    /**
-     * Recursively resolves nested property path
-     */
-    private static Object readNestedProperty(
-            Persistent p,
-            String path,
-            String[] tokenizedPath,
-            int tokenIndex,
-            int pathIndex) {
+        if (o == null) {
+            return null;
+        }
+        else if (o instanceof DataObject) {
+            return ((DataObject) o).readNestedProperty(path);
+        }
+        else if (o instanceof Collection<?>) {
 
-        Object property = readSimpleProperty(p, tokenizedPath[tokenIndex]);
+            // This allows people to put @size at the end of a property
+            // path and be able to find out the size of a relationship.
 
-        if (tokenIndex == tokenizedPath.length - 1) { // last component
-            return property;
-        }
+            Collection<?> collection = (Collection<?>) o;
 
-        pathIndex += tokenizedPath[tokenIndex].length() + 1;
-        if (property == null) {
-            return null;
-        }
-        else if (property instanceof Persistent) {
-            return readNestedProperty(
-                    (Persistent) property,
-                    path,
-                    tokenizedPath,
-                    tokenIndex + 1,
-                    tokenIndex);
-        }
-        else if (property instanceof Collection) {
-
-            Collection<?> collection = (Collection) property;
-
-            if (tokenIndex < tokenizedPath.length - 1) {
-                if (tokenizedPath[tokenIndex + 1].equals(PROPERTY_COLLECTION_SIZE)) {
-                    return collection.size();
-                }
+            if (path.equals(PROPERTY_COLLECTION_SIZE)) {
+                return collection.size();
             }
 
             // Support for collection property in the middle of the path
-            Collection<Object> result = property instanceof List
+            Collection<Object> result = o instanceof List<?>
                     ? new ArrayList<Object>()
                     : new HashSet<Object>();
-            for (Object object : collection) {
-                if (object instanceof CayenneDataObject) {
 
-                    Object tail = readNestedProperty(
-                            (CayenneDataObject) object,
-                            path,
-                            tokenizedPath,
-                            tokenIndex + 1,
-                            tokenIndex);
+            for (Object item : collection) {
+                if (item instanceof DataObject) {
+                    DataObject cdo = (DataObject) item;
+                    Object rest = cdo.readNestedProperty(path);
 
-                    if (tail instanceof Collection) {
+                    if (rest instanceof Collection<?>) {
 
                         // We don't want nested collections. E.g.
-                        // readNestedProperty("paintingArray.paintingTitle")
-                        // should return List<String>
-                        result.addAll((Collection<?>) tail);
+                        // readNestedProperty("paintingArray.paintingTitle") should return
+                        // List<String>
+                        result.addAll((Collection<?>) rest);
                     }
                     else {
-                        result.add(tail);
+                        result.add(rest);
                     }
                 }
             }
+
             return result;
         }
-        else {
-            // read the rest of the path via introspection
-            return PropertyUtils.getProperty(property, path.substring(pathIndex));
-        }
-    }
 
-    private static final String[] tokenizePath(String path) {
-        if (path == null) {
-            throw new NullPointerException("Null property path.");
+        if ((null == path) || (0 == path.length())) {
+            throw new IllegalArgumentException(
+                    "the path must be supplied in order to lookup a nested property");
         }
 
-        if (path.length() == 0) {
-            throw new IllegalArgumentException("Empty property path.");
+        int dotIndex = path.indexOf('.');
+
+        if (0 == dotIndex) {
+            throw new IllegalArgumentException(
+                    "the path is invalid because it starts with a period character");
         }
 
-        // take a shortcut for simple properties
-        if (!path.contains(".")) {
-            return new String[] {
-                path
-            };
+        if (dotIndex == path.length() - 1) {
+            throw new IllegalArgumentException(
+                    "the path is invalid because it ends with a period character");
         }
 
-        StringTokenizer tokens = new StringTokenizer(path, ".");
-        int length = tokens.countTokens();
-        String[] tokenized = new String[length];
-        for (int i = 0; i < length; i++) {
-            String temp = tokens.nextToken();
-            if (temp.endsWith("+")) {
-                tokenized[i] = temp.substring(0, temp.length() - 1);
-            }
-            else {
-                tokenized[i] = temp;
-            }
+        if (-1 == dotIndex) {
+            return readSimpleProperty(o, path);
         }
-        return tokenized;
-    }
 
-    private static final Object readSimpleProperty(Persistent p, String propertyName) {
-        Property property = getProperty(p, propertyName);
+        String path0 = path.substring(0, dotIndex);
+        String pathRemainder = path.substring(dotIndex + 1);
 
-        if (property != null) {
-            // side effect - resolves HOLLOW object
-            return property.readProperty(p);
-        }
+        // this is copied from the old code where the placement of a plus
+        // character at the end of a segment of a property path would
+        // simply strip out the plus. I am not entirely sure why this is
+        // done. See unit test 'testReadNestedPropertyToManyInMiddle1'.
 
-        // handling non-persistent property
-        Object result = null;
-        if (p instanceof DataObject) {
-            result = ((DataObject) p).readPropertyDirectly(propertyName);
+        if ('+' == path0.charAt(path0.length() - 1)) {
+            path0 = path0.substring(0, path0.length() - 1);
         }
 
-        if (result != null) {
-            return result;
+        Object property = readSimpleProperty(o, path0);
+        return readNestedProperty(property, pathRemainder);
+    }
+
+    private static final Object readSimpleProperty(Object o, String propertyName) {
+        if (o instanceof Persistent) {
+
+            Property property = getProperty((Persistent) o, propertyName);
+
+            if (property != null) {
+                return property.readProperty(o);
+            }
         }
 
-        // there is still a change to return a property via introspection
-        return PropertyUtils.getProperty(p, propertyName);
+        // handling non-persistent property
+        return PropertyUtils.getProperty(o, propertyName);
     }
 
     /**

Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/CayenneDataObject.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/CayenneDataObject.java?rev=1032829&r1=1032828&r2=1032829&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/CayenneDataObject.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/CayenneDataObject.java Tue Nov  9 03:05:30 2010
@@ -37,6 +37,7 @@ import org.apache.cayenne.map.EntityReso
 import org.apache.cayenne.map.ObjAttribute;
 import org.apache.cayenne.map.ObjEntity;
 import org.apache.cayenne.map.ObjRelationship;
+import org.apache.cayenne.reflect.PropertyUtils;
 import org.apache.cayenne.validation.BeanValidationFailure;
 import org.apache.cayenne.validation.ValidationFailure;
 import org.apache.cayenne.validation.ValidationResult;
@@ -108,7 +109,65 @@ public class CayenneDataObject extends P
      * @since 1.0.5
      */
     public Object readNestedProperty(String path) {
-        return Cayenne.readNestedProperty(this, path);
+
+        if ((null == path) || (0 == path.length())) {
+            throw new IllegalArgumentException(
+                    "the path must be supplied in order to lookup a nested property");
+        }
+
+        int dotIndex = path.indexOf('.');
+
+        if (0 == dotIndex) {
+            throw new IllegalArgumentException(
+                    "the path is invalid because it starts with a period character");
+        }
+
+        if (dotIndex == path.length() - 1) {
+            throw new IllegalArgumentException(
+                    "the path is invalid because it ends with a period character");
+        }
+
+        if (-1 == dotIndex) {
+            return readSimpleProperty(path);
+        }
+
+        String path0 = path.substring(0, dotIndex);
+        String pathRemainder = path.substring(dotIndex + 1);
+
+        // this is copied from the old code where the placement of a plus
+        // character at the end of a segment of a property path would
+        // simply strip out the plus. I am not entirely sure why this is
+        // done. See unit test 'testReadNestedPropertyToManyInMiddle1'.
+
+        if ('+' == path0.charAt(path0.length() - 1)) {
+            path0 = path0.substring(0, path0.length() - 1);
+        }
+
+        Object property = readSimpleProperty(path0);
+
+        if (property == null) {
+            return null;
+        }
+        else if (property instanceof DataObject) {
+            return ((DataObject) property).readNestedProperty(pathRemainder);
+        }
+        else {
+            return Cayenne.readNestedProperty(property, pathRemainder);
+        }
+    }
+
+    private final Object readSimpleProperty(String property) {
+
+        // side effect - resolves HOLLOW object
+        Object object = readProperty(property);
+
+        // if a null value is returned, there is still a chance to
+        // find a non-persistent property via reflection
+        if (object == null && !values.containsKey(property)) {
+            object = PropertyUtils.getProperty(this, property);
+        }
+
+        return object;
     }
 
     public Object readProperty(String propertyName) {