Java tutorial
/* This file is part of BioNet. BioNet 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. BioNet 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 BioNet. If not, see <http://www.gnu.org/licenses/>. */ package edu.purdue.cc.bionet.ui; import edu.purdue.bbc.util.Language; import edu.purdue.bbc.util.NumberList; import edu.purdue.bbc.util.Settings; import edu.purdue.cc.bionet.io.SaveImageAction; import edu.purdue.cc.bionet.util.SampleComparator; import edu.purdue.cc.bionet.util.Experiment; import edu.purdue.cc.bionet.util.Molecule; import edu.purdue.cc.bionet.util.Sample; import edu.purdue.cc.bionet.util.SampleGroup; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeSet; import javax.swing.JButton; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import net.sf.javaml.clustering.Clusterer; import net.sf.javaml.clustering.KMeans; import net.sf.javaml.clustering.SOM; import net.sf.javaml.core.Dataset; import net.sf.javaml.core.DefaultDataset; import net.sf.javaml.core.Instance; import net.sf.javaml.core.DenseInstance; import net.sf.javaml.tools.data.StreamHandler; import net.sf.javaml.core.DefaultDataset; import it.cnr.imaa.essi.lablib.gui.checkboxtree.CheckboxTree; import it.cnr.imaa.essi.lablib.gui.checkboxtree.TreeCheckingEvent; import it.cnr.imaa.essi.lablib.gui.checkboxtree.TreeCheckingListener; import it.cnr.imaa.essi.lablib.gui.checkboxtree.TreeCheckingModel; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartTheme; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.StandardChartTheme; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.TickUnits; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.labels.XYItemLabelGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.category.BoxAndWhiskerRenderer; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.chart.renderer.xy.XYItemRendererState; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.statistics.BoxAndWhiskerCalculator; import org.jfree.data.statistics.BoxAndWhiskerItem; import org.jfree.data.statistics.DefaultBoxAndWhiskerCategoryDataset; import org.jfree.data.statistics.DefaultBoxAndWhiskerXYDataset; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.apache.log4j.Logger; public class ClusteringDisplayPanel extends AbstractDisplayPanel implements ActionListener { private JMenuBar menuBar; private JMenu groupsMenu; private JMenuItem chooseSampleGroupsMenuItem; private JMenuItem removeSampleGroupsMenuItem; private JMenuItem recomputeMenuItem; private JPanel selectorPanel; private SampleSelectorTreePanel sampleSelectorTree; private JSplitPane splitPane; private JSplitPane treeSplitPane; private JPanel clusterGraphPanel; private Collection<Experiment> experiments; private Collection<Sample> samples; private Collection<Molecule> molecules; private Clusterer clusterer; private static final int ROOT = 0; private static final int CLUSTER = 1; private static final int MOLECULE = 2; /** * A class for displaying information about a Clustering */ public ClusteringDisplayPanel() { super(new BorderLayout()); Language language = Settings.getLanguage(); this.addComponentListener(new InitialSetup()); this.menuBar = new JMenuBar(); this.groupsMenu = new JMenu(language.get("Groups")); this.groupsMenu.setMnemonic(KeyEvent.VK_G); this.removeSampleGroupsMenuItem = new JMenuItem(language.get("Reset Sample Groups"), KeyEvent.VK_R); this.chooseSampleGroupsMenuItem = new JMenuItem(language.get("Choose Sample Groups"), KeyEvent.VK_C); this.recomputeMenuItem = new JMenuItem(Settings.getLanguage().get("Recompute/Set Parameters")); this.recomputeMenuItem.addActionListener(this); this.groupsMenu.add(this.removeSampleGroupsMenuItem); this.groupsMenu.add(this.chooseSampleGroupsMenuItem); this.chooseSampleGroupsMenuItem.addActionListener(this); this.removeSampleGroupsMenuItem.addActionListener(this); this.add(menuBar, BorderLayout.NORTH); this.menuBar.add(this.groupsMenu); this.menuBar.add(this.recomputeMenuItem); } /** * Creates the visualization instance for a ClusteringDisplayPanel * * @param experiments The experiments to be associated with this instance. * @return true if creating the visualization succeeded. */ public boolean createView(Collection<Experiment> experiments) { Logger logger = Logger.getLogger(getClass()); this.experiments = experiments; this.samples = new SampleGroup(""); this.molecules = new TreeSet<Molecule>(); for (Experiment experiment : experiments) { this.samples.addAll(experiment.getSamples()); this.molecules.addAll(experiment.getMolecules()); } Collection<SampleGroup> sampleGroups = new ArrayList<SampleGroup>(); sampleGroups.add(new SampleGroup("", samples)); this.sampleSelectorTree = new SampleSelectorTreePanel(this.experiments); this.splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); this.treeSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); JPanel treePanel = new JPanel(new BorderLayout()); this.treeSplitPane.setBottomComponent(this.sampleSelectorTree); treePanel.add(this.treeSplitPane, BorderLayout.CENTER); this.splitPane.setLeftComponent(treePanel); this.clusterGraphPanel = new JPanel(new GridLayout(1, 1, 3, 3)); this.clusterGraphPanel.setBackground(Color.LIGHT_GRAY); this.splitPane.setRightComponent(this.clusterGraphPanel); this.add(this.splitPane, BorderLayout.CENTER); this.splitPane.setDividerLocation(250); this.selectorPanel = new JPanel(new GridLayout(1, 1)); this.treeSplitPane.setTopComponent(this.selectorPanel); this.setSampleGroups(sampleGroups); return true; } /** * Sets the clusterer to be used by this ClusteringDisplayPanel. * * @param clusterer The new clusterer to use. */ private void setClusterer(Clusterer clusterer) { this.clusterer = clusterer; } /** * Gets the current clusterer being used by this ClusteringDisplayPanel. * * @return The clusterer currently in use. */ private Clusterer getClusterer() { return this.clusterer; } /** * Gets the molecules associated with a Dataset (cluster) * * @param d A dataset containing instances with the same string value as * Molecules in the experiment. * @return A Collection containing the molecules indicated by the instances. */ private Collection<Molecule> getMoleculesForDataset(Dataset d) { Collection<Molecule> returnValue = new TreeSet<Molecule>(); for (Instance i : d) { Molecule m = this.getMoleculeForInstance(i); if (m != null) { returnValue.add(m); } } return returnValue; } /** * Returns the particular Molecule for the experiment which has the same Id as * The instance class value. * * @param i The Instance to look up the Molecule for. * @return The Molecule associated with this Instance. */ private Molecule getMoleculeForInstance(Instance i) { Object classValue = i.classValue(); if (classValue instanceof Molecule) { return (Molecule) classValue; } for (Molecule m : this.molecules) { if (m.getId().equals(classValue.toString())) { return m; } } return null; } /** * Gets the title of this panel. * * @return The title of this panel as a String. */ public String getTitle() { return "Clustering"; } /** * The actionPerformed method of the ActionListener interface. Performs * cluster recalculation when the 'recalculate' button is clicked. * * @param e The event which triggered this action. */ public void actionPerformed(ActionEvent e) { Logger logger = Logger.getLogger(getClass()); Language language = Settings.getLanguage(); Object source = e.getSource(); if (source == this.recomputeMenuItem) { // don't change the groups, just trigger the clusterer this.setSampleGroups(this.getSampleGroups()); } else if (source == this.chooseSampleGroupsMenuItem) { // Choose sample groups. Component frame = this; while (!(frame instanceof Frame) && frame != null) { frame = frame.getParent(); } Collection<SampleGroup> groups = SampleGroupDialog.showInputDialog((Frame) frame, Settings.getLanguage().get("Choose groups"), this.samples); if (groups != null) { if (this.getSampleGroups() != null) { for (SampleGroup group : this.getSampleGroups()) { logger.debug(group.toString()); for (Sample sample : group) { logger.debug("\t" + sample.toString()); } } } this.setSampleGroups(groups); } } else if (source == this.removeSampleGroupsMenuItem) { Collection<SampleGroup> groups = new ArrayList<SampleGroup>(); groups.add(new SampleGroup("", this.samples)); this.setSampleGroups(groups); } } public void setSampleGroups(Collection<SampleGroup> sampleGroups) { Component frame = this; while (!(frame instanceof Frame) && frame != null) { frame = frame.getParent(); } ClusterSelectionDialog dialog = new ClusterSelectionDialog((Frame) frame, Settings.getLanguage().get("Choose Clustering Method"), this.getSampleGroups() != null); if (dialog.getReturnValue() == null) { return; } super.setSampleGroups(sampleGroups); Collection<Molecule> molecules; try { ClusterSelectorTreePanel clusterPanel = (ClusterSelectorTreePanel) this.selectorPanel.getComponent(0); molecules = clusterPanel.getCheckedMolecules(); } catch (ArrayIndexOutOfBoundsException e) { molecules = this.molecules; } catch (ClassCastException e) { molecules = this.molecules; Logger.getLogger(this.getClass()).error(e, e); } // clear the panels and set new layouts based on the new groups. this.selectorPanel.removeAll(); this.clusterGraphPanel.removeAll(); int rows = (int) Math.ceil(Math.sqrt(sampleGroups.size())); int cols = (int) Math.ceil(sampleGroups.size() / rows); GridLayout layout = (GridLayout) this.selectorPanel.getLayout(); layout.setRows(rows); layout.setColumns(cols); layout = (GridLayout) this.clusterGraphPanel.getLayout(); layout.setRows(rows); layout.setColumns(cols); // start the clusterer(s) in parallel. Map<Thread, RunnableClusterer> clusterers = new HashMap<Thread, RunnableClusterer>(); for (SampleGroup group : sampleGroups) { RunnableClusterer clusterer = new RunnableClusterer(dialog.getReturnValue()); SampleGroup filteredGroup = new SampleGroup(group); filteredGroup.retainAll(this.sampleSelectorTree.getSamples()); clusterer.setDataset(this.getDataset(molecules, filteredGroup)); Thread thread = new Thread(clusterer); thread.start(); clusterers.put(thread, clusterer); } // iterate through each group and join the threads Iterator<SampleGroup> groupIter = sampleGroups.iterator(); Collection<ClusterSelectorTreePanel> clusterTreeList = new ArrayList<ClusterSelectorTreePanel>(); this.clusterGraphPanel.removeAll(); for (Map.Entry<Thread, RunnableClusterer> clusterer : clusterers.entrySet()) { try { clusterer.getKey().join(); Dataset[] result = clusterer.getValue().getResult(); Collection<Collection<Molecule>> clusters = new TreeSet<Collection<Molecule>>( new DatasetComparator()); Collection<Molecule> unclustered = new ArrayList<Molecule>(this.molecules); for (Dataset dataset : result) { Collection<Molecule> cluster = this.getMoleculesForDataset(dataset); clusters.add(cluster); unclustered.removeAll(cluster); } // create a new clustertree and add the appropriate listeners ClusterSelectorTreePanel clusterTree = new ClusterSelectorTreePanel(clusters); if (unclustered.size() > 0) { clusterTree.add(unclustered, String.format(Settings.getLanguage().get("Unclustered") + " (%d)", unclustered.size()), false); } for (ClusterSelectorTreePanel tree : clusterTreeList) { tree.addTreeCheckingListener(clusterTree); clusterTree.addTreeCheckingListener(tree); } clusterTreeList.add(clusterTree); this.selectorPanel.add(clusterTree); ClusterGraph graph = new ClusterGraph(this.sampleSelectorTree, clusterTree, groupIter.next()); this.clusterGraphPanel.add(graph); graph.setMeanGraph(clusterTree.getRoot()); } catch (InterruptedException e) { Logger.getLogger(this.getClass()).debug(e, e); } } this.removeSampleGroupsMenuItem.setEnabled(sampleGroups.size() > 1); this.validate(); } /** * Creates a Dataset for use with JavaML * * @param molecules A Collection of Molecules to use in the Dataset * @param samples A Collection of Samples to use for determining values. * @return A Dataset suitable for use with JavaML Clustering. */ public Dataset getDataset(Collection<Molecule> molecules, Collection<Sample> samples) { Dataset returnValue = new DefaultDataset(); for (Molecule m : molecules) { returnValue.add(new DenseInstance(m.getValues(samples).toDoubleArray(), m)); } return returnValue; } // ============================= PRIVATE CLASSES ============================= // ============================ RunnableClusterer ============================ /** * A class for running a Clusterer instance in a seperate thread. */ private class RunnableClusterer implements Runnable { private Clusterer clusterer; private Dataset dataset; private Dataset[] result; /** * Creates a new RunnableClusterer which will run the underlying Clusterer * when it's run( ) method is called. * * @param clusterer The Clusterer to be run in a thread. */ public RunnableClusterer(Clusterer clusterer) { this.clusterer = clusterer; } /** * Creates a new RunnableClusterer which will run the underlying Clusterer * when it's run( ) method is called. * * @param clusterer The Clusterer to be run in a thread. * @param dataset The dataset to be evaluated with the passed in Clusterer. */ public RunnableClusterer(Clusterer clusterer, Dataset dataset) { this.clusterer = clusterer; this.dataset = dataset; } /** * Sets the Dataset to be evaluated with the Clusterer. * * @param dataset The Dataset to be evlauated. */ public void setDataset(Dataset dataset) { this.dataset = dataset; } /** * Runs the underlying Clusterer. */ public void run() { this.result = this.clusterer.cluster(this.dataset); } /** * Returns an array of Datasets which are the result of the underlying * Clusterer. * * @return The clusters found by the underlying Clusterer. */ public Dataset[] getResult() { return result; } } // ============================ InitialSetup ================================= /** * A class for handling the sizng of panels and such in * ClusteringDisplayPanel when it is initially shown. */ private class InitialSetup extends ComponentAdapter { public InitialSetup() { super(); } /** * Fired when the component is shown. Adjusts the size of the treeSplitPane * based on the overall size of the panel when it is made visible, then * unregisters itself as a listener so it is only fired once. * * @param e The event which triggered this action. */ public void componentShown(ComponentEvent e) { Component source = e.getComponent(); treeSplitPane.setDividerLocation(source.getHeight() * 2 / 3); // we only need to perform this action once. source.removeComponentListener(this); } } // =========================== DatasetComparator ============================= /** * Compares and orders a Collection of Collections of Molecules. */ private class DatasetComparator implements Comparator<Collection<Molecule>> { /** * Compares 2 Collections of Collections of Mollecules. First compares based * on size, and orders the larger one to be first. If they are the same * size, the compareTo( ) method is called on the molecules contained within * until they do not match, and that value is returned. If they are the same * size and contain the same molecules, 0 is returned (indicating they are * equal) * * @param c1 The first Collection to be compared. * @param c2 The second Collection to be comapred. * @return A value indicating the preferred ordering of the 2 collections. */ public int compare(Collection<Molecule> c1, Collection<Molecule> c2) { int returnValue = c2.size() - c1.size(); if (returnValue != 0) return returnValue; Iterator<Molecule> c1Iterator = c1.iterator(); Iterator<Molecule> c2Iterator = c2.iterator(); while (c1Iterator.hasNext()) { returnValue = c1Iterator.next().getId().compareTo(c2Iterator.next().getId()); if (returnValue != 0) return returnValue; } return returnValue; } } // ======================= ClusterSelectorTreePanel ========================== /** * A Panel containing a CheckboxTree with all clusters and their associated * Molecules. */ private class ClusterSelectorTreePanel extends CheckboxTreePanel implements TreeCheckingListener { private JScrollPane scrollPane; private static final int CLUSTER = 1; private static final int MOLECULE = 2; /** * Creates a new emtpy ClusterSelectorTreePanel. */ public ClusterSelectorTreePanel() { super(new DefaultMutableTreeNode(Settings.getLanguage().get("Clusters"))); } public ClusterSelectorTreePanel(TreeNode rootNode) { super(rootNode); } /** * Creates a new ClusterSelectorTreePanel * * @param clusters The clusters to be displayed in the panel. */ public ClusterSelectorTreePanel(TreeNode rootNode, Collection<Collection<Molecule>> clusters) { this(rootNode); int clusterCount = 0; String clusterString = Settings.getLanguage().get("Cluster") + " %d (%d)"; for (Collection<Molecule> cluster : clusters) { if (cluster.size() > 0) { this.add(cluster, String.format(clusterString, ++clusterCount, cluster.size())); } } } public ClusterSelectorTreePanel(Collection<Collection<Molecule>> clusters) { this(new DefaultMutableTreeNode(Settings.getLanguage().get("Clusters")), clusters); } public void clear() { DefaultMutableTreeNode rootNode = this.getRoot(); while (rootNode.getChildCount() > 0) { rootNode.remove(0); } ((DefaultTreeModel) this.tree.getModel()).reload(); this.reload(); } /** * Adds a cluster of molecules to the tree and sets it as checked. * * @param cluster The cluster to be added to the tree * @param name The name for this cluster, usually "Cluster" followed by a * number. */ public void add(Collection<Molecule> cluster, String name) { this.add(cluster, name, true); } /** * Adds a cluster of molecules to the tree. * * @param cluster The cluster to be added to the tree * @param name The name for this cluster, usually "Cluster" followed by a * @param checked whether the initial state should be checked. * number. */ public void add(Collection<Molecule> cluster, String name, boolean checked) { Language language = Settings.getLanguage(); int clusterCount = 0; // add the cluster to the tree. DefaultMutableTreeNode clusterNode = new CustomMutableTreeNode(cluster, name); for (Molecule molecule : cluster) { DefaultMutableTreeNode moleculeNode = new DefaultMutableTreeNode(molecule); clusterNode.add(moleculeNode); } ((DefaultTreeModel) this.tree.getModel()).insertNodeInto(clusterNode, this.getRoot(), this.getRoot().getChildCount()); if (checked) { this.check(this.getRoot()); this.check(clusterNode); } else { this.uncheck(clusterNode); } this.tree.expandPath(new TreePath(this.getRoot().getPath())); this.tree.setSelectsByChecking(false); this.tree.getCheckingModel() .setCheckingMode(TreeCheckingModel.CheckingMode.PROPAGATE_PRESERVING_UNCHECK); this.repaint(); } /** * Returns a Collection containing all of the molecules whose checkboxes are * selected. * * @return A Collection of the "Checked" molecules in the tree. */ public Collection<Molecule> getCheckedMolecules() { Collection<Molecule> returnValue = new TreeSet<Molecule>(); Enumeration nodeEnum = ((DefaultMutableTreeNode) this.getTree().getModel().getRoot()) .breadthFirstEnumeration(); while (nodeEnum.hasMoreElements()) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) nodeEnum.nextElement(); if (node.getLevel() == MOLECULE && this.isChecked(node)) { returnValue.add((Molecule) node.getUserObject()); } } return returnValue; } /** * The valueChanged method of the TreeCheckingListener interface. Listens for * changes to other ClusterTreeSelectorPanels and mirrors them. * * @param e The event which triggered this action. */ public void valueChanged(TreeCheckingEvent e) { boolean checked = e.isCheckedPath(); ClusterSelectorTreePanel eventPanel = (ClusterSelectorTreePanel) e.getSource(); TreeNode eventNode = (TreeNode) e.getPath().getLastPathComponent(); Logger.getLogger(this.getClass()).debug( e.getPath().getLastPathComponent().toString() + " " + (checked ? "checked" : "unchecked")); int level = e.getPath().getPathCount() - 1; if (level <= MOLECULE) { // cache the list of Molecule nodes for performance. ArrayList<TreeNode> myMoleculeNodes = new ArrayList<TreeNode>(); Iterator<TreeNode> descIter = this.descendantIterator(this.getRoot(), MOLECULE); Iterator<TreeNode> otherDescIter = eventPanel.descendantIterator(eventNode, MOLECULE); while (descIter.hasNext()) { myMoleculeNodes.add(descIter.next()); } while (otherDescIter.hasNext()) { TreeNode otherMoleculeNode = otherDescIter.next(); String changed = eventNode.toString(); for (TreeNode node : myMoleculeNodes) { if (node.toString().equals(otherMoleculeNode.toString())) { if (this.isChecked(node) != checked) { this.setChecked(node, checked); } } } } } } } // ========================== ClusterGraph =================================== /** * A class which displays the visualization of Sample values in a JFreeChart. */ private class ClusterGraph extends JPanel implements TreeSelectionListener, TreeCheckingListener { private JFreeChart chart; private SampleSelectorTreePanel sampleTree; private ClusterSelectorTreePanel clusterTree; private SampleGroup samples; /** * Creates a new ClusterGraph. * * @param sampleTree The SampleSelectorTreePanel used to manipulate * this graph. */ public ClusterGraph(SampleSelectorTreePanel sampleTree, ClusterSelectorTreePanel clusterTree, SampleGroup samples) { super(); this.sampleTree = sampleTree; this.clusterTree = clusterTree; this.samples = samples; this.sampleTree.addTreeCheckingListener(this); this.clusterTree.addTreeCheckingListener(this); this.clusterTree.addTreeSelectionListener(this); // add a context menu for saving the graph to an image new ContextMenu(this).add(new SaveImageAction(this)); } /** * Displays a graph which contains the mean Sample value for each set of * Molecules in a cluseter. * * @param node The TreeNode to display information about. Should only be * called on the root node, or possibly an individual cluster. * @return true if there is data to be displayed. */ public boolean setMeanGraph(DefaultMutableTreeNode node) { Language language = Settings.getLanguage(); Object userObject = node.getUserObject(); SampleGroup samples = new SampleGroup(this.samples); samples.retainAll(this.sampleTree.getSamples()); if (samples.size() < 1) { this.chart = null; return false; } // Collections.sort( samples, new SampleComparator( )); XYSeriesCollection xyDataset = new XYSeriesCollection(); Collection<Dataset> clusters = null; // If the root node is selected (which should be the only time this // method is called) if (node.getLevel() == ROOT) { // make sure the root node has children. if (node.getChildCount() < 1) { this.chart = null; return false; } // iterate through each of the nodes indicating a dataset. for (DefaultMutableTreeNode datasetNode = (DefaultMutableTreeNode) node .getFirstChild(); datasetNode != null; datasetNode = datasetNode.getNextSibling()) { if (clusterTree.isChecked(datasetNode)) { XYSeries data = new XYSeries(datasetNode.toString()); // iterate through each of the nodes indicating a Molecule Collection<Molecule> molecules = new ArrayList<Molecule>(); for (DefaultMutableTreeNode moleculeNode = (DefaultMutableTreeNode) datasetNode .getFirstChild(); moleculeNode != null; moleculeNode = moleculeNode .getNextSibling()) { if (clusterTree.isChecked(moleculeNode)) { molecules.add((Molecule) moleculeNode.getUserObject()); } } int index = 0; for (Sample sample : samples) { data.add(index++, sample.getValues(molecules).getMean()); } xyDataset.addSeries(data); } } } // check to make sure there is actually data in the XYSeriesCollection if (xyDataset.getSeriesCount() == 0) { this.chart = null; return false; } String chartTitle = ((samples == null) ? "" : samples.toString() + " ") + language.get("Sample concentrations"); this.chart = ChartFactory.createXYLineChart(chartTitle, //title language.get("Sample"), // x axis label language.get("Response"), // y axis label xyDataset, // plot data PlotOrientation.VERTICAL, // Plot Orientation true, // show legend false, // use tooltips false // configure chart to generate URLs ); this.chart.getTitle().setFont(new Font("Arial", Font.BOLD, 18)); XYPlot plot = this.chart.getXYPlot(); XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer(); plot.setRenderer(renderer); // find the index of this experiment for appropriate coloring. for (int i = 0; i < xyDataset.getSeriesCount(); i++) { renderer.setSeriesStroke(i, new BasicStroke(2)); renderer.setSeriesShapesVisible(i, true); renderer.setSeriesPaint(i, Color.getHSBColor((float) i / xyDataset.getSeriesCount(), 1.0f, 0.5f)); } plot.getDomainAxis().setRange(-0.5, samples.size() - 0.5); plot.setBackgroundPaint(Color.WHITE); plot.setRangeGridlinePaint(Color.GRAY); plot.setDomainGridlinePaint(Color.GRAY); TickUnits tickUnits = new TickUnits(); double tickIndex = 0.0; List<Sample> sampleList = new ArrayList<Sample>(samples); for (Sample sample : samples) { tickUnits.add(new CustomTickUnit(tickIndex, sampleList)); tickIndex++; } plot.getDomainAxis().setStandardTickUnits(tickUnits); plot.getDomainAxis().setVerticalTickLabels(true); return true; } /** * Sets the graph to display data from each experiment on the passed in * TreeNode. * * @param node The selected node for the graph. * @returns true if creating the graph was successful. */ public boolean setGraph(DefaultMutableTreeNode node) { Object userObject = node.getUserObject(); Language language = Settings.getLanguage(); SampleGroup samples = new SampleGroup(this.samples); samples.retainAll(this.sampleTree.getSamples()); if (samples.size() < 1) { this.chart = null; return false; } // Collections.sort( samples, new SampleComparator( )); XYSeriesCollection xyDataset = new XYSeriesCollection(); XYSeries data; if (node.getLevel() == CLUSTER) { // If a cluster node is selected, follow the tree down to molecule nodes // and add them to the xyDataset if they are checked. if (node.getChildCount() > 0) { for (DefaultMutableTreeNode moleculeNode = (DefaultMutableTreeNode) node .getFirstChild(); moleculeNode != null; moleculeNode = moleculeNode.getNextSibling()) { if (clusterTree.isChecked(moleculeNode)) { Molecule molecule = (Molecule) moleculeNode.getUserObject(); data = new XYSeries(molecule.getId()); int index = 0; for (Sample sample : samples) { data.add(index++, sample.getValue(molecule)); } xyDataset.addSeries(data); } } } } else if (node.getLevel() == MOLECULE) { // If an instance node is selected, add it if it is checked. if (clusterTree.isChecked(node)) { Molecule molecule = (Molecule) userObject; data = new XYSeries(molecule.getId()); int index = 0; for (Sample sample : samples) { data.add(index++, sample.getValue(molecule)); } xyDataset.addSeries(data); } } // check to make sure there is actually data in the XYSeriesCollection if (xyDataset.getSeriesCount() == 0) { this.chart = null; return false; } String chartTitle = ((samples == null) ? "" : samples.toString() + " ") + language.get("Sample concentrations"); this.chart = ChartFactory.createXYLineChart(chartTitle, //title language.get("Sample"), // x axis label language.get("Response"), // y axis label xyDataset, // plot data PlotOrientation.VERTICAL, // Plot Orientation true, // show legend false, // use tooltips false // configure chart to generate URLs (?) ); this.chart.getTitle().setFont(new Font("Arial", Font.BOLD, 18)); XYPlot plot = this.chart.getXYPlot(); XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer(); plot.setRenderer(renderer); // find the index of this experiment for appropriate coloring. for (int i = 0; i < xyDataset.getSeriesCount(); i++) { renderer.setSeriesStroke(i, new BasicStroke(2)); renderer.setSeriesShapesVisible(i, true); renderer.setSeriesPaint(i, Color.getHSBColor((float) i / xyDataset.getSeriesCount(), 1.0f, 0.5f)); } plot.getDomainAxis().setRange(-0.5, samples.size() - 0.5); plot.setBackgroundPaint(Color.WHITE); plot.setRangeGridlinePaint(Color.GRAY); plot.setDomainGridlinePaint(Color.GRAY); TickUnits tickUnits = new TickUnits(); double tickIndex = 0.0; List<Sample> sampleList = new ArrayList<Sample>(samples); for (Sample sample : samples) { tickUnits.add(new CustomTickUnit(tickIndex, sampleList)); tickIndex++; } plot.getDomainAxis().setStandardTickUnits(tickUnits); plot.getDomainAxis().setVerticalTickLabels(true); return true; } /** * The valueChanged method of the TreeSelectionListener interface * * @param e The event which triggered this action. */ public void valueChanged(TreeSelectionEvent e) { TreePath path = e.getPath(); int level = path.getPathCount(); if (level > MOLECULE) { this.setGraph((DefaultMutableTreeNode) path.getPathComponent(MOLECULE)); } else if (level > CLUSTER) { this.setGraph((DefaultMutableTreeNode) path.getPathComponent(CLUSTER)); } else { this.setMeanGraph((DefaultMutableTreeNode) path.getPathComponent(ROOT)); } this.repaint(); } /** * The valueChanged method of the TreeCheckingListner interface. * * @param e The event which triggered this action. */ public void valueChanged(TreeCheckingEvent e) { if (clusterTree.getTree().isSelectionEmpty()) { clusterTree.getTree().setSelectionRow(0); } this.valueChanged(new TreeSelectionEvent(e.getSource(), clusterTree.getTree().getSelectionPath(), false, null, null)); } /** * Draws the graph. * * @param g The Graphics object of this component. */ public void paintComponent(Graphics g) { super.paintComponent(g); if (chart != null) { Dimension size = this.getSize(null); BufferedImage drawing = this.chart.createBufferedImage(size.width, size.height); g.drawImage(drawing, 0, 0, Color.WHITE, this); } } } }