blob: cccca711113fb9b7851bd174e82dea3fd1dba0dc [file] [log] [blame]
/*
* Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.hotspot.igv.view;
import com.sun.hotspot.igv.data.*;
import com.sun.hotspot.igv.difference.Difference;
import com.sun.hotspot.igv.filter.CustomFilter;
import com.sun.hotspot.igv.filter.FilterChain;
import com.sun.hotspot.igv.graph.Diagram;
import com.sun.hotspot.igv.graph.Figure;
import com.sun.hotspot.igv.settings.Settings;
import com.sun.hotspot.igv.util.RangeSliderModel;
import java.awt.Color;
import java.util.*;
/**
*
* @author Thomas Wuerthinger
*/
public class DiagramViewModel extends RangeSliderModel implements ChangedListener<RangeSliderModel> {
// Warning: Update setData method if fields are added
private Group group;
private ArrayList<InputGraph> graphs;
private Set<Integer> hiddenNodes;
private Set<Integer> onScreenNodes;
private Set<Integer> selectedNodes;
private FilterChain filterChain;
private FilterChain sequenceFilterChain;
private Diagram diagram;
private InputGraph inputGraph;
private ChangedEvent<DiagramViewModel> groupChangedEvent;
private ChangedEvent<DiagramViewModel> diagramChangedEvent;
private ChangedEvent<DiagramViewModel> viewChangedEvent;
private ChangedEvent<DiagramViewModel> hiddenNodesChangedEvent;
private ChangedEvent<DiagramViewModel> viewPropertiesChangedEvent;
private boolean showBlocks;
private boolean showNodeHull;
private boolean hideDuplicates;
private ChangedListener<FilterChain> filterChainChangedListener = new ChangedListener<FilterChain>() {
@Override
public void changed(FilterChain source) {
diagramChanged();
}
};
@Override
public DiagramViewModel copy() {
DiagramViewModel result = new DiagramViewModel(group, filterChain, sequenceFilterChain);
result.setData(this);
return result;
}
public Group getGroup() {
return group;
}
public void setData(DiagramViewModel newModel) {
super.setData(newModel);
boolean diagramChanged = false;
boolean viewChanged = false;
boolean viewPropertiesChanged = false;
boolean groupChanged = (group == newModel.group);
this.group = newModel.group;
if (groupChanged) {
filterGraphs();
}
diagramChanged |= (filterChain != newModel.filterChain);
this.filterChain = newModel.filterChain;
diagramChanged |= (sequenceFilterChain != newModel.sequenceFilterChain);
this.sequenceFilterChain = newModel.sequenceFilterChain;
diagramChanged |= (diagram != newModel.diagram);
this.diagram = newModel.diagram;
viewChanged |= (hiddenNodes != newModel.hiddenNodes);
this.hiddenNodes = newModel.hiddenNodes;
viewChanged |= (onScreenNodes != newModel.onScreenNodes);
this.onScreenNodes = newModel.onScreenNodes;
viewChanged |= (selectedNodes != newModel.selectedNodes);
this.selectedNodes = newModel.selectedNodes;
viewPropertiesChanged |= (showBlocks != newModel.showBlocks);
this.showBlocks = newModel.showBlocks;
viewPropertiesChanged |= (showNodeHull != newModel.showNodeHull);
this.showNodeHull = newModel.showNodeHull;
if (groupChanged) {
groupChangedEvent.fire();
}
if (diagramChanged) {
diagramChangedEvent.fire();
}
if (viewPropertiesChanged) {
viewPropertiesChangedEvent.fire();
}
if (viewChanged) {
viewChangedEvent.fire();
}
}
public boolean getShowBlocks() {
return showBlocks;
}
public void setShowBlocks(boolean b) {
showBlocks = b;
viewPropertiesChangedEvent.fire();
}
public boolean getShowNodeHull() {
return showNodeHull;
}
public void setShowNodeHull(boolean b) {
showNodeHull = b;
viewPropertiesChangedEvent.fire();
}
public boolean getHideDuplicates() {
return hideDuplicates;
}
public void setHideDuplicates(boolean b) {
System.err.println("setHideDuplicates: " + b);
hideDuplicates = b;
InputGraph currentGraph = getFirstGraph();
if (hideDuplicates) {
// Back up to the unhidden equivalent graph
int index = graphs.indexOf(currentGraph);
while (graphs.get(index).getProperties().get("_isDuplicate") != null) {
index--;
}
currentGraph = graphs.get(index);
}
filterGraphs();
selectGraph(currentGraph);
viewPropertiesChangedEvent.fire();
}
public DiagramViewModel(Group g, FilterChain filterChain, FilterChain sequenceFilterChain) {
super(Arrays.asList("default"));
this.showBlocks = false;
this.showNodeHull = true;
this.group = g;
filterGraphs();
assert filterChain != null;
this.filterChain = filterChain;
assert sequenceFilterChain != null;
this.sequenceFilterChain = sequenceFilterChain;
hiddenNodes = new HashSet<>();
onScreenNodes = new HashSet<>();
selectedNodes = new HashSet<>();
super.getChangedEvent().addListener(this);
diagramChangedEvent = new ChangedEvent<>(this);
viewChangedEvent = new ChangedEvent<>(this);
hiddenNodesChangedEvent = new ChangedEvent<>(this);
viewPropertiesChangedEvent = new ChangedEvent<>(this);
groupChangedEvent = new ChangedEvent<>(this);
groupChangedEvent.addListener(groupChangedListener);
groupChangedEvent.fire();
filterChain.getChangedEvent().addListener(filterChainChangedListener);
sequenceFilterChain.getChangedEvent().addListener(filterChainChangedListener);
}
private final ChangedListener<DiagramViewModel> groupChangedListener = new ChangedListener<DiagramViewModel>() {
private Group oldGroup;
@Override
public void changed(DiagramViewModel source) {
if (oldGroup != null) {
oldGroup.getChangedEvent().removeListener(groupContentChangedListener);
}
group.getChangedEvent().addListener(groupContentChangedListener);
oldGroup = group;
}
};
private final ChangedListener<Group> groupContentChangedListener = new ChangedListener<Group>() {
@Override
public void changed(Group source) {
assert source == group;
filterGraphs();
setSelectedNodes(selectedNodes);
}
};
public ChangedEvent<DiagramViewModel> getDiagramChangedEvent() {
return diagramChangedEvent;
}
public ChangedEvent<DiagramViewModel> getViewChangedEvent() {
return viewChangedEvent;
}
public ChangedEvent<DiagramViewModel> getHiddenNodesChangedEvent() {
return hiddenNodesChangedEvent;
}
public ChangedEvent<DiagramViewModel> getViewPropertiesChangedEvent() {
return viewPropertiesChangedEvent;
}
public Set<Integer> getSelectedNodes() {
return selectedNodes;
}
public Set<Integer> getHiddenNodes() {
return hiddenNodes;
}
public Set<Integer> getOnScreenNodes() {
return onScreenNodes;
}
public void setSelectedNodes(Set<Integer> nodes) {
this.selectedNodes = nodes;
List<Color> colors = new ArrayList<>();
for (String s : getPositions()) {
colors.add(Color.black);
}
if (nodes.size() >= 1) {
for (Integer id : nodes) {
if (id < 0) {
id = -id;
}
InputNode last = null;
int index = 0;
for (InputGraph g : graphs) {
Color curColor = colors.get(index);
InputNode cur = g.getNode(id);
if (cur != null) {
if (last == null) {
curColor = Color.green;
} else {
if (last.equals(cur) && last.getProperties().equals(cur.getProperties())) {
if (curColor == Color.black) {
curColor = Color.white;
}
} else {
if (curColor != Color.green) {
curColor = Color.orange;
}
}
}
}
last = cur;
colors.set(index, curColor);
index++;
}
}
}
setColors(colors);
viewChangedEvent.fire();
}
public void showNot(final Set<Integer> nodes) {
setHiddenNodes(nodes);
}
public void showFigures(Collection<Figure> f) {
HashSet<Integer> newHiddenNodes = new HashSet<>(getHiddenNodes());
for (Figure fig : f) {
newHiddenNodes.removeAll(fig.getSource().getSourceNodesAsSet());
}
setHiddenNodes(newHiddenNodes);
}
public Set<Figure> getSelectedFigures() {
Set<Figure> result = new HashSet<>();
for (Figure f : diagram.getFigures()) {
for (InputNode node : f.getSource().getSourceNodes()) {
if (getSelectedNodes().contains(node.getId())) {
result.add(f);
}
}
}
return result;
}
public void showAll(final Collection<Figure> f) {
showFigures(f);
}
public void showOnly(final Set<Integer> nodes) {
final HashSet<Integer> allNodes = new HashSet<>(getGraphToView().getGroup().getAllNodes());
allNodes.removeAll(nodes);
setHiddenNodes(allNodes);
}
public void setHiddenNodes(Set<Integer> nodes) {
this.hiddenNodes = nodes;
hiddenNodesChangedEvent.fire();
}
public void setOnScreenNodes(Set<Integer> onScreenNodes) {
this.onScreenNodes = onScreenNodes;
viewChangedEvent.fire();
}
public FilterChain getSequenceFilterChain() {
return filterChain;
}
public void setSequenceFilterChain(FilterChain chain) {
assert chain != null : "sequenceFilterChain must never be null";
sequenceFilterChain.getChangedEvent().removeListener(filterChainChangedListener);
sequenceFilterChain = chain;
sequenceFilterChain.getChangedEvent().addListener(filterChainChangedListener);
diagramChanged();
}
private void diagramChanged() {
// clear diagram
diagram = null;
getDiagramChangedEvent().fire();
}
public FilterChain getFilterChain() {
return filterChain;
}
public void setFilterChain(FilterChain chain) {
assert chain != null : "filterChain must never be null";
filterChain.getChangedEvent().removeListener(filterChainChangedListener);
filterChain = chain;
filterChain.getChangedEvent().addListener(filterChainChangedListener);
diagramChanged();
}
/*
* Select the set of graphs to be presented.
*/
private void filterGraphs() {
ArrayList<InputGraph> result = new ArrayList<>();
List<String> positions = new ArrayList<>();
for (InputGraph graph : group.getGraphs()) {
String duplicate = graph.getProperties().get("_isDuplicate");
if (duplicate == null || !hideDuplicates) {
result.add(graph);
positions.add(graph.getName());
}
}
this.graphs = result;
setPositions(positions);
}
public InputGraph getFirstGraph() {
if (getFirstPosition() < graphs.size()) {
return graphs.get(getFirstPosition());
}
return graphs.get(graphs.size() - 1);
}
public InputGraph getSecondGraph() {
if (getSecondPosition() < graphs.size()) {
return graphs.get(getSecondPosition());
}
return getFirstGraph();
}
public void selectGraph(InputGraph g) {
int index = graphs.indexOf(g);
if (index == -1 && hideDuplicates) {
// A graph was selected that's currently hidden, so unhide and select it.
setHideDuplicates(false);
index = graphs.indexOf(g);
}
assert index != -1;
setPositions(index, index);
}
public Diagram getDiagramToView() {
if (diagram == null) {
diagram = Diagram.createDiagram(getGraphToView(), Settings.get().get(Settings.NODE_TEXT, Settings.NODE_TEXT_DEFAULT));
getFilterChain().apply(diagram, getSequenceFilterChain());
if (getFirstPosition() != getSecondPosition()) {
CustomFilter f = new CustomFilter(
"difference", "colorize('state', 'same', white);"
+ "colorize('state', 'changed', orange);"
+ "colorize('state', 'new', green);"
+ "colorize('state', 'deleted', red);");
f.apply(diagram);
}
}
return diagram;
}
public InputGraph getGraphToView() {
if (inputGraph == null) {
if (getFirstGraph() != getSecondGraph()) {
inputGraph = Difference.createDiffGraph(getFirstGraph(), getSecondGraph());
} else {
inputGraph = getFirstGraph();
}
}
return inputGraph;
}
@Override
public void changed(RangeSliderModel source) {
inputGraph = null;
diagramChanged();
}
void setSelectedFigures(List<Figure> list) {
Set<Integer> newSelectedNodes = new HashSet<>();
for (Figure f : list) {
newSelectedNodes.addAll(f.getSource().getSourceNodesAsSet());
}
this.setSelectedNodes(newSelectedNodes);
}
void close() {
filterChain.getChangedEvent().removeListener(filterChainChangedListener);
sequenceFilterChain.getChangedEvent().removeListener(filterChainChangedListener);
}
Iterable<InputGraph> getGraphsForward() {
return new Iterable<InputGraph>() {
@Override
public Iterator<InputGraph> iterator() {
return new Iterator<InputGraph>() {
int index = getFirstPosition();
@Override
public boolean hasNext() {
return index + 1 < graphs.size();
}
@Override
public InputGraph next() {
return graphs.get(++index);
}
};
}
};
}
Iterable<InputGraph> getGraphsBackward() {
return new Iterable<InputGraph>() {
@Override
public Iterator<InputGraph> iterator() {
return new Iterator<InputGraph>() {
int index = getFirstPosition();
@Override
public boolean hasNext() {
return index - 1 > 0;
}
@Override
public InputGraph next() {
return graphs.get(--index);
}
};
}
};
}
}