/* * RapidMiner * * Copyright (C) 2001-2011 by Rapid-I and the contributors * * Complete list of developers available at our web site: * * http://rapid-i.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.tools; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import javax.swing.ImageIcon; import com.rapidminer.gui.tools.SwingTools; import com.rapidminer.operator.OperatorDescription; import com.rapidminer.tools.OperatorService.OperatorServiceListener; import com.rapidminer.tools.documentation.OperatorDocBundle; /** * A group tree manages operator descriptions in a tree like manner. This is * useful to present the operators in groups and subgroups and eases operator * selection in the GUI. * * The group tree heavily depends on the associated OperatorService, since it reflects * the registered Operators of that Service. Each {@link OperatorService} can have multiple * GroupTrees, which register as listener to be able to update on new registration or unregistration events. * * The listening is done by the {@link GroupTreeRoot} class implementing the {@link OperatorServiceListener} interface. * * @author Ingo Mierswa, Sebastian Land */ public abstract class GroupTree implements Comparable<GroupTree> { private static final ImageIcon[] NO_ICONS = new ImageIcon[3]; /** The list of operators in this group. */ private final List<OperatorDescription> operators = new LinkedList<OperatorDescription>(); /** The subgroups of this group. */ private final Map<String, GroupTreeNode> children = new LinkedHashMap<String, GroupTreeNode>(); private String iconName; private ImageIcon[] icons; protected GroupTree() { } /** * Clone constructor. * This will keep the link to the {@link OperatorService}. Whenever a new Operator is added or removed, * this will be reflected in the copy as well. * For detecting such events, please register on {@link OperatorService#addOperatorServiceListener(OperatorServiceListener)}. * */ protected GroupTree(GroupTree other) { this.operators.addAll(other.operators); Iterator<? extends GroupTree> g = other.getSubGroups().iterator(); while (g.hasNext()) { GroupTree child = g.next(); addSubGroup((GroupTreeNode) child.clone()); } } /** Returns the parent of this group. Returns null if no parent does exist. */ public abstract GroupTree getParent(); /** Returns or creates the subgroup with the given key. This is not the fully qualified key! */ public GroupTree getSubGroup(String key) { return children.get(key); } /** Returns or creates the subgroup with the given name, creating it if not present. */ public GroupTree getOrCreateSubGroup(String key, OperatorDocBundle bundle) { GroupTreeNode child = children.get(key); if (child == null) { child = new GroupTreeNode(this, key, bundle); addSubGroup(child); } return child; } /** Returns a set of all children group trees. */ public Collection<? extends GroupTree> getSubGroups() { return children.values(); } /** Returns the index of the given subgroup or -1 if the sub group is not a child of this node. */ public int getIndexOfSubGroup(GroupTree child) { Iterator<? extends GroupTree> i = getSubGroups().iterator(); int index = 0; while (i.hasNext()) { GroupTree current = i.next(); if (current.equals(child)) return index; index++; } return -1; } /** Returns the i-th sub group. */ public GroupTree getSubGroup(int index) { Collection<? extends GroupTree> allChildren = getSubGroups(); if (index >= allChildren.size()) { return null; } else { Iterator<? extends GroupTree> i = allChildren.iterator(); int counter = 0; while (i.hasNext()) { GroupTree current = i.next(); if (counter == index) return current; counter++; } return null; } } /** Adds an operator to this group. */ protected void addOperatorDescription(OperatorDescription description) { operators.add(description); } /** * This removes the given {@link OperatorDescription} from this GroupTree */ protected void removeOperatorDescription(OperatorDescription description) { operators.remove(description); } /** * Returns all operator descriptions in this group or an empty list if this * group does not contain any operators. */ public List<OperatorDescription> getOperatorDescriptions() { return operators; } /** * Returns all operator in this group and recursively the operators of all * children. */ public Set<OperatorDescription> getAllOperatorDescriptions() { Set<OperatorDescription> result = new TreeSet<OperatorDescription>(); addAllOperatorDescriptions(result); return result; } private void addAllOperatorDescriptions(Set<OperatorDescription> operators) { operators.addAll(this.operators); Iterator<GroupTreeNode> i = children.values().iterator(); while (i.hasNext()) { GroupTree child = i.next(); child.addAllOperatorDescriptions(operators); } } /** * This returns a deep clone of this {@link GroupTree}. */ @Override public abstract GroupTree clone(); /** * This returns a group description depending on internationalization */ public abstract String getDescription(); /** * Gets the key of this group. This is used for internationalization and does not contain the parent groups. * See {@link #getFullyQualifiedKey()} for this one. */ public abstract String getKey(); /** * Returns the fully qualified key where each group key is separated by dot. The qualified key starts * with the highest parent group. */ public abstract String getFullyQualifiedKey(); /** * Deprecated method that returns the fully qualified key. See {@link #getFullyQualifiedKey()} for Details. */ @Deprecated public String getQName() { return getFullyQualifiedKey(); } /** Returns the name of this group. This name depends on internationalization! */ public abstract String getName(); public void setIconName(String icon) { this.iconName = icon; loadIcons(); } public String getIconName() { if (this.iconName != null) { return this.iconName; } else { return null; } } public ImageIcon[] getIcons() { if (icons != null) { return icons; } else { return NO_ICONS; } } protected final int countOperators() { int count = operators.size(); for (GroupTree tree : children.values()) { count += tree.countOperators(); } return count; } /** * This method will sort this GroupTree according to the given comparator. */ public void sort(Comparator<OperatorDescription> comparator) { Collections.sort(operators, comparator); for (GroupTree child : children.values()) { child.sort(comparator); } } /** Adds a subgroup to this group. */ private void addSubGroup(GroupTreeNode child) { children.put(child.getKey(), child); child.setParent(this); } /** * Loads the icons if defined. */ private void loadIcons() { if (iconName == null) { icons = null; return; } icons = new ImageIcon[3]; icons[0] = SwingTools.createIcon("16/" + iconName); icons[1] = SwingTools.createIcon("24/" + iconName); icons[2] = SwingTools.createIcon("48/" + iconName); } @Override public String toString() { String result = getName(); if (getParent() == null) result = "Root"; int size = countOperators(); return result + (size > 0 ? " (" + size + ")" : ""); } @Override public int compareTo(GroupTree o) { return this.getName().compareTo(o.getName()); } @Override public boolean equals(Object o) { if (!(o instanceof GroupTree)) return false; return true; } @Override public int hashCode() { return this.getKey().hashCode(); } /** * This method remains only for compatibility. If existing the group will * be returned. * Please notice, that it is not advised to interact with the * GroupTree manually. Please use the {@link OperatorService#registerOperator(OperatorDescription)} method to add operators and modify * the GroupTree accordingly. */ @Deprecated public static GroupTree findGroup(String fullyQualifiedGroupName, OperatorDocBundle object) { GroupTreeRoot root = (GroupTreeRoot) OperatorService.getGroups(); return root.findGroup(fullyQualifiedGroupName); } }