[dna-commits] DNA SVN: r375 - in trunk: connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor and 8 other directories.

dna-commits at lists.jboss.org dna-commits at lists.jboss.org
Mon Jul 28 17:51:05 EDT 2008


Author: rhauch
Date: 2008-07-28 17:51:05 -0400 (Mon, 28 Jul 2008)
New Revision: 375

Added:
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/MultiChildContribution.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/MultiPropertyContribution.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/NonEmptyContribution.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/OneChildContribution.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/OnePropertyContribution.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/ThreePropertyContribution.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/TwoPropertyContribution.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/FiveContributionMergePlan.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/FourContributionMergePlan.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/OneContributionMergePlan.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/ThreeContributionMergePlan.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/TwoContributionMergePlan.java
Removed:
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/AbstractContribution.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/PropertyContribution.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/BasicMergePlan.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/DoubleContributionMergePlan.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/SingleContributionMergePlan.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/TripleContributionMergePlan.java
Modified:
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/Contribution.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/EmptyContribution.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/NodeContribution.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/ActsOnProjectedPathCommand.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/FederatingCommandExecutor.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/MergePlan.java
   trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/MultipleContributionMergePlan.java
   trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepository.java
   trunk/connectors/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheConnection.java
   trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/Path.java
   trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/PathNotFoundException.java
   trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/commands/NodeConflictBehavior.java
   trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/commands/impl/BasicCreateNodeCommand.java
   trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicPath.java
   trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/impl/BasicPathTest.java
Log:
More work on the federated connector.

