From de17a6ef391910a37c6d207d27fc393f8e684b48 Mon Sep 17 00:00:00 2001 From: Philipp Spohr <spohr.philipp@web.de> Date: Fri, 15 Sep 2017 18:43:43 +0200 Subject: [PATCH] Some work on invalidating a result upon change in the original graph --- .../graphModel/YoshikoCluster.java | 55 ++++--- .../graphModel/YoshikoResult.java | 59 ++++++-- .../graphModel/YoshikoSolution.java | 35 ++--- .../swing/components/ClusterView.java | 40 +++--- .../swing/components/ClusterViewList.java | 28 ++++ .../swing/components/ResultPanel.java | 135 ++++++++++++++---- .../swing/components/SolutionTab.java | 120 ++++++++++------ .../yoshikoWrapper/tasks/AlgorithmTask.java | 17 +-- .../tasks/CreateClusterViews.java | 52 +++++++ .../tasks/CreateMetaGraphTask.java | 10 +- src/main/resources/YoshikoStrings.properties | 5 + 11 files changed, 401 insertions(+), 155 deletions(-) create mode 100644 src/main/java/de/hhu/ba/yoshikoWrapper/tasks/CreateClusterViews.java diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoCluster.java b/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoCluster.java index 0292e61..6495bbe 100644 --- a/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoCluster.java +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoCluster.java @@ -23,6 +23,7 @@ package de.hhu.ba.yoshikoWrapper.graphModel; import java.awt.Image; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import javax.swing.ImageIcon; @@ -57,41 +58,63 @@ public class YoshikoCluster { private final long id; private ArrayList<CyNode> nodes; - private CySubNetwork net; private Image img; //SYMBOLIC LINKS - private final CyNetwork originalGraph; + private final YoshikoSolution solution; private Logger logger = YoshikoLogger.getInstance().getLogger(); + //STATICS - public YoshikoCluster(long id, CyNetwork originalGraph) { + public static Comparator<YoshikoCluster> lessThanComparator = new Comparator<YoshikoCluster>() { + + @Override + public int compare(YoshikoCluster o1, YoshikoCluster o2) { + if (o1.getSize() == o2.getSize()) { + return 0; + } + else if(o1.getSize() < o2.getSize()) { + return 1; + } + return -1; + } + + }; + + + public YoshikoCluster(YoshikoSolution solution, long id) { this.id = id; - this.originalGraph = originalGraph; + this.solution = solution; this.nodes = new ArrayList<CyNode>(); } - public void createSubNetwork() { + /** + * Generates a subgraph for this cluster by choosing the nodes and induced edges from the original graph + * @return + */ + public CySubNetwork getSubNetwork() { - CyRootNetwork originalGraphAsRoot = CyCore.rootNetworkManager.getRootNetwork(originalGraph); + CyRootNetwork originalGraphAsRoot = + CyCore.rootNetworkManager.getRootNetwork(solution.getOriginalGraph()); //Create nested graph and cluster subnet ArrayList<CyEdge> inducedEdges = new ArrayList<CyEdge>(); for (CyNode n: nodes) { //Sadly Cytoscape doesnt provide a comfort function here - List<CyEdge> adjacentEdges = originalGraph.getAdjacentEdgeList(n, CyEdge.Type.ANY); + List<CyEdge> adjacentEdges = solution.getOriginalGraph().getAdjacentEdgeList(n, CyEdge.Type.ANY); for (CyEdge e: adjacentEdges) { if (nodes.contains(e.getSource()) && nodes.contains(e.getTarget())) { inducedEdges.add(e); } } } + CySubNetwork subnet = originalGraphAsRoot.addSubNetwork(nodes ,inducedEdges); subnet.getRow(subnet).set(CyNetwork.NAME, LocalizationManager.get("cluster")+" "+(id+1)); - net = subnet; //Save for further reference + return subnet; } /** @@ -99,13 +122,13 @@ public class YoshikoCluster { */ public void highlightInOriginalGraph() { try { - List<CyRow> allRows = originalGraph.getDefaultNodeTable().getAllRows(); + List<CyRow> allRows = solution.getOriginalGraph().getDefaultNodeTable().getAllRows(); for (CyRow r: allRows) { r.set("selected", false); } //Select nodes corresponding to the cluster for (CyNode n : nodes) { - originalGraph.getRow(n).set("selected", true); + solution.getOriginalGraph().getRow(n).set("selected", true); } } catch (Exception e) { @@ -116,10 +139,6 @@ public class YoshikoCluster { public void applyImage(JLabel label, int width, int height) throws Exception { - if (net == null) { - throw new Exception("Can't have an image without having a network associated"); - } - if (img != null) { label.setIcon(new ImageIcon(img)); } @@ -129,7 +148,7 @@ public class YoshikoCluster { } private void getImage(int width, int height,JLabel label) { - final CyNetworkView view = CyCore.networkViewFactory.createNetworkView(net); + final CyNetworkView view = CyCore.networkViewFactory.createNetworkView(getSubNetwork()); //layout cluster final CyLayoutAlgorithm layout = CyCore.layoutAlgorithmManager.getDefaultLayout(); @@ -188,16 +207,12 @@ public class YoshikoCluster { return id; } - public CySubNetwork getSubNetwork() { - return net; - } - public void addNode(CyNode node) { nodes.add(node); } public String getNodeName(CyNode n) { - return originalGraph.getRow(n).get("name", String.class); + return solution.getOriginalGraph().getRow(n).get("name", String.class); } } diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoResult.java b/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoResult.java index cc8b34e..c3247f1 100644 --- a/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoResult.java +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoResult.java @@ -1,16 +1,16 @@ /******************************************************************************* * Copyright (C) 2017 Philipp Spohr - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -23,22 +23,59 @@ package de.hhu.ba.yoshikoWrapper.graphModel; import java.util.ArrayList; +import org.cytoscape.model.CyNetwork; + import de.hhu.ba.yoshikoWrapper.swig.SolutionFlags; /**Basic data class that represents a CES instance internally. By using this class the C++ resources can be freed upon retrieval. - * * */ public class YoshikoResult { - - public ArrayList<YoshikoSolution> solutions; - - public SolutionFlags flags; - - public YoshikoResult() { + private CyNetwork originalGraph; + + private ArrayList<YoshikoSolution> solutions; + + private SolutionFlags flags; + + + public YoshikoResult(CyNetwork net, SolutionFlags flags) { solutions = new ArrayList<YoshikoSolution>(); - } + + this.originalGraph = net; + this.flags = flags; + } + + public void dispose() { + flags.delete(); + originalGraph.dispose(); + } + + + //___________SETTER GETTER_____________// + + /** + * @return the flags asspciated with this result + */ + public SolutionFlags getFlags() { + return flags; + } + + + public void addSolution(YoshikoSolution solution) { + solutions.add(solution); + } + + + public ArrayList<YoshikoSolution> getSolutions() { + return solutions; + } + + + public CyNetwork getOriginalGraph() { + return originalGraph; + } + } diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoSolution.java b/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoSolution.java index f351f01..bf4a8a1 100644 --- a/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoSolution.java +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/graphModel/YoshikoSolution.java @@ -22,52 +22,29 @@ package de.hhu.ba.yoshikoWrapper.graphModel; import java.util.ArrayList; -import java.util.HashMap; -import org.cytoscape.model.CyEdge; import org.cytoscape.model.CyNetwork; -import org.cytoscape.model.CyNode; -import org.cytoscape.model.CyEdge.Type; - -import org.cytoscape.model.subnetwork.CySubNetwork; -import org.cytoscape.view.layout.CyLayoutAlgorithm; -import org.cytoscape.view.model.CyNetworkView; -import org.cytoscape.work.Task; -import org.cytoscape.work.TaskIterator; -import org.cytoscape.work.TaskMonitor; - -import de.hhu.ba.yoshikoWrapper.core.CyCore; -import de.hhu.ba.yoshikoWrapper.core.LocalizationManager; -import de.hhu.ba.yoshikoWrapper.cytoUtil.StyleManager; public class YoshikoSolution { - /** - * The original Graph from which the solution was generated. - * NOTE: This can be destroyed or modified during runtime while the solution still exists and may become invalid - */ - private final CyNetwork originalGraph; + private final YoshikoResult result; public ArrayList<YoshikoCluster> cluster; private final long id; - public YoshikoSolution(CyNetwork originalGraph, long id) { + public YoshikoSolution(YoshikoResult yoshikoResult, long id) { cluster = new ArrayList<YoshikoCluster>(); - this.originalGraph = originalGraph; + this.result = yoshikoResult; this.id = id; } //_____________GETTER / SETTER ________________// - public ArrayList<YoshikoCluster> getCluster() { + public ArrayList<YoshikoCluster> getClusters() { return cluster; } - public CyNetwork getOriginalGraph() { - return originalGraph; - } - /** * @return the id */ @@ -75,4 +52,8 @@ public class YoshikoSolution { return id; } + public CyNetwork getOriginalGraph() { + return result.getOriginalGraph(); + } + } diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/swing/components/ClusterView.java b/src/main/java/de/hhu/ba/yoshikoWrapper/swing/components/ClusterView.java index 3259160..ea067d9 100644 --- a/src/main/java/de/hhu/ba/yoshikoWrapper/swing/components/ClusterView.java +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/swing/components/ClusterView.java @@ -25,13 +25,10 @@ import static javax.swing.GroupLayout.DEFAULT_SIZE; import static javax.swing.GroupLayout.PREFERRED_SIZE; import java.awt.Color; -import java.awt.Dimension; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.BorderFactory; -import javax.swing.Box; -import javax.swing.BoxLayout; import javax.swing.GroupLayout; import javax.swing.JLabel; import javax.swing.JPanel; @@ -49,28 +46,34 @@ import de.hhu.ba.yoshikoWrapper.swing.SwingUtil; @SuppressWarnings("serial") public class ClusterView extends JPanel { + //MACRO / STATIC private final static int CLUSTER_ICON_SIZE = 128; + private static final Border regularBorder = BorderFactory.createLineBorder(Color.GRAY,3); + private static final Border highlightBorder = BorderFactory.createLineBorder(GraphicsLoader.yoshikoGreen,3); + private static final Border selectedBorder = BorderFactory.createLineBorder(Color.BLUE,3); + + //SWING COMPONENTS + private final JLabel title; private final JLabel clusterSize; private final JLabel icon; private final BasicCollapsiblePanel nodeList; - private final Border regularBorder = BorderFactory.createLineBorder(Color.GRAY,3); - private final Border highlightBorder = BorderFactory.createLineBorder(GraphicsLoader.yoshikoGreen,3); - private final Border selectedBorder = BorderFactory.createLineBorder(Color.BLUE,3); + //INTERNAL + + private boolean isSelected; //SYMBOLIC LINKS - /**Simple array list containing references to the nodes that are represented by this cluster view - * - */ private YoshikoCluster cluster; public ClusterView(YoshikoCluster c) throws Exception { this.cluster = c; + this.isSelected = false; + //Swing init title = new JLabel(LocalizationManager.get("cluster")+" "+(c.getID()+1)); @@ -108,7 +111,7 @@ public class ClusterView extends JPanel { .addComponent(title,DEFAULT_SIZE,DEFAULT_SIZE,PREFERRED_SIZE) .addComponent(clusterSize,DEFAULT_SIZE,DEFAULT_SIZE,PREFERRED_SIZE) ) - .addGap(6) + .addGap(12) .addComponent(icon) ) .addComponent(nodeList) @@ -119,7 +122,7 @@ public class ClusterView extends JPanel { .addComponent(title) .addComponent(clusterSize) ) - .addGap(6) + .addGap(12) .addComponent(icon) ) .addComponent(nodeList) @@ -132,24 +135,25 @@ public class ClusterView extends JPanel { private MouseListener mouseListener = new MouseListener() { @Override - public void mouseClicked(MouseEvent e) {} - - @Override - public void mousePressed(MouseEvent e) { - cluster.highlightInOriginalGraph(); + public void mouseClicked(MouseEvent e) { + isSelected = !isSelected; + setBorder(isSelected ? selectedBorder : regularBorder); } + @Override + public void mousePressed(MouseEvent e) {} @Override public void mouseReleased(MouseEvent e) {} @Override public void mouseEntered(MouseEvent e) { - setBorder(highlightBorder); + cluster.highlightInOriginalGraph(); + setBorder(isSelected ? selectedBorder : highlightBorder); } @Override public void mouseExited(MouseEvent e) { - setBorder(regularBorder); + setBorder(isSelected ? selectedBorder : regularBorder); } diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/swing/components/ClusterViewList.java b/src/main/java/de/hhu/ba/yoshikoWrapper/swing/components/ClusterViewList.java index 396439f..ad45bb5 100644 --- a/src/main/java/de/hhu/ba/yoshikoWrapper/swing/components/ClusterViewList.java +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/swing/components/ClusterViewList.java @@ -1,14 +1,22 @@ package de.hhu.ba.yoshikoWrapper.swing.components; +import java.awt.Component; import java.awt.Dimension; import java.awt.Rectangle; +import java.util.ArrayList; import javax.swing.JPanel; import javax.swing.Scrollable; +import org.slf4j.Logger; + +import de.hhu.ba.yoshikoWrapper.logging.YoshikoLogger; + @SuppressWarnings("serial") public class ClusterViewList extends JPanel implements Scrollable { + private static final Logger logger = YoshikoLogger.getInstance().getLogger(); + @Override public Dimension getPreferredScrollableViewportSize() { return this.getPreferredSize(); @@ -34,4 +42,24 @@ public class ClusterViewList extends JPanel implements Scrollable { return false; } + @Override + public ClusterView add(Component c) throws NullPointerException{ + //Simple type check + if (c instanceof ClusterView) { + return (ClusterView) super.add(c); + } + logger.error("Attempted to add a component to ClusterViewList that is not a ClusterView"); + return null; + } + + public ArrayList<ClusterView> getClusterViews() { + ArrayList<ClusterView> ret = new ArrayList<ClusterView>(); + for (Component c : this.getComponents()) { + if (c instanceof ClusterView) { + ret.add((ClusterView)c); + } + } + return ret; + } + } diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/swing/components/ResultPanel.java b/src/main/java/de/hhu/ba/yoshikoWrapper/swing/components/ResultPanel.java index 928ffd5..1395910 100644 --- a/src/main/java/de/hhu/ba/yoshikoWrapper/swing/components/ResultPanel.java +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/swing/components/ResultPanel.java @@ -25,9 +25,10 @@ import static javax.swing.GroupLayout.*; import java.awt.Color; import java.awt.Component; -import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Properties; import javax.swing.BoxLayout; import javax.swing.GroupLayout; @@ -41,6 +42,18 @@ import javax.swing.JTabbedPane; import org.cytoscape.application.swing.CytoPanelComponent; import org.cytoscape.application.swing.CytoPanelName; +import org.cytoscape.event.AbstractCyEvent; +import org.cytoscape.model.CyNetwork; +import org.cytoscape.model.events.AboutToRemoveEdgesEvent; +import org.cytoscape.model.events.AboutToRemoveEdgesListener; +import org.cytoscape.model.events.AboutToRemoveNodesEvent; +import org.cytoscape.model.events.AboutToRemoveNodesListener; +import org.cytoscape.model.events.AddedEdgesEvent; +import org.cytoscape.model.events.AddedEdgesListener; +import org.cytoscape.model.events.AddedNodesEvent; +import org.cytoscape.model.events.AddedNodesListener; +import org.cytoscape.model.events.NetworkAboutToBeDestroyedEvent; +import org.cytoscape.model.events.NetworkAboutToBeDestroyedListener; import org.cytoscape.util.swing.BasicCollapsiblePanel; import de.hhu.ba.yoshikoWrapper.core.CyCore; @@ -54,13 +67,27 @@ import de.hhu.ba.yoshikoWrapper.swing.SwingUtil; * Conforms to CY 3.5 by being a CytoPanelComponent which is the norm for "result" panels */ @SuppressWarnings("serial")//Will never be serialized -public class ResultPanel extends JPanel implements CytoPanelComponent{ +public class ResultPanel extends JPanel implements CytoPanelComponent, +//Implements listeners to invalidate results when the graph the result was generated for changes +AboutToRemoveEdgesListener, +AboutToRemoveNodesListener, +AddedEdgesListener, +AddedNodesListener, +NetworkAboutToBeDestroyedListener +{ + + //MACRO + + private static final int HACKFIX_FIXED_WIDTH = 128+256; + + private final JTabbedPane tabbedPane; - private final JTabbedPane solutionTabs; private final JButton destroyButton; private BasicCollapsiblePanel marker; + private final JLabel invalidLabel; + private ArrayList<SolutionTab> solutionTabs; private final YoshikoResult result; public ResultPanel(YoshikoResult result) throws Exception { @@ -68,17 +95,21 @@ public class ResultPanel extends JPanel implements CytoPanelComponent{ this.result = result; //Init subcomponents - solutionTabs = new JTabbedPane(); - for (YoshikoSolution s : result.solutions) { - addSolutionTab(s); - } - - if (result.flags.getIlpGenerated()) { - System.out.println("Setting up ILP Info Marker"); + invalidLabel = new JLabel(); + tabbedPane = new JTabbedPane(); + for (YoshikoSolution s : result.getSolutions()) { + SolutionTab tab = new SolutionTab(s); + tabbedPane.add( + LocalizationManager.get("solution")+" "+(s.getId()+1), + tab + ); } + + if (result.getFlags().getIlpGenerated()) { + //TODO: Move to MarkerPanel for better codestyle //Optional Markers marker = new BasicCollapsiblePanel(LocalizationManager.get("ilpMarker")); marker.setLayout(new BoxLayout(marker,BoxLayout.Y_AXIS)); - if(result.flags.getOptimal()) { + if(result.getFlags().getOptimal()) { JLabel optimalLabel; optimalLabel = new JLabel(LocalizationManager.get("optimal")); optimalLabel.setForeground(GraphicsLoader.yoshikoGreen); @@ -89,12 +120,12 @@ public class ResultPanel extends JPanel implements CytoPanelComponent{ optimalLabel = new JLabel(LocalizationManager.get("notOptimal")); optimalLabel.setForeground(Color.RED); marker.add(optimalLabel); - marker.add(new JLabel(result.flags.getSolvedInstances()+"/"+result.flags.getReducedInstances()+" "+LocalizationManager.get("redSolved"))); - marker.add(new JLabel(LocalizationManager.get("lastInstanceGap")+" "+(int)(100*result.flags.getLastGap())+"%")); + marker.add(new JLabel(result.getFlags().getSolvedInstances()+"/"+result.getFlags().getReducedInstances()+" "+LocalizationManager.get("redSolved"))); + marker.add(new JLabel(LocalizationManager.get("lastInstanceGap")+" "+(int)(100*result.getFlags().getLastGap())+"%")); } - JLabel costLabel = new JLabel(LocalizationManager.get("cost")+" "+result.flags.getTotalCost()); + JLabel costLabel = new JLabel(LocalizationManager.get("cost")+" "+result.getFlags().getTotalCost()); marker.add(costLabel); - if (result.flags.getTimedOut()) { + if (result.getFlags().getTimedOut()) { marker.add(new JLabel(LocalizationManager.get("timeoutMarker"))); } marker.setCollapsed(false); @@ -111,7 +142,7 @@ public class ResultPanel extends JPanel implements CytoPanelComponent{ }); - SwingUtil.addAll(this,solutionTabs,destroyButton); + SwingUtil.addAll(this,invalidLabel,tabbedPane,destroyButton); //Layout GroupLayout layout = new GroupLayout(this); @@ -122,16 +153,18 @@ public class ResultPanel extends JPanel implements CytoPanelComponent{ layout.setHorizontalGroup(horizontalGroup); layout.setVerticalGroup(verticalGroup); - if (result.flags.getIlpGenerated()) { + if (result.getFlags().getIlpGenerated()) { horizontalGroup.addComponent(marker,DEFAULT_SIZE,DEFAULT_SIZE,DEFAULT_SIZE); verticalGroup.addComponent(marker,DEFAULT_SIZE,DEFAULT_SIZE,PREFERRED_SIZE); } horizontalGroup - .addComponent(solutionTabs,DEFAULT_SIZE, DEFAULT_SIZE,Short.MAX_VALUE) + .addComponent(invalidLabel,HACKFIX_FIXED_WIDTH, PREFERRED_SIZE,Short.MAX_VALUE) + .addComponent(tabbedPane,HACKFIX_FIXED_WIDTH, PREFERRED_SIZE,Short.MAX_VALUE) .addComponent(destroyButton,DEFAULT_SIZE, DEFAULT_SIZE,DEFAULT_SIZE); verticalGroup - .addComponent(solutionTabs,DEFAULT_SIZE, DEFAULT_SIZE,PREFERRED_SIZE) + .addComponent(invalidLabel,DEFAULT_SIZE, DEFAULT_SIZE,PREFERRED_SIZE) + .addComponent(tabbedPane,DEFAULT_SIZE, DEFAULT_SIZE,PREFERRED_SIZE) .addComponent(destroyButton,DEFAULT_SIZE, DEFAULT_SIZE,PREFERRED_SIZE); layout.setAutoCreateGaps(true); @@ -139,7 +172,17 @@ public class ResultPanel extends JPanel implements CytoPanelComponent{ this.setLayout(layout); - this.setPreferredSize(new Dimension(this.getPreferredSize().width*2,this.getPreferredSize().height)); + + registerAllListeners(); + + } + + private void registerAllListeners() { + CyCore.registrar.registerService(this, AboutToRemoveEdgesListener.class, new Properties()); + CyCore.registrar.registerService(this, AboutToRemoveNodesListener.class, new Properties()); + CyCore.registrar.registerService(this, AddedEdgesListener.class, new Properties()); + CyCore.registrar.registerService(this, AddedNodesListener.class, new Properties()); + CyCore.registrar.registerService(this, NetworkAboutToBeDestroyedListener.class, new Properties()); } public void deleteSolution() { @@ -152,20 +195,29 @@ public class ResultPanel extends JPanel implements CytoPanelComponent{ if (dialogResult != JOptionPane.YES_OPTION) { return; } - + result.dispose(); CyCore.registrar.unregisterService(this,CytoPanelComponent.class); removeAll(); super.setVisible(false); } - private void addSolutionTab(YoshikoSolution s) throws Exception { - SolutionTab tab = new SolutionTab(s); - solutionTabs.add( - LocalizationManager.get("solution")+" "+(s.getId()+1), - tab - ); + private void invalidateResult() { + invalidLabel.setText(LocalizationManager.get("invalidated")); + invalidLabel.setForeground(Color.RED); + for (SolutionTab t: solutionTabs) { + t.invalidateResult(); + } + } + + + private void invalidateIfRelevant(AbstractCyEvent<CyNetwork> e) { + if (e.getSource() == result.getOriginalGraph()) { + invalidateResult(); + } } + //______________SETTER/GETTER_______________// + @Override public Component getComponent() { return this; @@ -190,4 +242,33 @@ public class ResultPanel extends JPanel implements CytoPanelComponent{ return result; } + + //LISTENER METHODS + + @Override + public void handleEvent(NetworkAboutToBeDestroyedEvent e) { + if (e.getNetwork() == result.getOriginalGraph()) { + invalidate(); + } + } + @Override + public void handleEvent(AddedNodesEvent e) { + invalidateIfRelevant(e); + } + + @Override + public void handleEvent(AddedEdgesEvent e) { + invalidateIfRelevant(e); + } + + @Override + public void handleEvent(AboutToRemoveNodesEvent e) { + invalidateIfRelevant(e); + } + + @Override + public void handleEvent(AboutToRemoveEdgesEvent e) { + invalidateIfRelevant(e); + } + } diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/swing/components/SolutionTab.java b/src/main/java/de/hhu/ba/yoshikoWrapper/swing/components/SolutionTab.java index 6506fe0..6d22d6c 100644 --- a/src/main/java/de/hhu/ba/yoshikoWrapper/swing/components/SolutionTab.java +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/swing/components/SolutionTab.java @@ -22,15 +22,12 @@ package de.hhu.ba.yoshikoWrapper.swing.components; import static javax.swing.GroupLayout.DEFAULT_SIZE; + import static javax.swing.GroupLayout.PREFERRED_SIZE; -import java.awt.Dimension; -import java.awt.ScrollPane; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.Comparator; -import javax.swing.BoxLayout; import javax.swing.GroupLayout; import javax.swing.GroupLayout.Alignment; import javax.swing.GroupLayout.Group; @@ -47,68 +44,76 @@ import de.hhu.ba.yoshikoWrapper.core.LocalizationManager; import de.hhu.ba.yoshikoWrapper.graphModel.YoshikoCluster; import de.hhu.ba.yoshikoWrapper.graphModel.YoshikoSolution; import de.hhu.ba.yoshikoWrapper.swing.SwingUtil; +import de.hhu.ba.yoshikoWrapper.tasks.CreateClusterViews; import de.hhu.ba.yoshikoWrapper.tasks.CreateMetaGraphTask; - +/** + * Swing Component, that represents one solution of a Yoshiko result and displays it + * + */ @SuppressWarnings("serial") public class SolutionTab extends JPanel { + //SWING COMPONENTS + private final JScrollPane scrollPane; private final ClusterViewList scrollPaneContent; private final JLabel clusterCount; + private final JButton createClusterView; private final JButton createMetaGraph; + //SWING LAYOUT OBJECTS + private final GroupLayout layout; + private final GroupLayout scrollLayout; + private final Group horizontalGroup_scrollLayout; + private final Group verticalGroup_scrollLayout; + + //SYMBOLIC LINKS + private final YoshikoSolution solution; + /** + * Basic constructor, creates a new SolutionTab representing a given YoshikoSolution. + * @param s The solution that is to be displayed by this tab + * @throws Exception + */ public SolutionTab(YoshikoSolution s) throws Exception { + //Save abstract solution object for reference this.solution = s; - //init swing components + //Declaration of Swing Components scrollPaneContent = new ClusterViewList(); - //Sort cluster by size, descending - solution.cluster.sort(new Comparator<YoshikoCluster>() { - - @Override - public int compare(YoshikoCluster o1, YoshikoCluster o2) { - if (o1.getSize() == o2.getSize()) { - return 0; - } - else if(o1.getSize() < o2.getSize()) { - return 1; - } - return -1; - } - - }); - - GroupLayout scrollLayout = new GroupLayout(scrollPaneContent); - scrollLayout.setAutoCreateContainerGaps(true); - scrollLayout.setAutoCreateGaps(true); - - Group horizontalGroup = scrollLayout.createParallelGroup(); - Group verticalGroup = scrollLayout.createSequentialGroup(); - for (YoshikoCluster c: solution.cluster) { ClusterView clusterView = new ClusterView(c); scrollPaneContent.add(clusterView); - horizontalGroup.addComponent(clusterView); - verticalGroup.addComponent(clusterView); } - scrollLayout.setHorizontalGroup(horizontalGroup); - scrollLayout.setVerticalGroup(verticalGroup); + scrollPane = new JScrollPane(scrollPaneContent); - scrollPaneContent.setLayout(scrollLayout); + clusterCount = new JLabel(LocalizationManager.get("clusterFound")+" "+s.cluster.size()); - scrollPane = new JScrollPane(scrollPaneContent); + createClusterView = new JButton(LocalizationManager.get("createClusterView")); + createMetaGraph = new JButton(LocalizationManager.get("createMetaGraph")); + + //Configuration of Swing Components scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); - clusterCount = new JLabel(LocalizationManager.get("clusterFound")+" "+s.cluster.size()); + createClusterView.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + CyCore.dialogTaskManager.execute( + new TaskIterator(1, + new CreateClusterViews(solution.getClusters()) + ) + ); + } + + }); - createMetaGraph = new JButton(LocalizationManager.get("createMetaGraph")); createMetaGraph.addActionListener(new ActionListener() { @Override @@ -121,22 +126,57 @@ public class SolutionTab extends JPanel { } }); - SwingUtil.addAll(this,clusterCount,scrollPane,createMetaGraph); + + //Add configured Swing Components to self + + SwingUtil.addAll(this,clusterCount,scrollPane,createClusterView,createMetaGraph); //Layout - GroupLayout layout = new GroupLayout(this); - layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING,true) + + scrollLayout = new GroupLayout(scrollPaneContent); + + horizontalGroup_scrollLayout = scrollLayout.createParallelGroup(); + verticalGroup_scrollLayout = scrollLayout.createSequentialGroup(); + + scrollLayout.setHorizontalGroup(horizontalGroup_scrollLayout); + scrollLayout.setVerticalGroup(verticalGroup_scrollLayout); + + scrollLayout.setAutoCreateContainerGaps(true); + scrollLayout.setAutoCreateGaps(true); + + for (ClusterView v: scrollPaneContent.getClusterViews()) { + horizontalGroup_scrollLayout.addComponent(v); + verticalGroup_scrollLayout.addComponent(v); + } + + scrollPaneContent.setLayout(scrollLayout); + + layout = new GroupLayout(this); + + layout.setHorizontalGroup(layout.createParallelGroup(Alignment.CENTER,true) + .addGap(8) .addComponent(clusterCount,DEFAULT_SIZE,DEFAULT_SIZE,DEFAULT_SIZE) + .addGap(8) .addComponent(scrollPane,PREFERRED_SIZE,DEFAULT_SIZE,Short.MAX_VALUE) + .addComponent(createClusterView,DEFAULT_SIZE,DEFAULT_SIZE,DEFAULT_SIZE) .addComponent(createMetaGraph,DEFAULT_SIZE,DEFAULT_SIZE,DEFAULT_SIZE) ); layout.setVerticalGroup(layout.createSequentialGroup() + .addGap(8) .addComponent(clusterCount,DEFAULT_SIZE,DEFAULT_SIZE,DEFAULT_SIZE) + .addGap(8) .addComponent(scrollPane,DEFAULT_SIZE,DEFAULT_SIZE,DEFAULT_SIZE) + .addComponent(createClusterView,DEFAULT_SIZE,DEFAULT_SIZE,DEFAULT_SIZE) .addComponent(createMetaGraph,DEFAULT_SIZE,DEFAULT_SIZE,DEFAULT_SIZE) ); this.setLayout(layout); } + public void invalidateResult() { + createClusterView.setEnabled(false); + createMetaGraph.setEnabled(false); + } + + } diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/tasks/AlgorithmTask.java b/src/main/java/de/hhu/ba/yoshikoWrapper/tasks/AlgorithmTask.java index 787ee1c..fbe450f 100644 --- a/src/main/java/de/hhu/ba/yoshikoWrapper/tasks/AlgorithmTask.java +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/tasks/AlgorithmTask.java @@ -143,17 +143,17 @@ public class AlgorithmTask extends AbstractTask { long numberOfSolutions = result.getNumberOfSolutions(); - taskMonitor.setStatusMessage("Found: "+numberOfSolutions+" solutions!"); //TODO localize - - YoshikoResult yoshikoResult = new YoshikoResult(); - yoshikoResult.flags = result.getFlags(); + taskMonitor.setStatusMessage("Found: "+numberOfSolutions+" solutions!"); //TODO: Localize + YoshikoResult yoshikoResult = new YoshikoResult(net,result.getFlags()); //Loop over (multiple) solutions for (long i=0;i<numberOfSolutions;i++) { taskMonitor.setStatusMessage("Processing solution "+(i+1)+" of "+numberOfSolutions); - YoshikoSolution solution = new YoshikoSolution(net,i); + + YoshikoSolution solution = new YoshikoSolution(yoshikoResult,i); + String columnName = SOLUTION_COLUMN_PREFIX+(i+1); net.getDefaultNodeTable().deleteColumn(columnName); @@ -165,7 +165,7 @@ public class AlgorithmTask extends AbstractTask { //Loop over cluster for (long k=0;k<numberOfClusters;k++) { //Create java instance - YoshikoCluster cluster = new YoshikoCluster(k,net); + YoshikoCluster cluster = new YoshikoCluster(solution,k); taskMonitor.setStatusMessage("Processing cluster "+(k+1)+" of "+numberOfClusters); @@ -181,12 +181,13 @@ public class AlgorithmTask extends AbstractTask { net.getRow(node).set(columnName, ""+(k+1)); //Add Cluster ID in table (Remove in final version?) } - cluster.createSubNetwork(); //Register cluster with solution for further reference solution.cluster.add(cluster); } + //Sort clusters by size, descending as the biggest clusters are usually the most relevant + solution.cluster.sort(YoshikoCluster.lessThanComparator); //Register solution with result for further reference - yoshikoResult.solutions.add(solution); + yoshikoResult.addSolution(solution); } taskMonitor.setProgress(0.99); diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/tasks/CreateClusterViews.java b/src/main/java/de/hhu/ba/yoshikoWrapper/tasks/CreateClusterViews.java new file mode 100644 index 0000000..a681836 --- /dev/null +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/tasks/CreateClusterViews.java @@ -0,0 +1,52 @@ +package de.hhu.ba.yoshikoWrapper.tasks; + +import java.util.ArrayList; + +import org.cytoscape.model.subnetwork.CySubNetwork; +import org.cytoscape.view.layout.CyLayoutAlgorithm; +import org.cytoscape.view.model.CyNetworkView; +import org.cytoscape.work.Task; +import org.cytoscape.work.TaskMonitor; + +import de.hhu.ba.yoshikoWrapper.core.CyCore; +import de.hhu.ba.yoshikoWrapper.core.LocalizationManager; +import de.hhu.ba.yoshikoWrapper.cytoUtil.StyleManager; +import de.hhu.ba.yoshikoWrapper.graphModel.YoshikoCluster; + +public class CreateClusterViews implements Task { + + private ArrayList<YoshikoCluster> clusters; + + public CreateClusterViews(ArrayList<YoshikoCluster> clusters) { + this.clusters = clusters; + } + + @Override + public void run(TaskMonitor taskMonitor) throws Exception { + CyLayoutAlgorithm layout = CyCore.layoutAlgorithmManager.getDefaultLayout(); + for (YoshikoCluster c : clusters) { + CySubNetwork subnet = c.getSubNetwork(); + taskMonitor.setStatusMessage(LocalizationManager.get("status_createCV")+" "+(c.getID()+1)); + CyCore.networkManager.addNetwork(subnet); + //Create network view and register it + CyNetworkView subnetView = CyCore.networkViewFactory.createNetworkView(subnet); + //layout cluster + CyCore.dialogTaskManager.execute( + layout.createTaskIterator( + subnetView, + layout.getDefaultLayoutContext(), + CyLayoutAlgorithm.ALL_NODE_VIEWS, + null + ) + ); + + StyleManager.style(subnetView,CyCore.visualMappingManager.getCurrentVisualStyle()); + + CyCore.networkViewManager.addNetworkView(subnetView); + } + } + + @Override + public void cancel() {} + +} diff --git a/src/main/java/de/hhu/ba/yoshikoWrapper/tasks/CreateMetaGraphTask.java b/src/main/java/de/hhu/ba/yoshikoWrapper/tasks/CreateMetaGraphTask.java index 0eb0e0e..b5fd244 100644 --- a/src/main/java/de/hhu/ba/yoshikoWrapper/tasks/CreateMetaGraphTask.java +++ b/src/main/java/de/hhu/ba/yoshikoWrapper/tasks/CreateMetaGraphTask.java @@ -45,10 +45,12 @@ public class CreateMetaGraphTask extends AbstractTask { //Add nodes - for (YoshikoCluster c: solution.getCluster()) { + for (YoshikoCluster c: solution.getClusters()) { CyNode clusterNode = metaGraph.addNode(); CySubNetwork subnet = c.getSubNetwork(); + + taskMonitor.setStatusMessage(LocalizationManager.get("status_createCV")+" "+(c.getID()+1)); CyCore.networkManager.addNetwork(subnet); //Create network view and register it CyNetworkView subnetView = CyCore.networkViewFactory.createNetworkView(subnet); @@ -72,10 +74,10 @@ public class CreateMetaGraphTask extends AbstractTask { metaGraph.getRow(clusterNode).set(StyleManager.CLUSTERSIZE_COLUMN_NAME,c.getSize()); map.put(c, clusterNode); } - + taskMonitor.setStatusMessage(LocalizationManager.get("metaGraph_edges")); //Add edges (O(|C|^2*|V|^2) can maybe be improved? //TODO - for (YoshikoCluster c1:solution.getCluster()) { - for (YoshikoCluster c2:solution.getCluster()) { + for (YoshikoCluster c1:solution.getClusters()) { + for (YoshikoCluster c2:solution.getClusters()) { if ( metaGraph.containsEdge(map.get(c1),map.get(c2)) || c1 == c2 diff --git a/src/main/resources/YoshikoStrings.properties b/src/main/resources/YoshikoStrings.properties index df0759a..e21c072 100644 --- a/src/main/resources/YoshikoStrings.properties +++ b/src/main/resources/YoshikoStrings.properties @@ -28,6 +28,7 @@ clusterSize = Cluster Size: continueTimeout = The ILP has exceeded the given time limit. Do you want to continue? (This may take a long time) cost = Editing Cost: createMetaGraph = Create Meta-Graph +createClusterView = Create Cluster Views currentGap = [ILP] Current Instance Gap defaultDeletion = Default deletion cost: defaultInsertion = Default insertion cost: @@ -35,6 +36,7 @@ deleteSolution = Do you really want to delete this solution? disableMultiThreading = Disable Multithreading discardSolution = Discard editingCostPanel = Editing Costs +exportSelectedClusters = Export selected clusters firstStart = Thanks for using the Yoshiko Clustering Tool!\nIt appears, that this is your first time using this tool.\nIf you want an in-depth explanation of the algorithm please refer to: [INSERT COOL LINK HERE] gap = Gap getYoshiko = Get Yoshiko Library @@ -46,6 +48,7 @@ lastInstanceGap = Gap (Current Instance): libFail = Failed to load library! Make sure you got the right file. The required version for this version of the wrapper is: libraryPanel = Library metaGraph = Meta Graph +metaGraph_edges = Fetching edges and calculating edge strengths multFactor = Multiplicative Factor for SNR: noFeasible = No feasible solution found! noLibMessage = There is no Yoshiko Library currently loaded! You might have to specify its location. @@ -66,6 +69,8 @@ run = Perform Algorithm showAdvanced = Show advanced options solution = Solution +status_createCV = Creating cluster view for cluster: + status_reductionAC = Applying "Almost-Clique" reduction rule status_reductionCR = Applying "Clique" reduction rule status_reductionCC = Applying "Critical-Clique" reduction rule -- GitLab