You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@jackrabbit.apache.org by Vijai Kalyan <vi...@gmail.com> on 2007/12/12 18:37:36 UTC

Reordering of Child Nodes

Hello All,

We think we have discovered a problem in reordering of nodes. I apologize
upfront for the long post, but we are hoping the information will help.

Background

We have been attempting to use JackRabbit for meta-data management. 

We have adopted a model where we have meta-meta artifacts, for example like


Model
Atom
Implementation


so that the node names will actually be "Model" and "Atom" and
"Implementation". One reason for this is that this helps us retrieve "all
models in the repository". The othery way of doing this is of course, to
store nodes with the actual name and then include a type property that is
one of "Model", "Atom" or "Implementation".

Attributes are likewise stored as nodes instead of as properties (so that we
can reorder them or look for all defined atttributes across all models and
so on). 

Problem

Assume we have a node "Parent" as a child of the root node. Let us assume
further that "Parent" has three children "Child", "Child", "Child" with the
name property of each set to "1", "2" and "3". Thus we have


root
  |
  + parent
        |
        + child [name = 1]
        |
        + child [name = 2]
        |
        + child [name = 3]


Now we want to reorder the "child" nodes in reverse order of "name"
property. That is, we want to change the above to the following


root
  |
  + parent
        |
        + child [name = 3]
        |
        + child [name = 2]
        |
        + child [name = 1]


We figured that this is equivalent to an algorithm for doing the following

Given an input list {1, 2, ... , N} and a required output list {a1, a2, ...
, aN}, generate a sequence of operations using only the primitive operation 

orderBefore (y, x): x,y ---> y,x

That is orderBefore will place element y before x. I think this is a good
enough abstraction of using the Node::orderBefore method to achieve this. 

We wrote some tests to test our assumptions. Our first program was:-


public class OrderAttributes 
{
	/**
	 * @param args
	 */
	public static void main(String[] args) 
		throws Exception
	{
		Repository repository = new TransientRepository();
		Session session = repository.login(new SimpleCredentials("username",
"password".toCharArray()));
		
	    try 
	    {
	        Node root = session.getRootNode();
	        
	        Node node1 = root.addNode("node1");
	        Node node2 = root.addNode("node2");
	        Node node3 = root.addNode("node3");
	        
	        System.out.println(node1.getName() + " @ " + node1.getIndex());
	        System.out.println(node2.getName() + " @ " + node2.getIndex());
	        System.out.println(node3.getName() + " @ " + node3.getIndex());
	        
	        printChildren(root, System.out);
	        
	        root.orderBefore("node2", "node1");
	        
	        printChildren(root, System.out);
	        
	        root.orderBefore("node3", "node2");

	        printChildren(root, System.out);
	    }
	    finally
	    {
	    	session.logout();
	    }
	}
	
    /**
     * 
     * @param node
     * @param target
     * @throws RepositoryException
     */
    public static void printChildren (Node node, PrintStream target)
        throws RepositoryException
    {
        if (node == null || target == null) 
        {
            return;
        }
        
        target.println(node.getName());
        
        NodeIterator iter = node.getNodes();
        
        while (iter.hasNext())
        {
            Node child = iter.nextNode();
            
            target.println(child.getName());
        }
    }
}


This worked correctly and produce the following output:-


node1 @ 1
node2 @ 1
node3 @ 1

jcr:system
node1
node2
node3

jcr:system
node2
node1
node3

jcr:system
node3
node2
node1


We then attempted to do what we wanted to do:-


public class ReOrderAttributes 
{
	/**
	 * @param args
	 */
	public static void main(String[] args) 
		throws Exception
	{
	        Repository repository = new TransientRepository();
	        Session session = repository.login(new
SimpleCredentials("username", "password".toCharArray()));
		
	    try 
	    {
	        Node root = session.getRootNode();
	        
	        Node parent = root.addNode("parent");
	        
	        Node node1 = createNode(parent, 1);
	        Node node2 = createNode(parent, 2);
	        Node node3 = createNode(parent, 3);
	        
	        session.save();
	        
                     printChildren(parent, System.out);
	        
	        String path1 = getRelativePath(parent, node1.getPath());
	        String path2 = getRelativePath(parent, node2.getPath());
	        String path3 = getRelativePath(parent, node3.getPath());
	        
	        printChildren(parent, System.out);
	        
	        parent.orderBefore(getRelativePath(parent, node2.getPath()),
getRelativePath(parent, node1.getPath()));
	        
	        System.out.println(node1.getPath());
                    System.out.println(node2.getPath());
                    System.out.println(node3.getPath());
	        
	        printChildren(parent, System.out);
	        
	        parent.orderBefore(getRelativePath(parent, node3.getPath()),
getRelativePath(parent, node2.getPath()));

	        printChildren(parent, System.out);
	    }
	    finally
	    {
	    	session.logout();
	    }
	}
	
	/**
	 * 
	 * @param index
	 * @return
	 * @throws RepositoryException
	 */
	private static Node createNode (Node parent, int index)
	    throws RepositoryException
	{
	    Node node = parent.addNode("ChildNode");
	    
	    node.setProperty("name", String.valueOf(index));
	    
	    return node;
	}
	
    /**
     * 
     * @param parent
     * @param target
     * @throws RepositoryException
     */
    private static void printChildren (Node parent, PrintStream target)
        throws RepositoryException
    {
        if (parent == null || target == null) 
        {
            return;
        }
        
        target.println(parent.getName());
        
        NodeIterator iter = parent.getNodes();
        
        while (iter.hasNext())
        {
            Node child = iter.nextNode();
            
            target.print(child.getName());
            
            try
            {
                target.println("[name = " +
child.getProperty("name").getString() + "]");
            }
            catch (Exception ex)
            {
                ex.printStackTrace(System.out);
            }
        }
    }
    
