ls-projects command can show the project hierarchy
The ls-projects command has a new --tree option that will make the output of the project names be printed in a ascii art hierarcy to show the projects access rights inheritance.
This commit is contained in:
		@@ -32,6 +32,11 @@ OPTIONS
 | 
			
		||||
\-b::
 | 
			
		||||
	Name of the branch for which the command will display the sha of each project.
 | 
			
		||||
 | 
			
		||||
\--tree::
 | 
			
		||||
\-t::
 | 
			
		||||
	Displays project inheritance in a tree-like format.
 | 
			
		||||
	This option does not work together with the show-branch option.
 | 
			
		||||
 | 
			
		||||
EXAMPLES
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -33,8 +33,16 @@ import org.kohsuke.args4j.Option;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.PrintWriter;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.TreeMap;
 | 
			
		||||
 | 
			
		||||
final class ListProjects extends BaseCommand {
 | 
			
		||||
  private static final String NODE_PREFIX = "|-- ";
 | 
			
		||||
  private static final String LAST_NODE_PREFIX = "`-- ";
 | 
			
		||||
  private static final String DEFAULT_TAB_SEPARATOR = "|";
 | 
			
		||||
  private static final String NOT_VISIBLE_PROJECT = "(x)";
 | 
			
		||||
 | 
			
		||||
  @Inject
 | 
			
		||||
  private ReviewDb db;
 | 
			
		||||
 | 
			
