/* * 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.gui.processeditor; import java.io.Serializable; import java.util.Comparator; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; import com.rapidminer.RapidMiner; import com.rapidminer.gui.tools.CamelCaseFilter; import com.rapidminer.operator.OperatorDescription; import com.rapidminer.operator.ProcessRootOperator; import com.rapidminer.tools.GroupTree; import com.rapidminer.tools.OperatorService; import com.rapidminer.tools.OperatorService.OperatorServiceListener; import com.rapidminer.tools.documentation.OperatorDocBundle; import com.rapidminer.tools.usagestats.OperatorStatisticsValue; import com.rapidminer.tools.usagestats.OperatorUsageStatistics; import com.rapidminer.tools.usagestats.UsageStatistics; import com.rapidminer.tools.usagestats.UsageStatistics.StatisticsScope; /** * This is the model for the group selection tree in the new operator editor panel. * * @author Ingo Mierswa, Tobias Malbrecht, Sebastian Land */ public class NewOperatorGroupTreeModel implements TreeModel, OperatorServiceListener { /** Compares operator descriptions based on their usage statistics. */ private static final class UsageStatsComparator implements Comparator<OperatorDescription>, Serializable { private static final long serialVersionUID = 1L; @Override public int compare(OperatorDescription op1, OperatorDescription op2) { OperatorUsageStatistics operatorStatistics1 = UsageStatistics.getInstance().getOperatorStatistics(StatisticsScope.ALL_TIME, op1); int usageCount1 = (operatorStatistics1 == null) ? 0 : operatorStatistics1.getStatistics(OperatorStatisticsValue.EXECUTION); OperatorUsageStatistics operatorStatistics2 = UsageStatistics.getInstance().getOperatorStatistics(StatisticsScope.ALL_TIME, op2); int usageCount2 = (operatorStatistics2 == null) ? 0 : operatorStatistics2.getStatistics(OperatorStatisticsValue.EXECUTION); return usageCount2 - usageCount1; } } private final GroupTree completeTree; private GroupTree displayedTree; private boolean filterDeprecated = true; private String filter = null; /** The list of all tree model listeners. */ private final List<TreeModelListener> treeModelListeners = new LinkedList<TreeModelListener>(); private boolean sortByUsage = false; public NewOperatorGroupTreeModel() { this.completeTree = OperatorService.getGroups(); OperatorService.addOperatorServiceListener(this); removeHidden(completeTree); this.displayedTree = this.completeTree; this.filterDeprecated = true; updateTree(); } public void setFilterDeprecated(boolean filterDeprecated) { this.filterDeprecated = filterDeprecated; updateTree(); } @Override public void addTreeModelListener(TreeModelListener l) { treeModelListeners.add(l); } public boolean contains(Object o) { return contains(this.getRoot(), o); } private boolean contains(Object start, Object o) { if (o.equals(start)) { return true; } else { for (int i = 0; i < getChildCount(start); i++) { if (contains(getChild(start, i), o)) { return true; } } } return false; } @Override public Object getChild(Object parent, int index) { if (parent instanceof GroupTree) { GroupTree tree = (GroupTree) parent; int numSubGroups = tree.getSubGroups().size(); if (index < numSubGroups) { return tree.getSubGroup(index); } else { return tree.getOperatorDescriptions().get(index - numSubGroups); } } else { return null; } } @Override public int getChildCount(Object parent) { if (parent instanceof GroupTree) { GroupTree tree = (GroupTree)parent; return tree.getSubGroups().size() + tree.getOperatorDescriptions().size(); } else { return 0; } } @Override public int getIndexOfChild(Object parent, Object child) { GroupTree tree = (GroupTree)parent; if (child instanceof GroupTree) { return tree.getIndexOfSubGroup((GroupTree)child); } else { return tree.getOperatorDescriptions().indexOf(child) + tree.getSubGroups().size(); } } @Override public Object getRoot() { return displayedTree; } @Override public boolean isLeaf(Object node) { return !(node instanceof GroupTree); } @Override public void removeTreeModelListener(TreeModelListener l) { treeModelListeners.remove(l); } /** Will be invoked after editing changes of nodes. */ @Override public void valueForPathChanged(TreePath path, Object node) { fireTreeChanged(node, path); } private void fireTreeChanged(Object source, TreePath path) { Iterator i = treeModelListeners.iterator(); while (i.hasNext()) { ((TreeModelListener) i.next()).treeStructureChanged(new TreeModelEvent(source, path)); } } private void fireCompleteTreeChanged(Object source) { Iterator i = treeModelListeners.iterator(); while (i.hasNext()) { ((TreeModelListener) i.next()).treeStructureChanged(new TreeModelEvent(this, new TreePath(getRoot()))); } } public int applyFilter(String filter) { this.filter = filter; return updateTree(); } public int updateTree() { int hits = Integer.MAX_VALUE; GroupTree filteredTree = this.completeTree.clone(); if (!"true".equals(System.getProperty(RapidMiner.PROPERTY_DEVELOPER_MODE))) { removeDeprecatedGroup(filteredTree); } if (filter != null && filter.trim().length() > 0) { CamelCaseFilter ccFilter = new CamelCaseFilter(filter); hits = removeFilteredInstances(ccFilter, filteredTree); } if (filterDeprecated) { hits = removeDeprecated(filteredTree); } this.displayedTree = filteredTree; if (sortByUsage) { filteredTree.sort(new UsageStatsComparator()); } fireCompleteTreeChanged(this); return hits; } public GroupTree getNonDeprecatedGroupTree(GroupTree tree) { GroupTree filteredTree = tree.clone(); removeDeprecated(filteredTree); return filteredTree; } private void removeHidden(GroupTree tree) { Iterator<? extends GroupTree> g = tree.getSubGroups().iterator(); while (g.hasNext()) { GroupTree child = g.next(); removeHidden(child); if (child.getAllOperatorDescriptions().size() == 0) { g.remove(); } } Iterator<OperatorDescription> o = tree.getOperatorDescriptions().iterator(); while (o.hasNext()) { OperatorDescription description = o.next(); if (description.getOperatorClass().equals(ProcessRootOperator.class)) { o.remove(); } } } private void removeDeprecatedGroup(GroupTree tree) { Iterator<? extends GroupTree> g = tree.getSubGroups().iterator(); while (g.hasNext()) { GroupTree child = g.next(); if (child.getKey().equals("deprecated")) { g.remove(); } else { removeDeprecatedGroup(child); } } } private int removeDeprecated(GroupTree tree) { int hits = 0; Iterator<? extends GroupTree> g = tree.getSubGroups().iterator(); while (g.hasNext()) { GroupTree child = g.next(); hits += removeDeprecated(child); if (child.getAllOperatorDescriptions().size() == 0) { g.remove(); } } Iterator<OperatorDescription> o = tree.getOperatorDescriptions().iterator(); while (o.hasNext()) { OperatorDescription description = o.next(); if (description.isDeprecated()) { o.remove(); } else { hits++; } } return hits; } private int removeFilteredInstances(CamelCaseFilter filter, GroupTree filteredTree) { int hits = 0; Iterator<? extends GroupTree> g = filteredTree.getSubGroups().iterator(); while (g.hasNext()) { GroupTree child = g.next(); boolean matches = filter.matches(child.getName()); if (!matches) { hits += removeFilteredInstances(filter, child); if (child.getAllOperatorDescriptions().size() == 0) { g.remove(); } } } // remove non matching operator descriptions if the group does not match, keep all in matching group boolean groupMatches = filter.matches(filteredTree.getName()); if (!groupMatches) { Iterator<OperatorDescription> o = filteredTree.getOperatorDescriptions().iterator(); while (o.hasNext()) { OperatorDescription description = o.next(); boolean matches = filter.matches(description.getName()) || filter.matches(description.getShortName()); if (!filterDeprecated) { for (String replaces : description.getReplacedKeys()) { matches |= filter.matches(replaces); } } if (!matches) { o.remove(); } else { hits++; } } } return hits; } public void setSortByUsage(boolean sort) { if (sort != this.sortByUsage ) { this.sortByUsage = sort; updateTree(); } } @Override public void operatorRegistered(OperatorDescription description, OperatorDocBundle bundle) { updateTree(); } @Override public void operatorUnregistered(OperatorDescription description) { updateTree(); } }