    /**
     * Given an absolute path and a node, this function computes the
relative path
     * from that node to the node pointed to by the given absolute path.
Note that
     * this function does not handle namespace resolution. Hence namespace
qualified
     * paths will not be properly handled by calls to this function.
     * 
     * @param node A {@link javax.jcr.Node} instance relative to which we
want the
     *             node pointed to by the given absolute path.
     * @param absolutePath The absolute path to convert into a relative
path.
     * @return A relative path string.
     * @throws RepositoryException
     * @throws IllegalArgumentException
     * @throws MalformedPathException
     * @throws NoPrefixDeclaredException
     */
    public static String getRelativePath (Node node, String absolutePath)
        throws RepositoryException, MalformedPathException,
NoPrefixDeclaredException
    {
        if (absolutePath.length() <= 0)
        {
            throw new IllegalArgumentException("Invalid absolute path
argument.");
        }
        
        Path mainNodePath = PathFormat.parse(node.getPath(),
DummyNamespaceResolver.getInstance());
        Path relNodePath = PathFormat.parse(absolutePath,
DummyNamespaceResolver.getInstance());
        
        Path relPath = mainNodePath.computeRelativePath(relNodePath);
        
        return PathFormat.format(relPath,
DummyNamespaceResolver.getInstance());
    }
    
    /**
     * @author Vijai Kalyan
     */
    private static class DummyNamespaceResolver extends
AbstractNamespaceResolver
    {
        /** Required by PROS coding standards. Do not remove. */
        public static final String SOURCE_FILE_REVISION = "$ Revision: $";
        
        static final NamespaceResolver instance = new
DummyNamespaceResolver();
        
        static final NamespaceResolver getInstance()
        {
            return instance;
        }

        /**
         * 
         */
        private DummyNamespaceResolver()
        {
            super(false);
        }

        /* (non-Javadoc)
         * @see
org.apache.jackrabbit.name.NamespaceResolver#getPrefix(java.lang.String)
         */
        public String getPrefix(String uri) throws NamespaceException
        {
            return "";
        }

        /* (non-Javadoc)
         * @see
org.apache.jackrabbit.name.NamespaceResolver#getURI(java.lang.String)
         */
        public String getURI(String prefix) throws NamespaceException
        {
            return "";
        }
    }
}


This produced the following output:-


parent
ChildNode[name = 1]
ChildNode[name = 2]
ChildNode[name = 3]
parent
ChildNode[name = 1]
ChildNode[name = 2]
ChildNode[name = 3]
/parent/ChildNode
/parent/ChildNode[2]
/parent/ChildNode[3]
parent
ChildNode[name = 2]
ChildNode[name = 1]
ChildNode[name = 3]
parent
ChildNode[name = 2]
ChildNode[name = 3]
ChildNode[name = 1]


When we debugged, we found that in the following code
(NodeImpl::orderBefore):-


    /**
     * Same as {@link Node#orderBefore(String, String)} except that
     * this method takes a Path.PathElement arguments instead of
     * Strings.
     *
     * @param srcName
     * @param dstName
     * @throws UnsupportedRepositoryOperationException
     * @throws VersionException
     * @throws ConstraintViolationException
     * @throws ItemNotFoundException
     * @throws LockException
     * @throws RepositoryException
     */
    public synchronized void orderBefore(Path.PathElement srcName,
                                         Path.PathElement dstName)
            throws UnsupportedRepositoryOperationException,
VersionException,
            ConstraintViolationException, ItemNotFoundException,
LockException,
            RepositoryException {

        // check state of this instance
        sanityCheck();

        if (!getPrimaryNodeType().hasOrderableChildNodes()) {
            throw new UnsupportedRepositoryOperationException(
                    "child node ordering not supported on node "
                    + safeGetJCRPath());
        }

        // check arguments
        if (srcName.equals(dstName)) {
            // there's nothing to do
            return;
        }

        // check existence
        if (!hasNode(srcName.getName(), srcName.getIndex())) {
            String name;
            try {
                Path.PathElement[] path = new Path.PathElement[] { srcName
};
                name = session.getJCRPath(new
Path.PathBuilder(path).getPath());
            } catch (NameException e) {
                name = srcName.toString();
            } catch (NamespaceException e) {
                name = srcName.toString();
            }
            throw new ItemNotFoundException(safeGetJCRPath()
                    + " has no child node with name " + name);
        }
        if (dstName != null && !hasNode(dstName.getName(),
dstName.getIndex())) {
            String name;
            try {
                Path.PathElement[] path = new Path.PathElement[] { dstName
};
                name = session.getJCRPath(new
Path.PathBuilder(path).getPath());
            } catch (NameException e) {
                name = dstName.toString();
            } catch (NamespaceException e) {
                name = dstName.toString();
            }
            throw new ItemNotFoundException(safeGetJCRPath()
                    + " has no child node with name " + name);
        }

        // make sure this node is checked-out
        if (!internalIsCheckedOut()) {
            String msg = safeGetJCRPath()
                    + ": cannot change child node ordering of a checked-in
node";
            log.debug(msg);
            throw new VersionException(msg);
        }

        // check protected flag
        if (definition.isProtected()) {
            String msg = safeGetJCRPath()
                    + ": cannot change child node ordering of a protected
node";
            log.debug(msg);
            throw new ConstraintViolationException(msg);
        }

        // check lock status
        checkLock();

        ArrayList list = new ArrayList(((NodeState)
state).getChildNodeEntries());
        int srcInd = -1, destInd = -1;
        for (int i = 0; i < list.size(); i++) {
            NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry)
list.get(i);
            if (srcInd == -1) {
                if (entry.getName().equals(srcName.getName())
                        && (entry.getIndex() == srcName.getIndex()
                        || srcName.getIndex() == 0 && entry.getIndex() ==
1)) {
                    srcInd = i;
                }
            }
            if (destInd == -1 && dstName != null) {
                if (entry.getName().equals(dstName.getName())
                        && (entry.getIndex() == dstName.getIndex()
                        || dstName.getIndex() == 0 && entry.getIndex() ==
1)) {
                    destInd = i;
                    if (srcInd != -1) {
                        break;
                    }
                }
            } else {
                if (srcInd != -1) {
                    break;
                }
            }
        }