Deleted: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/AbstractContribution.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/AbstractContribution.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/AbstractContribution.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -1,132 +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.contribution;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import org.jboss.dna.spi.graph.DateTime;
-import org.jboss.dna.spi.graph.Name;
-import org.jboss.dna.spi.graph.Property;
-import org.jboss.dna.spi.graph.Path.Segment;
-
-/**
- * @author Randall Hauch
- */
-public abstract class AbstractContribution implements Contribution {
-
-    /**
-     * This is the first version of this class. See the documentation of BasicMergePlan.serialVersionUID.
-     */
-    private static final long serialVersionUID = 1L;
-
-    protected static final List<Segment> EMPTY_CHILDREN = Collections.emptyList();
-    protected static final Map<Name, Property> EMPTY_PROPERTIES = Collections.emptyMap();
-
-    private final String sourceName;
-    private DateTime expirationTimeInUtc;
-
-    /**
-     * Create a contribution for the source with the supplied name.
-     * 
-     * @param sourceName the name of the source, which may not be null or blank
-     */
-    protected AbstractContribution( String sourceName ) {
-        assert sourceName != null && sourceName.trim().length() != 0;
-        this.sourceName = sourceName;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public String getSourceName() {
-        return this.sourceName;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public List<Segment> getChildren() {
-        return EMPTY_CHILDREN;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Map<Name, Property> getProperties() {
-        return EMPTY_PROPERTIES;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.connector.federation.contribution.Contribution#isExpired(org.jboss.dna.spi.graph.DateTime)
-     */
-    public boolean isExpired( DateTime utcTime ) {
-        assert utcTime != null;
-        assert utcTime.toUtcTimeZone().equals(utcTime); // check that it is passed UTC time
-        return !expirationTimeInUtc.isAfter(utcTime);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public DateTime getExpirationTimeInUtc() {
-        return this.expirationTimeInUtc;
-    }
-
-    /**
-     * @param expirationTimeInUtc Sets expirationTimeInUtc to the specified value.
-     */
-    public void setExpirationTimeInUtc( DateTime expirationTimeInUtc ) {
-        this.expirationTimeInUtc = expirationTimeInUtc;
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * This implementation returns the hash code of the {@link #getSourceName() source name}, and is compatible with the
-     * implementation of {@link #equals(Object)}.
-     * </p>
-     */
-    @Override
-    public int hashCode() {
-        return this.sourceName.hashCode();
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * This implementation only compares the {@link #getSourceName() source name}.
-     * </p>
-     */
-    @Override
-    public boolean equals( Object obj ) {
-        if (obj == this) return true;
-        if (obj instanceof AbstractContribution) {
-            AbstractContribution that = (AbstractContribution)obj;
-            return this.getSourceName().equals(that.getSourceName());
-        }
-        return false;
-    }
-
-}

Modified: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/Contribution.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/Contribution.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/Contribution.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -22,9 +22,10 @@
 package org.jboss.dna.connector.federation.contribution;
 
 import java.io.Serializable;
-import java.util.List;
-import java.util.Map;
-import net.jcip.annotations.NotThreadSafe;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import net.jcip.annotations.Immutable;
 import org.jboss.dna.spi.graph.DateTime;
 import org.jboss.dna.spi.graph.Name;
 import org.jboss.dna.spi.graph.Path;
@@ -39,22 +40,131 @@
  * 
  * @author Randall Hauch
  */
- at NotThreadSafe
-public interface Contribution extends Serializable {
+ at Immutable
+public abstract class Contribution implements Serializable {
 
     /**
+     * Create an empty contribution from the named source.
+     * 
+     * @param sourceName the name of the source, which may not be null or blank
+     * @return the contribution
+     */
+    public static Contribution create( String sourceName ) {
+        return new EmptyContribution(sourceName);
+    }
+
+    /**
+     * Create a contribution of a single property from the named source.
+     * 
+     * @param sourceName the name of the source, which may not be null or blank
+     * @param pathInSource the path in the source for this contributed information; may not be null
+     * @param property the property from the source; may not be null
+     * @return the contribution
+     */
+    public static Contribution create( String sourceName,
+                                       Path pathInSource,
+                                       Property property ) {
+        if (property == null) {
+            return new EmptyContribution(sourceName);
+        }
+        return new OnePropertyContribution(sourceName, pathInSource, property);
+    }
+
+    /**
+     * Create a contribution of a single child from the named source.
+     * 
+     * @param sourceName the name of the source, which may not be null or blank
+     * @param pathInSource the path in the source for this contributed information; may not be null
+     * @param child the child from the source; may not be null or empty
+     * @return the contribution
+     */
+    public static Contribution create( String sourceName,
+                                       Path pathInSource,
+                                       Segment child ) {
+        if (child == null) {
+            return new EmptyContribution(sourceName);
+        }
+        return new OneChildContribution(sourceName, pathInSource, child);
+    }
+
+    /**
+     * Create a contribution of the supplied properties and children from the named source.
+     * 
+     * @param sourceName the name of the source, which may not be null or blank
+     * @param pathInSource the path in the source for this contributed information; may not be null
+     * @param properties the properties from the source; may not be null
+     * @param children the children from the source; may not be null or empty
+     * @return the contribution
+     */
+    public static Contribution create( String sourceName,
+                                       Path pathInSource,
+                                       Collection<Property> properties,
+                                       Collection<Segment> children ) {
+        if (properties == null || properties.isEmpty()) {
+            // There are no properties ...
+            if (children == null || children.isEmpty()) {
+                return new EmptyContribution(sourceName);
+            }
+            if (children.size() == 1) {
+                return new OneChildContribution(sourceName, pathInSource, children.iterator().next());
+            }
+            return new MultiChildContribution(sourceName, pathInSource, children);
+        }
+        // There are some properties ...
+        if (children == null || children.isEmpty()) {
+            // There are no children ...
+            switch (properties.size()) {
+                case 1:
+                    return new OnePropertyContribution(sourceName, pathInSource, properties.iterator().next());
+                case 2:
+                    Iterator<Property> iter = properties.iterator();
+                    return new TwoPropertyContribution(sourceName, pathInSource, iter.next(), iter.next());
+                case 3:
+                    Iterator<Property> iter3 = properties.iterator();
+                    return new ThreePropertyContribution(sourceName, pathInSource, iter3.next(), iter3.next(), iter3.next());
+            }
+            return new MultiPropertyContribution(sourceName, pathInSource, properties);
+        }
+        // There are some properties AND some children ...
+        return new NodeContribution(sourceName, pathInSource, properties, children);
+    }
+
+    /**
+     * This is the first version of this class. See the documentation of BasicMergePlan.serialVersionUID.
+     */
+    private static final long serialVersionUID = 1L;
+
+    protected static final Iterator<Property> EMPTY_PROPERTY_ITERATOR = new EmptyIterator<Property>();
+    protected static final Iterator<Segment> EMPTY_CHILDREN_ITERATOR = new EmptyIterator<Segment>();
+
+    private final String sourceName;
+    private DateTime expirationTimeInUtc;
+
+    /**
+     * Create a contribution for the source with the supplied name and path.
+     * 
+     * @param sourceName the name of the source, which may not be null or blank
+     */
+    protected Contribution( String sourceName ) {
+        assert sourceName != null && sourceName.trim().length() != 0;
+        this.sourceName = sourceName;
+    }
+
+    /**
      * Get the name of the source that made this contribution.
      * 
      * @return the name of the contributing source
      */
-    public String getSourceName();
+    public String getSourceName() {
+        return this.sourceName;
+    }
 
     /**
      * Get the source-specific path of this information.
      * 
      * @return the path as known to the source, or null for {@link EmptyContribution}
      */
-    public Path getPathInSource();
+    public abstract Path getPathInSource();
 
     /**
      * Determine whether this contribution has expired given the supplied current time.
@@ -62,26 +172,307 @@
      * @param utcTime the current time expressed in UTC; may not be null
      * @return true if at least one contribution has expired, or false otherwise
      */
-    public boolean isExpired( DateTime utcTime );
+    public boolean isExpired( DateTime utcTime ) {
+        assert utcTime != null;
+        assert utcTime.toUtcTimeZone().equals(utcTime); // check that it is passed UTC time
+        return !expirationTimeInUtc.isAfter(utcTime);
+    }
 
     /**
      * Get the expiration time, already in UTC.
      * 
      * @return the expiration time in UTC
      */
-    public DateTime getExpirationTimeInUtc();
+    public DateTime getExpirationTimeInUtc() {
+        return this.expirationTimeInUtc;
+    }
 
     /**
-     * Get the properties that make up this contribution. This map is immutable.
+     * Get the properties that are in this contribution. This resulting iterator does not support {@link Iterator#remove()
+     * removal}.
      * 
-     * @return the map of properties; never null
+     * @return the properties; never null
      */
-    public Map<Name, Property> getProperties();
+    public Iterator<Property> getProperties() {
+        return EMPTY_PROPERTY_ITERATOR;
+    }
 
     /**
-     * Get the children that make up this contribution. This list is immutable.
+     * Get the number of properties that are in this contribution.
      * 
-     * @return the list of children; never null
+     * @return the number of properties
      */
-    public List<Segment> getChildren();
+    public int getPropertyCount() {
+        return 0;
+    }
+
+    /**
+     * Get the contributed property with the supplied name.
+     * 
+     * @param name the name of the property
+     * @return the contributed property that matches the name, or null if no such property is in the contribution
+     */
+    public Property getProperty( Name name ) {
+        return null;
+    }
+
+    /**
+     * Get the children that make up this contribution. This resulting iterator does not support {@link Iterator#remove() removal}
+     * .
+     * 
+     * @return the children; never null
+     */
+    public Iterator<Segment> getChildren() {
+        return EMPTY_CHILDREN_ITERATOR;
+    }
+
+    /**
+     * Get the number of children that make up this contribution.
+     * 
+     * @return the number of children
+     */
+    public int getChildrenCount() {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * This implementation returns the hash code of the {@link #getSourceName() source name}, and is compatible with the
+     * implementation of {@link #equals(Object)}.
+     * </p>
+     */
+    @Override
+    public int hashCode() {
+        return this.sourceName.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * This implementation only compares the {@link #getSourceName() source name}.
+     * </p>
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof Contribution) {
+            Contribution that = (Contribution)obj;
+            if (!this.getSourceName().equals(that.getSourceName())) return false;
+            return true;
+        }
+        return false;
+    }
+
+    protected static class ImmutableIterator<T> implements Iterator<T> {
+        private final Iterator<T> iter;
+
+        protected ImmutableIterator( Iterator<T> iter ) {
+            this.iter = iter;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#hasNext()
+         */
+        public boolean hasNext() {
+            return iter.hasNext();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#next()
+         */
+        public T next() {
+            return iter.next();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#remove()
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    protected static class EmptyIterator<T> implements Iterator<T> {
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#hasNext()
+         */
+        public boolean hasNext() {
+            return false;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#next()
+         */
+        public T next() {
+            throw new NoSuchElementException();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#remove()
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+    }
+
+    protected static class OneValueIterator<T> implements Iterator<T> {
+
+        private final T value;
+        private boolean next = true;
+
+        protected OneValueIterator( T value ) {
+            this.value = value;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#hasNext()
+         */
+        public boolean hasNext() {
+            return next;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#next()
+         */
+        public T next() {
+            if (!next) {
+                next = false;
+                return value;
+            }
+            throw new NoSuchElementException();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#remove()
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+    }
+
+    protected static class TwoValueIterator<T> implements Iterator<T> {
+
+        private final T value1;
+        private final T value2;
+        private int next = 2;
+
+        protected TwoValueIterator( T value1,
+                                    T value2 ) {
+            this.value1 = value1;
+            this.value2 = value2;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#hasNext()
+         */
+        public boolean hasNext() {
+            return next > 0;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#next()
+         */
+        public T next() {
+            if (next == 2) {
+                next = 1;
+                return value1;
+            }
+            if (next == 1) {
+                next = 0;
+                return value2;
+            }
+            throw new NoSuchElementException();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#remove()
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    protected static class ThreeValueIterator<T> implements Iterator<T> {
+
+        private final T value1;
+        private final T value2;
+        private final T value3;
+        private int next = 3;
+
+        protected ThreeValueIterator( T value1,
+                                      T value2,
+                                      T value3 ) {
+            this.value1 = value1;
+            this.value2 = value2;
+            this.value3 = value3;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#hasNext()
+         */
+        public boolean hasNext() {
+            return next > 0;
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#next()
+         */
+        public T next() {
+            if (next == 3) {
+                next = 2;
+                return value1;
+            }
+            if (next == 2) {
+                next = 1;
+                return value2;
+            }
+            if (next == 1) {
+                next = 0;
+                return value3;
+            }
+            throw new NoSuchElementException();
+        }
+
+        /**
+         * {@inheritDoc}
+         * 
+         * @see java.util.Iterator#remove()
+         */
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+    }
 }

Modified: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/EmptyContribution.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/EmptyContribution.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/EmptyContribution.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -21,6 +21,7 @@
  */
 package org.jboss.dna.connector.federation.contribution;
 
+import net.jcip.annotations.Immutable;
 import org.jboss.dna.spi.graph.Path;
 
 /**
@@ -33,10 +34,11 @@
  * 
  * @author Randall Hauch
  */
-public class EmptyContribution extends AbstractContribution {
+ at Immutable
+public class EmptyContribution extends Contribution {
 
     /**
-     * This is the first version of this class. See the documentation of BasicMergePlan.serialVersionUID.
+     * This is the first version of this class. See the documentation of MergePlan.serialVersionUID.
      */
     private static final long serialVersionUID = 1L;
 
@@ -54,8 +56,22 @@
      * 
      * @see org.jboss.dna.connector.federation.contribution.Contribution#getPathInSource()
      */
+    @Override
     public Path getPathInSource() {
         return null;
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof EmptyContribution) {
+            EmptyContribution that = (EmptyContribution)obj;
+            if (!this.getSourceName().equals(that.getSourceName())) return false;
+            return true;
+        }
+        return false;
+    }
 }

Added: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/MultiChildContribution.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/MultiChildContribution.java	                        (rev 0)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/MultiChildContribution.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -0,0 +1,82 @@
+/*
+ * 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.contribution;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.spi.graph.Path;
+import org.jboss.dna.spi.graph.Path.Segment;
+
+/**
+ * The contribution of a source to the information for a single federated node.
+ * 
+ * @author Randall Hauch
+ */
+ at Immutable
+public class MultiChildContribution extends NonEmptyContribution {
+
+    /**
+     * This is the first version of this class. See the documentation of MergePlan.serialVersionUID.
+     */
+    private static final long serialVersionUID = 1L;
+
+    private List<Segment> children;
+
+    /**
+     * Create a contribution of children from the source with the supplied name.
+     * 
+     * @param sourceName the name of the source, which may not be null or blank
+     * @param pathInSource the path in the source for this contributed information; may not be null
+     * @param children the children from the source; may not be null or empty
+     */
+    public MultiChildContribution( String sourceName,
+                                   Path pathInSource,
+                                   Iterable<Segment> children ) {
+        super(sourceName, pathInSource);
+        this.children = new LinkedList<Segment>();
+        for (Segment child : children) {
+            if (child != null) this.children.add(child);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getChildren()
+     */
+    @Override
+    public Iterator<Segment> getChildren() {
+        return new ImmutableIterator<Segment>(children.iterator());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getChildrenCount()
+     */
+    @Override
+    public int getChildrenCount() {
+        return children.size();
+    }
+}


Property changes on: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/MultiChildContribution.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Copied: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/MultiPropertyContribution.java (from rev 369, trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/PropertyContribution.java)
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/MultiPropertyContribution.java	                        (rev 0)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/MultiPropertyContribution.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -0,0 +1,93 @@
+/*
+ * 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.contribution;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.Path;
+import org.jboss.dna.spi.graph.Property;
+
+/**
+ * The record of a source contributing only properties to a node.
+ * 
+ * @author Randall Hauch
+ */
+ at Immutable
+public class MultiPropertyContribution extends NonEmptyContribution {
+
+    /**
+     * This is the first version of this class. See the documentation of MergePlan.serialVersionUID.
+     */
+    private static final long serialVersionUID = 1L;
+
+    private final Map<Name, Property> properties;
+
+    /**
+     * Create a contribution of node properties from the source with the supplied name.
+     * 
+     * @param sourceName the name of the source, which may not be null or blank
+     * @param pathInSource the path in the source for this contributed information; may not be null
+     * @param properties the properties from the source; may not be null
+     */
+    public MultiPropertyContribution( String sourceName,
+                                      Path pathInSource,
+                                      Iterable<Property> properties ) {
+        super(sourceName, pathInSource);
+        this.properties = new HashMap<Name, Property>();
+        for (Property property : properties) {
+            if (property != null) this.properties.put(property.getName(), property);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getProperties()
+     */
+    @Override
+    public Iterator<Property> getProperties() {
+        return new ImmutableIterator<Property>(this.properties.values().iterator());
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getPropertyCount()
+     */
+    @Override
+    public int getPropertyCount() {
+        return this.properties.size();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getProperty(org.jboss.dna.spi.graph.Name)
+     */
+    @Override
+    public Property getProperty( Name name ) {
+        return this.properties.get(name);
+    }
+}

Modified: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/NodeContribution.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/NodeContribution.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/NodeContribution.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -21,27 +21,24 @@
  */
 package org.jboss.dna.connector.federation.contribution;
 
-import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
-import net.jcip.annotations.NotThreadSafe;
+import net.jcip.annotations.Immutable;
 import org.jboss.dna.spi.graph.Path;
 import org.jboss.dna.spi.graph.Property;
 import org.jboss.dna.spi.graph.Path.Segment;
 
 /**
  * The contribution of a source to the information for a single federated node.
- * <p>
- * This class does return a mutable list of {@link #getChildren() children} and mutable map of {@link #getProperties() properties}
- * .
- * </p>
  * 
  * @author Randall Hauch
  */
- at NotThreadSafe
-public class NodeContribution extends PropertyContribution {
+ at Immutable
+public class NodeContribution extends MultiPropertyContribution {
 
     /**
-     * This is the first version of this class. See the documentation of BasicMergePlan.serialVersionUID.
+     * This is the first version of this class. See the documentation of MergePlan.serialVersionUID.
      */
     private static final long serialVersionUID = 1L;
 
@@ -60,19 +57,29 @@
                              Iterable<Property> properties,
                              Iterable<Segment> children ) {
         super(sourceName, pathInSource, properties);
-        this.children = new ArrayList<Segment>(1);
+        this.children = new LinkedList<Segment>();
         for (Segment child : children) {
-            this.children.add(child);
+            if (child != null) this.children.add(child);
         }
     }
 
     /**
-     * Get the children that make up this contribution. This list is immutable.
+     * {@inheritDoc}
      * 
-     * @return the list of children; never null
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getChildren()
      */
     @Override
-    public List<Segment> getChildren() {
-        return this.children;
+    public Iterator<Segment> getChildren() {
+        return new ImmutableIterator<Segment>(children.iterator());
     }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getChildrenCount()
+     */
+    @Override
+    public int getChildrenCount() {
+        return children.size();
+    }
 }

Added: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/NonEmptyContribution.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/NonEmptyContribution.java	                        (rev 0)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/NonEmptyContribution.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -0,0 +1,92 @@
+/*
+ * 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.contribution;
+
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.spi.graph.Path;
+
+/**
+ * The record of a non-empty source contribution from a single location within the source.
+ * 
+ * @author Randall Hauch
+ */
+ at Immutable
+public abstract class NonEmptyContribution extends Contribution {
+
+    /**
+     * This is the first version of this class. See the documentation of MergePlan.serialVersionUID.
+     */
+    private static final long serialVersionUID = 1L;
+
+    private final Path pathInSource;
+
+    /**
+     * Create a contribution of node properties from the source with the supplied name.
+     * 
+     * @param sourceName the name of the source, which may not be null or blank
+     * @param pathInSource the path in the source for this contributed information; may not be null
+     */
+    protected NonEmptyContribution( String sourceName,
+                                    Path pathInSource ) {
+        super(sourceName);
+        assert pathInSource != null;
+        this.pathInSource = pathInSource;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getPathInSource()
+     */
+    @Override
+    public Path getPathInSource() {
+        return pathInSource;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * This implementation returns the hash code of the {@link #getSourceName() source name}, and is compatible with the
+     * implementation of {@link #equals(Object)}.
+     * </p>
+     */
+    @Override
+    public int hashCode() {
+        return HashCode.compute(this.getSourceName(), this.getPathInSource());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals( Object obj ) {
+        if (obj == this) return true;
+        if (obj instanceof NonEmptyContribution) {
+            NonEmptyContribution that = (NonEmptyContribution)obj;
+            if (!this.getSourceName().equals(that.getSourceName())) return false;
+            if (!this.getPathInSource().equals(that.getPathInSource())) return false;
+            return true;
+        }
+        return false;
+    }
+}


Property changes on: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/NonEmptyContribution.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/OneChildContribution.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/OneChildContribution.java	                        (rev 0)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/OneChildContribution.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -0,0 +1,79 @@
+/*
+ * 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.contribution;
+
+import java.util.Iterator;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.spi.graph.Path;
+import org.jboss.dna.spi.graph.Path.Segment;
+
+/**
+ * The record of a source contributing only properties to a node.
+ * 
+ * @author Randall Hauch
+ */
+ at Immutable
+public class OneChildContribution extends NonEmptyContribution {
+
+    /**
+     * This is the first version of this class. See the documentation of MergePlan.serialVersionUID.
+     */
+    private static final long serialVersionUID = 1L;
+
+    private final Segment child;
+
+    /**
+     * Create a contribution of node properties from the source with the supplied name.
+     * 
+     * @param sourceName the name of the source, which may not be null or blank
+     * @param pathInSource the path in the source for this contributed information; may not be null
+     * @param child the child contributed from the source; may not be null
+     */
+    public OneChildContribution( String sourceName,
+                                 Path pathInSource,
+                                 Segment child ) {
+        super(sourceName, pathInSource);
+        assert child != null;
+        this.child = child;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getChildren()
+     */
+    @Override
+    public Iterator<Segment> getChildren() {
+        return new OneValueIterator<Segment>(child);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getChildrenCount()
+     */
+    @Override
+    public int getChildrenCount() {
+        return 1;
+    }
+
+}


Property changes on: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/OneChildContribution.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/OnePropertyContribution.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/OnePropertyContribution.java	                        (rev 0)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/OnePropertyContribution.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -0,0 +1,91 @@
+/*
+ * 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.contribution;
+
+import java.util.Iterator;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.Path;
+import org.jboss.dna.spi.graph.Property;
+
+/**
+ * The record of a source contributing only properties to a node.
+ * 
+ * @author Randall Hauch
+ */
+ at Immutable
+public class OnePropertyContribution extends NonEmptyContribution {
+
+    /**
+     * This is the first version of this class. See the documentation of MergePlan.serialVersionUID.
+     */
+    private static final long serialVersionUID = 1L;
+
+    private final Property property;
+
+    /**
+     * Create a contribution of node properties from the source with the supplied name.
+     * 
+     * @param sourceName the name of the source, which may not be null or blank
+     * @param pathInSource the path in the source for this contributed information; may not be null
+     * @param property the property from the source; may not be null
+     */
+    public OnePropertyContribution( String sourceName,
+                                    Path pathInSource,
+                                    Property property ) {
+        super(sourceName, pathInSource);
+        assert property != null;
+        assert property.isEmpty() == false;
+        this.property = property;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getProperties()
+     */
+    @Override
+    public Iterator<Property> getProperties() {
+        return new OneValueIterator<Property>(property);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getPropertyCount()
+     */
+    @Override
+    public int getPropertyCount() {
+        return 1;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getProperty(org.jboss.dna.spi.graph.Name)
+     */
+    @Override
+    public Property getProperty( Name name ) {
+        return this.property.getName().equals(name) ? property : null;
+    }
+
+}


Property changes on: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/OnePropertyContribution.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Deleted: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/PropertyContribution.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/PropertyContribution.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/PropertyContribution.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -1,84 +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.contribution;
-
-import java.util.HashMap;
-import java.util.Map;
-import org.jboss.dna.spi.graph.Name;
-import org.jboss.dna.spi.graph.Path;
-import org.jboss.dna.spi.graph.Property;
-
-/**
- * The record of a source contributing only properties to a node.
- * <p>
- * This class does return a mutable mutable map of {@link #getProperties() properties}.
- * </p>
- * 
- * @author Randall Hauch
- */
-public class PropertyContribution extends AbstractContribution {
-
-    /**
-     * This is the first version of this class. See the documentation of BasicMergePlan.serialVersionUID.
-     */
-    private static final long serialVersionUID = 1L;
-
-    private final Map<Name, Property> properties;
-    private final Path pathInSource;
-
-    /**
-     * Create a contribution of node properties from the source with the supplied name.
-     * 
-     * @param sourceName the name of the source, which may not be null or blank
-     * @param pathInSource the path in the source for this contributed information; may not be null
-     * @param properties the properties from the source; may not be null
-     */
-    public PropertyContribution( String sourceName,
-                                 Path pathInSource,
-                                 Iterable<Property> properties ) {
-        super(sourceName);
-        assert pathInSource != null;
-        this.properties = new HashMap<Name, Property>();
-        this.pathInSource = pathInSource;
-        for (Property property : properties) {
-            this.properties.put(property.getName(), property);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public Map<Name, Property> getProperties() {
-        return this.properties;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.connector.federation.contribution.Contribution#getPathInSource()
-     */
-    public Path getPathInSource() {
-        return pathInSource;
-    }
-
-}

Added: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/ThreePropertyContribution.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/ThreePropertyContribution.java	                        (rev 0)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/ThreePropertyContribution.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -0,0 +1,106 @@
+/*
+ * 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.contribution;
+
+import java.util.Iterator;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.Path;
+import org.jboss.dna.spi.graph.Property;
+
+/**
+ * The record of a source contributing only properties to a node.
+ * 
+ * @author Randall Hauch
+ */
+ at Immutable
+public class ThreePropertyContribution extends NonEmptyContribution {
+
+    /**
+     * This is the first version of this class. See the documentation of MergePlan.serialVersionUID.
+     */
+    private static final long serialVersionUID = 1L;
+
+    private final Property property1;
+    private final Property property2;
+    private final Property property3;
+
+    /**
+     * Create a contribution of node properties from the source with the supplied name.
+     * 
+     * @param sourceName the name of the source, which may not be null or blank
+     * @param pathInSource the path in the source for this contributed information; may not be null
+     * @param property1 the first property from the source; may not be null
+     * @param property2 the second property from the source; may not be null
+     * @param property3 the third property from the source; may not be null
+     */
+    public ThreePropertyContribution( String sourceName,
+                                      Path pathInSource,
+                                      Property property1,
+                                      Property property2,
+                                      Property property3 ) {
+        super(sourceName, pathInSource);
+        assert property1 != null;
+        assert property1.isEmpty() == false;
+        assert property2 != null;
+        assert property2.isEmpty() == false;
+        assert property3 != null;
+        assert property3.isEmpty() == false;
+        this.property1 = property1;
+        this.property2 = property2;
+        this.property3 = property3;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getProperties()
+     */
+    @Override
+    public Iterator<Property> getProperties() {
+        return new ThreeValueIterator<Property>(property1, property2, property3);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getPropertyCount()
+     */
+    @Override
+    public int getPropertyCount() {
+        return 3;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getProperty(org.jboss.dna.spi.graph.Name)
+     */
+    @Override
+    public Property getProperty( Name name ) {
+        if (this.property1.getName().equals(name)) return property1;
+        if (this.property2.getName().equals(name)) return property2;
+        if (this.property3.getName().equals(name)) return property3;
+        return null;
+    }
+
+}


Property changes on: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/ThreePropertyContribution.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/TwoPropertyContribution.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/TwoPropertyContribution.java	                        (rev 0)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/TwoPropertyContribution.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -0,0 +1,99 @@
+/*
+ * 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.contribution;
+
+import java.util.Iterator;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.spi.graph.Name;
+import org.jboss.dna.spi.graph.Path;
+import org.jboss.dna.spi.graph.Property;
+
+/**
+ * The record of a source contributing only properties to a node.
+ * 
+ * @author Randall Hauch
+ */
+ at Immutable
+public class TwoPropertyContribution extends NonEmptyContribution {
+
+    /**
+     * This is the first version of this class. See the documentation of MergePlan.serialVersionUID.
+     */
+    private static final long serialVersionUID = 1L;
+
+    private final Property property1;
+    private final Property property2;
+
+    /**
+     * Create a contribution of node properties from the source with the supplied name.
+     * 
+     * @param sourceName the name of the source, which may not be null or blank
+     * @param pathInSource the path in the source for this contributed information; may not be null
+     * @param property1 the first property from the source; may not be null
+     * @param property2 the first property from the source; may not be null
+     */
+    public TwoPropertyContribution( String sourceName,
+                                    Path pathInSource,
+                                    Property property1,
+                                    Property property2 ) {
+        super(sourceName, pathInSource);
+        assert property1 != null;
+        assert property1.isEmpty() == false;
+        assert property2 != null;
+        assert property2.isEmpty() == false;
+        this.property1 = property1;
+        this.property2 = property2;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getProperties()
+     */
+    @Override
+    public Iterator<Property> getProperties() {
+        return new TwoValueIterator<Property>(property1, property2);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getPropertyCount()
+     */
+    @Override
+    public int getPropertyCount() {
+        return 2;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.contribution.Contribution#getProperty(org.jboss.dna.spi.graph.Name)
+     */
+    @Override
+    public Property getProperty( Name name ) {
+        if (this.property1.getName().equals(name)) return property1;
+        if (this.property2.getName().equals(name)) return property2;
+        return null;
+    }
+
+}


Property changes on: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/contribution/TwoPropertyContribution.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/ActsOnProjectedPathCommand.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/ActsOnProjectedPathCommand.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/ActsOnProjectedPathCommand.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -29,7 +29,7 @@
  * @author Randall Hauch
  * @param <T> the command type
  */
-public class ActsOnProjectedPathCommand<T extends GraphCommand> implements ActsOnPath, GraphCommand {
+public class ActsOnProjectedPathCommand<T extends ActsOnPath & GraphCommand> implements ActsOnPath, GraphCommand {
 
     private final T delegate;
     private final Path projectedPath;

Modified: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/FederatingCommandExecutor.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/FederatingCommandExecutor.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/executor/FederatingCommandExecutor.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -21,30 +21,34 @@
  */
 package org.jboss.dna.connector.federation.executor;
 
-import java.io.ObjectInputStream;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import net.jcip.annotations.NotThreadSafe;
-import org.jboss.dna.common.i18n.I18n;
 import org.jboss.dna.common.util.Logger;
-import org.jboss.dna.connector.federation.FederationI18n;
 import org.jboss.dna.connector.federation.Projection;
 import org.jboss.dna.connector.federation.contribution.Contribution;
-import org.jboss.dna.connector.federation.merge.BasicMergePlan;
+import org.jboss.dna.connector.federation.merge.MergePlan;
 import org.jboss.dna.spi.ExecutionContext;
-import org.jboss.dna.spi.graph.Binary;
 import org.jboss.dna.spi.graph.DateTime;
 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.PathNotFoundException;
 import org.jboss.dna.spi.graph.Property;
-import org.jboss.dna.spi.graph.ValueFactory;
+import org.jboss.dna.spi.graph.Path.Segment;
 import org.jboss.dna.spi.graph.commands.GetChildrenCommand;
 import org.jboss.dna.spi.graph.commands.GetNodeCommand;
 import org.jboss.dna.spi.graph.commands.GetPropertiesCommand;
+import org.jboss.dna.spi.graph.commands.GraphCommand;
+import org.jboss.dna.spi.graph.commands.NodeConflictBehavior;
 import org.jboss.dna.spi.graph.commands.executor.AbstractCommandExecutor;
+import org.jboss.dna.spi.graph.commands.impl.BasicCreateNodeCommand;
 import org.jboss.dna.spi.graph.commands.impl.BasicGetNodeCommand;
 import org.jboss.dna.spi.graph.commands.impl.BasicGetPropertiesCommand;
 import org.jboss.dna.spi.graph.connection.RepositoryConnection;
@@ -114,6 +118,9 @@
         this.connectionsBySourceName = new HashMap<String, RepositoryConnection>();
         this.mergePlanPropertyName = context.getValueFactories().getNameFactory().create("dna:mergePlan");
         this.sourceNames = new HashSet<String>();
+        for (Projection projection : this.sourceProjections) {
+            this.sourceNames.add(projection.getSourceName());
+        }
     }
 
     /**
@@ -215,14 +222,20 @@
         RepositoryConnection cacheConnection = getConnectionToCache();
         BasicGetNodeCommand fromCache = new BasicGetNodeCommand(path);
         cacheConnection.execute(getEnvironment(), fromCache);
+        if (fromCache.hasError()) {
+            if (fromCache.getError() instanceof PathNotFoundException) {
+                // Start at the root and populate the cache down to this node ...
+            }
+        }
+
         if (fromCache.hasError()) return fromCache;
 
         // Look up the merge plan ...
-        BasicMergePlan basicMergePlan = getMergePlan(fromCache);
-        if (basicMergePlan != null) {
-            if (isCurrent(path, basicMergePlan)) return fromCache;
+        MergePlan mergePlan = getMergePlan(fromCache);
+        if (mergePlan != null) {
+            if (isCurrent(path, mergePlan)) return fromCache;
             // Some of the merge plan is out of date, so we need to read the information from those regions that are expired ...
-            BasicMergePlan newMergePlan = new BasicMergePlan();
+            // MergePlan newMergePlan = new BasicMergePlan();
             // for (Projection projection : sourceProjections) {
             // // Does this region apply to the path ...
             // if (projection.appliesTo(path)) {
@@ -246,53 +259,93 @@
             // What about other (new) regions?
         } else {
             // At this point, there is no merge plan, so read information from the sources ...
-            // for (FederatedRegion region : regions) {
-            // if (!region.appliesTo(path)) continue;
-            // // Issue a command to get the node from the
-            // // BasicGetNode
-            // }
+            PathFactory pathFactory = getEnvironment().getValueFactories().getPathFactory();
+            List<Contribution> contributions = new LinkedList<Contribution>();
+            for (Projection projection : this.sourceProjections) {
+                final String source = projection.getSourceName();
+                // Get the paths-in-source where we should fetch node contributions ...
+                Set<Path> pathsInSource = projection.getPathsInSource(path, pathFactory);
+                if (pathsInSource.isEmpty()) {
+                    // The source has no contributions ...
+                    contributions.add(Contribution.create(source));
+                } else {
+                    // There is at least one contribution ...
+                    RepositoryConnection sourceConnection = getConnection(projection);
+
+                    // Get the contributions ...
+                    final int numPaths = pathsInSource.size();
+                    if (numPaths == 1) {
+                        Path pathInSource = pathsInSource.iterator().next();
+                        BasicGetNodeCommand fromSource = new BasicGetNodeCommand(pathInSource);
+                        sourceConnection.execute(getEnvironment(), fromSource);
+                        if (!fromSource.hasError()) {
+                            Collection<Property> properties = fromSource.getProperties().values();
+                            Collection<Segment> children = fromSource.getChildren();
+                            Contribution contribution = Contribution.create(source, pathInSource, properties, children);
+                            contributions.add(contribution);
+                        }
+                    } else {
+                        BasicGetNodeCommand[] fromSourceCommands = new BasicGetNodeCommand[numPaths];
+                        int i = 0;
+                        for (Path pathInSource : pathsInSource) {
+                            fromSourceCommands[i++] = new BasicGetNodeCommand(pathInSource);
+                        }
+                        sourceConnection.execute(getEnvironment(), fromSourceCommands);
+                        for (BasicGetNodeCommand fromSource : fromSourceCommands) {
+                            if (fromSource.hasError()) continue;
+                            Collection<Property> properties = fromSource.getProperties().values();
+                            Collection<Segment> children = fromSource.getChildren();
+                            Contribution contribution = Contribution.create(source, fromSource.getPath(), properties, children);
+                            contributions.add(contribution);
+                        }
+                    }
+                }
+            }
+            // Merge the results into a single set of results ...
+            mergePlan = MergePlan.create(contributions);
+            BasicGetNodeCommand mergedNode = new BasicGetNodeCommand(null);
+
+            // Place the results into the cache ...
+            NodeConflictBehavior conflictBehavior = NodeConflictBehavior.UPDATE;
+            BasicCreateNodeCommand newNode = new BasicCreateNodeCommand(path, mergedNode.getProperties().values(),
+                                                                        conflictBehavior);
+            List<Segment> children = mergedNode.getChildren();
+            GraphCommand[] intoCache = new GraphCommand[1 + children.size()];
+            int i = 0;
+            intoCache[i++] = newNode;
+            List<Property> noProperties = Collections.emptyList();
+            for (Segment child : mergedNode.getChildren()) {
+                intoCache[i++] = new BasicCreateNodeCommand(pathFactory.create(path, child), noProperties, conflictBehavior);
+            }
+            cacheConnection.execute(getEnvironment(), mergedNode);
+
+            // Return the results ...
+            return mergedNode;
         }
-        // And read the information from any new region ...
+
         return null;
     }
 
-    protected Contribution getContribution( String sourceName,
-                                            Path path ) {
-        return null;
+    protected Contribution getContribution( RepositoryConnection connection,
+                                            String sourceName,
+                                            Path path ) throws RepositorySourceException, InterruptedException {
+        BasicGetNodeCommand fromSource = new BasicGetNodeCommand(path);
+        connection.execute(getEnvironment(), fromSource);
+        if (fromSource.hasError()) return null;
+
+        Collection<Property> properties = fromSource.getProperties().values();
+        Collection<Segment> children = fromSource.getChildren();
+        Contribution contribution = Contribution.create(sourceName, path, properties, children);
+        return contribution;
     }
 
-    protected BasicMergePlan getMergePlan( BasicGetPropertiesCommand command ) {
+    protected MergePlan getMergePlan( BasicGetPropertiesCommand command ) {
         Property mergePlanProperty = command.getProperties().get(mergePlanPropertyName);
         if (mergePlanProperty == null || mergePlanProperty.isEmpty()) {
             return null;
         }
-        ValueFactory<Binary> binaryFactory = getEnvironment().getValueFactories().getBinaryFactory();
-        Binary binaryValue = binaryFactory.create(mergePlanProperty.getValues().next());
-        binaryValue.acquire();
-        ObjectInputStream stream = null;
-        BasicMergePlan basicMergePlan = null;
-        RepositorySourceException error = null;
-        try {
-            stream = new ObjectInputStream(binaryValue.getStream());
-            basicMergePlan = (BasicMergePlan)stream.readObject();
-        } catch (Throwable err) {
-            I18n msg = FederationI18n.errorReadingMergePlan;
-            error = new RepositorySourceException(getSourceName(), msg.text(command.getPath()), err);
-            throw error;
-        } finally {
-            try {
-                if (stream != null) stream.close();
-            } catch (Throwable err) {
-                if (error == null) {
-                    I18n msg = FederationI18n.errorReadingMergePlan;
-                    error = new RepositorySourceException(getSourceName(), msg.text(command.getPath()), err);
-                    throw error;
-                }
-            } finally {
-                binaryValue.release();
-            }
-        }
-        return basicMergePlan;
+        Object value = mergePlanProperty.getValues().next();
+        return value instanceof MergePlan ? (MergePlan)value : null;
     }
 
     /**
@@ -303,27 +356,27 @@
      * @return true if the merge plan is current, or false if it needs to be (at least partially) rebuilt
      */
     protected boolean isCurrent( Path path,
-                                 BasicMergePlan plan ) {
+                                 MergePlan plan ) {
         // First check the time ...
         DateTime now = getCurrentTimeInUtc();
         if (plan.isExpired(now)) return false;
 
         // Does the plan have any contributions from sources that don't exist ?
-        for (String contributingSource : plan.getNamesOfContributingSources()) {
-            if (!sourceNames.contains(contributingSource)) return false;
+        for (Contribution contribution : plan) {
+            if (!sourceNames.contains(contribution.getSourceName())) return false;
         }
-
-        // Determine if any new source projections exists that aren't part of the plan ...
-        for (String sourceName : sourceNames) {
-            if (plan.isSource(sourceName)) continue;
-            // The source is new ... see whether there are any regions that apply ...
-            // for (FederatedRegion region : this.regionsBySourceName.get(sourceName)) {
-            // // If the region's path is not at/above the path, the region doesn't matter
-            // if (!region.appliesTo(path)) continue;
-            // // The region applies to the path ...
-            // return false;
-            // }
-        }
+        //
+        // // Determine if any new source projections exists that aren't part of the plan ...
+        // for (String sourceName : sourceNames) {
+        // if (plan.isSource(sourceName)) continue;
+        // // The source is new ... see whether there are any regions that apply ...
+        // // for (FederatedRegion region : this.regionsBySourceName.get(sourceName)) {
+        // // // If the region's path is not at/above the path, the region doesn't matter
+        // // if (!region.appliesTo(path)) continue;
+        // // // The region applies to the path ...
+        // // return false;
+        // // }
+        // }
         return true;
     }
 

Deleted: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/BasicMergePlan.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/BasicMergePlan.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/BasicMergePlan.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -1,205 +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.io.InvalidClassException;
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import net.jcip.annotations.NotThreadSafe;
-import org.jboss.dna.connector.federation.contribution.Contribution;
-import org.jboss.dna.connector.federation.contribution.EmptyContribution;
-import org.jboss.dna.spi.graph.DateTime;
-import org.jboss.dna.spi.graph.Name;
-import org.jboss.dna.spi.graph.Property;
-
-/**
- * This class represents the details about how information from different sources are merged into a single federated node.
- * <p>
- * A merge plan basically consists of the individual contribution from each source and the information about how these
- * contributions were merged into the single federated node.
- * </p>
- * <p>
- * Merge plans are designed to be {@link Serializable serializable}, as they are persisted on the federated node and deserialized
- * to assist in the management of the federated node.
- * </p>
- * 
- * @author Randall Hauch
- */
- at NotThreadSafe
-public class BasicMergePlan implements Serializable {
-
-    /**
-     * Define the earliest version of this class that is supported. The Java runtime, upon deserialization, compares the
-     * serialized object's version to this, and if less than this version will throw a {@link InvalidClassException}. If, however,
-     * the serialized object's version is compatible with this class, it will be deserialized successfully.
-     * <p>
-     * <a href="http://java.sun.com/j2se/1.5.0/docs/guide/serialization/spec/version.html#6678">Sun's documentation</a> describes
-     * the following changes can be made without negatively affecting the deserialization of older versions:
-     * <ul>
-     * <li>Adding fields - When the class being reconstituted has a field that does not occur in the stream, that field in the
-     * object will be initialized to the default value for its type. If class-specific initialization is needed, the class may
-     * provide a readObject method that can initialize the field to nondefault values.</i>
-     * <li>Adding classes - The stream will contain the type hierarchy of each object in the stream. Comparing this hierarchy in
-     * the stream with the current class can detect additional classes. Since there is no information in the stream from which to
-     * initialize the object, the class's fields will be initialized to the default values.</i>
-     * <li>Removing classes - Comparing the class hierarchy in the stream with that of the current class can detect that a class
-     * has been deleted. In this case, the fields and objects corresponding to that class are read from the stream. Primitive
-     * fields are discarded, but the objects referenced by the deleted class are created, since they may be referred to later in
-     * the stream. They will be garbage-collected when the stream is garbage-collected or reset.</i>
-     * <li>Adding writeObject/readObject methods - If the version reading the stream has these methods then readObject is
-     * expected, as usual, to read the required data written to the stream by the default serialization. It should call
-     * defaultReadObject first before reading any optional data. The writeObject method is expected as usual to call
-     * defaultWriteObject to write the required data and then may write optional data.</i>
-     * <li>Removing writeObject/readObject methods - If the class reading the stream does not have these methods, the required
-     * data will be read by default serialization, and the optional data will be discarded.</i>
-     * <li>Adding java.io.Serializable - This is equivalent to adding types. There will be no values in the stream for this class
-     * so its fields will be initialized to default values. The support for subclassing nonserializable classes requires that the
-     * class's supertype have a no-arg constructor and the class itself will be initialized to default values. If the no-arg
-     * constructor is not available, the InvalidClassException is thrown.</i>
-     * <li>Changing the access to a field - The access modifiers public, package, protected, and private have no effect on the
-     * ability of serialization to assign values to the fields.</i>
-     * <li>Changing a field from static to nonstatic or transient to nontransient - When relying on default serialization to
-     * compute the serializable fields, this change is equivalent to adding a field to the class. The new field will be written to
-     * the stream but earlier classes will ignore the value since serialization will not assign values to static or transient
-     * fields.</i>
-     * </ul>
-     * All other kinds of modifications should be avoided.
-     * </p>
-     */
-    private static final long serialVersionUID = 1L;
-
-    private final Map<String, Contribution> contributions = new HashMap<String, Contribution>();
-    private Map<Name, Property> annotations = null;
-    private DateTime expirationTimeInUtc;
-
-    /**
-     * Create this version
-     */
-    public BasicMergePlan() {
-    }
-
-    /**
-     * Determine whether this merge plan has expired given the supplied current time. The {@link #getExpirationTimeInUtc()
-     * expiration time} is the earliest time that any of the {@link #getContributionFrom(String) contributions}
-     * {@link Contribution#getExpirationTimeInUtc()}.
-     * 
-     * @param utcTime the current time expressed in UTC; may not be null
-     * @return true if at least one contribution has expired, or false otherwise
-     */
-    public boolean isExpired( DateTime utcTime ) {
-        assert utcTime != null;
-        assert utcTime.toUtcTimeZone().equals(utcTime); // check that it is passed UTC time
-        return !expirationTimeInUtc.isAfter(utcTime);
-    }
-
-    /**
-     * Get the expiration time (in UTC) that is the earliest time that any of the {@link #getContributionFrom(String)
-     * contributions} {@link Contribution#getExpirationTimeInUtc()}.
-     * 
-     * @return the expiration time in UTC, or null if there is no known expiration time
-     */
-    public DateTime getExpirationTimeInUtc() {
-        return expirationTimeInUtc;
-    }
-
-    /**
-     * Get the contribution from the source with the supplied name. Note that contributions always include sources that contribute
-     * information and sources that contribute no information. If a source is not included in this list, its contributions are
-     * <i>unknown</i>; that is, it is unknown whether that source does or does not contribute to the node.
-     * 
-     * @param sourceName the name of the source
-     * @return the contribution, or null if the contribution of the source is unknown
-     */
-    public Contribution getContributionFrom( String sourceName ) {
-        return contributions.get(sourceName);
-    }
-
-    /**
-     * Return whether the named source was consulted for a contribution.
-     * 
-     * @param sourceName the name of the source
-     * @return true if the source has some {@link Contribution contribution} (even if it is an {@link EmptyContribution})
-     */
-    public boolean isSource( String sourceName ) {
-        return contributions.containsKey(sourceName);
-    }
-
-    /**
-     * Get the names of the contributing sources.
-     * 
-     * @return the names of the sources that have some contribution
-     */
-    public Set<String> getNamesOfContributingSources() {
-        return contributions.keySet();
-    }
-
-    /**
-     * Add the supplied contribution, replacing and returning any previous contribution for the same source.
-     * 
-     * @param contribution the new contribution
-     * @return the previous contribution for the source, or null if there was no previous contribution.
-     */
-    public Contribution addContribution( Contribution contribution ) {
-        assert contribution != null;
-        Contribution previous = contributions.put(contribution.getSourceName(), contribution);
-        DateTime contributionExpirationInUtc = contribution.getExpirationTimeInUtc();
-        if (expirationTimeInUtc == null || contributionExpirationInUtc.isBefore(expirationTimeInUtc)) {
-            expirationTimeInUtc = contributionExpirationInUtc;
-        }
-        return previous;
-    }
-
-    /**
-     * Get the plan annotation property with the given name. Plan annotations are custom properties that may be set by
-     * MergeProcessor implementations to store custom properties on the plan. This method does nothing if the supplied name is
-     * null
-     * 
-     * @param name the name of the annotation
-     * @return the existing annotation, or null if there is no annotation with the supplied name
-     * @see #setAnnotation(Property)
-     */
-    public Property getAnnotation( Name name ) {
-        if (name == null) return null;
-        if (this.annotations == null) return null;
-        return this.annotations.get(name);
-    }
-
-    /**
-     * Set the plan annotation property. This method replaces and returns any existing annotation property with the same name.
-     * This method also returns immediately if the supplied annotation is null.
-     * 
-     * @param annotation the new annotation
-     * @return the previous annotation property with the same name, or null if there was no previous annotation property for the
-     *         name
-     * @see #getAnnotation(Name)
-     */
-    public Property setAnnotation( Property annotation ) {
-        if (annotation == null) return null;
-        if (this.annotations == null) {
-            this.annotations = new HashMap<Name, Property>();
-        }
-        return this.annotations.put(annotation.getName(), annotation);
-    }
-
-}

Deleted: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/DoubleContributionMergePlan.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/DoubleContributionMergePlan.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/DoubleContributionMergePlan.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -1,115 +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.Iterator;
-import java.util.NoSuchElementException;
-import net.jcip.annotations.ThreadSafe;
-import org.jboss.dna.connector.federation.contribution.Contribution;
-
-/**
- * @author Randall Hauch
- */
- at ThreadSafe
-public class DoubleContributionMergePlan extends MergePlan {
-
-    private static final long serialVersionUID = 1L;
-    private final Contribution contribution1;
-    private final Contribution contribution2;
-
-    /**
-     * @param contribution1 the first contribution for this merge plan
-     * @param contribution2 the second contribution for this merge plan
-     */
-    public DoubleContributionMergePlan( Contribution contribution1,
-                                        Contribution contribution2 ) {
-        assert contribution1 != null;
-        assert contribution2 != null;
-        this.contribution1 = contribution1;
-        this.contribution2 = contribution2;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.connector.federation.merge.MergePlan#getContributionCount()
-     */
-    @Override
-    public int getContributionCount() {
-        return 2;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.connector.federation.merge.MergePlan#getContributionFrom(java.lang.String)
-     */
-    @Override
-    public Contribution getContributionFrom( String sourceName ) {
-        if (contribution1.getSourceName().equals(sourceName)) return contribution1;
-        if (contribution2.getSourceName().equals(sourceName)) return contribution2;
-        return null;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Iterable#iterator()
-     */
-    public Iterator<Contribution> iterator() {
-        return new Iterator<Contribution>() {
-            private int next = 2;
-
-            public boolean hasNext() {
-                return next > 0;
-            }
-
-            @SuppressWarnings( "synthetic-access" )
-            public Contribution next() {
-                if (next == 2) {
-                    next = 1;
-                    return contribution2;
-                }
-                if (next == 1) {
-                    next = 0;
-                    return contribution1;
-                }
-                throw new NoSuchElementException();
-            }
-
-            public void remove() {
-                throw new UnsupportedOperationException();
-            }
-        };
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.connector.federation.merge.MergePlan#isSource(java.lang.String)
-     */
-    @Override
-    public boolean isSource( String sourceName ) {
-        return contribution1.getSourceName().equals(sourceName) || contribution2.getSourceName().equals(sourceName);
-    }
-
-}

Added: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/FiveContributionMergePlan.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/FiveContributionMergePlan.java	                        (rev 0)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/FiveContributionMergePlan.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -0,0 +1,150 @@
+/*
+ * 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.Iterator;
+import java.util.NoSuchElementException;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.connector.federation.contribution.Contribution;
+
+/**
+ * @author Randall Hauch
+ */
+ at ThreadSafe
+public class FiveContributionMergePlan extends MergePlan {
+
+    private static final long serialVersionUID = 1L;
+    private final Contribution contribution1;
+    private final Contribution contribution2;
+    private final Contribution contribution3;
+    private final Contribution contribution4;
+    private final Contribution contribution5;
+
+    /**
+     * @param contribution1 the first contribution for this merge plan
+     * @param contribution2 the second contribution for this merge plan
+     * @param contribution3 the third contribution for this merge plan
+     * @param contribution4 the fourth contribution for this merge plan
+     * @param contribution5 the fifth contribution for this merge plan
+     */
+    public FiveContributionMergePlan( Contribution contribution1,
+                                      Contribution contribution2,
+                                      Contribution contribution3,
+                                      Contribution contribution4,
+                                      Contribution contribution5 ) {
+        assert contribution1 != null;
+        assert contribution2 != null;
+        assert contribution3 != null;
+        assert contribution4 != null;
+        assert contribution5 != null;
+        this.contribution1 = contribution1;
+        this.contribution2 = contribution2;
+        this.contribution3 = contribution3;
+        this.contribution4 = contribution4;
+        this.contribution5 = contribution5;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.merge.MergePlan#getContributionCount()
+     */
+    @Override
+    public int getContributionCount() {
+        return 5;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.merge.MergePlan#getContributionFrom(java.lang.String)
+     */
+    @Override
+    public Contribution getContributionFrom( String sourceName ) {
+        if (contribution1.getSourceName().equals(sourceName)) return contribution1;
+        if (contribution2.getSourceName().equals(sourceName)) return contribution2;
+        if (contribution3.getSourceName().equals(sourceName)) return contribution3;
+        if (contribution4.getSourceName().equals(sourceName)) return contribution4;
+        if (contribution5.getSourceName().equals(sourceName)) return contribution5;
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Iterable#iterator()
+     */
+    public Iterator<Contribution> iterator() {
+        return new Iterator<Contribution>() {
+            private int next = 4;
+
+            public boolean hasNext() {
+                return next > 0;
+            }
+
+            @SuppressWarnings( "synthetic-access" )
+            public Contribution next() {
+                if (next == 5) {
+                    next = 4;
+                    return contribution1;
+                }
+                if (next == 4) {
+                    next = 3;
+                    return contribution2;
+                }
+                if (next == 3) {
+                    next = 2;
+                    return contribution3;
+                }
+                if (next == 2) {
+                    next = 1;
+                    return contribution4;
+                }
+                if (next == 1) {
+                    next = 0;
+                    return contribution5;
+                }
+                throw new NoSuchElementException();
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.merge.MergePlan#isSource(java.lang.String)
+     */
+    @Override
+    public boolean isSource( String sourceName ) {
+        if (contribution1.getSourceName().equals(sourceName)) return true;
+        if (contribution2.getSourceName().equals(sourceName)) return true;
+        if (contribution3.getSourceName().equals(sourceName)) return true;
+        if (contribution4.getSourceName().equals(sourceName)) return true;
+        if (contribution5.getSourceName().equals(sourceName)) return true;
+        return false;
+    }
+
+}


Property changes on: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/FiveContributionMergePlan.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Added: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/FourContributionMergePlan.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/FourContributionMergePlan.java	                        (rev 0)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/FourContributionMergePlan.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -0,0 +1,139 @@
+/*
+ * 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.Iterator;
+import java.util.NoSuchElementException;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.connector.federation.contribution.Contribution;
+
+/**
+ * @author Randall Hauch
+ */
+ at ThreadSafe
+public class FourContributionMergePlan extends MergePlan {
+
+    private static final long serialVersionUID = 1L;
+    private final Contribution contribution1;
+    private final Contribution contribution2;
+    private final Contribution contribution3;
+    private final Contribution contribution4;
+
+    /**
+     * @param contribution1 the first contribution for this merge plan
+     * @param contribution2 the second contribution for this merge plan
+     * @param contribution3 the third contribution for this merge plan
+     * @param contribution4 the fourth contribution for this merge plan
+     */
+    public FourContributionMergePlan( Contribution contribution1,
+                                      Contribution contribution2,
+                                      Contribution contribution3,
+                                      Contribution contribution4 ) {
+        assert contribution1 != null;
+        assert contribution2 != null;
+        assert contribution3 != null;
+        assert contribution4 != null;
+        this.contribution1 = contribution1;
+        this.contribution2 = contribution2;
+        this.contribution3 = contribution3;
+        this.contribution4 = contribution4;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.merge.MergePlan#getContributionCount()
+     */
+    @Override
+    public int getContributionCount() {
+        return 4;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.merge.MergePlan#getContributionFrom(java.lang.String)
+     */
+    @Override
+    public Contribution getContributionFrom( String sourceName ) {
+        if (contribution1.getSourceName().equals(sourceName)) return contribution1;
+        if (contribution2.getSourceName().equals(sourceName)) return contribution2;
+        if (contribution3.getSourceName().equals(sourceName)) return contribution3;
+        if (contribution4.getSourceName().equals(sourceName)) return contribution4;
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Iterable#iterator()
+     */
+    public Iterator<Contribution> iterator() {
+        return new Iterator<Contribution>() {
+            private int next = 4;
+
+            public boolean hasNext() {
+                return next > 0;
+            }
+
+            @SuppressWarnings( "synthetic-access" )
+            public Contribution next() {
+                if (next == 4) {
+                    next = 3;
+                    return contribution1;
+                }
+                if (next == 3) {
+                    next = 2;
+                    return contribution2;
+                }
+                if (next == 2) {
+                    next = 1;
+                    return contribution3;
+                }
+                if (next == 1) {
+                    next = 0;
+                    return contribution4;
+                }
+                throw new NoSuchElementException();
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.merge.MergePlan#isSource(java.lang.String)
+     */
+    @Override
+    public boolean isSource( String sourceName ) {
+        if (contribution1.getSourceName().equals(sourceName)) return true;
+        if (contribution2.getSourceName().equals(sourceName)) return true;
+        if (contribution3.getSourceName().equals(sourceName)) return true;
+        if (contribution4.getSourceName().equals(sourceName)) return true;
+        return false;
+    }
+
+}


Property changes on: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/FourContributionMergePlan.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain

Modified: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/MergePlan.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/MergePlan.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/MergePlan.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -23,6 +23,7 @@
 
 import java.io.InvalidClassException;
 import java.io.Serializable;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
@@ -54,16 +55,40 @@
         ArgCheck.isNotNull(contributions, "contributions");
         switch (contributions.length) {
             case 1:
-                return new SingleContributionMergePlan(contributions[0]);
+                return new OneContributionMergePlan(contributions[0]);
             case 2:
-                return new DoubleContributionMergePlan(contributions[0], contributions[1]);
+                return new TwoContributionMergePlan(contributions[0], contributions[1]);
             case 3:
-                return new TripleContributionMergePlan(contributions[0], contributions[1], contributions[2]);
+                return new ThreeContributionMergePlan(contributions[0], contributions[1], contributions[2]);
+            case 4:
+                return new FourContributionMergePlan(contributions[0], contributions[1], contributions[2], contributions[3]);
+            case 5:
+                return new FiveContributionMergePlan(contributions[0], contributions[1], contributions[2], contributions[3],
+                                                     contributions[4]);
             default:
                 return new MultipleContributionMergePlan(contributions);
         }
     }
 
+    public static MergePlan create( Collection<Contribution> contributions ) {
+        ArgCheck.isNotNull(contributions, "contributions");
+        Iterator<Contribution> iter = contributions.iterator();
+        switch (contributions.size()) {
+            case 1:
+                return new OneContributionMergePlan(iter.next());
+            case 2:
+                return new TwoContributionMergePlan(iter.next(), iter.next());
+            case 3:
+                return new ThreeContributionMergePlan(iter.next(), iter.next(), iter.next());
+            case 4:
+                return new FourContributionMergePlan(iter.next(), iter.next(), iter.next(), iter.next());
+            case 5:
+                return new FiveContributionMergePlan(iter.next(), iter.next(), iter.next(), iter.next(), iter.next());
+            default:
+                return new MultipleContributionMergePlan(contributions);
+        }
+    }
+
     public static MergePlan addContribution( MergePlan plan,
                                              Contribution contribution ) {
         if (plan instanceof MultipleContributionMergePlan) {
@@ -71,20 +96,26 @@
             multiPlan.addContribution(contribution);
             return multiPlan;
         }
-        if (plan instanceof SingleContributionMergePlan) {
-            MergePlan newPlan = new DoubleContributionMergePlan(plan.iterator().next(), contribution);
-            newPlan.setAnnotations(plan.getAnnotations());
+        MergePlan newPlan = null;
+        if (plan instanceof OneContributionMergePlan) {
+            newPlan = new TwoContributionMergePlan(plan.iterator().next(), contribution);
+        } else if (plan instanceof TwoContributionMergePlan) {
+            Iterator<Contribution> iter = plan.iterator();
+            newPlan = new ThreeContributionMergePlan(iter.next(), iter.next(), contribution);
+        } else if (plan instanceof ThreeContributionMergePlan) {
+            Iterator<Contribution> iter = plan.iterator();
+            newPlan = new FourContributionMergePlan(iter.next(), iter.next(), iter.next(), contribution);
+        } else if (plan instanceof FourContributionMergePlan) {
+            Iterator<Contribution> iter = plan.iterator();
+            newPlan = new FiveContributionMergePlan(iter.next(), iter.next(), iter.next(), iter.next(), contribution);
+        } else {
+            MultipleContributionMergePlan multiPlan = new MultipleContributionMergePlan();
+            for (Contribution existingContribution : plan) {
+                multiPlan.addContribution(existingContribution);
+            }
+            multiPlan.addContribution(contribution);
+            newPlan = multiPlan;
         }
-        if (plan instanceof DoubleContributionMergePlan) {
-            Iterator<Contribution> iterator = plan.iterator();
-            MergePlan newPlan = new TripleContributionMergePlan(iterator.next(), iterator.next(), contribution);
-            newPlan.setAnnotations(plan.getAnnotations());
-        }
-        MultipleContributionMergePlan newPlan = new MultipleContributionMergePlan();
-        for (Contribution existingContribution : plan) {
-            newPlan.addContribution(existingContribution);
-        }
-        newPlan.addContribution(contribution);
         newPlan.setAnnotations(plan.getAnnotations());
         return newPlan;
     }

Modified: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/MultipleContributionMergePlan.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/MultipleContributionMergePlan.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/MultipleContributionMergePlan.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -49,6 +49,31 @@
     }
 
     /**
+     * @param contributions the contributions for this merge plan
+     */
+    public MultipleContributionMergePlan( Iterable<Contribution> contributions ) {
+        assert contributions != null;
+        this.contributions = new CopyOnWriteArrayList<Contribution>();
+        for (Contribution contribution : contributions) {
+            assert contribution != null;
+            this.contributions.add(contribution);
+        }
+    }
+
+    /**
+     * @param contributions the contributions for this merge plan
+     */
+    public MultipleContributionMergePlan( Iterator<Contribution> contributions ) {
+        assert contributions != null;
+        this.contributions = new CopyOnWriteArrayList<Contribution>();
+        while (contributions.hasNext()) {
+            Contribution contribution = contributions.next();
+            assert contribution != null;
+            this.contributions.add(contribution);
+        }
+    }
+
+    /**
      * {@inheritDoc}
      * 
      * @see org.jboss.dna.connector.federation.merge.MergePlan#getContributionCount()

Copied: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/OneContributionMergePlan.java (from rev 369, trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/SingleContributionMergePlan.java)
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/OneContributionMergePlan.java	                        (rev 0)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/OneContributionMergePlan.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -0,0 +1,104 @@
+/*
+ * 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.Iterator;
+import java.util.NoSuchElementException;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.connector.federation.contribution.Contribution;
+
+/**
+ * @author Randall Hauch
+ */
+ at ThreadSafe
+public class OneContributionMergePlan extends MergePlan {
+
+    private static final long serialVersionUID = 1L;
+    private final Contribution contribution;
+
+    /**
+     * @param contribution the contribution for this merge plan
+     */
+    public OneContributionMergePlan( Contribution contribution ) {
+        assert contribution != null;
+        this.contribution = contribution;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.merge.MergePlan#getContributionCount()
+     */
+    @Override
+    public int getContributionCount() {
+        return 1;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.merge.MergePlan#getContributionFrom(java.lang.String)
+     */
+    @Override
+    public Contribution getContributionFrom( String sourceName ) {
+        return isSource(sourceName) ? contribution : null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Iterable#iterator()
+     */
+    public Iterator<Contribution> iterator() {
+        return new Iterator<Contribution>() {
+            private boolean next = true;
+
+            public boolean hasNext() {
+                return next;
+            }
+
+            @SuppressWarnings( "synthetic-access" )
+            public Contribution next() {
+                if (next) {
+                    next = false;
+                    return contribution;
+                }
+                throw new NoSuchElementException();
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.merge.MergePlan#isSource(java.lang.String)
+     */
+    @Override
+    public boolean isSource( String sourceName ) {
+        return contribution.getSourceName().equals(sourceName);
+    }
+
+}

Deleted: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/SingleContributionMergePlan.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/SingleContributionMergePlan.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/SingleContributionMergePlan.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -1,104 +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.Iterator;
-import java.util.NoSuchElementException;
-import net.jcip.annotations.ThreadSafe;
-import org.jboss.dna.connector.federation.contribution.Contribution;
-
-/**
- * @author Randall Hauch
- */
- at ThreadSafe
-public class SingleContributionMergePlan extends MergePlan {
-
-    private static final long serialVersionUID = 1L;
-    private final Contribution contribution;
-
-    /**
-     * @param contribution the contribution for this merge plan
-     */
-    public SingleContributionMergePlan( Contribution contribution ) {
-        assert contribution != null;
-        this.contribution = contribution;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.connector.federation.merge.MergePlan#getContributionCount()
-     */
-    @Override
-    public int getContributionCount() {
-        return 1;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.connector.federation.merge.MergePlan#getContributionFrom(java.lang.String)
-     */
-    @Override
-    public Contribution getContributionFrom( String sourceName ) {
-        return isSource(sourceName) ? contribution : null;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Iterable#iterator()
-     */
-    public Iterator<Contribution> iterator() {
-        return new Iterator<Contribution>() {
-            private boolean next = true;
-
-            public boolean hasNext() {
-                return next;
-            }
-
-            @SuppressWarnings( "synthetic-access" )
-            public Contribution next() {
-                if (next) {
-                    next = false;
-                    return contribution;
-                }
-                throw new NoSuchElementException();
-            }
-
-            public void remove() {
-                throw new UnsupportedOperationException();
-            }
-        };
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.connector.federation.merge.MergePlan#isSource(java.lang.String)
-     */
-    @Override
-    public boolean isSource( String sourceName ) {
-        return contribution.getSourceName().equals(sourceName);
-    }
-
-}

Copied: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/ThreeContributionMergePlan.java (from rev 369, trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/TripleContributionMergePlan.java)
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/ThreeContributionMergePlan.java	                        (rev 0)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/ThreeContributionMergePlan.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -0,0 +1,128 @@
+/*
+ * 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.Iterator;
+import java.util.NoSuchElementException;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.connector.federation.contribution.Contribution;
+
+/**
+ * @author Randall Hauch
+ */
+ at ThreadSafe
+public class ThreeContributionMergePlan extends MergePlan {
+
+    private static final long serialVersionUID = 1L;
+    private final Contribution contribution1;
+    private final Contribution contribution2;
+    private final Contribution contribution3;
+
+    /**
+     * @param contribution1 the first contribution for this merge plan
+     * @param contribution2 the second contribution for this merge plan
+     * @param contribution3 the third contribution for this merge plan
+     */
+    public ThreeContributionMergePlan( Contribution contribution1,
+                                       Contribution contribution2,
+                                       Contribution contribution3 ) {
+        assert contribution1 != null;
+        assert contribution2 != null;
+        assert contribution3 != null;
+        this.contribution1 = contribution1;
+        this.contribution2 = contribution2;
+        this.contribution3 = contribution3;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.merge.MergePlan#getContributionCount()
+     */
+    @Override
+    public int getContributionCount() {
+        return 3;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.merge.MergePlan#getContributionFrom(java.lang.String)
+     */
+    @Override
+    public Contribution getContributionFrom( String sourceName ) {
+        if (contribution1.getSourceName().equals(sourceName)) return contribution1;
+        if (contribution2.getSourceName().equals(sourceName)) return contribution2;
+        if (contribution3.getSourceName().equals(sourceName)) return contribution3;
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Iterable#iterator()
+     */
+    public Iterator<Contribution> iterator() {
+        return new Iterator<Contribution>() {
+            private int next = 3;
+
+            public boolean hasNext() {
+                return next > 0;
+            }
+
+            @SuppressWarnings( "synthetic-access" )
+            public Contribution next() {
+                if (next == 3) {
+                    next = 2;
+                    return contribution1;
+                }
+                if (next == 2) {
+                    next = 1;
+                    return contribution2;
+                }
+                if (next == 1) {
+                    next = 0;
+                    return contribution3;
+                }
+                throw new NoSuchElementException();
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.merge.MergePlan#isSource(java.lang.String)
+     */
+    @Override
+    public boolean isSource( String sourceName ) {
+        if (contribution1.getSourceName().equals(sourceName)) return true;
+        if (contribution2.getSourceName().equals(sourceName)) return true;
+        if (contribution3.getSourceName().equals(sourceName)) return true;
+        return false;
+    }
+
+}

Deleted: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/TripleContributionMergePlan.java
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/TripleContributionMergePlan.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/TripleContributionMergePlan.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -1,126 +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.Iterator;
-import java.util.NoSuchElementException;
-import net.jcip.annotations.ThreadSafe;
-import org.jboss.dna.connector.federation.contribution.Contribution;
-
-/**
- * @author Randall Hauch
- */
- at ThreadSafe
-public class TripleContributionMergePlan extends MergePlan {
-
-    private static final long serialVersionUID = 1L;
-    private final Contribution contribution1;
-    private final Contribution contribution2;
-    private final Contribution contribution3;
-
-    /**
-     * @param contribution1 the first contribution for this merge plan
-     * @param contribution2 the second contribution for this merge plan
-     * @param contribution3 the third contribution for this merge plan
-     */
-    public TripleContributionMergePlan( Contribution contribution1,
-                                        Contribution contribution2,
-                                        Contribution contribution3 ) {
-        assert contribution1 != null;
-        assert contribution2 != null;
-        assert contribution3 != null;
-        this.contribution1 = contribution1;
-        this.contribution2 = contribution2;
-        this.contribution3 = contribution3;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.connector.federation.merge.MergePlan#getContributionCount()
-     */
-    @Override
-    public int getContributionCount() {
-        return 3;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.connector.federation.merge.MergePlan#getContributionFrom(java.lang.String)
-     */
-    @Override
-    public Contribution getContributionFrom( String sourceName ) {
-        if (contribution1.getSourceName().equals(sourceName)) return contribution1;
-        if (contribution2.getSourceName().equals(sourceName)) return contribution2;
-        if (contribution3.getSourceName().equals(sourceName)) return contribution3;
-        return null;
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see java.lang.Iterable#iterator()
-     */
-    public Iterator<Contribution> iterator() {
-        return new Iterator<Contribution>() {
-            private int next = 3;
-
-            public boolean hasNext() {
-                return next > 0;
-            }
-
-            @SuppressWarnings( "synthetic-access" )
-            public Contribution next() {
-                if (next == 3) {
-                    next = 2;
-                    return contribution3;
-                }
-                if (next == 2) {
-                    next = 1;
-                    return contribution2;
-                }
-                if (next == 1) {
-                    next = 0;
-                    return contribution1;
-                }
-                throw new NoSuchElementException();
-            }
-
-            public void remove() {
-                throw new UnsupportedOperationException();
-            }
-        };
-    }
-
-    /**
-     * {@inheritDoc}
-     * 
-     * @see org.jboss.dna.connector.federation.merge.MergePlan#isSource(java.lang.String)
-     */
-    @Override
-    public boolean isSource( String sourceName ) {
-        return contribution1.getSourceName().equals(sourceName) || contribution2.getSourceName().equals(sourceName)
-               || contribution3.getSourceName().equals(sourceName);
-    }
-
-}

Copied: trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/TwoContributionMergePlan.java (from rev 369, trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/DoubleContributionMergePlan.java)
===================================================================
--- trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/TwoContributionMergePlan.java	                        (rev 0)
+++ trunk/connectors/dna-connector-federation/src/main/java/org/jboss/dna/connector/federation/merge/TwoContributionMergePlan.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -0,0 +1,115 @@
+/*
+ * 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.Iterator;
+import java.util.NoSuchElementException;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.connector.federation.contribution.Contribution;
+
+/**
+ * @author Randall Hauch
+ */
+ at ThreadSafe
+public class TwoContributionMergePlan extends MergePlan {
+
+    private static final long serialVersionUID = 1L;
+    private final Contribution contribution1;
+    private final Contribution contribution2;
+
+    /**
+     * @param contribution1 the first contribution for this merge plan
+     * @param contribution2 the second contribution for this merge plan
+     */
+    public TwoContributionMergePlan( Contribution contribution1,
+                                     Contribution contribution2 ) {
+        assert contribution1 != null;
+        assert contribution2 != null;
+        this.contribution1 = contribution1;
+        this.contribution2 = contribution2;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.merge.MergePlan#getContributionCount()
+     */
+    @Override
+    public int getContributionCount() {
+        return 2;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.merge.MergePlan#getContributionFrom(java.lang.String)
+     */
+    @Override
+    public Contribution getContributionFrom( String sourceName ) {
+        if (contribution1.getSourceName().equals(sourceName)) return contribution1;
+        if (contribution2.getSourceName().equals(sourceName)) return contribution2;
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see java.lang.Iterable#iterator()
+     */
+    public Iterator<Contribution> iterator() {
+        return new Iterator<Contribution>() {
+            private int next = 2;
+
+            public boolean hasNext() {
+                return next > 0;
+            }
+
+            @SuppressWarnings( "synthetic-access" )
+            public Contribution next() {
+                if (next == 2) {
+                    next = 1;
+                    return contribution1;
+                }
+                if (next == 1) {
+                    next = 0;
+                    return contribution2;
+                }
+                throw new NoSuchElementException();
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.connector.federation.merge.MergePlan#isSource(java.lang.String)
+     */
+    @Override
+    public boolean isSource( String sourceName ) {
+        return contribution1.getSourceName().equals(sourceName) || contribution2.getSourceName().equals(sourceName);
+    }
+
+}

Modified: trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepository.java
===================================================================
--- trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepository.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/connectors/dna-connector-inmemory/src/main/java/org/jboss/dna/connector/inmemory/InMemoryRepository.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -49,6 +49,7 @@
 import org.jboss.dna.spi.graph.commands.SetPropertiesCommand;
 import org.jboss.dna.spi.graph.commands.executor.AbstractCommandExecutor;
 import org.jboss.dna.spi.graph.commands.executor.CommandExecutor;
+import org.jboss.dna.spi.graph.impl.BasicPath;
 
 /**
  * @author Randall Hauch
@@ -135,6 +136,37 @@
         return node;
     }
 
+    /**
+     * Find the lowest existing node along the path.
+     * 
+     * @param path the path to the node; may not be null
+     * @return the lowest existing node along the path, or the root node if no node exists on the path
+     */
+    public Path getLowestExistingPath( Path path ) {
+        assert path != null;
+        Node node = getRoot();
+        int segmentNumber = 0;
+        for (Path.Segment segment : path) {
+            Node desiredChild = null;
+            for (Node child : node.getChildren()) {
+                if (child == null) continue;
+                Path.Segment childName = child.getName();
+                if (childName == null) continue;
+                if (childName.equals(segment)) {
+                    desiredChild = child;
+                    break;
+                }
+            }
+            if (desiredChild != null) {
+                node = desiredChild;
+            } else {
+                return path.subpath(0, segmentNumber);
+            }
+            ++segmentNumber;
+        }
+        return BasicPath.ROOT;
+    }
+
     protected UUID generateUuid() {
         return UUID.randomUUID();
     }
@@ -305,6 +337,10 @@
             Path parent = path.getAncestor();
             // Look up the parent node, which must exist ...
             Node parentNode = getNode(parent);
+            if (parentNode == null) {
+                Path lowestExisting = getLowestExistingPath(parent);
+                throw new PathNotFoundException(path, lowestExisting, InMemoryConnectorI18n.nodeDoesNotExist.text(parent));
+            }
             Node node = createNode(getEnvironment(), parentNode, path.getLastSegment().getName());
             // Now add the properties to the supplied node ...
             for (Property property : command.getProperties()) {
@@ -404,7 +440,8 @@
             // Look up the node with the supplied path ...
             Node node = InMemoryRepository.this.getNode(path);
             if (node == null) {
-                throw new PathNotFoundException(path, InMemoryConnectorI18n.nodeDoesNotExist.text(path));
+                Path lowestExisting = getLowestExistingPath(path);
+                throw new PathNotFoundException(path, lowestExisting, InMemoryConnectorI18n.nodeDoesNotExist.text(path));
             }
             return null;
         }

Modified: trunk/connectors/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheConnection.java
===================================================================
--- trunk/connectors/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheConnection.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/connectors/dna-connector-jbosscache/src/main/java/org/jboss/dna/connector/jbosscache/JBossCacheConnection.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -35,6 +35,7 @@
 import org.jboss.dna.spi.cache.CachePolicy;
 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.PathNotFoundException;
 import org.jboss.dna.spi.graph.Property;
 import org.jboss.dna.spi.graph.PropertyFactory;
@@ -185,6 +186,15 @@
         return Fqn.fromElements(pathSegment);
     }
 
+    protected Path getPath( PathFactory factory,
+                            Fqn<Path.Segment> fqn ) {
+        Path.Segment[] segments = new Path.Segment[fqn.size()];
+        for (int i = 0; i != segments.length; ++i) {
+            segments[i] = fqn.get(i);
+        }
+        return factory.create(factory.createRootPath(), segments);
+    }
+
     protected Node<Name, Object> getNode( ExecutionContext context,
                                           Path path ) {
         // Look up the node with the supplied path ...
@@ -192,7 +202,16 @@
         Node<Name, Object> node = cache.getNode(fqn);
         if (node == null) {
             String nodePath = path.getString(context.getNamespaceRegistry());
-            throw new PathNotFoundException(path, JBossCacheConnectorI18n.nodeDoesNotExist.text(nodePath));
+            Path lowestExisting = null;
+            while (fqn != null) {
+                fqn = fqn.getParent();
+                node = cache.getNode(fqn);
+                if (node != null) {
+                    lowestExisting = getPath(context.getValueFactories().getPathFactory(), fqn);
+                    fqn = null;
+                }
+            }
+            throw new PathNotFoundException(path, lowestExisting, JBossCacheConnectorI18n.nodeDoesNotExist.text(nodePath));
         }
         return node;
 

Modified: trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/Path.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/Path.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/Path.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -401,7 +401,7 @@
      * @param degree
      * @return the ancestor of the supplied degree
      * @throws IllegalArgumentException if the degree is negative
-     * @throws PathNotFoundException if the degree is greater than the {@link #size() length} of this path
+     * @throws InvalidPathException if the degree is greater than the {@link #size() length} of this path
      */
     public Path getAncestor( int degree );
 

Modified: trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/PathNotFoundException.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/PathNotFoundException.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/PathNotFoundException.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -31,44 +31,53 @@
     private static final long serialVersionUID = -3703984046286975978L;
 
     private final Path path;
+    private final Path lowestAncestorThatDoesExist;
 
     /**
      * @param path
+     * @param lowestAncestorThatDoesExist
      */
-    public PathNotFoundException( Path path ) {
+    public PathNotFoundException( Path path, Path lowestAncestorThatDoesExist ) {
         this.path = path;
+        this.lowestAncestorThatDoesExist = lowestAncestorThatDoesExist;
     }
 
     /**
      * @param path
+     * @param lowestAncestorThatDoesExist
      * @param message
      */
-    public PathNotFoundException( Path path,
+    public PathNotFoundException( Path path, Path lowestAncestorThatDoesExist,
                                   String message ) {
         super(message);
         this.path = path;
+        this.lowestAncestorThatDoesExist = lowestAncestorThatDoesExist;
     }
 
     /**
      * @param path
+     * @param lowestAncestorThatDoesExist
      * @param cause
      */
-    public PathNotFoundException( Path path,
+    public PathNotFoundException( Path path, Path lowestAncestorThatDoesExist,
                                   Throwable cause ) {
         super(cause);
         this.path = path;
+        this.lowestAncestorThatDoesExist = lowestAncestorThatDoesExist;
     }
 
     /**
      * @param path
+     * @param lowestAncestorThatDoesExist
      * @param message
      * @param cause
      */
-    public PathNotFoundException( Path path,
+    public PathNotFoundException( Path path, Path lowestAncestorThatDoesExist,
                                   String message,
                                   Throwable cause ) {
         super(message, cause);
         this.path = path;
+        this.lowestAncestorThatDoesExist = lowestAncestorThatDoesExist;
     }
 
     /**
@@ -87,4 +96,12 @@
     public Path getPath() {
         return path;
     }
+    
+    /**
+     * Get the lowest (closest) existing {@link Path#getAncestor() ancestor} of the {@link #getPath() non-existant path}.
+     * @return the lowest ancestor that does exist
+     */
+    public Path getLowestAncestorThatDoesExist() {
+        return lowestAncestorThatDoesExist;
+    }
 }

Modified: trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/commands/NodeConflictBehavior.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/commands/NodeConflictBehavior.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/commands/NodeConflictBehavior.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -31,6 +31,7 @@
 
     DO_NOT_REPLACE,
     APPEND,
-    REPLACE;
+    REPLACE,
+    UPDATE;
 
 }

Modified: trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/commands/impl/BasicCreateNodeCommand.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/commands/impl/BasicCreateNodeCommand.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/commands/impl/BasicCreateNodeCommand.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -21,7 +21,7 @@
  */
 package org.jboss.dna.spi.graph.commands.impl;
 
-import java.util.List;
+import java.util.Collection;
 import net.jcip.annotations.NotThreadSafe;
 import org.jboss.dna.common.util.StringUtil;
 import org.jboss.dna.spi.graph.Path;
@@ -39,7 +39,7 @@
      */
     private static final long serialVersionUID = -5452285887178397354L;
     private final Path path;
-    private final List<Property> properties;
+    private final Collection<Property> properties;
     private final NodeConflictBehavior conflictBehavior;
 
     /**
@@ -48,7 +48,7 @@
      * @param conflictBehavior the desired behavior when a node exists at the <code>path</code>; may not be null
      */
     public BasicCreateNodeCommand( Path path,
-                                   List<Property> properties,
+                                   Collection<Property> properties,
                                    NodeConflictBehavior conflictBehavior ) {
         super();
         assert path != null;

Modified: trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicPath.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicPath.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/BasicPath.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -35,7 +35,6 @@
 import org.jboss.dna.spi.graph.InvalidPathException;
 import org.jboss.dna.spi.graph.NamespaceRegistry;
 import org.jboss.dna.spi.graph.Path;
-import org.jboss.dna.spi.graph.PathNotFoundException;
 
 /**
  * A basic implementation of {@link Path}.
@@ -99,7 +98,7 @@
         int endIndex = this.segments.size() - degree;
         if (endIndex < 0) {
             String msg = SpiI18n.pathAncestorDegreeIsInvalid.text(this.getString(), Inflector.getInstance().ordinalize(degree));
-            throw new PathNotFoundException(this, msg);
+            throw new InvalidPathException(msg);
         }
         return subpath(0, endIndex);
     }

Modified: trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/impl/BasicPathTest.java
===================================================================
--- trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/impl/BasicPathTest.java	2008-07-28 15:00:27 UTC (rev 374)
+++ trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/impl/BasicPathTest.java	2008-07-28 21:51:05 UTC (rev 375)
@@ -35,7 +35,6 @@
 import org.jboss.dna.spi.graph.InvalidPathException;
 import org.jboss.dna.spi.graph.Name;
 import org.jboss.dna.spi.graph.Path;
-import org.jboss.dna.spi.graph.PathNotFoundException;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -241,7 +240,7 @@
         assertThat(path.getAncestor(3), is(ROOT));
     }
 
-    @Test( expected = PathNotFoundException.class )
+    @Test( expected = InvalidPathException.class )
     public void shouldNotAllowAncestorDegreeLargerThanSize() {
         path.getAncestor(path.size() + 1);
     }




More information about the dna-commits mailing list