Author: rhauch
Date: 2008-08-26 13:21:32 -0400 (Tue, 26 Aug 2008)
New Revision: 470
Added:
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/SimpleMergeStrategy.java
trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/merge/SimpleMergeStrategyTest.java
Removed:
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/StandardMergeStrategy.java
Modified:
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/FederatingCommandExecutor.java
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/OneContributionMergeStrategy.java
trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/merge/OneContributionMergeStrategyTest.java
Log:
DNA-188 - Complete the federating command executor
http://jira.jboss.com/jira/browse/DNA-188
Renamed the StandardMergeStrategy to SimpleMergeStrategy, and fully implemented and tested
it to merge multiple contributions. This merge strategy may not be sufficient for many
uses, but it works now and does merge properties. Note that while it does not merge any
children, it does ensure that any same-name siblings (from all contributions) have the
proper indexes.
Modified:
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/FederatingCommandExecutor.java
===================================================================
---
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/FederatingCommandExecutor.java 2008-08-26
09:33:40 UTC (rev 469)
+++
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/FederatingCommandExecutor.java 2008-08-26
17:21:32 UTC (rev 470)
@@ -41,7 +41,7 @@
import org.jboss.dna.connector.federation.merge.MergePlan;
import org.jboss.dna.connector.federation.merge.MergeStrategy;
import org.jboss.dna.connector.federation.merge.OneContributionMergeStrategy;
-import org.jboss.dna.connector.federation.merge.StandardMergeStrategy;
+import org.jboss.dna.connector.federation.merge.SimpleMergeStrategy;
import org.jboss.dna.spi.DnaLexicon;
import org.jboss.dna.spi.ExecutionContext;
import org.jboss.dna.spi.cache.CachePolicy;
@@ -155,7 +155,7 @@
if (this.sourceProjections.size() == 1 &&
this.sourceProjections.get(0).isSimple()) {
this.mergingStrategy = new OneContributionMergeStrategy();
} else {
- this.mergingStrategy = new StandardMergeStrategy();
+ this.mergingStrategy = new SimpleMergeStrategy();
}
}
assert this.mergingStrategy != null;
Modified:
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/OneContributionMergeStrategy.java
===================================================================
---
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/OneContributionMergeStrategy.java 2008-08-26
09:33:40 UTC (rev 469)
+++
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/OneContributionMergeStrategy.java 2008-08-26
17:21:32 UTC (rev 470)
@@ -25,6 +25,7 @@
import java.util.List;
import java.util.Map;
import java.util.UUID;
+import net.jcip.annotations.ThreadSafe;
import org.jboss.dna.connector.federation.contribution.Contribution;
import org.jboss.dna.spi.ExecutionContext;
import org.jboss.dna.spi.graph.IoException;
@@ -34,11 +35,32 @@
import org.jboss.dna.spi.graph.Path.Segment;
/**
+ * A merge strategy that is optimized for merging when there is a single contribution.
+ *
* @author Randall Hauch
*/
+@ThreadSafe
public class OneContributionMergeStrategy implements MergeStrategy {
+ public static final boolean DEFAULT_REUSE_UUID_FROM_CONTRIBUTION = true;
+
+ private boolean useUuidFromContribution = DEFAULT_REUSE_UUID_FROM_CONTRIBUTION;
+
/**
+ * @return reuseUuidFromContribution
+ */
+ public boolean isContributionUuidUsedForFederatedNode() {
+ return useUuidFromContribution;
+ }
+
+ /**
+ * @param useUuidFromContribution Sets useUuidFromContribution to the specified
value.
+ */
+ public void setContributionUuidUsedForFederatedNode( boolean useUuidFromContribution
) {
+ this.useUuidFromContribution = useUuidFromContribution;
+ }
+
+ /**
* {@inheritDoc}
* <p>
* This method only uses the one and only one non-null {@link Contribution} in the
<code>contributions</code>.
@@ -51,10 +73,12 @@
List<Contribution> contributions,
ExecutionContext context ) {
assert federatedNode != null;
+ assert context != null;
assert contributions != null;
assert contributions.size() > 0;
Contribution contribution = contributions.get(0);
assert contribution != null;
+ final boolean findUuid = isContributionUuidUsedForFederatedNode();
// Copy the children ...
List<Segment> children = federatedNode.getChildren();
children.clear();
@@ -72,7 +96,7 @@
while (propertyIterator.hasNext()) {
Property property = propertyIterator.next();
properties.put(property.getName(), property);
- if (uuid == null &&
property.getName().getLocalName().equals("uuid") && property.isSingle())
{
+ if (findUuid && uuid == null &&
property.getName().getLocalName().equals("uuid") && property.isSingle())
{
if (uuidFactory == null) uuidFactory =
context.getValueFactories().getUuidFactory();
try {
uuid = uuidFactory.create(property.getValues().next());
Copied:
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/SimpleMergeStrategy.java
(from rev 460,
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/StandardMergeStrategy.java)
===================================================================
---
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/SimpleMergeStrategy.java
(rev 0)
+++
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/SimpleMergeStrategy.java 2008-08-26
17:21:32 UTC (rev 470)
@@ -0,0 +1,275 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.federation.merge;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.UUID;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.connector.federation.contribution.Contribution;
+import org.jboss.dna.spi.ExecutionContext;
+import org.jboss.dna.spi.graph.IoException;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.Path;
+import org.jboss.dna.spi.graph.PathFactory;
+import org.jboss.dna.spi.graph.Property;
+import org.jboss.dna.spi.graph.PropertyFactory;
+import org.jboss.dna.spi.graph.UuidFactory;
+import org.jboss.dna.spi.graph.ValueComparators;
+import org.jboss.dna.spi.graph.Path.Segment;
+
+/**
+ * This merge strategy simply merges all of the contributions' properties and
combines the children according to the order of the
+ * contributions. No children are merged, and all properties are used (except if they are
deemed to be duplicates of the property
+ * in other contributions).
+ *
+ * @author Randall Hauch
+ */
+@ThreadSafe
+public class SimpleMergeStrategy implements MergeStrategy {
+
+ private boolean removeDuplicateProperties = true;
+
+ /**
+ * @return removeDuplicateProperties
+ */
+ public boolean isRemoveDuplicateProperties() {
+ return removeDuplicateProperties;
+ }
+
+ /**
+ * @param removeDuplicateProperties Sets removeDuplicateProperties to the specified
value.
+ */
+ public void setRemoveDuplicateProperties( boolean removeDuplicateProperties ) {
+ this.removeDuplicateProperties = removeDuplicateProperties;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.connector.federation.merge.MergeStrategy#merge(org.jboss.dna.connector.federation.merge.FederatedNode,
+ * java.util.List, org.jboss.dna.spi.ExecutionContext)
+ */
+ public void merge( FederatedNode federatedNode,
+ List<Contribution> contributions,
+ ExecutionContext context ) {
+ assert federatedNode != null;
+ assert context != null;
+ assert contributions != null;
+ assert contributions.size() > 0;
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ // Prepare the federated node ...
+ List<Segment> children = federatedNode.getChildren();
+ children.clear();
+ Map<Name, Integer> childNames = new HashMap<Name, Integer>();
+ Map<Name, Property> properties = federatedNode.getPropertiesByName();
+ properties.clear();
+ UUID uuid = null;
+ UuidFactory uuidFactory = null;
+ final boolean removeDuplicateProperties = isRemoveDuplicateProperties();
+ // Iterate over the set of contributions (in order) ...
+ for (Contribution contribution : contributions) {
+ // Copy the children ...
+ int childListIndex = 0;
+ Iterator<Segment> childIterator = contribution.getChildren();
+ while (childIterator.hasNext()) {
+ Segment child = childIterator.next();
+ int index = Path.NO_INDEX;
+ Integer previous = childNames.put(child.getName(), 1);
+ if (previous != null) {
+ int previousValue = previous.intValue();
+ if (previousValue == 1) {
+ // If the previous value was 1, then go back and look for the
child that has no index but that should ...
+ // Walk backwards, just in case the previous same-name-sibling is
closer to the end
+ for (int i = children.size() - 1; i >= 0; --i) {
+ Path.Segment childSegment = children.get(i);
+ if (childSegment.getName().equals(child.getName())) {
+ children.set(i,
pathFactory.createSegment(child.getName(), 1));
+ break;
+ }
+ }
+ }
+ // Add the previous value back in ...
+ childNames.put(child.getName(), ++previousValue);
+ index = previousValue;
+ }
+ children.add(pathFactory.createSegment(child.getName(), index));
+ ++childListIndex;
+ }
+
+ // Copy the properties ...
+ Iterator<Property> propertyIterator = contribution.getProperties();
+ while (propertyIterator.hasNext()) {
+ Property property = propertyIterator.next();
+ Property existing = properties.put(property.getName(), property);
+ if (existing != null) {
+ // There's already an existing property, so we need to merge them
...
+ Property merged = merge(existing, property,
context.getPropertyFactory(), removeDuplicateProperties);
+ properties.put(property.getName(), merged);
+ }
+
+ if (uuid == null &&
property.getName().getLocalName().equals("uuid") && property.isSingle())
{
+ if (uuidFactory == null) uuidFactory =
context.getValueFactories().getUuidFactory();
+ try {
+ uuid = uuidFactory.create(property.getValues().next());
+ } catch (IoException e) {
+ // Ignore conversion exceptions
+ assert uuid == null;
+ }
+ }
+ }
+ }
+ // If we found a single "uuid" property whose value is a valid UUID ..
+ if (uuid != null) {
+ // then set the UUID on the federated node ...
+ federatedNode.setUuid(uuid);
+ }
+ // Assign the merge plan ...
+ MergePlan mergePlan = MergePlan.create(contributions);
+ federatedNode.setMergePlan(mergePlan);
+ }
+
+ /**
+ * Merge the values from the two properties with the same name, returning a new
property with the newly merged values.
+ * <p>
+ * The current algorithm merges the values by concatenating the values from
<code>property1</code> and <code>property2</code>,
+ * and if <code>removeDuplicates</code> is true any values in
<code>property2</code> that are identical to values found in
+ * <code>property1</code> are skipped.
+ * </p>
+ *
+ * @param property1 the first property; may not be null, and must have the same
{@link Property#getName() name} as
+ * <code>property2</code>
+ * @param property2 the second property; may not be null, and must have the same
{@link Property#getName() name} as
+ * <code>property1</code>
+ * @param factory the property factory, used to create the result
+ * @param removeDuplicates true if this method removes any values in the second
property that duplicate values found in the
+ * first property.
+ * @return the property that contains the same {@link Property#getName() name} as the
input properties, but with values that
+ * are merged from both of the input properties
+ */
+ protected Property merge( Property property1,
+ Property property2,
+ PropertyFactory factory,
+ boolean removeDuplicates ) {
+ assert property1 != null;
+ assert property2 != null;
+ assert property1.getName().equals(property2.getName());
+ if (property1.isEmpty()) return property2;
+ if (property2.isEmpty()) return property1;
+
+ // If they are both single-valued, then we can use a more efficient algorithm
...
+ if (property1.isSingle() && property2.isSingle()) {
+ Object value1 = property1.getValues().next();
+ Object value2 = property2.getValues().next();
+ if (removeDuplicates &&
ValueComparators.OBJECT_COMPARATOR.compare(value1, value2) == 0) return property1;
+ return factory.create(property1.getName(), new Object[] {value1, value2});
+ }
+
+ // One or both properties are multi-valued, so use an algorithm that works with
in all cases ...
+ if (!removeDuplicates) {
+ Iterator<?> valueIterator = new DualIterator(property1.getValues(),
property2.getValues());
+ return factory.create(property1.getName(), valueIterator);
+ }
+
+ // First copy all the values from property 1 ...
+ Object[] values = new Object[property1.size() + property2.size()];
+ int index = 0;
+ for (Object property1Value : property1) {
+ values[index++] = property1Value;
+ }
+ assert index == property1.size();
+ // Now add any values of property2 that don't match a value in property1 ...
+ for (Object property2Value : property2) {
+ // Brute force, go through the values of property1 and compare ...
+ boolean matched = false;
+ for (Object property1Value : property1) {
+ if (ValueComparators.OBJECT_COMPARATOR.compare(property1Value,
property2Value) == 0) {
+ // The values are the same ...
+ matched = true;
+ break;
+ }
+ }
+ if (!matched) values[index++] = property2Value;
+ }
+ if (index != values.length) {
+ Object[] newValues = new Object[index];
+ System.arraycopy(values, 0, newValues, 0, index);
+ values = newValues;
+ }
+ return factory.create(property1.getName(), values);
+ }
+
+ protected static class DualIterator implements Iterator<Object> {
+
+ private final Iterator<?>[] iterators;
+ private Iterator<?> current;
+ private int index = 0;
+
+ protected DualIterator( Iterator<?>... iterators ) {
+ assert iterators != null;
+ assert iterators.length > 0;
+ this.iterators = iterators;
+ this.current = this.iterators[0];
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.Iterator#hasNext()
+ */
+ public boolean hasNext() {
+ if (this.current != null) return this.current.hasNext();
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.Iterator#next()
+ */
+ public Object next() {
+ while (this.current != null) {
+ if (this.current.hasNext()) return this.current.next();
+ // Get the next iterator ...
+ if (++this.index < iterators.length) {
+ this.current = this.iterators[this.index];
+ } else {
+ this.current = null;
+ }
+ }
+ throw new NoSuchElementException();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.Iterator#remove()
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+}
Property changes on:
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/SimpleMergeStrategy.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Deleted:
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/StandardMergeStrategy.java
===================================================================
---
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/StandardMergeStrategy.java 2008-08-26
09:33:40 UTC (rev 469)
+++
trunk/extensions/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/StandardMergeStrategy.java 2008-08-26
17:21:32 UTC (rev 470)
@@ -1,53 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2008, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
- */
-package org.jboss.dna.connector.federation.merge;
-
-import java.util.List;
-import org.jboss.dna.connector.federation.contribution.Contribution;
-import org.jboss.dna.spi.ExecutionContext;
-
-/**
- * @author Randall Hauch
- */
-public class StandardMergeStrategy implements MergeStrategy {
-
- /**
- * {@inheritDoc}
- *
- * @see
org.jboss.dna.connector.federation.merge.MergeStrategy#merge(org.jboss.dna.connector.federation.merge.FederatedNode,
- * java.util.List, org.jboss.dna.spi.ExecutionContext)
- */
- public void merge( FederatedNode federatedNode,
- List<Contribution> contributions,
- ExecutionContext context ) {
-
- // Children whose identity properties are the same will be considered to be the
same node ...
-
- // Assemble the children ...
-
- // Assemble the properties (and look for UUID property) ...
-
- // Create a merge plan with the contributions ...
- MergePlan plan = MergePlan.create(contributions);
- federatedNode.setMergePlan(plan);
- }
-}
Modified:
trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/merge/OneContributionMergeStrategyTest.java
===================================================================
---
trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/merge/OneContributionMergeStrategyTest.java 2008-08-26
09:33:40 UTC (rev 469)
+++
trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/merge/OneContributionMergeStrategyTest.java 2008-08-26
17:21:32 UTC (rev 470)
@@ -82,7 +82,7 @@
}
@Test
- public void shouldMegeTheChildrenFromTheFirstContribution() {
+ public void shouldMergeTheChildrenFromTheFirstContribution() {
stub(contribution.getChildren()).toReturn(children.iterator());
stub(contribution.getProperties()).toReturn(properties.values().iterator());
strategy.merge(node, contributions, context);
@@ -90,7 +90,7 @@
}
@Test
- public void shouldMegeThePropertiesFromTheFirstContribution() {
+ public void shouldMergeThePropertiesFromTheFirstContribution() {
stub(contribution.getChildren()).toReturn(children.iterator());
stub(contribution.getProperties()).toReturn(properties.values().iterator());
stub(contribution.getProperties()).toReturn(properties.values().iterator());
Added:
trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/merge/SimpleMergeStrategyTest.java
===================================================================
---
trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/merge/SimpleMergeStrategyTest.java
(rev 0)
+++
trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/merge/SimpleMergeStrategyTest.java 2008-08-26
17:21:32 UTC (rev 470)
@@ -0,0 +1,304 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.connector.federation.merge;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import org.hamcrest.Matcher;
+import org.jboss.dna.common.collection.IsIteratorContaining;
+import org.jboss.dna.connector.federation.contribution.Contribution;
+import org.jboss.dna.spi.ExecutionContext;
+import org.jboss.dna.spi.connector.BasicExecutionContext;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.Path;
+import org.jboss.dna.spi.graph.Property;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * @author Randall Hauch
+ */
+public class SimpleMergeStrategyTest {
+
+ private SimpleMergeStrategy strategy;
+ private List<Contribution> contributions;
+ private ExecutionContext context;
+ private FederatedNode node;
+
+ @Before
+ public void beforeEach() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ Path path = mock(Path.class);
+ node = new FederatedNode(path, UUID.randomUUID());
+ strategy = new SimpleMergeStrategy();
+ contributions = new LinkedList<Contribution>();
+ context = new BasicExecutionContext();
+ context.getNamespaceRegistry().register("dna",
"http://www.jboss.org/dna/something");
+ context.getNamespaceRegistry().register("jcr",
"http://www.jcr.org");
+ }
+
+ @Test
+ public void shouldAddChildrenFromOneContribution() {
+ addContribution("source1").addChildren("childA",
"childB[1]", "childB[2]");
+ strategy.merge(node, contributions, context);
+ assertThat(node.getChildren(), hasSegments("childA",
"childB[1]", "childB[2]"));
+ }
+
+ @Test
+ public void shouldCombinePropertiesFromOneContribution() {
+ addContribution("source1").setProperty("p1", "p1
value");
+ strategy.merge(node, contributions, context);
+ assertThat(node.getProperties().size(), is(1));
+ assertThat(node.getPropertiesByName().get(name("p1")),
is(property("p1", "p1 value")));
+ }
+
+ @Test
+ public void
shouldAddChildrenFromMultipleContributionsInOrderAndShouldNotChangeSameNameSiblingIndexesWhenChildrenDoNotShareNamesWithChildrenFromDifferentContributions()
{
+ addContribution("source1").addChildren("childA",
"childB[1]", "childB[2]");
+ addContribution("source2").addChildren("childX",
"childY[1]", "childY[2]");
+ strategy.merge(node, contributions, context);
+ assertThat(node.getChildren(), hasSegments("childA",
"childB[1]", "childB[2]", "childX", "childY[1]",
"childY[2]"));
+ }
+
+ @Test
+ public void
shouldAddChildrenFromMultipleContributionsInOrderAndShouldChangeSameNameSiblingIndexesWhenChildrenDoShareNamesWithChildrenFromDifferentContributions()
{
+ addContribution("source1").addChildren("childA",
"childB[1]", "childB[2]");
+ addContribution("source2").addChildren("childX",
"childB", "childY");
+ addContribution("source3").addChildren("childX",
"childB");
+ strategy.merge(node, contributions, context);
+ assertThat(node.getChildren(), hasSegments("childA",
+ "childB[1]",
+ "childB[2]",
+ "childX[1]",
+ "childB[3]",
+ "childY",
+ "childX[2]",
+ "childB[4]"));
+ }
+
+ @Test
+ public void
shouldCombinePropertiesFromMultipleContributionsAndRemoveNoValuesWhenNoContributionsContainSameProperty()
{
+ addContribution("source1").setProperty("p1", "p1
value");
+ addContribution("source2").setProperty("p2", "p2
value");
+ strategy.merge(node, contributions, context);
+ assertThat(node.getProperties().size(), is(2));
+ assertThat(node.getPropertiesByName().get(name("p1")),
is(property("p1", "p1 value")));
+ assertThat(node.getPropertiesByName().get(name("p2")),
is(property("p2", "p2 value")));
+ }
+
+ @Test
+ public void
shouldCombinePropertiesFromMultipleContributionsAndRemoveDuplicateValuesWhenContributionsContainSameProperty()
{
+ addContribution("source1").setProperty("p1", "p1
value").setProperty("p12", "1", "2", "3");
+ addContribution("source2").setProperty("p2", "p2
value").setProperty("p12", "3", "4");
+ strategy.merge(node, contributions, context);
+ assertThat(node.getProperties().size(), is(3));
+ assertThat(node.getPropertiesByName().get(name("p1")),
is(property("p1", "p1 value")));
+ assertThat(node.getPropertiesByName().get(name("p2")),
is(property("p2", "p2 value")));
+ assertThat(node.getPropertiesByName().get(name("p12")),
is(property("p12", "1", "2", "3",
"4")));
+ }
+
+ @Test
+ public void
shouldCombinePropertiesFromMultipleContributionsAndMaintainAllValuesForEveryProperty() {
+ addContribution("source1").setProperty("p1", "p1
value").setProperty("p12", "1", "2", "3");
+ addContribution("source2").setProperty("p2", "p2
value").setProperty("p12", "3", "4");
+ strategy.merge(node, contributions, context);
+ assertThat(node.getProperties().size(), is(3));
+ for (Contribution contribution : contributions) {
+ Iterator<Property> iter = contribution.getProperties();
+ while (iter.hasNext()) {
+ Property contributionProperty = iter.next();
+ Property mergedProperty =
node.getPropertiesByName().get(contributionProperty.getName());
+ assertThat(mergedProperty, is(notNullValue()));
+ // Make sure that the merged property has each value ...
+ for (Object contributedValue : contributionProperty.getValuesAsArray())
{
+ boolean foundValue = false;
+ for (Object mergedValue : mergedProperty.getValuesAsArray()) {
+ if (mergedValue.equals(contributedValue)) {
+ foundValue = true;
+ break;
+ }
+ }
+ assertThat(foundValue, is(true));
+ }
+ }
+ }
+ }
+
+ @Test
+ public void
shouldCombinePropertiesFromMultipleContributionsWhenPropertyValuesAreOfDifferentPropertyTypes()
{
+ addContribution("source1").setProperty("p1", "p1
value").setProperty("p12", "1", "2", "3");
+ addContribution("source2").setProperty("p2", "p2
value").setProperty("p12", 3, 4);
+ strategy.merge(node, contributions, context);
+ assertThat(node.getProperties().size(), is(3));
+ assertThat(node.getPropertiesByName().get(name("p1")),
is(property("p1", "p1 value")));
+ assertThat(node.getPropertiesByName().get(name("p2")),
is(property("p2", "p2 value")));
+ assertThat(node.getPropertiesByName().get(name("p12")),
is(property("p12", "1", "2", "3", 4)));
+ }
+
+ @Test
+ public void shouldCreateMergePlanWhenMergingContributions() {
+ addContribution("source1").addChildren("childA",
"childB[1]", "childB[2]").setProperty("p1", "p1
value");
+ addContribution("source2").addChildren("childX",
"childB", "childY").setProperty("p2", "p2
value");
+ strategy.merge(node, contributions, context);
+ assertThat(node.getMergePlan(), is(notNullValue()));
+ assertThat(node.getMergePlan().getContributionCount(), is(2));
+ assertThat(node.getMergePlan().getContributionFrom("source1"),
is(sameInstance(contributions.get(0))));
+ assertThat(node.getMergePlan().getContributionFrom("source2"),
is(sameInstance(contributions.get(1))));
+ }
+
+ @Test
+ public void shouldCorrectlyBuildMockUsingContributionBuilder() {
+ assertThat(contributions.size(), is(0));
+ addContribution("source1").addChildren("childA",
"childB[1]", "childB[2]").setProperty("p1", "p1
value");
+ assertThat(contributions.size(), is(1));
+ assertThat(contributions.get(0).getChildren(),
hasSegmentIterator("childA", "childB[1]", "childB[2]"));
+ }
+
+ @Ignore
+ @Test
+ public void shouldCreateMergePlanInTheFederatedNode() {
+ strategy.merge(node, contributions, context);
+ }
+
+ protected Matcher<List<Path.Segment>> hasSegments( String... segment ) {
+ List<Path.Segment> segments = new ArrayList<Path.Segment>();
+ for (String seg : segment) {
+
segments.add(context.getValueFactories().getPathFactory().createSegment(seg));
+ }
+ return equalTo(segments);
+ }
+
+ protected Matcher<Iterator<Path.Segment>> hasSegmentIterator( String...
segment ) {
+ Path.Segment[] segments = new Path.Segment[segment.length];
+ int index = 0;
+ for (String seg : segment) {
+ segments[index++] =
context.getValueFactories().getPathFactory().createSegment(seg);
+ }
+ return IsIteratorContaining.hasItems(segments);
+ }
+
+ protected Name name( String name ) {
+ return context.getValueFactories().getNameFactory().create(name);
+ }
+
+ protected Property property( String name,
+ Object... values ) {
+ return context.getPropertyFactory().create(name(name), values);
+ }
+
+ protected ContributionBuilder addContribution( String sourceName ) {
+ ContributionBuilder builder = new ContributionBuilder(context, sourceName,
contributions);
+ contributions.add(builder.getMock());
+ return builder;
+ }
+
+ protected class ContributionBuilder {
+ protected final Contribution mockContribution;
+ protected final ExecutionContext context;
+ protected final Map<Name, Property> properties = new HashMap<Name,
Property>();
+ protected final List<Path.Segment> children = new
ArrayList<Path.Segment>();
+
+ protected ContributionBuilder( ExecutionContext context,
+ String name,
+ List<Contribution> contributions ) {
+ this.context = context;
+ this.mockContribution = Mockito.mock(Contribution.class);
+ stub(mockContribution.getSourceName()).toReturn(name);
+ stub(mockContribution.getChildren()).toAnswer(new
Answer<Iterator<Path.Segment>>() {
+ public Iterator<Path.Segment> answer( InvocationOnMock invocation )
throws Throwable {
+ return ContributionBuilder.this.children.iterator();
+ }
+ });
+ stub(mockContribution.getChildrenCount()).toAnswer(new
Answer<Integer>() {
+ public Integer answer( InvocationOnMock invocation ) throws Throwable {
+ return ContributionBuilder.this.children.size();
+ }
+ });
+ stub(mockContribution.getProperties()).toAnswer(new
Answer<Iterator<Property>>() {
+ public Iterator<Property> answer( InvocationOnMock invocation )
throws Throwable {
+ return ContributionBuilder.this.properties.values().iterator();
+ }
+ });
+ stub(mockContribution.getPropertyCount()).toAnswer(new
Answer<Integer>() {
+ public Integer answer( InvocationOnMock invocation ) throws Throwable {
+ return ContributionBuilder.this.properties.size();
+ }
+ });
+ }
+
+ public Contribution getMock() {
+ return this.mockContribution;
+ }
+
+ public ContributionBuilder addChildren( String... segmentNamesForChildren ) {
+ for (String childSegmentName : segmentNamesForChildren) {
+
children.add(context.getValueFactories().getPathFactory().createSegment(childSegmentName));
+ }
+ return this;
+ }
+
+ public ContributionBuilder addChildren( Name... segmentNamesForChildren ) {
+ for (Name childSegmentName : segmentNamesForChildren) {
+
children.add(context.getValueFactories().getPathFactory().createSegment(childSegmentName));
+ }
+ return this;
+ }
+
+ public ContributionBuilder addChildren( Path.Segment... segmentNamesForChildren )
{
+ for (Path.Segment childSegmentName : segmentNamesForChildren) {
+ children.add(childSegmentName);
+ }
+ return this;
+ }
+
+ public ContributionBuilder setProperty( String name,
+ Object... values ) {
+ Name propertyName =
context.getValueFactories().getNameFactory().create(name);
+ Property property = context.getPropertyFactory().create(propertyName,
values);
+ stub(mockContribution.getProperty(propertyName)).toReturn(property);
+ if (values != null && values.length > 0) {
+ properties.put(propertyName, property);
+ } else {
+ properties.remove(propertyName);
+ }
+ return this;
+ }
+ }
+
+}
Property changes on:
trunk/extensions/dna-connector-federation/src/test/java/org/jboss/dna/connector/federation/merge/SimpleMergeStrategyTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain