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 <[EMAIL PROTECTED]> 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 [EMAIL PROTECTED] 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 [EMAIL PROTECTED] 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.
>