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