		||||
@@ -54,6 +62,12 @@ final class ListProjects extends BaseCommand {
 | 
			
		||||
  @Option(name = "--show-branch", aliases = {"-b"}, usage = "displays the sha of each project in the specified branch")
 | 
			
		||||
  private String showBranch;
 | 
			
		||||
 | 
			
		||||
  @Option(name = "--tree", aliases = {"-t"}, usage = "displays project inheritance in a tree-like format\n" +
 | 
			
		||||
      "this option does not work together with the show-branch option")
 | 
			
		||||
  private boolean showTree;
 | 
			
		||||
 | 
			
		||||
  private String currentTabSeparator = DEFAULT_TAB_SEPARATOR;
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void start(final Environment env) {
 | 
			
		||||
    startThread(new CommandRunnable() {
 | 
			
		||||
@@ -66,7 +80,18 @@ final class ListProjects extends BaseCommand {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void display() throws Failure {
 | 
			
		||||
    if (showTree && (showBranch != null)) {
 | 
			
		||||
      throw new UnloggedFailure(1, "fatal: --tree and --show-branch options are not compatible.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final PrintWriter stdout = toPrintWriter(out);
 | 
			
		||||
 | 
			
		||||
    TreeMap<String, TreeNode> treeMap = null;
 | 
			
		||||
 | 
			
		||||
    if (showTree) {
 | 
			
		||||
      treeMap = new TreeMap<String, TreeNode>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      for (final Project p : db.projects().all()) {
 | 
			
		||||
        if (p.getNameKey().equals(wildProject)) {
 | 
			
		||||
@@ -83,27 +108,58 @@ final class ListProjects extends BaseCommand {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        final ProjectControl pctl = e.controlFor(currentUser);
 | 
			
		||||
        if (!pctl.isVisible()) {
 | 
			
		||||
          // Require the project itself to be visible to the user.
 | 
			
		||||
          //
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (showBranch != null) {
 | 
			
		||||
          final Ref ref = getBranchRef(p.getNameKey());
 | 
			
		||||
          if (ref == null || ref.getObjectId() == null
 | 
			
		||||
              || !pctl.controlForRef(ref.getLeaf().getName()).isVisible()) {
 | 
			
		||||
            // No branch, or the user can't see this branch, so skip it.
 | 
			
		||||
        if (!showTree) {
 | 
			
		||||
 | 
			
		||||
          if (!pctl.isVisible()) {
 | 
			
		||||
            // Require the project itself to be visible to the user.
 | 
			
		||||
            //
 | 
			
		||||
            continue;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          stdout.print(ref.getObjectId().name());
 | 
			
		||||
          stdout.print(' ');
 | 
			
		||||
          if (showBranch != null) {
 | 
			
		||||
            final Ref ref = getBranchRef(p.getNameKey());
 | 
			
		||||
            if (ref == null || ref.getObjectId() == null
 | 
			
		||||
                || !pctl.controlForRef(ref.getLeaf().getName()).isVisible()) {
 | 
			
		||||
              // No branch, or the user can't see this branch, so skip it.
 | 
			
		||||
              //
 | 
			
		||||
              continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            stdout.print(ref.getObjectId().name());
 | 
			
		||||
            stdout.print(' ');
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          stdout.print(p.getName() + "\n");
 | 
			
		||||
        } else {
 | 
			
		||||
          treeMap.put(p.getName(), new TreeNode(p, pctl.isVisible()));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (showTree && treeMap.size() > 0) {
 | 
			
		||||
        final List<TreeNode> sortedNodes = new ArrayList<TreeNode>();
 | 
			
		||||
 | 
			
		||||
        // Builds the inheritance tree using a list.
 | 
			
		||||
        //
 | 
			
		||||
        for (final TreeNode key : treeMap.values()) {
 | 
			
		||||
          final String parentName = key.getParentName();
 | 
			
		||||
          if (parentName != null) {
 | 
			
		||||
            final TreeNode node = treeMap.get((String)parentName);
 | 
			
		||||
            if (node != null) {
 | 
			
		||||
              node.addChild(key);
 | 
			
		||||
            } else {
 | 
			
		||||
              sortedNodes.add(key);
 | 
			
		||||
            }
 | 
			
		||||
          } else {
 | 
			
		||||
            sortedNodes.add(key);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stdout.print(p.getName());
 | 
			
		||||
        stdout.println();
 | 
			
		||||
        // Builds a fake root node, which contains the sorted projects.
 | 
			
		||||
        //
 | 
			
		||||
        final TreeNode fakeRoot = new TreeNode(null, sortedNodes, false);
 | 
			
		||||
        printElement(stdout, fakeRoot, -1, false, sortedNodes.get(sortedNodes.size() - 1));
 | 
			
		||||
        stdout.flush();
 | 
			
		||||
      }
 | 
			
		||||
    } catch (OrmException e) {
 | 
			
		||||
      throw new Failure(1, "fatal: database error", e);
 | 
			
		||||
@@ -124,4 +180,139 @@ final class ListProjects extends BaseCommand {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Class created to manipulate the nodes of the project inheritance tree **/
 | 
			
		||||
  private static class TreeNode {
 | 
			
		||||
    private final List<TreeNode> children;
 | 
			
		||||
    private final Project project;
 | 
			
		||||
    private final boolean isVisible;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor
 | 
			
		||||
     * @param p Project
 | 
			
		||||
     */
 | 
			
		||||
    public TreeNode(Project p, boolean visible) {
 | 
			
		||||
      this.children = new ArrayList<TreeNode>();
 | 
			
		||||
      this.project = p;
 | 
			
		||||
      this.isVisible = visible;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor used for creating the fake node
 | 
			
		||||
     * @param p Project
 | 
			
		||||
     * @param c List of nodes
 | 
			
		||||
     */
 | 
			
		||||
    public TreeNode(Project p, List<TreeNode> c, boolean visible) {
 | 
			
		||||
      this.children = c;
 | 
			
		||||
      this.project = p;
 | 
			
		||||
      this.isVisible = visible;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns if the the node is leaf
 | 
			
		||||
     * @return True if is lead, false, otherwise
 | 
			
		||||
     */
 | 
			
		||||
    public boolean isLeaf() {
 | 
			
		||||
      return children.size() == 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the project parent name
 | 
			
		||||
     * @return Project parent name
 | 
			
		||||
     */
 | 
			
		||||
    public String getParentName() {
 | 
			
		||||
      if (project.getParent() != null) {
 | 
			
		||||
        return project.getParent().get();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adds a child to the list
 | 
			
		||||
     * @param node TreeNode child
 | 
			
		||||
     */
 | 
			
		||||
    public void addChild(TreeNode node) {
 | 
			
		||||
      children.add(node);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the project instance
 | 
			
		||||
     * @return Project instance
 | 
			
		||||
     */
 | 
			
		||||
    public Project getProject() {
 | 
			
		||||
      return project;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the list of children nodes
 | 
			
		||||
     * @return List of children nodes
 | 
			
		||||
     */
 | 
			
		||||
    public List<TreeNode> getChildren() {
 | 
			
		||||
      return children;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns if the project is visible to the user
 | 
			
		||||
     * @return True if is visible, false, otherwise
 | 
			
		||||
     */
 | 
			
		||||
    public boolean isVisible() {
 | 
			
		||||
      return isVisible;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Used to display the project inheritance tree recursively
 | 
			
		||||
   * @param stdout PrintWriter used do print
 | 
			
		||||
   * @param node Tree node
 | 
			
		||||
   * @param level Current level of the tree
 | 
			
		||||
   * @param isLast True, if is the last node of a level, false, otherwise
 | 
			
		||||
   * @param lastParentNode Last "root" parent node
 | 
			
		||||
   */
 | 
			
		||||
  private void printElement(final PrintWriter stdout, TreeNode node, int level, boolean isLast,
 | 
			
		||||
      final TreeNode lastParentNode) {
 | 
			
		||||
    // Checks if is not the "fake" root project.
 | 
			
		||||
    //
 | 
			
		||||
    if (node.getProject() != null) {
 | 
			
		||||
 | 
			
		||||
      // Check if is not the last "root" parent node,
 | 
			
		||||
      // so the "|" separator will not longer be needed.
 | 
			
		||||
      //
 | 
			
		||||
      if (!currentTabSeparator.equals(" ")) {
 | 
			
		||||
        final String nodeProject = node.getProject().getName();
 | 
			
		||||
        final String lastParentProject = lastParentNode.getProject().getName();
 | 
			
		||||
 | 
			
		||||
        if (nodeProject.equals(lastParentProject)) {
 | 
			
		||||
          currentTabSeparator = " ";
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (level > 0) {
 | 
			
		||||
        stdout.print(String.format("%-" + 4 * level + "s", currentTabSeparator));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      final String prefix = isLast ? LAST_NODE_PREFIX : NODE_PREFIX ;
 | 
			
		||||
 | 
			
		||||
      String printout;
 | 
			
		||||
 | 
			
		||||
      if (node.isVisible()) {
 | 
			
		||||
        printout = prefix + node.getProject().getName();
 | 
			
		||||
      } else {
 | 
			
		||||
        printout = prefix + NOT_VISIBLE_PROJECT;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      stdout.print(printout + "\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (node.isLeaf()) {
 | 
			
		||||
      return;
 | 
			
		||||
    } else {
 | 
			
		||||
      final List<TreeNode> children = node.getChildren();
 | 
			
		||||
      ++level;
 | 
			
		||||
      for(TreeNode treeNode : children) {
 | 
			
		||||
        final boolean isLastIndex = children.indexOf(treeNode) == children.size() - 1;
 | 
			
		||||
        printElement(stdout, treeNode, level, isLastIndex, lastParentNode);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user