        // check if resulting order would be different to current order
        if (destInd == -1) {
            if (srcInd == list.size() - 1) {
                // no change, we're done
                return;
            }
        } else {
            if ((destInd - srcInd) == 1) {
                // no change, we're done
                return;
            }
        }

        // reorder list
        if (destInd == -1) {
            list.add(list.remove(srcInd));
        } else {
            if (srcInd < destInd) {
                list.add(destInd, list.get(srcInd));
                list.remove(srcInd);
            } else {
                list.add(destInd, list.remove(srcInd));
            }
        }

        // modify the state of 'this', i.e. the parent node
        NodeState thisState = (NodeState) getOrCreateTransientItemState();
        thisState.setChildNodeEntries(list);
    }


The value of "destInd" and "srcInd" are such that it looks like the original
order of the child nodes is being used. That is, when the second
"orderBefore" call is done, destInd should be 0 and srcInd should be 1.
However, we found that in the above, destInd was 1 and srcInd was 2 (which
is what it would be if the original order of child node entries was being
used).

So, although it looks like the state change is being changed at the end of
the "orderBefore" method, it doesn't seem to be reflected in the next
iteration.

Looking at the documentation, it indicates that calls to "orderBefore" may
not be persisted until a "save" method call is done. So, changing the
program slighlty, we introduced a line "session.save()" just after the first
call to "orderBefore". 

That ended up with the following output:-


parent
ChildNode[name = 1]
ChildNode[name = 2]
ChildNode[name = 3]
parent
ChildNode[name = 1]
ChildNode[name = 2]
ChildNode[name = 3]
/parent[2]/ChildNode[2]
/ChildNode[2]
/parent[2]/ChildNode[3]
parent
ChildNode[name = 2]
ChildNode[name = 1]
ChildNode[name = 3]
Exception in thread "main" javax.jcr.RepositoryException: invalid name:
../ChildNode[2]
	at org.apache.jackrabbit.core.NodeImpl.orderBefore(NodeImpl.java:2044)
	at ReOrderAttributes.main(ReOrderAttributes.java:69)


Note in the above that one of the "child" nodes has lost its parent.

Can someone point out to us what we may be doing wrong?

Thanks,

Vijai.
-- 
View this message in context: http://www.nabble.com/Reordering-of-Child-Nodes-tp14300056p14300056.html
Sent from the Jackrabbit - Users mailing list archive at Nabble.com.

Re: Reordering of Child Nodes

Posted by Stefan Guggisberg <st...@gmail.com>.
hi,

i ran your second test and everything seemed to work ok for me.
however, your test code seems to be overly complicated. reordering
same name sibling child nodes can be a bit tricky as the paths of the
child nodes change after a reorder operation.

if you need to simply reverse the order of some node's child nodes,
you could use the following code:

    public static void reverseChildNodeOrder(Node parent) throws
RepositoryException {
        List children = new ArrayList();
        for (NodeIterator it = parent.getNodes(); it.hasNext(); ) {
            children.add(it.nextNode());
        }

        Node dst = (Node) children.get(0);
        for (int i = children.size() - 1; i > 0; i--) {
            Node src = (Node) children.get(i);

            String srcName = src.getName() + "[" + src.getIndex() + "]";
            String dstName = dst.getName() + "[" + dst.getIndex() + "]";
            parent.orderBefore(srcName, dstName);
        }
    }


cheers
stefan

On Dec 12, 2007 6:37 PM, Vijai Kalyan <vi...@gmail.com> wrote:
>
> Hello All,
>
> We think we have discovered a problem in reordering of nodes. I apologize
> upfront for the long post, but we are hoping the information will help.
>
> Background
>
> We have been attempting to use JackRabbit for meta-data management.
>
> We have adopted a model where we have meta-meta artifacts, for example like
>
>
> Model
> Atom
> Implementation
>
>
> so that the node names will actually be "Model" and "Atom" and
> "Implementation". One reason for this is that this helps us retrieve "all
> models in the repository". The othery way of doing this is of course, to
> store nodes with the actual name and then include a type property that is
> one of "Model", "Atom" or "Implementation".
>
> Attributes are likewise stored as nodes instead of as properties (so that we
> can reorder them or look for all defined atttributes across all models and
> so on).
>
> Problem
>
> Assume we have a node "Parent" as a child of the root node. Let us assume
> further that "Parent" has three children "Child", "Child", "Child" with the
> name property of each set to "1", "2" and "3". Thus we have
>
>
> root
>   |
>   + parent
>         |
>         + child [name = 1]
>         |
>         + child [name = 2]
>         |
>         + child [name = 3]
>
>
> Now we want to reorder the "child" nodes in reverse order of "name"
> property. That is, we want to change the above to the following
>
>
> root
>   |
>   + parent
>         |
>         + child [name = 3]
>         |
>         + child [name = 2]
>         |
>         + child [name = 1]
>
>
> We figured that this is equivalent to an algorithm for doing the following
>
> Given an input list {1, 2, ... , N} and a required output list {a1, a2, ...
> , aN}, generate a sequence of operations using only the primitive operation
>
> orderBefore (y, x): x,y ---> y,x
>
> That is orderBefore will place element y before x. I think this is a good
> enough abstraction of using the Node::orderBefore method to achieve this.
>
> We wrote some tests to test our assumptions. Our first program was:-
>
>
> public class OrderAttributes
> {
>         /**
>          * @param args
>          */
>         public static void main(String[] args)
>                 throws Exception
>         {
>                 Repository repository = new TransientRepository();
>                 Session session = repository.login(new SimpleCredentials("username",
> "password".toCharArray()));
>
>             try
>             {
>                 Node root = session.getRootNode();
>
>                 Node node1 = root.addNode("node1");
>                 Node node2 = root.addNode("node2");
>                 Node node3 = root.addNode("node3");
>
>                 System.out.println(node1.getName() + " @ " + node1.getIndex());
>                 System.out.println(node2.getName() + " @ " + node2.getIndex());
>                 System.out.println(node3.getName() + " @ " + node3.getIndex());
>
>                 printChildren(root, System.out);
>
>                 root.orderBefore("node2", "node1");
>
>                 printChildren(root, System.out);
>
>                 root.orderBefore("node3", "node2");
>
>                 printChildren(root, System.out);
>             }
>             finally
>             {
>                 session.logout();
>             }
>         }
>
>     /**
>      *
>      * @param node
>      * @param target
>      * @throws RepositoryException
>      */
>     public static void printChildren (Node node, PrintStream target)
>         throws RepositoryException
>     {
>         if (node == null || target == null)
>         {
>             return;
>         }
>
>         target.println(node.getName());
>
>         NodeIterator iter = node.getNodes();
>
>         while (iter.hasNext())
>         {
>             Node child = iter.nextNode();
>
>             target.println(child.getName());
>         }
>     }
> }
>
>
> This worked correctly and produce the following output:-
>
>
> node1 @ 1
> node2 @ 1
> node3 @ 1
>
> jcr:system
> node1
> node2
> node3
>
> jcr:system
> node2
> node1
> node3
>
> jcr:system
> node3
> node2
> node1
>
>
> We then attempted to do what we wanted to do:-
>
>
> public class ReOrderAttributes
> {
>         /**
>          * @param args
>          */
>         public static void main(String[] args)
>                 throws Exception
>         {
>                 Repository repository = new TransientRepository();
>                 Session session = repository.login(new
> SimpleCredentials("username", "password".toCharArray()));
>
>             try
>             {
>                 Node root = session.getRootNode();
>
>                 Node parent = root.addNode("parent");
>
>                 Node node1 = createNode(parent, 1);
>                 Node node2 = createNode(parent, 2);
>                 Node node3 = createNode(parent, 3);
>
>                 session.save();
>
>                      printChildren(parent, System.out);
>
>                 String path1 = getRelativePath(parent, node1.getPath());
>                 String path2 = getRelativePath(parent, node2.getPath());
>                 String path3 = getRelativePath(parent, node3.getPath());
>
>                 printChildren(parent, System.out);
>
>                 parent.orderBefore(getRelativePath(parent, node2.getPath()),
> getRelativePath(parent, node1.getPath()));
>
>                 System.out.println(node1.getPath());
>                     System.out.println(node2.getPath());
>                     System.out.println(node3.getPath());
>
>                 printChildren(parent, System.out);
>
>                 parent.orderBefore(getRelativePath(parent, node3.getPath()),
> getRelativePath(parent, node2.getPath()));
>
>                 printChildren(parent, System.out);
>             }
>             finally
>             {
>                 session.logout();
>             }
>         }
>
>         /**
>          *
>          * @param index
>          * @return
>          * @throws RepositoryException
>          */
>         private static Node createNode (Node parent, int index)
>             throws RepositoryException
>         {
>             Node node = parent.addNode("ChildNode");
>
>             node.setProperty("name", String.valueOf(index));
>
>             return node;
>         }
>
>     /**
>      *
>      * @param parent
>      * @param target
>      * @throws RepositoryException
>      */
>     private static void printChildren (Node parent, PrintStream target)
>         throws RepositoryException
>     {
>         if (parent == null || target == null)
>         {
>             return;
>         }
>
>         target.println(parent.getName());
>
>         NodeIterator iter = parent.getNodes();
>
>         while (iter.hasNext())
>         {
>             Node child = iter.nextNode();
>
>             target.print(child.getName());
>
>             try
>             {
>                 target.println("[name = " +
> child.getProperty("name").getString() + "]");
>             }
>             catch (Exception ex)
>             {
>                 ex.printStackTrace(System.out);
>             }
>         }
>     }
>
>     /**
>      * Given an absolute path and a node, this function computes the
> relative path
>      * from that node to the node pointed to by the given absolute path.
> Note that
>      * this function does not handle namespace resolution. Hence namespace
> qualified
>      * paths will not be properly handled by calls to this function.
>      *
>      * @param node A {@link javax.jcr.Node} instance relative to which we
> want the
>      *             node pointed to by the given absolute path.
>      * @param absolutePath The absolute path to convert into a relative
> path.
>      * @return A relative path string.
>      * @throws RepositoryException
>      * @throws IllegalArgumentException
>      * @throws MalformedPathException
>      * @throws NoPrefixDeclaredException
>      */
>     public static String getRelativePath (Node node, String absolutePath)
>         throws RepositoryException, MalformedPathException,
> NoPrefixDeclaredException
>     {
>         if (absolutePath.length() <= 0)
>         {
>             throw new IllegalArgumentException("Invalid absolute path
> argument.");
>         }
>
>         Path mainNodePath = PathFormat.parse(node.getPath(),
> DummyNamespaceResolver.getInstance());
>         Path relNodePath = PathFormat.parse(absolutePath,
> DummyNamespaceResolver.getInstance());
>
>         Path relPath = mainNodePath.computeRelativePath(relNodePath);
>
>         return PathFormat.format(relPath,
> DummyNamespaceResolver.getInstance());
>     }
>
>     /**
>      * @author Vijai Kalyan
>      */
>     private static class DummyNamespaceResolver extends
> AbstractNamespaceResolver
>     {
>         /** Required by PROS coding standards. Do not remove. */
>         public static final String SOURCE_FILE_REVISION = "$ Revision: $";
>
>         static final NamespaceResolver instance = new
> DummyNamespaceResolver();
>
>         static final NamespaceResolver getInstance()
>         {
>             return instance;
>         }
>
>         /**
>          *
>          */
>         private DummyNamespaceResolver()
>         {
>             super(false);
>         }
>
>         /* (non-Javadoc)
>          * @see
> org.apache.jackrabbit.name.NamespaceResolver#getPrefix(java.lang.String)
>          */
>         public String getPrefix(String uri) throws NamespaceException
>         {
>             return "";
>         }
>
>         /* (non-Javadoc)
>          * @see
> org.apache.jackrabbit.name.NamespaceResolver#getURI(java.lang.String)
>          */
>         public String getURI(String prefix) throws NamespaceException
>         {
>             return "";
>         }
>     }
> }
>
>
> This produced the following output:-
>
>
> parent
> ChildNode[name = 1]
> ChildNode[name = 2]
> ChildNode[name = 3]
> parent
> ChildNode[name = 1]
> ChildNode[name = 2]
> ChildNode[name = 3]
> /parent/ChildNode
> /parent/ChildNode[2]
> /parent/ChildNode[3]
> parent
> ChildNode[name = 2]
> ChildNode[name = 1]
> ChildNode[name = 3]
> parent
> ChildNode[name = 2]
> ChildNode[name = 3]
> ChildNode[name = 1]
>
>
> When we debugged, we found that in the following code
> (NodeImpl::orderBefore):-
>
>
>     /**
>      * Same as {@link Node#orderBefore(String, String)} except that
>      * this method takes a Path.PathElement arguments instead of
>      * Strings.
>      *
>      * @param srcName
>      * @param dstName
>      * @throws UnsupportedRepositoryOperationException
>      * @throws VersionException
>      * @throws ConstraintViolationException
>      * @throws ItemNotFoundException
>      * @throws LockException
>      * @throws RepositoryException
>      */
>     public synchronized void orderBefore(Path.PathElement srcName,
>                                          Path.PathElement dstName)
>             throws UnsupportedRepositoryOperationException,
> VersionException,
>             ConstraintViolationException, ItemNotFoundException,
> LockException,
>             RepositoryException {
>
>         // check state of this instance
>         sanityCheck();
>
>         if (!getPrimaryNodeType().hasOrderableChildNodes()) {
>             throw new UnsupportedRepositoryOperationException(
>                     "child node ordering not supported on node "
>                     + safeGetJCRPath());
>         }
>
>         // check arguments
>         if (srcName.equals(dstName)) {
>             // there's nothing to do
>             return;
>         }
>
>         // check existence
>         if (!hasNode(srcName.getName(), srcName.getIndex())) {
>             String name;
>             try {
>                 Path.PathElement[] path = new Path.PathElement[] { srcName
> };
>                 name = session.getJCRPath(new
> Path.PathBuilder(path).getPath());
>             } catch (NameException e) {
>                 name = srcName.toString();
>             } catch (NamespaceException e) {
>                 name = srcName.toString();
>             }
>             throw new ItemNotFoundException(safeGetJCRPath()
>                     + " has no child node with name " + name);
>         }
>         if (dstName != null && !hasNode(dstName.getName(),
> dstName.getIndex())) {
>             String name;
>             try {
>                 Path.PathElement[] path = new Path.PathElement[] { dstName
> };
>                 name = session.getJCRPath(new
> Path.PathBuilder(path).getPath());
>             } catch (NameException e) {
>                 name = dstName.toString();
>             } catch (NamespaceException e) {
>                 name = dstName.toString();
>             }
>             throw new ItemNotFoundException(safeGetJCRPath()
>                     + " has no child node with name " + name);
>         }
>
>         // make sure this node is checked-out
>         if (!internalIsCheckedOut()) {
>             String msg = safeGetJCRPath()
>                     + ": cannot change child node ordering of a checked-in
> node";
>             log.debug(msg);
>             throw new VersionException(msg);
>         }
>
>         // check protected flag
>         if (definition.isProtected()) {
>             String msg = safeGetJCRPath()
>                     + ": cannot change child node ordering of a protected
> node";
>             log.debug(msg);
>             throw new ConstraintViolationException(msg);
>         }
>
>         // check lock status
>         checkLock();
>
>         ArrayList list = new ArrayList(((NodeState)
> state).getChildNodeEntries());
>         int srcInd = -1, destInd = -1;
>         for (int i = 0; i < list.size(); i++) {
>             NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry)
> list.get(i);
>             if (srcInd == -1) {
>                 if (entry.getName().equals(srcName.getName())
>                         && (entry.getIndex() == srcName.getIndex()
>                         || srcName.getIndex() == 0 && entry.getIndex() ==
> 1)) {
>                     srcInd = i;
>                 }
>             }
>             if (destInd == -1 && dstName != null) {
>                 if (entry.getName().equals(dstName.getName())
>                         && (entry.getIndex() == dstName.getIndex()
>                         || dstName.getIndex() == 0 && entry.getIndex() ==
> 1)) {
>                     destInd = i;
>                     if (srcInd != -1) {
>                         break;
>                     }
>                 }
>             } else {
>                 if (srcInd != -1) {
>                     break;
>                 }
>             }
>         }
>
>         // check if resulting order would be different to current order
>         if (destInd == -1) {
>             if (srcInd == list.size() - 1) {
>                 // no change, we're done
>                 return;
>             }
>         } else {
>             if ((destInd - srcInd) == 1) {
>                 // no change, we're done
>                 return;
>             }
>         }
>
>         // reorder list
>         if (destInd == -1) {
>             list.add(list.remove(srcInd));
>         } else {
>             if (srcInd < destInd) {
>                 list.add(destInd, list.get(srcInd));
>                 list.remove(srcInd);
>             } else {
>                 list.add(destInd, list.remove(srcInd));
>             }
>         }
>
>         // modify the state of 'this', i.e. the parent node
>         NodeState thisState = (NodeState) getOrCreateTransientItemState();
>         thisState.setChildNodeEntries(list);
>     }
>
>
> The value of "destInd" and "srcInd" are such that it looks like the original
> order of the child nodes is being used. That is, when the second
> "orderBefore" call is done, destInd should be 0 and srcInd should be 1.
> However, we found that in the above, destInd was 1 and srcInd was 2 (which
> is what it would be if the original order of child node entries was being
> used).
>
> So, although it looks like the state change is being changed at the end of
> the "orderBefore" method, it doesn't seem to be reflected in the next
> iteration.
>
> Looking at the documentation, it indicates that calls to "orderBefore" may
> not be persisted until a "save" method call is done. So, changing the
> program slighlty, we introduced a line "session.save()" just after the first
> call to "orderBefore".
>
> That ended up with the following output:-
>
>
> parent
> ChildNode[name = 1]
> ChildNode[name = 2]
> ChildNode[name = 3]
> parent
> ChildNode[name = 1]
> ChildNode[name = 2]
> ChildNode[name = 3]
> /parent[2]/ChildNode[2]
> /ChildNode[2]
> /parent[2]/ChildNode[3]
> parent
> ChildNode[name = 2]
> ChildNode[name = 1]
> ChildNode[name = 3]
> Exception in thread "main" javax.jcr.RepositoryException: invalid name:
> ../ChildNode[2]
>         at org.apache.jackrabbit.core.NodeImpl.orderBefore(NodeImpl.java:2044)
>         at ReOrderAttributes.main(ReOrderAttributes.java:69)
>
>
> Note in the above that one of the "child" nodes has lost its parent.
>
> Can someone point out to us what we may be doing wrong?
>
> Thanks,
>
> Vijai.
> --
> View this message in context: http://www.nabble.com/Reordering-of-Child-Nodes-tp14300056p14300056.html
> Sent from the Jackrabbit - Users mailing list archive at Nabble.com.
>

