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 2011/05/15 11:59:57 UTC
svn commit: r1103306 - in /cayenne/main/trunk: docs/doc/src/main/resources/
framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/configuration/
framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/configuration/
Author: aadamchik
Date: Sun May 15 09:59:57 2011
New Revision: 1103306
URL: http://svn.apache.org/viewvc?rev=1103306&view=rev
Log:
CAY-943 Support multiple cayenne.xml files in the project
(finished)
* merging names
* merging DataMaps
* merging DataNodes
* merging DataNode to DataMap links
* merging properties
Added:
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/configuration/DefaultDataChannelDescriptorMergerTest.java
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/configuration/DataChannelDescriptor.java
cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/configuration/DefaultDataChannelDescriptorMerger.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=1103306&r1=1103305&r2=1103306&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 Sun May 15 09:59:57 2011
@@ -13,6 +13,7 @@ Date:
----------------------------------
Changes/New Features Since 3.1M2:
+CAY-943 Support multiple cayenne.xml files in the project
CAY-1556 Add path construction feature to make constructing paths from constants easier for queries and orderings
CAY-1525 CharType: don't trim spaces on the left
CAY-1544 Remove jdk1.6 module from Cayenne sources
Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/configuration/DataChannelDescriptor.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/configuration/DataChannelDescriptor.java?rev=1103306&r1=1103305&r2=1103306&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/configuration/DataChannelDescriptor.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/configuration/DataChannelDescriptor.java Sun May 15 09:59:57 2011
@@ -36,7 +36,8 @@ import org.apache.cayenne.util.XMLSerial
*
* @since 3.1
*/
-public class DataChannelDescriptor implements ConfigurationNode, Serializable, XMLSerializable {
+public class DataChannelDescriptor implements ConfigurationNode, Serializable,
+ XMLSerializable {
protected String name;
protected Map<String, String> properties;
@@ -140,6 +141,16 @@ public class DataChannelDescriptor imple
return nodeDescriptors;
}
+ public DataNodeDescriptor getNodeDescriptor(String name) {
+ for (DataNodeDescriptor node : nodeDescriptors) {
+ if (name.equals(node.getName())) {
+ return node;
+ }
+ }
+
+ return null;
+ }
+
public Resource getConfigurationSource() {
return configurationSource;
}
Modified: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/configuration/DefaultDataChannelDescriptorMerger.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/configuration/DefaultDataChannelDescriptorMerger.java?rev=1103306&r1=1103305&r2=1103306&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/configuration/DefaultDataChannelDescriptorMerger.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/main/java/org/apache/cayenne/configuration/DefaultDataChannelDescriptorMerger.java Sun May 15 09:59:57 2011
@@ -18,11 +18,39 @@
****************************************************************/
package org.apache.cayenne.configuration;
+import org.apache.cayenne.map.DataMap;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
/**
+ * A default implementation of {@link DataChannelDescriptorMerger}. The general rule of
+ * merge is that the order of descriptors on the merge list matters. If there are two
+ * conflicting metadata objects belonging to two descriptors, an object from the last
+ * descriptor takes precedence over the object from the first one. This way it is easy to
+ * override pieces of metadata. This is also similar to how DI modules are merged in
+ * Cayenne. So this is how the merge works:
+ * <ul>
+ * <li></li>
+ * <li>Merged descriptor name is the same as the name of the last descriptor on the merge
+ * list.</li> *
+ * <li>Merged descriptor properties are the same as the properties of the last descriptor
+ * on the merge list. I.e. properties are not merged to avoid invalid combinations and
+ * unexpected runtime behavior.</li>
+ * <li>If there are two or more DataMaps with the same name, only one DataMap is placed in
+ * the merged descriptor, the rest are discarded. DataMap with highest index in the
+ * descriptor array is chosen per precedence rule above.</li>
+ * <li>If there are two or more DataNodes with the same name, only one DataNodes is placed
+ * in the merged descriptor, the rest are discarded. DataNodes with highest index in the
+ * descriptor array is chosen per precedence rule above.</li>
+ * </ul>
+ *
* @since 3.1
*/
public class DefaultDataChannelDescriptorMerger implements DataChannelDescriptorMerger {
+ private static Log logger = LogFactory
+ .getLog(DefaultDataChannelDescriptorMerger.class);
+
public DataChannelDescriptor merge(DataChannelDescriptor... descriptors) {
if (descriptors == null || descriptors.length == 0) {
throw new IllegalArgumentException("Null or empty descriptors");
@@ -32,8 +60,91 @@ public class DefaultDataChannelDescripto
return descriptors[0];
}
- throw new UnsupportedOperationException(
- "Merging multiple descriptors is not yet implemented");
+ int len = descriptors.length;
+
+ // merge into a new descriptor; do not alter source descriptors
+ DataChannelDescriptor merged = new DataChannelDescriptor();
+ merged.setName(descriptors[len - 1].getName());
+ merged.getProperties().putAll(descriptors[len - 1].getProperties());
+
+ // iterate in reverse order to reduce add/remove operations
+ for (int i = len - 1; i >= 0; i--) {
+ DataChannelDescriptor descriptor = descriptors[i];
+
+ // DataMaps are merged by reference, as we don't change them
+ // TODO: they still have a link to the unmerged descriptor, is it bad?
+ for (DataMap map : descriptor.getDataMaps()) {
+
+ // report conflicting DataMap and leave the existing copy
+ DataMap existing = merged.getDataMap(map.getName());
+ if (existing != null) {
+
+ logger.info("Discarding overridden DataMap '"
+ + map.getName()
+ + "' from descriptor '"
+ + descriptor.getName()
+ + "'");
+ }
+ else {
+
+ logger.info("Using DataMap '"
+ + map.getName()
+ + "' from descriptor '"
+ + descriptor.getName()
+ + "' in merged descriptor");
+ merged.getDataMaps().add(map);
+ }
+ }
+
+ // DataNodes are merged by copy as we may modify them (changing map linking)
+ for (DataNodeDescriptor node : descriptor.getNodeDescriptors()) {
+
+ DataNodeDescriptor existing = merged.getNodeDescriptor(node.getName());
+ if (existing != null) {
+ logger.info("Discarding overridden DataNode '"
+ + node.getName()
+ + "' from descriptor '"
+ + descriptor.getName()
+ + "'");
+
+ for (String mapName : node.getDataMapNames()) {
+ if (!existing.getDataMapNames().contains(mapName)) {
+ existing.getDataMapNames().add(mapName);
+ }
+ }
+ }
+ else {
+ logger.info("Using DataNode '"
+ + node.getName()
+ + "' from descriptor '"
+ + descriptor.getName()
+ + "' in merged descriptor");
+ merged
+ .getNodeDescriptors()
+ .add(cloneDataNodeDescriptor(node, merged));
+ }
+ }
+ }
+
+ return merged;
}
+ protected DataNodeDescriptor cloneDataNodeDescriptor(
+ DataNodeDescriptor original,
+ DataChannelDescriptor targetOwner) {
+ DataNodeDescriptor clone = new DataNodeDescriptor(original.getName());
+
+ // do not clone 'configurationSource' as we may change the structure of the node
+
+ clone.setAdapterType(original.getAdapterType());
+ clone.setDataChannelDescriptor(targetOwner);
+ clone.setDataSourceDescriptor(original.getDataSourceDescriptor());
+ clone.setDataSourceFactoryType(original.getDataSourceFactoryType());
+ clone.setParameters(original.getParameters());
+ clone.setSchemaUpdateStrategyType(original.getSchemaUpdateStrategyType());
+
+ clone.getDataMapNames().addAll(original.getDataMapNames());
+
+ return clone;
+ }
}
Added: cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/configuration/DefaultDataChannelDescriptorMergerTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/configuration/DefaultDataChannelDescriptorMergerTest.java?rev=1103306&view=auto
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/configuration/DefaultDataChannelDescriptorMergerTest.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.5-unpublished/src/test/java/org/apache/cayenne/configuration/DefaultDataChannelDescriptorMergerTest.java Sun May 15 09:59:57 2011
@@ -0,0 +1,165 @@
+/*****************************************************************
+ * 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.configuration;
+
+import org.apache.cayenne.map.DataMap;
+
+import junit.framework.TestCase;
+
+public class DefaultDataChannelDescriptorMergerTest extends TestCase {
+
+ public void testSingleDescriptor() {
+ DataChannelDescriptor descriptor = new DataChannelDescriptor();
+ descriptor.setName("Zx");
+
+ DefaultDataChannelDescriptorMerger merger = new DefaultDataChannelDescriptorMerger();
+
+ DataChannelDescriptor merged = merger.merge(descriptor);
+ assertSame(descriptor, merged);
+ assertEquals("Zx", merged.getName());
+ }
+
+ public void testMerged_Name() {
+ DataChannelDescriptor d1 = new DataChannelDescriptor();
+ d1.setName("Zx");
+
+ DataChannelDescriptor d2 = new DataChannelDescriptor();
+ d2.setName("Ym");
+
+ DefaultDataChannelDescriptorMerger merger = new DefaultDataChannelDescriptorMerger();
+
+ DataChannelDescriptor merged = merger.merge(d1, d2);
+ assertNotSame(d1, merged);
+ assertNotSame(d2, merged);
+ assertEquals("Ym", merged.getName());
+ }
+
+ public void testMerged_Properties() {
+ DataChannelDescriptor d1 = new DataChannelDescriptor();
+ d1.getProperties().put("X", "1");
+ d1.getProperties().put("Y", "2");
+
+ DataChannelDescriptor d2 = new DataChannelDescriptor();
+ d2.getProperties().put("X", "3");
+ d2.getProperties().put("Z", "4");
+
+ DefaultDataChannelDescriptorMerger merger = new DefaultDataChannelDescriptorMerger();
+
+ DataChannelDescriptor merged = merger.merge(d1, d2);
+ assertEquals(2, merged.getProperties().size());
+ assertEquals("3", merged.getProperties().get("X"));
+ assertEquals("4", merged.getProperties().get("Z"));
+ }
+
+ public void testMerged_DataMaps() {
+ DataChannelDescriptor d1 = new DataChannelDescriptor();
+ d1.setName("Zx");
+ DataMap m11 = new DataMap("A");
+ DataMap m12 = new DataMap("B");
+ d1.getDataMaps().add(m11);
+ d1.getDataMaps().add(m12);
+
+ DataChannelDescriptor d2 = new DataChannelDescriptor();
+ d2.setName("Ym");
+ DataMap m21 = new DataMap("C");
+ DataMap m22 = new DataMap("A");
+ d2.getDataMaps().add(m21);
+ d2.getDataMaps().add(m22);
+
+ DefaultDataChannelDescriptorMerger merger = new DefaultDataChannelDescriptorMerger();
+
+ DataChannelDescriptor merged = merger.merge(d1, d2);
+
+ assertEquals(3, merged.getDataMaps().size());
+ assertSame(m22, merged.getDataMap("A"));
+ assertSame(m12, merged.getDataMap("B"));
+ assertSame(m21, merged.getDataMap("C"));
+ }
+
+ public void testMerge_DataNodes() {
+ DataChannelDescriptor d1 = new DataChannelDescriptor();
+ d1.setName("Zx");
+ DataNodeDescriptor dn11 = new DataNodeDescriptor("A");
+ DataNodeDescriptor dn12 = new DataNodeDescriptor("B");
+ dn12.setAdapterType("Xa");
+ d1.getNodeDescriptors().add(dn11);
+ d1.getNodeDescriptors().add(dn12);
+
+ DataChannelDescriptor d2 = new DataChannelDescriptor();
+ d2.setName("Ym");
+ DataNodeDescriptor dn21 = new DataNodeDescriptor("B");
+ dn21.setAdapterType("Uy");
+ DataNodeDescriptor dn22 = new DataNodeDescriptor("C");
+ d2.getNodeDescriptors().add(dn21);
+ d2.getNodeDescriptors().add(dn22);
+
+ DefaultDataChannelDescriptorMerger merger = new DefaultDataChannelDescriptorMerger();
+
+ DataChannelDescriptor merged = merger.merge(d1, d2);
+
+ assertEquals(3, merged.getNodeDescriptors().size());
+
+ // DataNodes are merged by copy .. so check they are not same as originals
+ DataNodeDescriptor mergedA = merged.getNodeDescriptor("A");
+ assertNotNull(mergedA);
+ assertNotSame(dn11, mergedA);
+
+ DataNodeDescriptor mergedB = merged.getNodeDescriptor("B");
+ assertNotNull(mergedB);
+ assertNotSame(dn12, mergedB);
+ assertNotSame(dn21, mergedB);
+ assertEquals("Uy", mergedB.getAdapterType());
+
+ DataNodeDescriptor mergedC = merged.getNodeDescriptor("C");
+ assertNotNull(mergedC);
+ assertNotSame(dn22, mergedC);
+ }
+
+ public void testMerge_DataNodesMapLinks() {
+ DataChannelDescriptor d1 = new DataChannelDescriptor();
+ d1.setName("Zx");
+ DataNodeDescriptor dn11 = new DataNodeDescriptor("A");
+ dn11.getDataMapNames().add("MA");
+ dn11.getDataMapNames().add("MB");
+ d1.getNodeDescriptors().add(dn11);
+
+ DataChannelDescriptor d2 = new DataChannelDescriptor();
+ d2.setName("Ym");
+ DataNodeDescriptor dn21 = new DataNodeDescriptor("A");
+ dn21.getDataMapNames().add("MA");
+ dn21.getDataMapNames().add("MC");
+ d2.getNodeDescriptors().add(dn21);
+
+ DefaultDataChannelDescriptorMerger merger = new DefaultDataChannelDescriptorMerger();
+
+ DataChannelDescriptor merged = merger.merge(d1, d2);
+
+ assertEquals(1, merged.getNodeDescriptors().size());
+
+ // DataNodes are merged by copy .. so check they are not same as originals
+ DataNodeDescriptor mergedA = merged.getNodeDescriptor("A");
+ assertNotNull(mergedA);
+ assertNotSame(dn11, mergedA);
+ assertNotSame(dn21, mergedA);
+ assertEquals(3, mergedA.getDataMapNames().size());
+ assertTrue(mergedA.getDataMapNames().contains("MA"));
+ assertTrue(mergedA.getDataMapNames().contains("MB"));
+ assertTrue(mergedA.getDataMapNames().contains("MC"));
+ }
+}