package br.uff.ic.dyevc.gui.graph;
//~--- non-JDK imports --------------------------------------------------------
import br.uff.ic.dyevc.application.IConstants;
import br.uff.ic.dyevc.beans.ApplicationSettingsBean;
import br.uff.ic.dyevc.exception.DyeVCException;
import br.uff.ic.dyevc.exception.VCSException;
import br.uff.ic.dyevc.graph.GraphBuilder;
import br.uff.ic.dyevc.graph.GraphDomainMapper;
import br.uff.ic.dyevc.graph.layout.RepositoryHistoryLayout;
import br.uff.ic.dyevc.graph.Position;
import br.uff.ic.dyevc.graph.transform.commithistory.CHTopologyVertexDrawPaintTransformer;
import br.uff.ic.dyevc.graph.transform.commithistory.CHTopologyVertexPaintTransformer;
import br.uff.ic.dyevc.graph.transform.commithistory.CHVertexLabelTransformer;
import br.uff.ic.dyevc.graph.transform.commithistory.CHVertexStrokeTransformer;
import br.uff.ic.dyevc.graph.transform.commithistory.CHVertexTooltipTransformer;
import br.uff.ic.dyevc.graph.transform.common.ClusterVertexShapeTransformer;
import br.uff.ic.dyevc.gui.core.MessageManager;
import br.uff.ic.dyevc.model.CollapsedCommitInfo;
import br.uff.ic.dyevc.model.CommitInfo;
import br.uff.ic.dyevc.model.CommitRelationship;
import br.uff.ic.dyevc.model.MonitoredRepositories;
import br.uff.ic.dyevc.model.MonitoredRepository;
import br.uff.ic.dyevc.model.topology.RepositoryInfo;
import br.uff.ic.dyevc.tools.vcs.git.GitCommitTools;
import br.uff.ic.dyevc.utils.PreferencesManager;
import br.uff.ic.dyevc.utils.RepositoryConverter;
import br.uff.ic.dyevc.utils.StopWatchLogger;
import edu.uci.ics.jung.algorithms.filters.EdgePredicateFilter;
import edu.uci.ics.jung.algorithms.filters.Filter;
import edu.uci.ics.jung.algorithms.filters.VertexPredicateFilter;
import edu.uci.ics.jung.graph.DirectedOrderedSparseMultigraph;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.visualization.control.CrossoverScalingControl;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ScalingControl;
import edu.uci.ics.jung.visualization.decorators.EdgeShape;
import edu.uci.ics.jung.visualization.DefaultVisualizationModel;
import edu.uci.ics.jung.visualization.GraphZoomScrollPane;
import edu.uci.ics.jung.visualization.Layer;
import edu.uci.ics.jung.visualization.renderers.Renderer;
import edu.uci.ics.jung.visualization.subLayout.GraphCollapser;
import edu.uci.ics.jung.visualization.transform.MutableTransformer;
import edu.uci.ics.jung.visualization.util.PredicatedParallelEdgeIndexFunction;
import edu.uci.ics.jung.visualization.VisualizationModel;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import org.apache.commons.collections15.Predicate;
import org.apache.commons.collections15.Transformer;
import org.apache.commons.lang.time.StopWatch;
import org.slf4j.LoggerFactory;
//~--- JDK imports ------------------------------------------------------------
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Point2D;
import java.awt.GridLayout;
import java.awt.Paint;
import java.awt.Point;
import java.awt.Stroke;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Iterator;
import java.util.HashMap;
import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.ToolTipManager;
import org.jfree.util.Log;
/**
* Displays the commit history for the specified repository
*
* @author cristiano
*/
public class CommitHistoryWindow extends javax.swing.JFrame {
// <editor-fold defaultstate="collapsed" desc="variables declaration">
private static final long serialVersionUID = 1689885032823010309L;
private MonitoredRepository rep;
private DirectedOrderedSparseMultigraph graph;
private DirectedOrderedSparseMultigraph collapsedGraph;
private VisualizationViewer vv;
private RepositoryHistoryLayout layout;
private GraphCollapser collapser;
Filter<CommitInfo, CommitRelationship> edgeFilter;
Filter<CommitInfo, CommitRelationship> nodeFilter;
private JComboBox edgeLineShapeCombo;
private JComboBox mouseModesCombo;
private JButton plusButton;
private JButton minusButton;
private JButton collapseButton;
private JButton expandButton;
private JButton resetButton;
private JButton collapseByTypeButton;
private JButton beginButton;
private JButton endButton;
private JButton helpButton;
private ApplicationSettingsBean settings;
private final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse<CommitInfo, CommitRelationship>();
private final ScalingControl scaler = new CrossoverScalingControl();
private String instructions =
"<html><p>Each vertex in the graph represents a known commit of this system in the topology.</p>"
+ "<p>Each vertex label shows the commit's five initial characters.</p>"
+ "<p>If commit is head of any local/remote branch or tag, it is painted with a heavier stroke. "
+ "The stroke <BR> will be black if it is pointed by any branch and orange if it is pointed by "
+ "any tags <BR> (even if also pointed by any branches).</p>"
+ "<p>Each vertex is painted according to its existence in this repository and those related to it <br>"
+ "(those which this one pushes to or pulls from): </p>" + "<ul>"
+ "<li>If vertex exists locally and in all related repositories, it is painted in WHITE;</li>"
+ "<li>If vertex exists locally but do not exists in any push list, it is painted in GREEN;</li>"
+ "<li>If vertex doesn't exist locally, but exists in any pull list, it is painted in YELLOW;</li>"
+ "<li>If vertex exists in a node not related to the local one (can't be pulled from it), it is painted in RED.</li>"
+ "<li>Finally, if vertex does not belong to a tracked branch, it is painted in GRAY;</li>" + "</ul>"
+ "<p>Place the mouse over a vertex to view detailed information regarding it.</p>" + "</html>";
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="constructor">
/**
* Constructs a CommitHistoryWindow
*
* @param rep
*/
public CommitHistoryWindow(MonitoredRepository rep) {
// SplashScreen splash = SplashScreen.getInstance();
try {
// splash.setStatus("Initializing Graph component");
this.rep = rep;
settings = PreferencesManager.getInstance().loadPreferences();
// SplashScreen.getInstance().setVisible(true);
StopWatchLogger watch = new StopWatchLogger(CommitHistoryWindow.class);
watch.start();
initGraphComponent();
watch.stopAndLog("Process commit history graph for repository <" + rep.getName() + "> with id <"
+ rep.getId() + ">.");
// SplashScreen.getInstance().setStatus("Initializing Window components");
watch.start();
initComponents();
translateGraph(Position.END);
if (settings.isPerformanceMode()) {
watch.stopAndLog("Plot commit history graph for repository <" + rep.getName() + "> with id <"
+ rep.getId() + ">.");
}
// SplashScreen.getInstance().setVisible(false);
} catch (DyeVCException ex) {
// splash.dispose();
JOptionPane.showMessageDialog(
null,
"Application received the following exception trying to show repository log:\n" + ex
+ "\n\nOpen console window to see error details.", "Error found!", JOptionPane.ERROR_MESSAGE);
WindowEvent wev = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev);
setVisible(false);
dispose();
} catch (RuntimeException ex) {
ex.printStackTrace(System.err);
// splash.dispose();
JOptionPane.showMessageDialog(
null,
"Application received the following exception trying to show repository log:\n" + ex
+ "\n\nOpen console window to see error details.", "Error found!", JOptionPane.ERROR_MESSAGE);
WindowEvent wev = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev);
setVisible(false);
dispose();
}
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="initComponents">
private void initComponents() {
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent we) {
resetComponents();
}
});
setAutoRequestFocus(true);
setTitle("Commit History for repository " + rep.getName());
java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
setBounds((screenSize.width - 700) / 2, (screenSize.height - 750) / 2, 700, 750);
mouseModesCombo = graphMouse.getModeComboBox();
mouseModesCombo.addItemListener(graphMouse.getModeListener());
graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING);
plusButton = new JButton("+");
plusButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
scaler.scale(vv, 1.1f, vv.getCenter());
}
});
minusButton = new JButton("-");
minusButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
scaler.scale(vv, 1 / 1.1f, vv.getCenter());
}
});
collapseButton = new JButton("Collapse Picked");
collapseButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
collapseActionPerformed(e);
}
});
expandButton = new JButton("Expand Picked");
expandButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
expandActionPerformed(e);
}
});
resetButton = new JButton("Reset Graph");
resetButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ResetActionPerformed(e);
}
});
collapseByTypeButton = new JButton("Collapse By Type");
collapseByTypeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
collapseByTypeActionPerformed(e);
}
});
helpButton = new JButton("Help");
helpButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, instructions, "Help", JOptionPane.PLAIN_MESSAGE);
}
});
beginButton = new JButton("Beginning");
beginButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
translateGraph(Position.START);
}
});
endButton = new JButton("End");
endButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
translateGraph(Position.END);
}
});
edgeLineShapeCombo = new JComboBox();
this.edgeLineShapeCombo.setModel(new DefaultComboBoxModel(new String[] { "QuadCurve", "Line", "CubicCurve" }));
this.edgeLineShapeCombo.setSelectedItem("CubicCurve");
this.edgeLineShapeCombo.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
edgeLineShapeSelectionActionPerformed(evt);
}
});
Container content = getContentPane();
Container panel = new JPanel(new BorderLayout());
GraphZoomScrollPane gzsp = new GraphZoomScrollPane(vv);
panel.add(gzsp);
JPanel controls = new JPanel();
JPanel zoomControls = new JPanel(new GridLayout(2, 1));
zoomControls.setBorder(BorderFactory.createTitledBorder("Zoom"));
zoomControls.add(plusButton);
zoomControls.add(minusButton);
controls.add(zoomControls);
JPanel collapseControls = new JPanel(new GridLayout(4, 1));
collapseControls.setBorder(BorderFactory.createTitledBorder("Collapsing"));
collapseControls.add(collapseButton);
collapseControls.add(expandButton);
collapseControls.add(collapseByTypeButton);
collapseControls.add(resetButton);
controls.add(collapseControls);
JPanel moveScreen = new JPanel(new GridLayout(2, 1));
moveScreen.setBorder(BorderFactory.createTitledBorder("Move to:"));
moveScreen.add(beginButton);
moveScreen.add(endButton);
controls.add(moveScreen);
JPanel mouseMode = new JPanel(new GridLayout(1, 1));
mouseMode.setBorder(BorderFactory.createTitledBorder("Mouse Mode"));
mouseMode.add(mouseModesCombo);
controls.add(mouseMode);
JPanel edgeLineType = new JPanel(new GridLayout(1, 1));
edgeLineType.setBorder(BorderFactory.createTitledBorder("Edge Line Type"));
edgeLineType.add(edgeLineShapeCombo);
controls.add(edgeLineType);
controls.add(helpButton);
content.add(panel);
content.add(controls, BorderLayout.SOUTH);
} // </editor-fold>
// <editor-fold defaultstate="collapsed" desc="initGraphComponent">
private void initGraphComponent() throws DyeVCException {
// create the commit history graph with all commits from repository and maps the graph to the source commit map
GitCommitTools tools = GitCommitTools.getInstance(rep, true);
RepositoryInfo info = new RepositoryConverter(rep).toRepositoryInfo();
tools.loadExternalCommits(info);
graph = GraphBuilder.createBasicRepositoryHistoryGraph(tools);
graph = autoCollapse1(graph);
graph = autoCollapse2(graph);
graph = autoCollapse1(graph);
graph = autoCollapse2(graph);
GraphDomainMapper<Map<String, CommitInfo>> mapper = new GraphDomainMapper(graph, tools.getCommitInfoMap());
collapsedGraph = graph;
Dimension preferredSize = new Dimension(580, 580);
// Choosing layout
layout = new RepositoryHistoryLayout(mapper, rep, preferredSize);
final VisualizationModel visualizationModel = new DefaultVisualizationModel(layout, preferredSize);
vv = new VisualizationViewer(visualizationModel, preferredSize);
// Scales the graph to show more nodes
scaler.scale(vv, 0.4761905F, vv.getCenter());
vv.scaleToLayout(scaler);
collapser = new GraphCollapser(graph);
final PredicatedParallelEdgeIndexFunction eif = PredicatedParallelEdgeIndexFunction.getInstance();
final Set exclusions = new HashSet();
eif.setPredicate(new Predicate<CommitRelationship>() {
@Override
public boolean evaluate(CommitRelationship e) {
return exclusions.contains(e);
}
});
vv.getRenderContext().setParallelEdgeIndexFunction(eif);
vv.setBackground(IConstants.BACKGROUND_COLOR);
// Adds interaction via mouse
vv.setGraphMouse(graphMouse);
vv.addKeyListener(graphMouse.getModeKeyListener());
nodeFilter = new VertexPredicateFilter(new Predicate<CommitInfo>() {
@Override
public boolean evaluate(CommitInfo ci) {
return true;
}
});
edgeFilter = new EdgePredicateFilter(new Predicate<CommitRelationship>() {
@Override
public boolean evaluate(CommitRelationship cr) {
return true;
}
});
// <editor-fold defaultstate="collapsed" desc="vertex tooltip transformer">
Transformer<Object, String> vertexTooltip = new CHVertexTooltipTransformer(info, tools.getHeadsCommitsMap(),
tools.getTagsCommitsMap());
vv.setVertexToolTipTransformer(vertexTooltip);
ToolTipManager.sharedInstance().setDismissDelay(15000);
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="vertex fillPaint transformer">
Transformer<Object, Paint> vertexPaint = new CHTopologyVertexPaintTransformer();
vv.getRenderContext().setVertexFillPaintTransformer(vertexPaint);
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="vertex drawPaint transformer">
Transformer<Object, Paint> drawPaint = new CHTopologyVertexDrawPaintTransformer(tools.getHeadsCommitsMap(),
tools.getTagsCommitsMap());
vv.getRenderContext().setVertexDrawPaintTransformer(drawPaint);
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="vertex stroke transformer">
Transformer<Object, Stroke> vertexStroke = new CHVertexStrokeTransformer(tools.getHeadsCommitsMap(),
tools.getTagsCommitsMap());
vv.getRenderContext().setVertexStrokeTransformer(vertexStroke);
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="vertex label transformer">
Transformer<Object, String> vertexLabel = new CHVertexLabelTransformer();
vv.getRenderContext().setVertexLabelTransformer(vertexLabel);
vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR);
// </editor-fold>
vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.CubicCurve());
vv.getRenderContext().setVertexShapeTransformer(new ClusterVertexShapeTransformer());
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="action handlers">
private void collapseActionPerformed(ActionEvent evt) {
Collection picked = new HashSet(vv.getPickedVertexState().getPicked());
collapse(picked);
}
private void collapseByTypeActionPerformed(ActionEvent evt) {
collapseByType();
}
private void expandActionPerformed(ActionEvent e) {
Collection picked = new HashSet(vv.getPickedVertexState().getPicked());
expand(picked);
}
private void ResetActionPerformed(ActionEvent evt) {
resetGraph();
}
private void edgeLineShapeSelectionActionPerformed(ActionEvent evt) {
String mode = (String)this.edgeLineShapeCombo.getSelectedItem();
if (mode.equalsIgnoreCase("QuadCurve")) {
vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.QuadCurve());
} else if (mode.equalsIgnoreCase("Line")) {
vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.Line());
} else if (mode.equalsIgnoreCase("CubicCurve")) {
vv.getRenderContext().setEdgeShapeTransformer(new EdgeShape.CubicCurve());
}
this.vv.repaint();
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="collapsing and expanding">
private void collapse(Collection picked) {
// AddFilters();
if (picked.size() > 1) {
Graph inGraph = layout.getGraph();
Graph clusterGraph = collapser.getClusterGraph(inGraph, picked);
collapsedGraph = ((DirectedOrderedSparseMultigraph)collapser.collapse(layout.getGraph(), clusterGraph));
double sumx = 0;
double sumy = 0;
for (Object v : picked) {
Point2D p = (Point2D)layout.transform(v);
sumx += p.getX();
sumy += p.getY();
}
Point2D cp = new Point2D.Double(sumx / picked.size(), sumy / picked.size());
vv.getRenderContext().getParallelEdgeIndexFunction().reset();
layout.setGraph(collapsedGraph);
layout.setLocation(clusterGraph, cp);
vv.getPickedVertexState().clear();
vv.repaint();
}
// RemoveFilters();
}
private void expand(Collection picked) {
for (Object v : picked) {
if (v instanceof Graph) {
// AddFilters();
collapsedGraph = ((DirectedOrderedSparseMultigraph)collapser.expand(layout.getGraph(), (Graph)v));
vv.getRenderContext().getParallelEdgeIndexFunction().reset();
layout.setGraph(collapsedGraph);
}
// RemoveFilters();
// Filter();
vv.getPickedVertexState().clear();
vv.repaint();
}
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="collapseByType">
private void collapseByType2() {
layout.setGraph(graph);
collapsedGraph = graph;
List<List> groups = new ArrayList<List>();
Map<Byte, List> groupsByTypeMap = new TreeMap<Byte, List>();
groupsByTypeMap.put(IConstants.COMMIT_MASK_ALL_HAVE, new ArrayList<List>());
groupsByTypeMap.put(IConstants.COMMIT_MASK_I_HAVE_PUSH_DONT, new ArrayList<List>());
groupsByTypeMap.put(IConstants.COMMIT_MASK_I_DONT_PULL_HAS, new ArrayList<List>());
groupsByTypeMap.put(IConstants.COMMIT_MASK_NON_RELATED_HAS, new ArrayList<List>());
groupsByTypeMap.put(IConstants.COMMIT_MASK_NOT_TRACKED, new ArrayList<List>());
byte currentType = IConstants.COMMIT_MASK_ALL_HAVE;
List currentGroup = new ArrayList();
for (Object o : graph.getVertices()) {
CommitInfo currentNode = (CommitInfo)o;
boolean childrenSameType = checkChildrenForSameType(currentNode);
if (currentNode.getType() == currentType) {
if (childrenSameType) {
currentGroup.add(currentNode);
} else { // not all children are of same type as currentType
if (!currentGroup.isEmpty()) { // include currentNode into currentGroup and close the group
groupsByTypeMap.get(currentType).add(currentGroup);
groups.add(currentGroup);
currentGroup = new ArrayList();
}
currentGroup.add(currentNode);
groupsByTypeMap.get(currentType).add(currentGroup);
groups.add(currentGroup);
currentGroup = new ArrayList();
}
} else { // currentNode has a different type from currentType
if (!currentGroup.isEmpty()) { // close currentGroup to create a new one
groupsByTypeMap.get(currentType).add(currentGroup);
groups.add(currentGroup);
currentGroup = new ArrayList();
}
currentType = currentNode.getType();
currentGroup.add(currentNode);
if (!childrenSameType) {
groupsByTypeMap.get(currentType).add(currentGroup);
groups.add(currentGroup);
currentGroup = new ArrayList();
}
}
}
Point2D currentCoords = new Point2D.Double(0, 0);
for (List list : groups) {
doCollapseByType(list, currentCoords);
currentCoords = new Point2D.Double(currentCoords.getX() + 2 * RepositoryHistoryLayout.XDISTANCE,
currentCoords.getY());
}
vv.getRenderContext().getParallelEdgeIndexFunction().reset();
vv.getPickedVertexState().clear();
translateGraph(Position.START);
vv.repaint();
}
private boolean checkChildrenForSameType(CommitInfo currentNode) {
boolean result = true;
for (Object o : graph.getPredecessors(currentNode)) {
CommitInfo ci = (CommitInfo)o;
if (ci.getType() != currentNode.getType()) {
result = false;
break;
}
}
return result;
}
private void collapseByType() {
layout.setGraph(graph);
collapsedGraph = graph;
Collection allHave = new ArrayList();
Collection iHavePushDont = new ArrayList();
Collection iDontPullHas = new ArrayList();
Collection nonRelatedHas = new ArrayList();
Collection notTracked = new ArrayList();
Point2D previousCoords = null;
for (Object o : layout.getGraph().getVertices()) {
CommitInfo ci = (CommitInfo)o;
if (layout.getGraph().getSuccessorCount(o) == 0) {
previousCoords = layout.transform(o);
}
switch (ci.getType()) {
case IConstants.COMMIT_MASK_ALL_HAVE :
allHave.add(o);
break;
case IConstants.COMMIT_MASK_I_HAVE_PUSH_DONT :
iHavePushDont.add(o);
break;
case IConstants.COMMIT_MASK_I_DONT_PULL_HAS :
iDontPullHas.add(o);
break;
case IConstants.COMMIT_MASK_NON_RELATED_HAS :
nonRelatedHas.add(o);
break;
case IConstants.COMMIT_MASK_NOT_TRACKED :
notTracked.add(o);
break;
}
}
Point2D newCoords = previousCoords;
if (allHave.size() > 0) {
newCoords = new Point2D.Double(previousCoords.getX() + 2 * RepositoryHistoryLayout.XDISTANCE,
previousCoords.getY());
doCollapseByType(allHave, newCoords);
previousCoords = newCoords;
}
if (iHavePushDont.size() > 0) {
newCoords = new Point2D.Double(previousCoords.getX() + 2 * RepositoryHistoryLayout.XDISTANCE,
previousCoords.getY());
doCollapseByType(iHavePushDont, newCoords);
previousCoords = newCoords;
}
if (notTracked.size() > 0) {
newCoords = new Point2D.Double(previousCoords.getX() + 2 * RepositoryHistoryLayout.XDISTANCE,
previousCoords.getY() + 2 * RepositoryHistoryLayout.XDISTANCE);
doCollapseByType(notTracked, newCoords);
}
if (iDontPullHas.size() > 0) {
newCoords = new Point2D.Double(previousCoords.getX() + 2 * RepositoryHistoryLayout.XDISTANCE,
previousCoords.getY());
doCollapseByType(iDontPullHas, newCoords);
previousCoords = newCoords;
}
if (nonRelatedHas.size() > 0) {
newCoords = new Point2D.Double(previousCoords.getX() + 2 * RepositoryHistoryLayout.XDISTANCE,
previousCoords.getY());
doCollapseByType(nonRelatedHas, newCoords);
previousCoords = newCoords;
}
vv.getRenderContext().getParallelEdgeIndexFunction().reset();
vv.getPickedVertexState().clear();
translateGraph(Position.START);
vv.repaint();
}
private void doCollapseByType(Collection picked, Point2D coords) {
Graph inGraph = layout.getGraph();
Graph clusterGraph = collapser.getClusterGraph(inGraph, picked);
collapsedGraph = ((DirectedOrderedSparseMultigraph)collapser.collapse(inGraph, clusterGraph));
layout.setGraph(collapsedGraph);
layout.setLocation(clusterGraph, coords);
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="reset">
private void resetGraph() {
layout.setGraph(graph);
collapsedGraph = graph;
layout.initialize(true);
vv.repaint();
}
private void resetComponents() {
rep = null;
graph = null;
collapsedGraph = null;
vv = null;
layout = null;
collapser = null;
edgeFilter = null;
nodeFilter = null;
}
// </editor-fold>
/**
* runs the graph with a demo repository
*/
public static void main(String[] args) {
MonitoredRepositories reps = PreferencesManager.getInstance().loadMonitoredRepositories();
MonitoredRepository rep = MonitoredRepositories.getMonitoredProjectById("rep1391645758732"); // saposTeste
new CommitHistoryWindow(rep).setVisible(true);
}
/**
* Translates graph, positioning it at the farthest X position.
*/
private void translateGraph(Position position) {
MutableTransformer modelTransformer =
vv.getRenderContext().getMultiLayerTransformer().getTransformer(Layer.LAYOUT);
double currentX = modelTransformer.getTranslateX();
double currentY = modelTransformer.getTranslateY();
double dx;
double dy;
int showPosition = layout.getWidth() - vv.getPreferredSize().width;
Point graphEnd = new Point(showPosition, 0);
Point graphStart = new Point(0, 0);
if (position == Position.START) {
dx = (graphStart.getX() - currentX);
dy = (graphStart.getY() - currentY);
} else {
dx = (-currentX - graphEnd.getX());
dy = (-currentY - graphEnd.getY());
}
modelTransformer.translate(dx, dy);
}
/**
* Automatically collapses a graph based on the existence of commit in other partners.
* Complete method documentation...
* @param graph Graph to be collapsed
* @return The original graph, collapsed by....
*/
private DirectedOrderedSparseMultigraph autoCollapse1(DirectedOrderedSparseMultigraph graph) {
//TODO implement method to automatically collapse graph...
DirectedOrderedSparseMultigraph new_graph = new DirectedOrderedSparseMultigraph();
// Use sets for representing the all commit set
Set<CommitInfo> visited_set = new HashSet<CommitInfo>();
Set<CommitInfo> not_collapsed_set = new HashSet<CommitInfo>();
Set<CollapsedCommitInfo> collapses = new HashSet<CollapsedCommitInfo>();
Set<CommitRelationship> edges = new HashSet<CommitRelationship>();
for (Object v : graph.getVertices())
{
CommitInfo currentNode = (CommitInfo)v;
if(!visited_set.contains(currentNode) && DegreeTwo(currentNode))
{
CollapsedCommitInfo collapsed_nodes = new CollapsedCommitInfo(currentNode);
CommitInfo last_parent = AddAllParentsToCollapse(currentNode, collapsed_nodes, visited_set);
CommitInfo last_child = AddAllChildrenToCollapse(currentNode, collapsed_nodes, visited_set);
// If collapse is made
if (last_child != last_parent)
{
collapsed_nodes.SetAncestor(last_parent);
collapsed_nodes.incrementParents();
collapsed_nodes.SetDescendant(last_child);
collapsed_nodes.incrementChildren();
collapses.add(collapsed_nodes);
CommitInfo parent_of_collapse = GetFirstParent(last_parent);
CommitInfo child_of_collapse = GetFirstChild(last_child);
edges.add(new CommitRelationship(parent_of_collapse, collapsed_nodes));
edges.add(new CommitRelationship(collapsed_nodes, child_of_collapse));
}
else {not_collapsed_set.add(currentNode);}
}
else
{
if(!visited_set.contains(currentNode)) not_collapsed_set.add(currentNode);
}
visited_set.add(currentNode);
}
for (CommitInfo commitInfo : not_collapsed_set)
{
new_graph.addVertex(commitInfo);
}
for (CollapsedCommitInfo collapse : collapses)
{
new_graph.addVertex(collapse);
}
for (CommitRelationship commitRelationship : edges)
{
new_graph.addEdge(commitRelationship, commitRelationship.getChild(), commitRelationship.getParent());
}
for (Object commitRel : graph.getEdges())
{
CommitRelationship cr = (CommitRelationship) commitRel;
if(not_collapsed_set.contains(cr.getChild()) && not_collapsed_set.contains(cr.getParent()))
new_graph.addEdge(cr, cr.getChild(), cr.getParent());
}
return new_graph;
}
/**
* Collapses parents when they are chains of commits with different color
* @param collapsed_nodes
* @param parent_of_collapse
* @param currentNode
* @param visited_set
* @param collapses
* @param edges
*/
private void CollapseAdjacentParents(CollapsedCommitInfo collapsed_nodes, CommitInfo parent_of_collapse,
Set<CommitInfo> visited_set, Set<CollapsedCommitInfo> collapses, Set<CommitRelationship> edges) {
// Firstly, look the parent of collapse:
CollapsedCommitInfo previous_collapse_par = collapsed_nodes;
while(DegreeTwo(parent_of_collapse))
{
CollapsedCommitInfo collapsed_nodes_parents = new CollapsedCommitInfo(parent_of_collapse);
CommitInfo last_parent_p = AddAllParentsToCollapse(parent_of_collapse, collapsed_nodes_parents, visited_set);
if(last_parent_p != parent_of_collapse)
{
// There is a new collapsed node linked to previous
collapsed_nodes_parents.SetAncestor(last_parent_p);
collapsed_nodes_parents.SetDescendant(parent_of_collapse);
collapses.add(collapsed_nodes_parents);
edges.add(new CommitRelationship(collapsed_nodes_parents, previous_collapse_par));
previous_collapse_par = collapsed_nodes_parents;
parent_of_collapse = GetFirstParent(last_parent_p);
}
else
{
edges.add(new CommitRelationship(parent_of_collapse, previous_collapse_par));
break;
}
}
}
/**
* Collapses children when they are chains of commits with different color
* @param collapsed_nodes
* @param parent_of_collapse
* @param currentNode
* @param visited_set
* @param collapses
* @param edges
*/
private void CollapseAdjacentChildren(CollapsedCommitInfo collapsed_nodes, CommitInfo child_of_collapse,
Set<CommitInfo> visited_set, Set<CollapsedCommitInfo> collapses, Set<CommitRelationship> edges) {
CollapsedCommitInfo previous_collapse_ch = collapsed_nodes;
while(DegreeTwo(child_of_collapse))
{
CollapsedCommitInfo collapsed_nodes_children = new CollapsedCommitInfo(child_of_collapse);
CommitInfo last_children_c = AddAllChildrenToCollapse(child_of_collapse, collapsed_nodes_children, visited_set);
if(last_children_c != child_of_collapse)
{
// There is a new collapsed node linked to previous
collapsed_nodes_children.SetAncestor(child_of_collapse);
collapsed_nodes_children.SetDescendant(last_children_c);
collapses.add(collapsed_nodes_children);
edges.add(new CommitRelationship(previous_collapse_ch, collapsed_nodes_children));
previous_collapse_ch = collapsed_nodes_children;
child_of_collapse = GetFirstChild(last_children_c);
}
else
{
edges.add(new CommitRelationship(previous_collapse_ch, child_of_collapse));
break;
}
}
}
/**
* Visit and add to visited_set all parents of currentNode with same condition
* @param currentNode
* @param collapsed_nodes
* @param visited_set
* @return last element added
*/
private CommitInfo AddAllParentsToCollapse(CommitInfo currentNode, CollapsedCommitInfo collapsed_nodes, Set<CommitInfo> visited_set)
{
CommitInfo parent = currentNode;
CommitInfo last_collapsed_parent = null;
while(!visited_set.contains(parent) && DegreeTwo(parent) && parent.getType() == currentNode.getType())
{
//Add to new collapsed node
visited_set.add(parent);
collapsed_nodes.AddCommitToCollapse(parent);
last_collapsed_parent = parent;
parent = GetFirstParent(parent);
}
return last_collapsed_parent == null? currentNode : last_collapsed_parent;
}
/**
* Visit and add to visited_set all children of currentNode with same condition
* @param currentNode
* @param collapsed_nodes
* @param visited_set
* @return last element added
*/
private CommitInfo AddAllChildrenToCollapse(CommitInfo currentNode, CollapsedCommitInfo collapsed_nodes, Set<CommitInfo> visited_set)
{
CommitInfo child = currentNode;
CommitInfo last_collapsed_child = null;
while((!visited_set.contains(child) && DegreeTwo(child) && child.getType() == currentNode.getType())
|| child == currentNode)
{
//Add to new collapsed node
visited_set.add(child);
collapsed_nodes.AddCommitToCollapse(child);
last_collapsed_child = child;
child = GetFirstChild(child);
}
return last_collapsed_child == null? currentNode : last_collapsed_child;
}
private CommitInfo GetFirstParent(CommitInfo ci)
{
return (CommitInfo) graph.getPredecessors(ci).iterator().next();
}
private CommitInfo GetFirstChild(CommitInfo ci)
{
return (CommitInfo) graph.getSuccessors(ci).iterator().next();
}
private boolean DegreeTwo(CommitInfo node)
{
return graph.getPredecessorCount(node) == 1 && graph.getSuccessorCount(node) == 1;
}
private DirectedOrderedSparseMultigraph autoCollapse2(DirectedOrderedSparseMultigraph graph) {
//TODO implement method to automatically collapse graph...
DirectedOrderedSparseMultigraph new_graph = new DirectedOrderedSparseMultigraph();
// Use sets for representing the all commit set
Set<CommitInfo> visited_set = new HashSet<CommitInfo>();
Set<CommitInfo> not_collapsed_set = new HashSet<CommitInfo>();
Set<CollapsedCommitInfo> collapses = new HashSet<CollapsedCommitInfo>();
Set<CommitRelationship> edges = new HashSet<CommitRelationship>();
Map<CommitInfo, CollapsedCommitInfo> node_collapse_dict = new HashMap<CommitInfo, CollapsedCommitInfo>();
for (Object v : graph.getVertices())
{
CommitInfo currentNode = (CommitInfo)v;
if(!visited_set.contains(currentNode))
{
if(graph.getPredecessorCount(currentNode) == 1 && graph.getSuccessorCount(currentNode) == 2) // CONDITION 1
{
Object[] children = graph.getSuccessors(currentNode).toArray();
CommitInfo child1 = (CommitInfo) children[0];
CommitInfo child2 = (CommitInfo) children[1];
if(currentNode.getType() == child1.getType() && currentNode.getType() == child2.getType() &&
graph.getSuccessorCount(child1) == 1 && graph.getSuccessorCount(child2) == 1)
{
CollapsedCommitInfo collapsed_nodes = new CollapsedCommitInfo(currentNode);
// Collapse vertexes (2 cases)
if((GetFirstChild(child1) == child2 && graph.getPredecessorCount(child1) == 1 && graph.getPredecessorCount(child2) == 2) ||
(GetFirstChild(child2) == child1 && graph.getPredecessorCount(child1) == 2 && graph.getPredecessorCount(child2) == 1)) // First case
{
collapsed_nodes.AddCommitToCollapse(child1);
// If collapse is made, remove collapsed vertexes from not_collapsed_set
// (they can be there due to unknown order of enumeration)
not_collapsed_set.remove(child1);
visited_set.add(child1);
collapsed_nodes.AddCommitToCollapse(child2);
not_collapsed_set.remove(child2);
visited_set.add(child2);
collapsed_nodes.SetAncestor(GetFirstParent(currentNode));
collapsed_nodes.incrementParents();
collapses.add(collapsed_nodes);
// Add edges pointing outside the collapse
edges.add(new CommitRelationship(GetFirstParent(currentNode), currentNode));
CommitInfo collapse_child = GetFirstChild(child1) == child2 ? GetFirstChild(child2) : GetFirstChild(child1);
edges.add(new CommitRelationship(currentNode, collapse_child));
collapsed_nodes.SetDescendant(GetFirstChild(collapse_child));
collapsed_nodes.incrementChildren();
node_collapse_dict.put(currentNode, collapsed_nodes);
node_collapse_dict.put(child1, collapsed_nodes);
node_collapse_dict.put(child2, collapsed_nodes);
}
else if(graph.getPredecessorCount(child1) == 1 && graph.getPredecessorCount(child2) == 1 &&
GetFirstChild(child1) == GetFirstChild(child2) && graph.getSuccessorCount(GetFirstChild(child1)) == 1 &&
GetFirstChild(child1).getType() == currentNode.getType()) // Second case
{
CommitInfo child_of_childs = GetFirstChild(child1);
collapsed_nodes.AddCommitToCollapse(child1);
not_collapsed_set.remove(child1);
visited_set.add(child1);
collapsed_nodes.AddCommitToCollapse(child2);
not_collapsed_set.remove(child2);
visited_set.add(child2);
collapsed_nodes.AddCommitToCollapse(child_of_childs);
not_collapsed_set.remove(child_of_childs);
visited_set.add(child_of_childs);
collapsed_nodes.SetAncestor(GetFirstParent(currentNode));
collapsed_nodes.incrementParents();
collapses.add(collapsed_nodes);
edges.add(new CommitRelationship(GetFirstParent(currentNode), currentNode));
CommitInfo child_of_child_of_childs = GetFirstChild(child_of_childs);
edges.add(new CommitRelationship(currentNode, child_of_child_of_childs));
collapsed_nodes.SetDescendant(GetFirstChild(child_of_child_of_childs));
collapsed_nodes.incrementChildren();
node_collapse_dict.put(currentNode, collapsed_nodes);
node_collapse_dict.put(child1, collapsed_nodes);
node_collapse_dict.put(child2, collapsed_nodes);
node_collapse_dict.put(child_of_childs, collapsed_nodes);
}
else {not_collapsed_set.add(currentNode);}
}
else {not_collapsed_set.add(currentNode);}
}
else {not_collapsed_set.add(currentNode);}
visited_set.add(currentNode);
}
}
for (CommitInfo commitInfo : not_collapsed_set)
{
new_graph.addVertex(commitInfo);
}
for (CollapsedCommitInfo collapse : collapses)
{
new_graph.addVertex(collapse);
}
for (CommitRelationship commitRelationship : edges)
{
CommitInfo child = commitRelationship.getChild();
CommitInfo parent = commitRelationship.getParent();
if(!not_collapsed_set.contains(child) && not_collapsed_set.contains(parent))
{
CollapsedCommitInfo c = node_collapse_dict.get(child);
new_graph.addEdge(new CommitRelationship(parent, c), c, parent);
}
else if(not_collapsed_set.contains(child) && !not_collapsed_set.contains(parent))
{
CollapsedCommitInfo p = node_collapse_dict.get(parent);
new_graph.addEdge(new CommitRelationship(p, child), child, p);
}
else if(!not_collapsed_set.contains(child) && !not_collapsed_set.contains(parent))
{
CollapsedCommitInfo c = node_collapse_dict.get(child);
CollapsedCommitInfo p = node_collapse_dict.get(parent);
if(new_graph.findEdge(c, p) == null)
new_graph.addEdge(new CommitRelationship(p, c), c, p);
}
}
for (Object commitRel : graph.getEdges())
{
CommitRelationship cr = (CommitRelationship) commitRel;
if(not_collapsed_set.contains(cr.getChild()) && not_collapsed_set.contains(cr.getParent()))
new_graph.addEdge(cr, cr.getChild(), cr.getParent());
}
return new_graph;
}
}