weka.gui.knowledgeflow.StepTree Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of weka-stable Show documentation
Show all versions of weka-stable Show documentation
The Waikato Environment for Knowledge Analysis (WEKA), a machine
learning workbench. This is the stable version. Apart from bugfixes, this version
does not receive any other updates.
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/*
* StepTree.java
* Copyright (C) 2011-2013 University of Waikato, Hamilton, New Zealand
*
*/
package weka.gui.knowledgeflow;
import weka.core.PluginManager;
import weka.core.Utils;
import weka.core.WekaException;
import weka.gui.GenericObjectEditor;
import weka.gui.GenericPropertiesCreator;
import weka.gui.HierarchyPropertyParser;
import weka.gui.knowledgeflow.VisibleLayout.LayoutOperation;
import weka.knowledgeflow.steps.KFStep;
import weka.knowledgeflow.steps.Step;
import weka.knowledgeflow.steps.WekaAlgorithmWrapper;
import javax.swing.Icon;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeSelectionModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.Beans;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* Subclass of JTree for displaying available steps.
*
* @author Mark Hall (mhall{[at]}pentaho{[dot]}com)
* @version $Revision: $
*/
public class StepTree extends JTree {
/** Property file that lists built-in steps */
protected static final String STEP_LIST_PROPS =
"weka/knowledgeflow/steps/steps.props";
/**
* For serialization
*/
private static final long serialVersionUID = 3646119269455293741L;
/** Reference to the main knowledge flow perspective */
protected MainKFPerspective m_mainPerspective;
/** Lookup for searching text of global info/tip texts */
protected Map m_nodeTextIndex =
new HashMap();
/**
* Constructor
*
* @param mainPerspective the main knowledge flow perspective
*/
public StepTree(MainKFPerspective mainPerspective) {
m_mainPerspective = mainPerspective;
DefaultMutableTreeNode jtreeRoot = new DefaultMutableTreeNode("Weka");
// populate tree
InvisibleTreeModel model = new InvisibleTreeModel(jtreeRoot);
model.activateFilter(true);
this.setModel(model);
setEnabled(true);
setToolTipText("");
setShowsRootHandles(true);
setCellRenderer(new StepIconRenderer());
DefaultTreeSelectionModel selectionModel = new DefaultTreeSelectionModel();
selectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
setSelectionModel(selectionModel);
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (((e.getModifiers() & InputEvent.BUTTON1_MASK) != InputEvent.BUTTON1_MASK)
|| e.isAltDown()) {
m_mainPerspective.setFlowLayoutOperation(LayoutOperation.NONE);
m_mainPerspective.setPalleteSelectedStep(null);
m_mainPerspective.setCursor(Cursor
.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
StepTree.this.clearSelection();
}
TreePath p = StepTree.this.getSelectionPath();
if (p != null) {
if (p.getLastPathComponent() instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode tNode =
(DefaultMutableTreeNode) p.getLastPathComponent();
if (tNode.isLeaf()) {
Object userObject = tNode.getUserObject();
if (userObject instanceof StepTreeLeafDetails) {
try {
StepVisual visual =
((StepTreeLeafDetails) userObject).instantiateStep();
m_mainPerspective.setCursor(Cursor
.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
if (m_mainPerspective.getDebug()) {
System.err.println("Instantiated " + visual.getStepName());
}
m_mainPerspective.setPalleteSelectedStep(visual
.getStepManager());
} catch (Exception ex) {
m_mainPerspective.showErrorDialog(ex);
}
}
}
}
}
}
});
try {
populateTree(jtreeRoot);
} catch (Exception ex) {
ex.printStackTrace();
}
expandRow(0);
setRootVisible(false);
}
/**
* Populates the tree from the given root
*
* @param jtreeRoot the root to populate
* @throws Exception if a problem occurs
*/
protected void populateTree(DefaultMutableTreeNode jtreeRoot)
throws Exception {
Properties GOEProps = initGOEProps();
// builtin steps don't get added to the plugin manager because,
// due to the package loading process, they would get added after
// any plugin steps. This would stuff up the ordering we want in
// the design palette
InputStream inputStream =
getClass().getClassLoader().getResourceAsStream(STEP_LIST_PROPS);
Properties builtinSteps = new Properties();
builtinSteps.load(inputStream);
inputStream.close();
inputStream = null;
String stepClassNames =
builtinSteps.getProperty("weka.knowledgeflow.steps.Step");
String[] s = stepClassNames.split(",");
Set stepImpls = new LinkedHashSet();
stepImpls.addAll(Arrays.asList(s));
populateTree(stepImpls, jtreeRoot, GOEProps);
// get any plugin steps here
Set stepClasses =
PluginManager.getPluginNamesOfType("weka.knowledgeflow.steps.Step");
if (stepClasses != null && stepClasses.size() > 0) {
// filtering here because the LegacyFlowLoader adds all builtin
// steps to the PluginManager. This is really only necessary if
// a KnowledgeFlowApp is constructed a second time, as the first
// time round StepTree gets constructed before the LegacyFlowLoader
// class gets loaded into the classpath (and thus populates the
// PluginManager). We can remove this filtering when LegacyFlowLoader
// is no longer needed.
Set filteredStepClasses = new LinkedHashSet();
for (String plugin : stepClasses) {
if (!stepClassNames.contains(plugin)) {
filteredStepClasses.add(plugin);
}
}
populateTree(filteredStepClasses, jtreeRoot, GOEProps);
}
}
/**
* Populate the tree from the given root using a set of step classes
*
* @param stepClasses the set of step classes to go into the tree
* @param jtreeRoot the root of the tree
* @param GOEProps generic object editor properties
* @throws Exception if a problem occurs
*/
protected void populateTree(Set stepClasses,
DefaultMutableTreeNode jtreeRoot, Properties GOEProps) throws Exception {
for (String stepClass : stepClasses) {
try {
Step toAdd =
(Step) Beans.instantiate(getClass().getClassLoader(), stepClass);
// check for ignore
if (toAdd.getClass().getAnnotation(StepTreeIgnore.class) != null
|| toAdd.getClass().getAnnotation(weka.gui.beans.KFIgnore.class) != null) {
continue;
}
String category = getStepCategory(toAdd);
DefaultMutableTreeNode targetFolder =
getCategoryFolder(jtreeRoot, category);
if (toAdd instanceof WekaAlgorithmWrapper) {
populateForWekaWrapper(targetFolder, (WekaAlgorithmWrapper) toAdd,
GOEProps);
} else {
StepTreeLeafDetails leafData = new StepTreeLeafDetails(toAdd);
DefaultMutableTreeNode fixedLeafNode = new InvisibleNode(leafData);
targetFolder.add(fixedLeafNode);
String tipText =
leafData.getToolTipText() != null ? leafData.getToolTipText() : "";
m_nodeTextIndex.put(stepClass.toLowerCase() + " " + tipText,
fixedLeafNode);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
/**
* Populates a target folder in the tree for a particular class of weka
* algorithm wrapper step
*
* @param targetFolder the folder to populate
* @param wrapper the {@code WekaAlgorithmWrapper} implementation
* @param GOEProps generic object editor properties
* @throws Exception if a problem occurs
*/
protected void populateForWekaWrapper(DefaultMutableTreeNode targetFolder,
WekaAlgorithmWrapper wrapper, Properties GOEProps) throws Exception {
Class wrappedAlgoClass = wrapper.getWrappedAlgorithmClass();
String implList = GOEProps.getProperty(wrappedAlgoClass.getCanonicalName());
String hppRoot = wrappedAlgoClass.getCanonicalName();
hppRoot = hppRoot.substring(0, hppRoot.lastIndexOf('.'));
if (implList == null) {
throw new WekaException(
"Unable to get a list of weka implementations for " + "class '"
+ wrappedAlgoClass.getCanonicalName() + "'");
}
Hashtable roots =
GenericObjectEditor.sortClassesByRoot(implList);
for (Map.Entry e : roots.entrySet()) {
String classes = e.getValue();
HierarchyPropertyParser hpp = new HierarchyPropertyParser();
hpp.build(classes, ", ");
hpp.goTo(hppRoot);
processPackage(hpp, targetFolder, wrapper);
}
}
/**
* Processes a package from the {@code HierarchPropertyParser}
*
* @param hpp the property parser to use
* @param parentFolder the folder to populate
* @param wrapper the {@code WekaAlgorithmWrapper} implementation to use
* for the class of algorithm being processed
* @throws Exception if a problem occurs
*/
protected void processPackage(HierarchyPropertyParser hpp,
DefaultMutableTreeNode parentFolder, WekaAlgorithmWrapper wrapper)
throws Exception {
String[] primaryPackages = hpp.childrenValues();
for (String primaryPackage : primaryPackages) {
hpp.goToChild(primaryPackage);
if (hpp.isLeafReached()) {
String algName = hpp.fullValue();
Object wrappedA =
Beans.instantiate(this.getClass().getClassLoader(), algName);
if (wrappedA.getClass().getAnnotation(StepTreeIgnore.class) == null
&& wrappedA.getClass().getAnnotation(weka.gui.beans.KFIgnore.class) == null) {
WekaAlgorithmWrapper wrapperCopy =
(WekaAlgorithmWrapper) Beans.instantiate(this.getClass()
.getClassLoader(), wrapper.getClass().getCanonicalName());
wrapperCopy.setWrappedAlgorithm(wrappedA);
StepTreeLeafDetails leafData = new StepTreeLeafDetails(wrapperCopy);
DefaultMutableTreeNode wrapperLeafNode = new InvisibleNode(leafData);
parentFolder.add(wrapperLeafNode);
String tipText =
leafData.getToolTipText() != null ? leafData.getToolTipText() : "";
m_nodeTextIndex.put(algName.toLowerCase() + " " + tipText,
wrapperLeafNode);
}
hpp.goToParent();
} else {
DefaultMutableTreeNode firstLevelOfMainAlgoType =
new InvisibleNode(primaryPackage);
parentFolder.add(firstLevelOfMainAlgoType);
processPackage(hpp, firstLevelOfMainAlgoType, wrapper);
hpp.goToParent();
}
}
}
/**
* Get a folder for a particular category from the tree. Creates the
* category folder if it doesn't already exist
*
* @param jtreeRoot the root of the tree
* @param category the name of the category to get the folder for
* @return return the folder
*/
protected DefaultMutableTreeNode getCategoryFolder(
DefaultMutableTreeNode jtreeRoot, String category) {
DefaultMutableTreeNode targetFolder = null;
@SuppressWarnings("unchecked")
Enumeration