package org.teiid.test.cli; import java.io.BufferedReader; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; /** * TreeInputConsole * * @author kylin * */ public class TreeInputConsole extends InputConsole { private static final String DEFAULT_NAME = "TreeInputConsole"; private String name; private TreeNode currentNode; /** * A TreeInputConsole has one root node, root node no father node */ private TreeNode rootNode = new TreeNode("/", "", null, null); private String cursor = "~"; private String cursorStr ; private BufferedReader in ; private boolean isDebug = false ; protected Validation addValidation ; public TreeInputConsole(String name){ this(name, null); } public TreeInputConsole(String name, TreeNode currentNode){ this(name, currentNode, false); } public TreeInputConsole(String name, TreeNode currentNode, Boolean isDebug) { super(); this.name = name; this.currentNode = currentNode; this.isDebug = isDebug ; if(null == name || name.equals("")) { this.name = DEFAULT_NAME ; } // register TreeNode to rootNode, if TreeNode didn't have a parent if(null != currentNode && null == currentNode.getFather()){ currentNode.setFather(rootNode); rootNode.getSons().add(currentNode); } updateCursorStr(currentNode); InputStreamReader converter = new InputStreamReader(System.in); in = new BufferedReader(converter); addValidation = new AddNodeValidation(); } public TreeNode getCurrentNode() { if(null == currentNode) { currentNode = getRootNode(); } return currentNode; } public String getName() { return name; } public boolean isDebug() { return isDebug; } /** * Threshold for debug TreeNode information. * if isDebug is true, all exist TreeNode will be printed * if isDebug is false, debug logic will be ignored * @param isDebug */ public void setDebug(boolean isDebug) { this.isDebug = isDebug; } /** * add treeNode to current node * @param treeNode */ public void addTreeNode(TreeNode treeNode) { if(treeNode.getFather() == null) { treeNode.setFather(getCurrentNode()); } if(!exist(treeNode)) { getCurrentNode().addSon(treeNode); } } /** * Remove TreeNode from Current TreeNode * @param name */ public void removeTreeNode(String name) { removeTreeNode(getCurrentNode().getSons(), name); } public void updateCurrentNode(TreeNode currentNode) { if(null == currentNode) { currentNode = getRootNode(); } this.currentNode = currentNode; } public void start() throws IOException { isRunning = true; String pointer = ""; while (isRunning) { // for debug TreeNode Content printTreeNodes(pointer); // always print cursor string, simulate Linux Commands print(cursorStr); pointer = in.readLine(); updateTreeNodes(pointer); switch (type(pointer)) { case NULL : break ; case LS : handleLS(pointer); break; case CD : handleCD(pointer); break; case PWD : handlePWD(pointer); break; case RM : handleRM(pointer); break; case ADD : handleADD(pointer); break; case HELP : handleHELP(pointer); break; case TREE : handleTREE(pointer); break; case OTHER : handleOther(pointer); break; } updateCursorStr(getCurrentNode()); } } /** * Override by subclass for initial TreeNodes */ protected void updateTreeNodes(String prompt) { } private boolean isRunning; public void stop() { isRunning = false; } private PrintWriter out = null ; private void printTreeNodes(String prompt) { if (!isDebug()) return; if(null == out) { try { prompt("Debug TreeNode Content is enable"); File file = new File(System.currentTimeMillis() + ".log"); out = new PrintWriter(new FileWriter(file), true); prompt("TreeNode Content Stack will output to " + file.getName()); } catch (IOException e) { throw new TreeInputConsoleException("instantiate PrintWriter error", e); } } StringBuffer sb = new StringBuffer(); sb.append(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss SSS]").format(new Date()) + " TreeNode Content Stack(" + prompt + "):"); sb.append("\n"); out.print(sb.toString()); recursivePrint(rootNode, 0, true, out); out.flush(); } private void recursivePrint(TreeNode root, int index, boolean isPrintDetails, PrintWriter writer) { StringBuffer sb = new StringBuffer(); appendPrarentsPrefix(root, sb); sb.append(assemble31Holder(index, root)); if(isPrintDetails) { sb.append(root.getName() + " - " + root.getContent()); } else { sb.append(root.getName()) ; } if(null != writer) { writer.println(sb.toString()); } else { println(sb.toString()); } index ++ ; for(TreeNode son : root.getSons()) { recursivePrint(son, index, isPrintDetails, writer); } } String l3holder_1 = "├──", l3holder_2 = "└──", l3holder_3 = " " ; String l1holder_1 = "│", l1holder_2 = " " ; /** * String prefix = (index -1) * (l1holder + l3holder) + 1 * (l3holder + l1holder) * * @param root * @param index * @param isPrintDetails */ private void recursivePrint(TreeNode root, int index, boolean isPrintDetails) { recursivePrint(root, index, isPrintDetails, null); } private void appendPrarentsPrefix(TreeNode node, StringBuffer sb) { if(node.getFather() == null) { sb.append(""); } else { String prefix = ""; TreeNode tnode = node; while((tnode = tnode.getFather()) != null){ String holder = assemble13Holder(tnode) ; prefix = holder + prefix; } sb.append(prefix); } } private String assemble13Holder( TreeNode node) { if(node.getFather() == null) { return ""; } String prefix = "" ; if(existBuddy(node)) { prefix = l1holder_1 + l3holder_3 ; } else { prefix = l1holder_2 + l3holder_3 ; } return prefix ; } Map<String, HashSet<String>> map = new HashMap<String, HashSet<String>> (); private String assemble31Holder(int i, TreeNode node) { String prefix = "" ; if(i <= 0 || node.getFather() == null) { return ""; } String key = node.getFather().getName() + i; if(existBuddy(node) && !isLast(node, key)) { HashSet<String> set = map.get(key); if(null == set) { set = new HashSet<String>(); } set.add(node.getName()); map.put(key, set); prefix = l3holder_1 + l1holder_2 ; } else { prefix = l3holder_2 + l1holder_2 ; } return prefix ; } /** * We assume node have buddies */ private boolean isLast(TreeNode node, String key) { HashSet<String> set = map.get(key); if(set == null) { return false ; } //add all buddies name to Set HashSet<String> buddies = new HashSet<String>(); for(TreeNode tn : node.getFather().getSons()) { buddies.add(tn.getName()); } //recur all current added Set for(String name : set) { buddies.remove(name); } if(buddies.size() == 1 && buddies.contains(node.getName())) { return true ; } return false; } protected void handleLS(String pointer) { String[] array = pointer.split(" "); boolean isPrintDetail = false; for (int i = 0 ; i < array.length ; i ++) { if(array[i].equals("-l")) { isPrintDetail = true ; } } StringBuffer sb = new StringBuffer(); List<TreeNode> nodes = getPrintNodes(); if(isPrintDetail) { for(TreeNode node : nodes) { sb.append(node.getName() + " " + node.getContent()); sb.append("\n"); } if (nodes.size() > 0) print(sb.toString()); else println(sb.toString()); } else { for(TreeNode node : nodes) { sb.append(node.getName()); sb.append(TAB); } println(sb.toString()); } } protected void handlePWD(String pointer) { println(getAbsolutePath()); } protected void handleCD(String pointer) { String[] array = pointer.split(" "); if(array.length != 2) { promptErrorCommand(pointer); return ; } String[] path = array[1].split("/"); for(int i = 0 ; i < path.length ; i ++) { if(path[i].equals(".")){ updateCurrentNode(getCurrentNode()); } else if(path[i].equals("..")) { if(getCurrentNode() != null) { updateCurrentNode(getCurrentNode().getFather()); } } else { String name = path[i]; TreeNode node = findNode(getCurrentNode(), name); if(null == node) { prompt("[" + array[1] + "] does not exist"); break; } updateCurrentNode(node); } } } protected void handleRM(String pointer) { String[] array = pointer.split(" "); if(array.length < 2) { promptErrorCommand(pointer); return ; } if(getCurrentNode().getSons().size() == 0) { return ; } if(array.length == 2 && array[1].equals("*")) { getCurrentNode().getSons().clear(); return ; } List<TreeNode> list = new ArrayList<TreeNode>(); list.addAll(getCurrentNode().getSons()); for(int i = 1 ; i < array.length ; i ++) { String name = array[i]; for(int j = 0 ; j < list.size() ; j ++) { if(name.compareTo(list.get(j).getName()) == 0) { getCurrentNode().getSons().remove(list.get(j)); } } } } protected void handleADD(String pointer) { String[] array = pointer.split(" "); if(array.length != 1) { promptErrorCommand(pointer); return ; } String name = readString("Enter Node Name:", true); String content = readString("Enter Node Content:", false); TreeNode node = new TreeNode(name, content, getCurrentNode(), null); if(addValidation.validate(node)){ getCurrentNode().getSons().add(node); } } protected void handleHELP(String pointer) { println("[<ls>] list all nodes"); println("[<ls> <-l>] list all nodes with contents"); println("[<ls> <-list>] list all nodes with contents"); println("[<cd> <PATH>] redirect via PATH"); println("[<pwd>] show current path"); println("[<rm> <NODE_NAME> <*>] delete node, * hints delete all son nodes"); println("[<add>] add new node"); println("[<tree>] list whole node architecture"); println("[<tree> <-l>] list whole node architecture with contents"); println("[<tree> <-list>] list whole node architecture with contents"); } /** * Write this code when i was drunk * @param pointer */ protected void handleTREE(String pointer) { boolean isPrintDetails = pointer.equals("tree -l") || pointer.equals("tree -list"); map.clear(); recursivePrint(rootNode, 0, isPrintDetails); } protected void handleOther(String pointer) { promptErrorCommand(pointer) ; } protected void promptErrorCommand(String pointer) { prompt("[" + pointer + "] can not be recognized"); } protected TreeNode getRootNode() { return rootNode; } protected void setRootNode(TreeNode rootNode) { this.rootNode = rootNode; } protected String getAbsolutePath() { String tmp = getCurrentNode().getName(); TreeNode node = getCurrentNode(); while((node = getFatherNode(node)) != null) { String old = tmp; if(node.getName().compareTo("/") == 0){ tmp = node.getName() + old ; } else { tmp = node.getName() + File.separator + old ; } } return tmp; } protected void removeTreeNode(List<TreeNode> nodes, String name) { int size = nodes.size(); for(int i = 0 ; i < size ; i ++) { if(nodes.get(i).getName().compareTo(name) == 0) { nodes.remove(i); break; } } } protected TreeNode getTreeNode(TreeNode node, String path) { path = trimPath(path); if(path == null || path.equals("")) { return node; } String[] array = path.split("/"); for(int i = 0 ; i < array.length ; i ++) { node = findNode(node, array[i]); } if(null == node) { throw new TreeInputConsoleException("can not find TreeNode via " + path); } return node; } protected TreeNode getTreeNode(String path) { path = trimPath(path); if(path == null || path.equals("")) { return getRootNode(); } String[] array = path.split("/"); TreeNode node = getRootNode(); for(int i = 0 ; i < array.length ; i ++) { node = findNode(node, array[i]); } if(null == node) { throw new TreeInputConsoleException("can not find TreeNode via " + path); } return node; } protected String trimPath(String str) { String path = str ; if(path.startsWith("/")) { path = path.substring(1); } if(path.endsWith("/")) { path = path.substring(0, path.length() - 1); } return path; } protected boolean existBuddy(TreeNode node) { if(node.getFather() == null){ return false ; } return node.getFather().getSons().size() > 1; } private boolean exist(TreeNode treeNode) { for(TreeNode node : getCurrentNode().getSons()){ if(treeNode.getName().compareTo(node.getName()) == 0) { return true ; } } return false; } private void updateCursorStr(TreeNode node) { if(null == node) { cursor = "/"; } else { cursor = node.getName() ; } cursorStr = "[" +name + " " + cursor + "]"; } private List<TreeNode> getPrintNodes() { return getCurrentNode().getSons(); } private TreeNode findNode(TreeNode node, String name) { if(null == node) { throw new TreeInputConsoleException("findNode Error, node name: " + name); } TreeNode result = null; for(TreeNode n : node.getSons()) { if(n.getName().compareTo(name) == 0) { result = n ; break; } } return result; } private TreeNode getFatherNode(TreeNode node) { return node.getFather(); } private Action type(String pointer) { if(pointer.toLowerCase().startsWith("cd")) { return Action.CD ; } else if(pointer.toLowerCase().equals("ls") || pointer.toLowerCase().equals("ls -l") || pointer.toLowerCase().equals("ls -list")) { return Action.LS; } else if(pointer.toLowerCase().equals("pwd")) { return Action.PWD; } else if(pointer.toLowerCase().startsWith("rm")) { return Action.RM; } else if(pointer.toLowerCase().equals("add")) { return Action.ADD; } else if(pointer.toLowerCase().equals("tree") || pointer.toLowerCase().equals("tree -l") || pointer.toLowerCase().equals("tree -list")) { return Action.TREE; }else if(pointer.toLowerCase().equals("help")) { return Action.HELP; } else if(pointer.equals("")){ return Action.NULL; } else { return Action.OTHER; } } private enum Action { NULL, LS, CD, PWD, RM, ADD, HELP, TREE, OTHER, } protected class AddNodeValidation extends Validation { public boolean validate(Object obj) throws TreeInputConsoleException { TreeNode node = (TreeNode) obj; if(existBuddy(node)) { for(TreeNode tn : node.getFather().getSons()) { if(tn.getName().compareTo(node.getName()) == 0) { prompt("Illegal TreeNode Name, " + node.getName() + " already exist"); return false ; } } } return true; } } }