Commit 040c4379 authored by Philipp Spohr's avatar Philipp Spohr
Browse files

Merge remote-tracking branch 'origin/master' into dev_general

parents a5d44cfc 92bed06b
......@@ -33,7 +33,7 @@
<groupId>de.hhu.ba</groupId>
<artifactId>yoshikoWrapper</artifactId>
<version>0.1.1</version>
<version>0.1.3</version>
<name>YoshikoWrapper</name>
......
......@@ -21,6 +21,8 @@
******************************************************************************/
package de.hhu.ba.yoshikoWrapper.core;
import java.util.concurrent.CountDownLatch;
import org.cytoscape.application.CyApplicationManager;
import org.cytoscape.application.swing.CySwingApplication;
import org.cytoscape.model.CyNetwork;
......@@ -36,6 +38,10 @@ import org.cytoscape.view.presentation.RenderingEngineFactory;
import org.cytoscape.view.vizmap.VisualMappingFunctionFactory;
import org.cytoscape.view.vizmap.VisualMappingManager;
import org.cytoscape.view.vizmap.VisualStyleFactory;
import org.cytoscape.work.FinishStatus;
import org.cytoscape.work.ObservableTask;
import org.cytoscape.work.TaskIterator;
import org.cytoscape.work.TaskObserver;
import org.cytoscape.work.swing.DialogTaskManager;
import org.osgi.framework.BundleContext;
......@@ -68,4 +74,25 @@ public class CyCore {
public static String getConfig(String key) {
return cm.getProperties().getProperty(key);
}
/**
* Convenience function that runs all tasks in a task iterator but blocks until all tasks are finished
* @param taskIterator
* @throws InterruptedException
*/
public static synchronized void runAndWait(TaskIterator taskIterator) throws InterruptedException {
CountDownLatch blockLatch = new CountDownLatch(1);
dialogTaskManager.execute(taskIterator,new TaskObserver() {
@Override
public void taskFinished(ObservableTask task) {}
@Override
public void allFinished(FinishStatus finishStatus) {
blockLatch.countDown();
}
});
blockLatch.await();
}
}
......@@ -44,7 +44,9 @@ public class StatusInformer extends CplexInformer {
@Override
public void updateStatus(YoshikoState state, double value) {
if (state == YoshikoState.SOLVING_ILP){
taskMonitor.setStatusMessage(LocalizationManager.get("currentGap")+": "+SwingUtil.twoDecimals.format(value)+"%");
if (value <= 1.0) {
taskMonitor.setStatusMessage(LocalizationManager.get("currentGap")+": "+SwingUtil.twoDecimals.format(value)+"%");
}
}
}
......
package de.hhu.ba.yoshikoWrapper.core;
import org.osgi.framework.Version;
public class YoshUtil {
......
package de.hhu.ba.yoshikoWrapper.cytoUtil;
import java.util.List;
import org.cytoscape.model.CyEdge;
import org.cytoscape.model.CyNetwork;
public class GraphAnalyzer {
public static boolean isMultiGraph(CyNetwork net) {
//TODO: Better algorithm?
int n = net.getNodeCount();
List<CyEdge> edges = net.getEdgeList();
//Easiest check: Check if the graph contains more edges than a complete graph
if (edges.size() > (n*(n-1))/2) {
return true;
}
for (int i = 0; i < edges.size()-1; i++) {
for (int j=i+1; j<edges.size();j++) {
if (connectSameNodes(edges.get(i),edges.get(j))){
return true;
}
}
}
return false;
}
private static boolean connectSameNodes(CyEdge e1, CyEdge e2) {
if (//Treating all edges as undirected here
(e1.getSource() == e2.getTarget() && e1.getTarget() == e2.getSource()) ||
(e1.getSource() == e2.getSource() && e1.getTarget() == e2.getTarget())
) {
return true;
}
return false;
}
public static boolean isDirected(CyNetwork net) {
for (CyEdge e: net.getEdgeList()) {
if (e.isDirected()) {
return true;
}
}
return false;
}
}
......@@ -28,7 +28,6 @@ import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import org.cytoscape.model.CyEdge;
import org.cytoscape.model.CyNetwork;
......@@ -39,9 +38,10 @@ import org.cytoscape.model.subnetwork.CySubNetwork;
import org.cytoscape.view.layout.CyLayoutAlgorithm;
import org.cytoscape.view.model.CyNetworkView;
import org.cytoscape.view.presentation.RenderingEngine;
import org.cytoscape.work.Task;
import org.cytoscape.work.FinishStatus;
import org.cytoscape.work.ObservableTask;
import org.cytoscape.work.TaskIterator;
import org.cytoscape.work.TaskMonitor;
import org.cytoscape.work.TaskObserver;
import org.slf4j.Logger;
import de.hhu.ba.yoshikoWrapper.core.CyCore;
......@@ -61,6 +61,9 @@ public class YoshikoCluster {
private Image img;
private CySubNetwork subnet;
//SYMBOLIC LINKS
private final YoshikoSolution solution;
private Logger logger = YoshikoLogger.getInstance().getLogger();
......@@ -90,43 +93,54 @@ public class YoshikoCluster {
}
/**
* Generates a subgraph for this cluster by choosing the nodes and induced edges from the original graph
* Generates a subgraph for this clusters by choosing the nodes and induced edges from the original graph if such a graph doesn't exist yet.
* @return
*/
public CySubNetwork getSubNetwork() {
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 = solution.getOriginalGraph().getAdjacentEdgeList(n, CyEdge.Type.ANY);
for (CyEdge e: adjacentEdges) {
if (nodes.contains(e.getSource()) && nodes.contains(e.getTarget())) {
inducedEdges.add(e);
if (subnet == null) {
CyRootNetwork originalGraphAsRoot =
CyCore.rootNetworkManager.getRootNetwork(solution.getOriginalGraph());
//Create nested graph and clusters subnet
ArrayList<CyEdge> inducedEdges = new ArrayList<CyEdge>();
for (CyNode n: nodes) {
//Sadly Cytoscape doesnt provide a comfort function here
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));
subnet = originalGraphAsRoot.addSubNetwork(nodes ,inducedEdges);
subnet.getRow(subnet).set(CyNetwork.NAME, LocalizationManager.get("clusters")+" "+(id+1));
}
return subnet;
}
public void highlight() {
if (CyCore.cy.getCurrentNetwork() == solution.getOriginalGraph()) {
highlightInOriginalGraph();
}
else if (CyCore.cy.getCurrentNetwork() == solution.getMetaGraph()) {
solution.highlightInMetaGraph(this);
}
}
/**
* Attempt to select the nodes belonging to this cluster in the original Graph given that they still exist
* Attempt to select the nodes belonging to this clusters in the original Graph given that they still exist
*/
public void highlightInOriginalGraph() {
private void highlightInOriginalGraph() {
try {
List<CyRow> allRows = solution.getOriginalGraph().getDefaultNodeTable().getAllRows();
for (CyRow r: allRows) {
r.set("selected", false);
}
//Select nodes corresponding to the cluster
//Select nodes corresponding to the clusters
for (CyNode n : nodes) {
solution.getOriginalGraph().getRow(n).set("selected", true);
}
......@@ -136,67 +150,38 @@ public class YoshikoCluster {
}
}
public void applyImage(JLabel label, int width, int height) throws Exception {
if (img != null) {
label.setIcon(new ImageIcon(img));
}
else {
getImage(width,height,label);
}
}
private void getImage(int width, int height,JLabel label) {
public void generateClusterIcon(int width, int height,JLabel label) throws InterruptedException {
final CyNetworkView view = CyCore.networkViewFactory.createNetworkView(getSubNetwork());
//layout cluster
//layout clusters
final CyLayoutAlgorithm layout = CyCore.layoutAlgorithmManager.getDefaultLayout();
final TaskIterator createLayout = layout.createTaskIterator(
CyCore.runAndWait(layout.createTaskIterator(
view,
layout.getDefaultLayoutContext(),
CyLayoutAlgorithm.ALL_NODE_VIEWS,
null
)
);
createLayout.append(
new Task() {
@Override
public void run(TaskMonitor taskMonitor) throws Exception {
taskMonitor.setStatusMessage("Generating cluster view for C:"+id);
view.setVisualProperty(NETWORK_WIDTH, new Double(width));
view.setVisualProperty(NETWORK_HEIGHT, new Double(height));
view.fitContent();
view.setVisualProperty(NETWORK_WIDTH, new Double(width));
view.setVisualProperty(NETWORK_HEIGHT, new Double(height));
view.fitContent();
StyleManager.style(view, CyCore.visualMappingManager.getCurrentVisualStyle());
StyleManager.style(view, CyCore.visualMappingManager.getCurrentVisualStyle());
RenderingEngine<CyNetwork> renderingEngine = CyCore.renderingEngineFactory.createRenderingEngine(label, view);
SwingUtilities.invokeLater(new Runnable() {
RenderingEngine<CyNetwork> renderingEngine = CyCore.renderingEngineFactory.createRenderingEngine(label, view);
@Override
public void run() {
img = renderingEngine.createImage(width,height);
label.setIcon(new ImageIcon(img));
renderingEngine.dispose();
view.dispose();
}
});
}
@Override
public void cancel() {}
img = renderingEngine.createImage(width,height);
label.setIcon(new ImageIcon(img));
}
);
renderingEngine.dispose();
view.dispose();
}
CyCore.dialogTaskManager.execute(
createLayout
);
public void delete() {
if (subnet != null) {
CyCore.networkManager.destroyNetwork(subnet);
}
}
public int getSize() {
......@@ -215,4 +200,8 @@ public class YoshikoCluster {
return solution.getOriginalGraph().getRow(n).get("name", String.class);
}
}
......@@ -71,5 +71,11 @@ public class YoshikoResult {
return originalGraph;
}
public void delete() {
for (YoshikoSolution s: solutions) {
s.delete();
}
}
}
......@@ -22,19 +22,36 @@
package de.hhu.ba.yoshikoWrapper.graphModel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.swing.JOptionPane;
import org.cytoscape.model.CyNetwork;
import org.cytoscape.model.CyNode;
import org.cytoscape.model.CyRow;
import org.slf4j.Logger;
import de.hhu.ba.yoshikoWrapper.core.CyCore;
import de.hhu.ba.yoshikoWrapper.logging.YoshikoLogger;
public class YoshikoSolution {
private final YoshikoResult result;
public ArrayList<YoshikoCluster> cluster;
private CyNetwork metaGraph;
private HashMap<YoshikoCluster, CyNode> metaGraphMap;
public ArrayList<YoshikoCluster> clusters;
private final long id;
private Logger logger = YoshikoLogger.getInstance().getLogger();
public YoshikoSolution(YoshikoResult yoshikoResult, long id) {
cluster = new ArrayList<YoshikoCluster>();
clusters = new ArrayList<YoshikoCluster>();
this.result = yoshikoResult;
this.id = id;
}
......@@ -42,7 +59,7 @@ public class YoshikoSolution {
//_____________GETTER / SETTER ________________//
public ArrayList<YoshikoCluster> getClusters() {
return cluster;
return clusters;
}
/**
......@@ -56,4 +73,37 @@ public class YoshikoSolution {
return result.getOriginalGraph();
}
public void delete() {
for (YoshikoCluster c: clusters) {
c.delete();
}
if (this.metaGraph != null) {
CyCore.networkManager.destroyNetwork(metaGraph);
}
}
public void setMetaGraph(CyNetwork metaGraph, HashMap<YoshikoCluster, CyNode> map) {
this.metaGraph = metaGraph;
this.metaGraphMap = map;
}
public CyNetwork getMetaGraph() {
return metaGraph;
}
public void highlightInMetaGraph(YoshikoCluster yoshikoCluster) {
try {
List<CyRow> allRows = metaGraph.getDefaultNodeTable().getAllRows();
for (CyRow r: allRows) {
r.set("selected", false);
}
metaGraph.getRow(metaGraphMap.get(yoshikoCluster)).set("selected", true);
}
catch (Exception e) {
logger.warn("The graph doesn't exist anymore, can't highlight nodes!");
}
}
}
......@@ -49,7 +49,8 @@ public final class HelpLinks {
/**
* The base location of the bachelor thesis paper
*/
private static final String docLocation = "file:///home/philipp/workspace/cs/BachelorThesis/YoshikoWrapper/thesis/tex/Thesis.pdf";
//TODO: Add final location when published
private static final String docLocation = "https://gitlab.cs.uni-duesseldorf.de/spohr/YoshikoWrapper/tree/master";
/**
* A HashMap linking Classes to a link, pointing to a specific subsection of the BachelorThesis paper that is relevant for understanding the Class
......
......@@ -42,6 +42,7 @@ import de.hhu.ba.yoshikoWrapper.core.LocalizationManager;
import de.hhu.ba.yoshikoWrapper.graphModel.YoshikoCluster;
import de.hhu.ba.yoshikoWrapper.swing.GraphicsLoader;
import de.hhu.ba.yoshikoWrapper.swing.SwingUtil;
import de.hhu.ba.yoshikoWrapper.tasks.AlgorithmTask;
@SuppressWarnings("serial")
public class ClusterView extends JPanel {
......@@ -76,17 +77,17 @@ public class ClusterView extends JPanel {
//Swing init
title = new JLabel(LocalizationManager.get("cluster")+" "+(c.getID()+1));
title = new JLabel(LocalizationManager.get("clusters")+" "+(c.getID()+1));
clusterSize = new JLabel(LocalizationManager.get("clusterSize")+" "+c.getSize());
icon = new JLabel();
//icon.setBorder(BorderFactory.createLineBorder(Color.BLACK));
cluster.applyImage(icon,CLUSTER_ICON_SIZE, CLUSTER_ICON_SIZE);
cluster.generateClusterIcon(CLUSTER_ICON_SIZE, CLUSTER_ICON_SIZE,icon);
SwingUtil.addAll(this,title,clusterSize,icon);
nodeList = new BasicCollapsiblePanel(LocalizationManager.get("nodes"));
//Loop over nodes in the cluster and add them to the view
//Loop over nodes in the clusters and add them to the view
for (CyNode n : c.getSubNetwork().getNodeList()) {
nodeList.add(
new JLabel(
......@@ -147,7 +148,7 @@ public class ClusterView extends JPanel {
@Override
public void mouseEntered(MouseEvent e) {
cluster.highlightInOriginalGraph();
cluster.highlight();
setBorder(isSelected ? selectedBorder : highlightBorder);
}
......
......@@ -49,6 +49,7 @@ import javax.swing.ScrollPaneConstants;
import org.cytoscape.application.swing.CytoPanelComponent;
import org.cytoscape.application.swing.CytoPanelName;
import org.cytoscape.model.CyNetwork;
import org.cytoscape.util.swing.BasicCollapsiblePanel;
import org.cytoscape.work.AbstractTask;
import org.cytoscape.work.TaskIterator;
......@@ -57,6 +58,7 @@ import de.hhu.ba.yoshikoWrapper.core.CyCore;
import de.hhu.ba.yoshikoWrapper.core.LocalizationManager;
import de.hhu.ba.yoshikoWrapper.core.ParameterSet;
import de.hhu.ba.yoshikoWrapper.core.YoshikoLoader;
import de.hhu.ba.yoshikoWrapper.cytoUtil.GraphAnalyzer;
import de.hhu.ba.yoshikoWrapper.swing.AboutDialogFactory;
import de.hhu.ba.yoshikoWrapper.swing.GraphicsLoader;
import de.hhu.ba.yoshikoWrapper.swing.LanguageSwitcherPanel;
......@@ -263,6 +265,32 @@ public class MainPanel extends JPanel implements CytoPanelComponent {
@Override
public void actionPerformed(ActionEvent e) {
CyNetwork networkToBeProcessed = CyCore.cy.getCurrentNetwork();
//WARNINGS FOR FEATURES THAT ARE NOT RESEARCHED
//TODO: (Obviously) research!
if (GraphAnalyzer.isMultiGraph(networkToBeProcessed)) {
JOptionPane.showMessageDialog(
null,
LocalizationManager.get("multiGraphWarning"),
LocalizationManager.get("optionpane_title"),
JOptionPane.WARNING_MESSAGE
);
}
//Annotation: Currently Cytoscape is really weird concerning directed/undirected edges
//There is an API function isDirected() for edges but all edges are directed
//I'll leave this commented out until CS makes sense
// if (GraphAnalyzer.isDirected(networkToBeProcessed)) {
// JOptionPane.showMessageDialog(
// null,
// LocalizationManager.get("directedGraphWarning"),
// LocalizationManager.get("optionpane_title"),
// JOptionPane.WARNING_MESSAGE
// );
// }
//SWING BLACK MAGIC
Window noWindow = null;
JDialog statusWindow = new JDialog(noWindow);
......@@ -277,7 +305,7 @@ public class MainPanel extends JPanel implements CytoPanelComponent {
if (YoshikoLoader.isLibraryLoaded()){
AbstractTask yoshiko = new AlgorithmTask(
popupLevel,
CyCore.cy.getCurrentNetwork(),
networkToBeProcessed,
fetchParameter()
);
/**
......
......@@ -25,20 +25,26 @@ 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.Collection;
import java.util.Properties;
import javax.swing.BoxLayout;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Group;
import javax.swing.GroupLayout.ParallelGroup;
import javax.swing.GroupLayout.SequentialGroup;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.LayoutStyle;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import org.cytoscape.application.swing.CytoPanelComponent;
import org.cytoscape.application.swing.CytoPanelName;
......@@ -55,6 +61,7 @@ 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 org.cytoscape.view.model.CyNetworkView;
import de.hhu.ba.yoshikoWrapper.core.CyCore;
import de.hhu.ba.yoshikoWrapper.core.LocalizationManager;
......@@ -78,9 +85,6 @@ NetworkAboutToBeDestroyedListener
//MACRO
//TODO: (Nächstes Leben) find a good solution with Swing that scales the panel and respects layout
private static final int HACKFIX_FIXED_WIDTH = 128+256;
private final JTabbedPane tabbedPane;
private final JButton destroyButton;
......@@ -93,6 +97,11 @@ NetworkAboutToBeDestroyedListener
private boolean isValid;
//SWING LAYOUT
private ParallelGroup horizontalGroup;
private SequentialGroup verticalGroup;
public ResultPanel(YoshikoResult result) throws Exception {
this.result = result;
......@@ -101,6 +110,7 @@ NetworkAboutToBeDestroyedListener
//Init subcomponents
invalidLabel = new JLabel();
invalidLabel.setHorizontalAlignment(SwingConstants.CENTER);
tabbedPane = new JTabbedPane();