Re: Reordering of Child Nodes

Posted by Tobias Bocanegra <to...@day.com>.
hi,

> We think we have discovered a problem in reordering of nodes. I apologize
> upfront for the long post, but we are hoping the information will help.
i just swept over your post without reading it closer - you are using
same name siblings. and this will always cause problems. especially
with moving/reordering.

i suggest you create nodes that have distinct names and use the
nodetypes for your 'types'
regards, toby


>
> Background
>
> We have been attempting to use JackRabbit for meta-data management.
>
> We have adopted a model where we have meta-meta artifacts, for example like
>
>
> Model
> Atom
> Implementation
>
>
> so that the node names will actually be "Model" and "Atom" and
> "Implementation". One reason for this is that this helps us retrieve "all
> models in the repository". The othery way of doing this is of course, to
> store nodes with the actual name and then include a type property that is
> one of "Model", "Atom" or "Implementation".
>
> Attributes are likewise stored as nodes instead of as properties (so that we
> can reorder them or look for all defined atttributes across all models and
> so on).
>
> Problem
>
> Assume we have a node "Parent" as a child of the root node. Let us assume
> further that "Parent" has three children "Child", "Child", "Child" with the
> name property of each set to "1", "2" and "3". Thus we have
>
>
> root
>   |
>   + parent
>         |
>         + child [name = 1]
>         |
>         + child [name = 2]
>         |
>         + child [name = 3]
>
>
> Now we want to reorder the "child" nodes in reverse order of "name"
> property. That is, we want to change the above to the following
>
>
> root
>   |
>   + parent
>         |
>         + child [name = 3]
>         |
>         + child [name = 2]
>         |
>         + child [name = 1]
>
>
> We figured that this is equivalent to an algorithm for doing the following
>
> Given an input list {1, 2, ... , N} and a required output list {a1, a2, ...
> , aN}, generate a sequence of operations using only the primitive operation
>
> orderBefore (y, x): x,y ---> y,x
>
> That is orderBefore will place element y before x. I think this is a good
> enough abstraction of using the Node::orderBefore method to achieve this.
>
> We wrote some tests to test our assumptions. Our first program was:-
>
>
> public class OrderAttributes
> {
>         /**
>          * @param args
>          */
>         public static void main(String[] args)
>                 throws Exception
>         {
>                 Repository repository = new TransientRepository();
>                 Session session = repository.login(new SimpleCredentials("username",
> "password".toCharArray()));
>
>             try
>             {
>                 Node root = session.getRootNode();
>
>                 Node node1 = root.addNode("node1");
>                 Node node2 = root.addNode("node2");
>                 Node node3 = root.addNode("node3");
>
>                 System.out.println(node1.getName() + " @ " + node1.getIndex());
>                 System.out.println(node2.getName() + " @ " + node2.getIndex());
>                 System.out.println(node3.getName() + " @ " + node3.getIndex());
>
>                 printChildren(root, System.out);
>
>                 root.orderBefore("node2", "node1");
>
>                 printChildren(root, System.out);
>
>                 root.orderBefore("node3", "node2");
>
>                 printChildren(root, System.out);
>             }
>             finally
>             {
>                 session.logout();
>             }
>         }
>
>     /**
>      *
>      * @param node
>      * @param target
>      * @throws RepositoryException
>      */
>     public static void printChildren (Node node, PrintStream target)
>         throws RepositoryException
>     {
>         if (node == null || target == null)
>         {
>             return;
>         }
>
>         target.println(node.getName());
>
>         NodeIterator iter = node.getNodes();
>
>         while (iter.hasNext())
>         {
>             Node child = iter.nextNode();
>
>             target.println(child.getName());
>         }
>     }
> }
>
>
> This worked correctly and produce the following output:-
>
>
> node1 @ 1
> node2 @ 1
> node3 @ 1
>
> jcr:system
> node1
> node2
> node3
>
> jcr:system
> node2
> node1
> node3
>
> jcr:system
> node3
> node2
> node1
>
>
> We then attempted to do what we wanted to do:-
>
>
> public class ReOrderAttributes
> {
>         /**
>          * @param args
>          */
>         public static void main(String[] args)
>                 throws Exception
>         {
>                 Repository repository = new TransientRepository();
>                 Session session = repository.login(new
> SimpleCredentials("username", "password".toCharArray()));
>
>             try
>             {
>                 Node root = session.getRootNode();
>
>                 Node parent = root.addNode("parent");
>
>                 Node node1 = createNode(parent, 1);
>                 Node node2 = createNode(parent, 2);
>                 Node node3 = createNode(parent, 3);
>
>                 session.save();
>
>                      printChildren(parent, System.out);
>
>                 String path1 = getRelativePath(parent, node1.getPath());
>                 String path2 = getRelativePath(parent, node2.getPath());
>                 String path3 = getRelativePath(parent, node3.getPath());
>
>                 printChildren(parent, System.out);
>
>                 parent.orderBefore(getRelativePath(parent, node2.getPath()),
> getRelativePath(parent, node1.getPath()));
>
>                 System.out.println(node1.getPath());
>                     System.out.println(node2.getPath());
>                     System.out.println(node3.getPath());
>
>                 printChildren(parent, System.out);
>
>                 parent.orderBefore(getRelativePath(parent, node3.getPath()),
> getRelativePath(parent, node2.getPath()));
>
>                 printChildren(parent, System.out);
>             }
>             finally
>             {
>                 session.logout();
>             }
>         }
>
>         /**
>          *
>          * @param index
>          * @return
>          * @throws RepositoryException
>          */
>         private static Node createNode (Node parent, int index)
>             throws RepositoryException
>         {
>             Node node = parent.addNode("ChildNode");
>
>             node.setProperty("name", String.valueOf(index));
>
>             return node;
>         }
>
>     /**
>      *
>      * @param parent
>      * @param target
>      * @throws RepositoryException
>      */
>     private static void printChildren (Node parent, PrintStream target)
>         throws RepositoryException
>     {
>         if (parent == null || target == null)
>         {
>             return;
>         }
>
>         target.println(parent.getName());
>
>         NodeIterator iter = parent.getNodes();
>
>         while (iter.hasNext())
>         {
>             Node child = iter.nextNode();
>
>             target.print(child.getName());
>
>             try
>             {
>                 target.println("[name = " +
> child.getProperty("name").getString() + "]");
>             }
>             catch (Exception ex)
>             {
>                 ex.printStackTrace(System.out);
>             }
>         }
>     }
>
>     /**
>      * Given an absolute path and a node, this function computes the
> relative path
>      * from that node to the node pointed to by the given absolute path.
> Note that
>      * this function does not handle namespace resolution. Hence namespace
> qualified
>      * paths will not be properly handled by calls to this function.
>      *
>      * @param node A {@link javax.jcr.Node} instance relative to which we
> want the
>      *             node pointed to by the given absolute path.
>      * @param absolutePath The absolute path to convert into a relative
> path.
>      * @return A relative path string.
>      * @throws RepositoryException
>      * @throws IllegalArgumentException
>      * @throws MalformedPathException
>      * @throws NoPrefixDeclaredException
>      */
>     public static String getRelativePath (Node node, String absolutePath)
>         throws RepositoryException, MalformedPathException,
> NoPrefixDeclaredException
>     {
>         if (absolutePath.length() <= 0)
>         {
>             throw new IllegalArgumentException("Invalid absolute path
> argument.");
>         }
>
>         Path mainNodePath = PathFormat.parse(node.getPath(),
> DummyNamespaceResolver.getInstance());
>         Path relNodePath = PathFormat.parse(absolutePath,
> DummyNamespaceResolver.getInstance());
>
>         Path relPath = mainNodePath.computeRelativePath(relNodePath);
>
>         return PathFormat.format(relPath,
> DummyNamespaceResolver.getInstance());
>     }
>
>     /**
>      * @author Vijai Kalyan
>      */
>     private static class DummyNamespaceResolver extends
> AbstractNamespaceResolver
>     {
>         /** Required by PROS coding standards. Do not remove. */
>         public static final String SOURCE_FILE_REVISION = "$ Revision: $";
>
>         static final NamespaceResolver instance = new
> DummyNamespaceResolver();
>
>         static final NamespaceResolver getInstance()
>         {
>             return instance;
>         }
>
>         /**
>          *
>          */
>         private DummyNamespaceResolver()
>         {
>             super(false);
>         }
>
>         /* (non-Javadoc)
>          * @see
> org.apache.jackrabbit.name.NamespaceResolver#getPrefix(java.lang.String)
>          */
>         public String getPrefix(String uri) throws NamespaceException
>         {
>             return "";
>         }
>
>         /* (non-Javadoc)
>          * @see
> org.apache.jackrabbit.name.NamespaceResolver#getURI(java.lang.String)
>          */
>         public String getURI(String prefix) throws NamespaceException
>         {
>             return "";
>         }
>     }
> }
>
>
> This produced the following output:-
>
>
> parent
> ChildNode[name = 1]
> ChildNode[name = 2]
> ChildNode[name = 3]
> parent
> ChildNode[name = 1]
> ChildNode[name = 2]
> ChildNode[name = 3]
> /parent/ChildNode
> /parent/ChildNode[2]
> /parent/ChildNode[3]
> parent
> ChildNode[name = 2]
> ChildNode[name = 1]
> ChildNode[name = 3]
> parent
> ChildNode[name = 2]
> ChildNode[name = 3]
> ChildNode[name = 1]
>
>
> When we debugged, we found that in the following code
> (NodeImpl::orderBefore):-
>
>
>     /**
>      * Same as {@link Node#orderBefore(String, String)} except that
>      * this method takes a Path.PathElement arguments instead of
>      * Strings.
>      *
>      * @param srcName
>      * @param dstName
>      * @throws UnsupportedRepositoryOperationException
>      * @throws VersionException
>      * @throws ConstraintViolationException
>      * @throws ItemNotFoundException
>      * @throws LockException
>      * @throws RepositoryException
>      */
>     public synchronized void orderBefore(Path.PathElement srcName,
>                                          Path.PathElement dstName)
>             throws UnsupportedRepositoryOperationException,
> VersionException,
>             ConstraintViolationException, ItemNotFoundException,
> LockException,
>             RepositoryException {
>
>         // check state of this instance
>         sanityCheck();
>
>         if (!getPrimaryNodeType().hasOrderableChildNodes()) {
>             throw new UnsupportedRepositoryOperationException(
>                     "child node ordering not supported on node "
>                     + safeGetJCRPath());
>         }
>
>         // check arguments
>         if (srcName.equals(dstName)) {
>             // there's nothing to do
>             return;
>         }
>
>         // check existence
>         if (!hasNode(srcName.getName(), srcName.getIndex())) {
>             String name;
>             try {
>                 Path.PathElement[] path = new Path.PathElement[] { srcName
> };
>                 name = session.getJCRPath(new
> Path.PathBuilder(path).getPath());
>             } catch (NameException e) {
>                 name = srcName.toString();
>             } catch (NamespaceException e) {
>                 name = srcName.toString();
>             }
>             throw new ItemNotFoundException(safeGetJCRPath()
>                     + " has no child node with name " + name);
>         }
>         if (dstName != null && !hasNode(dstName.getName(),
> dstName.getIndex())) {
>             String name;
>             try {
>                 Path.PathElement[] path = new Path.PathElement[] { dstName
> };
>                 name = session.getJCRPath(new
> Path.PathBuilder(path).getPath());
>             } catch (NameException e) {
>                 name = dstName.toString();
>             } catch (NamespaceException e) {
>                 name = dstName.toString();
>             }
>             throw new ItemNotFoundException(safeGetJCRPath()
>                     + " has no child node with name " + name);
>         }
>
>         // make sure this node is checked-out
>         if (!internalIsCheckedOut()) {
>             String msg = safeGetJCRPath()
>                     + ": cannot change child node ordering of a checked-in
> node";
>             log.debug(msg);
>             throw new VersionException(msg);
>         }
>
>         // check protected flag
>         if (definition.isProtected()) {
>             String msg = safeGetJCRPath()
>                     + ": cannot change child node ordering of a protected
> node";
>             log.debug(msg);
>             throw new ConstraintViolationException(msg);
>         }
>
>         // check lock status
>         checkLock();
>
>         ArrayList list = new ArrayList(((NodeState)
> state).getChildNodeEntries());
>         int srcInd = -1, destInd = -1;
>         for (int i = 0; i < list.size(); i++) {
>             NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry)
> list.get(i);
>             if (srcInd == -1) {
>                 if (entry.getName().equals(srcName.getName())
>                         && (entry.getIndex() == srcName.getIndex()
>                         || srcName.getIndex() == 0 && entry.getIndex() ==
> 1)) {
>                     srcInd = i;
>                 }
>             }
>             if (destInd == -1 && dstName != null) {
>                 if (entry.getName().equals(dstName.getName())
>                         && (entry.getIndex() == dstName.getIndex()
>                         || dstName.getIndex() == 0 && entry.getIndex() ==
> 1)) {
>                     destInd = i;
>                     if (srcInd != -1) {
>                         break;
>                     }
>                 }
>             } else {
>                 if (srcInd != -1) {
>                     break;
>                 }
>             }
>         }
>
>         // check if resulting order would be different to current order
>         if (destInd == -1) {
>             if (srcInd == list.size() - 1) {
>                 // no change, we're done
>                 return;
>             }
>         } else {
>             if ((destInd - srcInd) == 1) {
>                 // no change, we're done
>                 return;
>             }
>         }
>
>         // reorder list
>         if (destInd == -1) {
>             list.add(list.remove(srcInd));
>         } else {
>             if (srcInd < destInd) {
>                 list.add(destInd, list.get(srcInd));
>                 list.remove(srcInd);
>             } else {
>                 list.add(destInd, list.remove(srcInd));
>             }
>         }
>
>         // modify the state of 'this', i.e. the parent node
>         NodeState thisState = (NodeState) getOrCreateTransientItemState();
>         thisState.setChildNodeEntries(list);
>     }
>
>
> The value of "destInd" and "srcInd" are such that it looks like the original
> order of the child nodes is being used. That is, when the second
> "orderBefore" call is done, destInd should be 0 and srcInd should be 1.
> However, we found that in the above, destInd was 1 and srcInd was 2 (which
> is what it would be if the original order of child node entries was being
> used).
>
> So, although it looks like the state change is being changed at the end of
> the "orderBefore" method, it doesn't seem to be reflected in the next
> iteration.
>
> Looking at the documentation, it indicates that calls to "orderBefore" may
> not be persisted until a "save" method call is done. So, changing the
> program slighlty, we introduced a line "session.save()" just after the first
> call to "orderBefore".
>
> That ended up with the following output:-
>
>
> parent
> ChildNode[name = 1]
> ChildNode[name = 2]
> ChildNode[name = 3]
> parent
> ChildNode[name = 1]
> ChildNode[name = 2]
> ChildNode[name = 3]
> /parent[2]/ChildNode[2]
> /ChildNode[2]
> /parent[2]/ChildNode[3]
> parent
> ChildNode[name = 2]
> ChildNode[name = 1]
> ChildNode[name = 3]
> Exception in thread "main" javax.jcr.RepositoryException: invalid name:
> ../ChildNode[2]
>         at org.apache.jackrabbit.core.NodeImpl.orderBefore(NodeImpl.java:2044)
>         at ReOrderAttributes.main(ReOrderAttributes.java:69)
>
>
> Note in the above that one of the "child" nodes has lost its parent.
>
> Can someone point out to us what we may be doing wrong?
>
> Thanks,
>
> Vijai.
> --
> View this message in context: http://www.nabble.com/Reordering-of-Child-Nodes-tp14300056p14300056.html
> Sent from the Jackrabbit - Users mailing list archive at Nabble.com.
>


-- 
-----------------------------------------< tobias.bocanegra@day.com >---
Tobias Bocanegra, Day Management AG, Barfuesserplatz 6, CH - 4001 Basel
T +41 61 226 98 98, F +41 61 226 98 97
-----------------------------------------------< http://www.day.com >---