### Eclipse Workspace Patch 1.0 #P oak-core Index: src/test/java/org/apache/jackrabbit/oak/core/ImmutableTreeTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/core/ImmutableTreeTest.java (revision 1500615) +++ src/test/java/org/apache/jackrabbit/oak/core/ImmutableTreeTest.java (working copy) @@ -18,11 +18,11 @@ */ package org.apache.jackrabbit.oak.core; +import static org.apache.jackrabbit.oak.OakAssert.assertSequence; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; - import org.apache.jackrabbit.oak.OakBaseTest; import org.apache.jackrabbit.oak.api.CommitFailedException; import org.apache.jackrabbit.oak.api.ContentSession; @@ -32,6 +32,7 @@ import org.junit.Before; import org.junit.Test; + public class ImmutableTreeTest extends OakBaseTest { private Root root; @@ -118,4 +119,34 @@ // success } } + + @Test + public void orderBefore() throws Exception { + TreeImpl t = (TreeImpl) root.getTree("/x/y/z"); + + t.addChild("node1"); + t.addChild("node2"); + t.addChild("node3"); + + + t.getChild("node1").orderBefore("node2"); + t.getChild("node3").orderBefore(null); + + root.commit(); + + ImmutableTree tree = new ImmutableTree(t.getNodeState()); + assertSequence(tree.getChildren(), "node1", "node2", "node3"); + + t.getChild("node3").orderBefore("node2"); + root.commit(); + + tree = new ImmutableTree(t.getNodeState()); + assertSequence(tree.getChildren(), "node1", "node3", "node2"); + + t.getChild("node1").orderBefore(null); + root.commit(); + + tree = new ImmutableTree(t.getNodeState()); + assertSequence(tree.getChildren(), "node3", "node2", "node1"); + } } Index: src/main/java/org/apache/jackrabbit/oak/core/ImmutableTree.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/core/ImmutableTree.java (revision 1500602) +++ src/main/java/org/apache/jackrabbit/oak/core/ImmutableTree.java (working copy) @@ -17,14 +17,20 @@ package org.apache.jackrabbit.oak.core; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.transform; import static org.apache.jackrabbit.oak.api.Type.STRING; import java.util.Iterator; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import com.google.common.base.Function; import com.google.common.base.Objects; +import com.google.common.base.Predicate; + import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Root; @@ -32,6 +38,7 @@ import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry; import org.apache.jackrabbit.oak.spi.state.NodeState; +import org.apache.jackrabbit.oak.spi.state.NodeStateUtils; /** * Immutable implementation of the {@code Tree} interface in order to provide @@ -95,6 +102,11 @@ * FIXME: merge with ReadOnlyTree */ public final class ImmutableTree extends ReadOnlyTree { + + /** + * Internal and hidden property that contains the child order + */ + public static final String OAK_CHILD_ORDER = ":childOrder"; private final ParentProvider parentProvider; private final TreeTypeProvider typeProvider; @@ -171,44 +183,37 @@ NodeState child = state.getChildNode(name); return new ImmutableTree(this, name, child); } - + /** * This implementation does not respect ordered child nodes, but always * returns them in some implementation specific order. *

- * TODO: respect orderable children (needed?) * * @return the children. */ @Override public Iterable getChildren() { - return new Iterable() { - @Override - public Iterator iterator() { - final Iterator iterator = state.getChildNodeEntries().iterator(); - return new Iterator() { + Iterable childNames; + if (hasOrderableChildren()) { + childNames = getOrderedChildNames(); + } else { + childNames = state.getChildNodeNames(); + } + return transform( + filter(childNames, new Predicate() { @Override - public boolean hasNext() { - return iterator.hasNext(); + public boolean apply(@Nullable String name) { + return !isHidden(name); } - + }), + new Function() { @Override - public Tree next() { - ChildNodeEntry entry = iterator.next(); - return new ImmutableTree( - ImmutableTree.this, - entry.getName(), entry.getNodeState()); + public Tree apply(String input) { + return new ImmutableTree(ImmutableTree.this, input,state.getChildNode(input)); } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } - }; + }); } - + //-------------------------------------------------------------< Object >--- @Override public int hashCode() { @@ -263,6 +268,52 @@ return PathUtils.concat(getParent().getIdentifier(), getName()); } } + + /** + * @return {@code true} if this tree has orderable children; + * {@code false} otherwise. + */ + private boolean hasOrderableChildren() { + return state.hasProperty(OAK_CHILD_ORDER); + } + + /** + * Returns the ordered child names. This method must only be called when + * this tree {@link #hasOrderableChildren()}. + * + * @return the ordered child names. + */ + private Iterable getOrderedChildNames() { + assert hasOrderableChildren(); + return new Iterable() { + @Override + public Iterator iterator() { + return new Iterator() { + final PropertyState childOrder = state.getProperty(OAK_CHILD_ORDER); + int index = 0; + + @Override + public boolean hasNext() { + return index < childOrder.count(); + } + + @Override + public String next() { + return childOrder.getValue(STRING, index++); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + + private static boolean isHidden(String name) { + return NodeStateUtils.isHidden(name); + } //-------------------------------------------------------------------------- public interface ParentProvider {