1 /* 2 * This file is part of ELKI: 3 * Environment for Developing KDD-Applications Supported by Index-Structures 4 * 5 * Copyright (C) 2018 6 * ELKI Development Team 7 * 8 * This program is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU Affero General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU Affero General Public License for more details. 17 * 18 * You should have received a copy of the GNU Affero General Public License 19 * along with this program. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 package de.lmu.ifi.dbs.elki.gui.util; 22 23 import java.util.Arrays; 24 import java.util.HashMap; 25 import java.util.List; 26 27 import javax.swing.tree.DefaultMutableTreeNode; 28 import javax.swing.tree.MutableTreeNode; 29 import javax.swing.tree.TreeNode; 30 31 import de.lmu.ifi.dbs.elki.utilities.ELKIServiceScanner; 32 33 /** 34 * Build a tree of available classes for use in Swing UIs. 35 * 36 * @author Erich Schubert 37 * @since 0.7.0 38 * 39 * @has - - - TreeNode 40 * @composed - - - PackageNode 41 * @composed - - - ClassNode 42 */ 43 public final class ClassTree { 44 /** 45 * Private constructor. Static methods only. 46 */ ClassTree()47 private ClassTree() { 48 // Do not use. 49 } 50 51 /** 52 * Build the class tree for a given set of choices. 53 * 54 * @param choices Class choices 55 * @param rootpkg Root package name (to strip / hide) 56 * @return Root node. 57 */ build(List<Class<?>> choices, String rootpkg)58 public static TreeNode build(List<Class<?>> choices, String rootpkg) { 59 MutableTreeNode root = new PackageNode(rootpkg, rootpkg); 60 HashMap<String, MutableTreeNode> lookup = new HashMap<>(); 61 if(rootpkg != null) { 62 lookup.put(rootpkg, root); 63 } 64 lookup.put("de.lmu.ifi.dbs.elki", root); 65 lookup.put("", root); 66 67 // Use the shorthand version of class names. 68 String prefix = rootpkg != null ? rootpkg + "." : null; 69 70 Class<?>[] choic = choices.toArray(new Class<?>[choices.size()]); 71 Arrays.sort(choic, ELKIServiceScanner.SORT_BY_PRIORITY); 72 for(Class<?> impl : choic) { 73 String name = impl.getName(); 74 name = (prefix != null && name.startsWith(prefix)) ? name.substring(prefix.length()) : name; 75 int plen = (impl.getPackage() != null) ? impl.getPackage().getName().length() + 1 : 0; 76 MutableTreeNode c = new ClassNode(impl.getName().substring(plen), name); 77 78 MutableTreeNode p = null; 79 int l = name.lastIndexOf('.'); 80 while(p == null) { 81 if(l < 0) { 82 p = root; 83 break; 84 } 85 String pname = name.substring(0, l); 86 p = lookup.get(pname); 87 if(p != null) { 88 break; 89 } 90 l = pname.lastIndexOf('.'); 91 MutableTreeNode tmp = new PackageNode(l >= 0 ? pname.substring(l + 1) : pname, pname); 92 tmp.insert(c, 0); 93 c = tmp; 94 lookup.put(pname, tmp); 95 name = pname; 96 } 97 p.insert(c, p.getChildCount()); 98 } 99 // Simplify tree, except for root node 100 for(int i = 0; i < root.getChildCount(); i++) { 101 MutableTreeNode c = (MutableTreeNode) root.getChildAt(i); 102 MutableTreeNode c2 = simplifyTree(c, null); 103 if(c != c2) { 104 root.remove(i); 105 root.insert(c2, i); 106 } 107 } 108 return root; 109 } 110 111 /** 112 * Simplify the tree. 113 * 114 * @param cur Current node 115 * @param prefix Prefix to add 116 * @return Replacement node 117 */ simplifyTree(MutableTreeNode cur, String prefix)118 private static MutableTreeNode simplifyTree(MutableTreeNode cur, String prefix) { 119 if(cur instanceof PackageNode) { 120 PackageNode node = (PackageNode) cur; 121 if(node.getChildCount() == 1) { 122 String newprefix = (prefix != null) ? prefix + "." + (String) node.getUserObject() : (String) node.getUserObject(); 123 cur = simplifyTree((MutableTreeNode) node.getChildAt(0), newprefix); 124 } 125 else { 126 if(prefix != null) { 127 node.setUserObject(prefix + "." + (String) node.getUserObject()); 128 } 129 for(int i = 0; i < node.getChildCount(); i++) { 130 MutableTreeNode c = (MutableTreeNode) node.getChildAt(i); 131 MutableTreeNode c2 = simplifyTree(c, null); 132 if(c != c2) { 133 node.remove(i); 134 node.insert(c2, i); 135 } 136 } 137 } 138 } 139 else if(cur instanceof ClassNode) { 140 ClassNode node = (ClassNode) cur; 141 if(prefix != null) { 142 node.setUserObject(prefix + "." + (String) node.getUserObject()); 143 } 144 } 145 return cur; 146 } 147 148 /** 149 * Tree node representing a single class. 150 * 151 * @author Erich Schubert 152 */ 153 public static class PackageNode extends DefaultMutableTreeNode { 154 /** 155 * Serial version 156 */ 157 private static final long serialVersionUID = 1L; 158 159 /** 160 * Class name. 161 */ 162 private String pkgname; 163 164 /** 165 * Current class name. 166 * 167 * @param display Displayed name 168 * @param pkgname Actual class name 169 */ PackageNode(String display, String pkgname)170 public PackageNode(String display, String pkgname) { 171 super(display); 172 this.pkgname = pkgname; 173 } 174 175 /** 176 * Return the package name. 177 * 178 * @return Package name 179 */ getPackageName()180 public String getPackageName() { 181 return pkgname; 182 } 183 } 184 185 /** 186 * Tree node representing a single class. 187 * 188 * @author Erich Schubert 189 */ 190 public static class ClassNode extends DefaultMutableTreeNode { 191 /** 192 * Serial version 193 */ 194 private static final long serialVersionUID = 1L; 195 196 /** 197 * Class name. 198 */ 199 private String clsname; 200 201 /** 202 * Current class name. 203 * 204 * @param display Displayed name 205 * @param clsname Actual class name 206 */ ClassNode(String display, String clsname)207 public ClassNode(String display, String clsname) { 208 super(display); 209 this.clsname = clsname; 210 } 211 212 /** 213 * Return the class name. 214 * 215 * @return Class name 216 */ getClassName()217 public String getClassName() { 218 return clsname; 219 } 220 } 221 } 222