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 2012/10/17 22:03:03 UTC
svn commit: r1399410 - in
/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src:
main/java/org/apache/cayenne/reflect/ test/java/org/apache/cayenne/reflect/
test/java/org/apache/cayenne/reflect/generic/
Author: aadamchik
Date: Wed Oct 17 20:03:02 2012
New Revision: 1399410
URL: http://svn.apache.org/viewvc?rev=1399410&view=rev
Log:
CAY-1729 PersistentDescriptor must have predictable property iteration order
Added:
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/PropertyComparator.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/reflect/generic/DataObjectDescriptorFactoryTest.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/reflect/generic/DataObjectDescriptorFactory_InheritanceMapsTest.java
Modified:
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/reflect/PersistentDescriptorTest.java
Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java?rev=1399410&r1=1399409&r2=1399410&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/PersistentDescriptor.java Wed Oct 17 20:03:02 2012
@@ -23,7 +23,10 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.PersistenceState;
@@ -50,7 +53,8 @@ public class PersistentDescriptor implem
// compiled properties ...
protected Class<?> objectClass;
protected Map<String, PropertyDescriptor> declaredProperties;
- protected Map<String, PropertyDescriptor> superProperties;
+ protected Map<String, PropertyDescriptor> properties;
+
protected Map<String, ClassDescriptor> subclassDescriptors;
protected Accessor persistenceStateAccessor;
@@ -73,7 +77,7 @@ public class PersistentDescriptor implem
*/
public PersistentDescriptor() {
this.declaredProperties = new HashMap<String, PropertyDescriptor>();
- this.superProperties = new HashMap<String, PropertyDescriptor>();
+ this.properties = new HashMap<String, PropertyDescriptor>();
this.subclassDescriptors = new HashMap<String, ClassDescriptor>();
// must be a set as duplicate addition attempts are expected...
@@ -93,7 +97,7 @@ public class PersistentDescriptor implem
* Registers a superclass property.
*/
public void addSuperProperty(PropertyDescriptor property) {
- superProperties.put(property.getName(), property);
+ properties.put(property.getName(), property);
indexAddedProperty(property);
}
@@ -103,6 +107,7 @@ public class PersistentDescriptor implem
*/
public void addDeclaredProperty(PropertyDescriptor property) {
declaredProperties.put(property.getName(), property);
+ properties.put(property.getName(), property);
indexAddedProperty(property);
}
@@ -112,7 +117,39 @@ public class PersistentDescriptor implem
public void addRootDbEntity(DbEntity dbEntity) {
this.rootDbEntities.add(dbEntity);
}
+
+ void sortProperties() {
+ // ensure properties are stored in predictable order per CAY-1729
+
+ // 'properties' is a superset of 'declaredProperties', so let's sort all
+ // properties, and populated both ordered collections at once
+ if (properties.size() > 1) {
+
+ List<Entry<String, PropertyDescriptor>> entries = new ArrayList<Entry<String, PropertyDescriptor>>(
+ properties.entrySet());
+
+ Collections.sort(entries, PropertyComparator.comparator);
+
+ Map<String, PropertyDescriptor> orderedProperties = new LinkedHashMap<String, PropertyDescriptor>(
+ (int) (entries.size() / 0.75));
+
+ Map<String, PropertyDescriptor> orderedDeclared = new LinkedHashMap<String, PropertyDescriptor>(
+ (int) (declaredProperties.size() / 0.75));
+
+ for (Entry<String, PropertyDescriptor> e : entries) {
+ orderedProperties.put(e.getKey(), e.getValue());
+
+ if (declaredProperties.containsKey(e.getKey())) {
+ orderedDeclared.put(e.getKey(), e.getValue());
+ }
+ }
+
+ this.properties = orderedProperties;
+ this.declaredProperties = orderedDeclared;
+ }
+ }
+
void indexAddedProperty(PropertyDescriptor property) {
if (property instanceof AttributeProperty) {
@@ -157,6 +194,8 @@ public class PersistentDescriptor implem
if (mapArcProperties != null) {
mapArcProperties.remove(removed);
}
+
+ properties.remove(propertyName);
}
}
@@ -337,19 +376,6 @@ public class PersistentDescriptor implem
/**
* @since 3.0
*/
- boolean visitSuperProperties(PropertyVisitor visitor) {
- for (PropertyDescriptor next : superProperties.values()) {
- if (!next.visit(visitor)) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * @since 3.0
- */
public boolean visitDeclaredProperties(PropertyVisitor visitor) {
for (PropertyDescriptor next : declaredProperties.values()) {
@@ -381,11 +407,14 @@ public class PersistentDescriptor implem
}
public boolean visitProperties(PropertyVisitor visitor) {
- if (!visitSuperProperties(visitor)) {
- return false;
+
+ for (PropertyDescriptor next : properties.values()) {
+ if (!next.visit(visitor)) {
+ return false;
+ }
}
- return visitDeclaredProperties(visitor);
+ return true;
}
public void setPersistenceStateAccessor(Accessor persistenceStateAccessor) {
Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java?rev=1399410&r1=1399409&r2=1399410&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/PersistentDescriptorFactory.java Wed Oct 17 20:03:02 2012
@@ -132,6 +132,8 @@ public abstract class PersistentDescript
indexRootDbEntities(descriptor, inheritanceTree);
indexSuperclassProperties(descriptor);
+
+ descriptor.sortProperties();
return descriptor;
}
Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/PropertyComparator.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/PropertyComparator.java?rev=1399410&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/PropertyComparator.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/reflect/PropertyComparator.java Wed Oct 17 20:03:02 2012
@@ -0,0 +1,48 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+package org.apache.cayenne.reflect;
+
+import java.util.Comparator;
+import java.util.Map.Entry;
+
+/**
+ * @since 3.1
+ */
+final class PropertyComparator implements
+ Comparator<Entry<String, PropertyDescriptor>> {
+
+ static final Comparator<Entry<String, PropertyDescriptor>> comparator = new PropertyComparator();
+
+ public int compare(Entry<String, PropertyDescriptor> o1,
+ Entry<String, PropertyDescriptor> o2) {
+ if (o1.getValue() instanceof ArcProperty) {
+ if (o2.getValue() instanceof ArcProperty) {
+ return o1.getKey().compareTo(o2.getKey());
+ } else {
+ return 1;
+ }
+ } else {
+ if (o2.getValue() instanceof ArcProperty) {
+ return -1;
+ } else {
+ return o1.getKey().compareTo(o2.getKey());
+ }
+ }
+ }
+}
Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/reflect/PersistentDescriptorTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/reflect/PersistentDescriptorTest.java?rev=1399410&r1=1399409&r2=1399410&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/reflect/PersistentDescriptorTest.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/reflect/PersistentDescriptorTest.java Wed Oct 17 20:03:02 2012
@@ -19,12 +19,10 @@
package org.apache.cayenne.reflect;
+import static org.mockito.Mockito.mock;
import junit.framework.TestCase;
-import org.apache.cayenne.reflect.FieldAccessor;
-import org.apache.cayenne.reflect.PersistentDescriptor;
-import org.apache.cayenne.reflect.PropertyDescriptor;
-import org.apache.cayenne.reflect.SimpleAttributeProperty;
+import org.apache.cayenne.map.ObjAttribute;
import org.apache.cayenne.unit.util.TestBean;
public class PersistentDescriptorTest extends TestCase {
@@ -42,10 +40,13 @@ public class PersistentDescriptorTest ex
public void testCopyObjectProperties() {
PersistentDescriptor d1 = new PersistentDescriptor();
- FieldAccessor accessor = new FieldAccessor(TestBean.class, "string", String.class);
- PropertyDescriptor property = new SimpleAttributeProperty(d1, accessor, null);
+ ObjAttribute attribute = mock(ObjAttribute.class);
+ FieldAccessor accessor = new FieldAccessor(TestBean.class, "string",
+ String.class);
+ PropertyDescriptor property = new SimpleAttributeProperty(d1, accessor,
+ attribute);
- d1.declaredProperties.put(property.getName(), property);
+ d1.addDeclaredProperty(property);
TestBean from = new TestBean();
from.setString("123");
@@ -55,4 +56,5 @@ public class PersistentDescriptorTest ex
d1.shallowMerge(from, to);
assertEquals("123", to.getString());
}
+
}
Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/reflect/generic/DataObjectDescriptorFactoryTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/reflect/generic/DataObjectDescriptorFactoryTest.java?rev=1399410&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/reflect/generic/DataObjectDescriptorFactoryTest.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/reflect/generic/DataObjectDescriptorFactoryTest.java Wed Oct 17 20:03:02 2012
@@ -0,0 +1,138 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+package org.apache.cayenne.reflect.generic;
+
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.reflect.ArcProperty;
+import org.apache.cayenne.reflect.AttributeProperty;
+import org.apache.cayenne.reflect.ClassDescriptor;
+import org.apache.cayenne.reflect.PropertyDescriptor;
+import org.apache.cayenne.reflect.PropertyVisitor;
+import org.apache.cayenne.reflect.SingletonFaultFactory;
+import org.apache.cayenne.reflect.ToManyProperty;
+import org.apache.cayenne.reflect.ToOneProperty;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+
+@UseServerRuntime(ServerCase.TESTMAP_PROJECT)
+public class DataObjectDescriptorFactoryTest extends ServerCase {
+
+ @Inject
+ private EntityResolver resolver;
+
+ public void testVisitDeclaredProperties_IterationOrder() {
+
+ DataObjectDescriptorFactory factory = new DataObjectDescriptorFactory(
+ resolver.getClassDescriptorMap(), new SingletonFaultFactory());
+
+ for (ObjEntity e : resolver.getObjEntities()) {
+ ClassDescriptor descriptor = factory.getDescriptor(e.getName());
+
+ final PropertyDescriptor[] lastProcessed = new PropertyDescriptor[1];
+
+ PropertyVisitor visitor = new PropertyVisitor() {
+
+ public boolean visitToOne(ToOneProperty property) {
+ assertPropertiesAreInOrder(lastProcessed[0], property);
+ lastProcessed[0] = property;
+ return true;
+ }
+
+ public boolean visitToMany(ToManyProperty property) {
+ assertPropertiesAreInOrder(lastProcessed[0], property);
+ lastProcessed[0] = property;
+ return true;
+ }
+
+ public boolean visitAttribute(AttributeProperty property) {
+ assertPropertiesAreInOrder(lastProcessed[0], property);
+ lastProcessed[0] = property;
+ return true;
+ }
+ };
+
+ descriptor.visitDeclaredProperties(visitor);
+ }
+
+ }
+
+ public void testVisitProperties_IterationOrder() {
+
+ DataObjectDescriptorFactory factory = new DataObjectDescriptorFactory(
+ resolver.getClassDescriptorMap(), new SingletonFaultFactory());
+
+ for (ObjEntity e : resolver.getObjEntities()) {
+ ClassDescriptor descriptor = factory.getDescriptor(e.getName());
+
+ final PropertyDescriptor[] lastProcessed = new PropertyDescriptor[1];
+
+ PropertyVisitor visitor = new PropertyVisitor() {
+
+ public boolean visitToOne(ToOneProperty property) {
+ assertPropertiesAreInOrder(lastProcessed[0], property);
+ lastProcessed[0] = property;
+ return true;
+ }
+
+ public boolean visitToMany(ToManyProperty property) {
+ assertPropertiesAreInOrder(lastProcessed[0], property);
+ lastProcessed[0] = property;
+ return true;
+ }
+
+ public boolean visitAttribute(AttributeProperty property) {
+ assertPropertiesAreInOrder(lastProcessed[0], property);
+ lastProcessed[0] = property;
+ return true;
+ }
+ };
+
+ descriptor.visitProperties(visitor);
+ }
+
+ }
+
+ static void assertPropertiesAreInOrder(PropertyDescriptor o1,
+ PropertyDescriptor o2) {
+
+ if (o1 == null) {
+ return;
+ }
+
+ if (o1 instanceof ArcProperty) {
+ if (o2 instanceof ArcProperty) {
+ assertTrue("Names are not ordered: " + o1.getName()
+ + " before " + o2.getName(),
+ o1.getName().compareTo(o2.getName()) < 0);
+ } else {
+ fail("Arc preceded regular property");
+ }
+ } else {
+ if (o2 instanceof ArcProperty) {
+ // this is correct
+ } else {
+ assertTrue("Names are not ordered: " + o1.getName()
+ + " before " + o2.getName(),
+ o1.getName().compareTo(o2.getName()) < 0);
+ }
+ }
+ }
+}
Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/reflect/generic/DataObjectDescriptorFactory_InheritanceMapsTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/reflect/generic/DataObjectDescriptorFactory_InheritanceMapsTest.java?rev=1399410&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/reflect/generic/DataObjectDescriptorFactory_InheritanceMapsTest.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/reflect/generic/DataObjectDescriptorFactory_InheritanceMapsTest.java Wed Oct 17 20:03:02 2012
@@ -0,0 +1,78 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+package org.apache.cayenne.reflect.generic;
+
+import org.apache.cayenne.di.Inject;
+import org.apache.cayenne.map.EntityResolver;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.reflect.AttributeProperty;
+import org.apache.cayenne.reflect.ClassDescriptor;
+import org.apache.cayenne.reflect.PropertyDescriptor;
+import org.apache.cayenne.reflect.PropertyVisitor;
+import org.apache.cayenne.reflect.SingletonFaultFactory;
+import org.apache.cayenne.reflect.ToManyProperty;
+import org.apache.cayenne.reflect.ToOneProperty;
+import org.apache.cayenne.unit.di.server.ServerCase;
+import org.apache.cayenne.unit.di.server.UseServerRuntime;
+
+@UseServerRuntime(ServerCase.INHERTITANCE_SINGLE_TABLE1_PROJECT)
+public class DataObjectDescriptorFactory_InheritanceMapsTest extends ServerCase {
+
+ @Inject
+ private EntityResolver resolver;
+
+ public void testVisitProperties_IterationOrder() {
+
+ DataObjectDescriptorFactory factory = new DataObjectDescriptorFactory(
+ resolver.getClassDescriptorMap(), new SingletonFaultFactory());
+
+ for (ObjEntity e : resolver.getObjEntities()) {
+ ClassDescriptor descriptor = factory.getDescriptor(e.getName());
+
+ final PropertyDescriptor[] lastProcessed = new PropertyDescriptor[1];
+
+ PropertyVisitor visitor = new PropertyVisitor() {
+
+ public boolean visitToOne(ToOneProperty property) {
+ DataObjectDescriptorFactoryTest.assertPropertiesAreInOrder(
+ lastProcessed[0], property);
+ lastProcessed[0] = property;
+ return true;
+ }
+
+ public boolean visitToMany(ToManyProperty property) {
+ DataObjectDescriptorFactoryTest.assertPropertiesAreInOrder(
+ lastProcessed[0], property);
+ lastProcessed[0] = property;
+ return true;
+ }
+
+ public boolean visitAttribute(AttributeProperty property) {
+ DataObjectDescriptorFactoryTest.assertPropertiesAreInOrder(
+ lastProcessed[0], property);
+ lastProcessed[0] = property;
+ return true;
+ }
+ };
+
+ descriptor.visitProperties(visitor);
+ }
+
+ }
+}