/* * Copyright (C) 2010 eXo Platform SAS. * * 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.exoplatform.portal.mop.navigation; import java.util.Arrays; import java.util.List; /** * Flexible scope implementations. */ public class GenericScope { public abstract static class Branch implements Scope { /** . */ private final Scope federated; /** * Create a new branch scope. * * @param federated the federated scope * @throws NullPointerException if the federated scope is null */ public Branch(Scope federated) throws NullPointerException { if (federated == null) { throw new NullPointerException("no null federated scope accepted"); } // this.federated = federated; } protected abstract int getSize(); protected abstract String getName(int index); public Visitor get() { return new Visitor() { @Override protected int getSize() { return GenericScope.Branch.this.getSize(); } @Override protected String getName(int index) { return GenericScope.Branch.this.getName(index); } @Override protected Scope.Visitor getFederated() { return federated.get(); } }; } public abstract static class Visitor implements Scope.Visitor { /** . */ private Scope.Visitor visitor; protected Visitor() { this.visitor = null; } protected abstract int getSize(); protected abstract String getName(int index); protected abstract Scope.Visitor getFederated(); public VisitMode enter(int depth, String id, String name, NodeState state) { int size = getSize(); // if (depth < size) { if (depth == 0 || name.equals(getName(depth - 1))) { return VisitMode.ALL_CHILDREN; } else { return VisitMode.NO_CHILDREN; } } else if (depth == size) { if (depth == 0 || name.equals(getName(depth - 1))) { Scope.Visitor visitor = getFederated(); VisitMode mode = visitor.enter(0, id, name, state); if (mode == VisitMode.ALL_CHILDREN) { this.visitor = visitor; } return mode; } else { return VisitMode.NO_CHILDREN; } } else { return visitor.enter(depth - size, id, name, state); } } public void leave(int depth, String id, String name, NodeState state) { int size = getSize(); // if (depth < size) { // Do nothing } else if (depth == size) { if (depth == 0 || name.equals(getName(depth - 1))) { visitor.leave(0, id, name, state); visitor = null; } else { // Do nothing } } else { visitor.leave(depth - size, id, name, state); } } } } /** * <p> * A scope with the shape of a tree branch following the rules: * <ul> * <li>the first node with depth 0 will have all of its children visited</li> * <li>any node above the root node that fits in the <code>path</code> array will be matched only if the node name matches * the corresponding value in the <code>path</code> array. The last node whose depth is equals to the <code>path</code> list * size will have its visit mode value delegated to the <code>federated</code> scope argument with a depth of 0 and the same * other arguments, any other node will have all of its children visited.</li> * <li>any other node will have its visit mode delegated to the <code>federated</code> scope argument with the same * arguments except the depth that will be subtracted the <code>path</code> list argument size.</li> * </ul> * </p> * * @param path the names that describing the tree path * @param federated the federated scope * @return the branch shape scope * @throws NullPointerException if any argument is null */ public static Scope branchShape(final List<String> path, Scope federated) throws NullPointerException { if (path == null) { throw new NullPointerException("No null path accepted"); } return new Branch(federated) { @Override protected int getSize() { return path.size(); } @Override protected String getName(int index) { return path.get(index); } }; } public static Scope branchShape(final String[] path, Scope federated) { return new Branch(federated) { @Override protected int getSize() { return path.length; } @Override protected String getName(int index) { return path[index]; } }; } public static Scope branchShape(List<String> path) { return branchShape(path, Scope.CHILDREN); } public static Scope branchShape(String[] path) { return branchShape(Arrays.asList(path), Scope.CHILDREN); } /** . */ private static final GenericScope.Tree ALL = new Tree(-1); /** . */ private static GenericScope.Tree[] PREDEFINED = { new Tree(0), new Tree(1), new Tree(2), new Tree(3), new Tree(4), new Tree(5), new Tree(6), new Tree(7), new Tree(8), new Tree(9) }; public static Scope treeShape(int height) { if (height < 0) { return ALL; } else if (height < PREDEFINED.length) { return PREDEFINED[height]; } else { return new Tree(height); } } public static class Tree implements Scope { /** . */ private final Visitor visitor; /** * Creates a new navigation scope. When the height is positive or zero, the tree will be pruned to the specified height, * when the height is negative no pruning will occur. * * @param height the max height of the pruned tree */ public Tree(final int height) { this.visitor = new Visitor() { public VisitMode enter(int depth, String id, String name, NodeState state) { if (height < 0 || depth < height) { return VisitMode.ALL_CHILDREN; } else { return VisitMode.NO_CHILDREN; } } public void leave(int depth, String id, String name, NodeState state) { } }; } public Visitor get() { return visitor; } } }