package jdepend.client.report.way.mapui; import java.awt.Rectangle; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.StringTokenizer; import java.util.logging.Logger; import jdepend.model.Element; import jdepend.model.Relation; import jdepend.client.report.filter.RelationFilter; import jdepend.client.report.way.mapui.layout.LayoutMgr; import jdepend.client.report.way.mapui.model.MapData; import prefuse.Constants; import prefuse.Display; import prefuse.Visualization; import prefuse.action.ActionList; import prefuse.action.GroupAction; import prefuse.action.ItemAction; import prefuse.action.RepaintAction; import prefuse.action.animate.ColorAnimator; import prefuse.action.animate.PolarLocationAnimator; import prefuse.action.animate.QualityControlAnimator; import prefuse.action.animate.VisibilityAnimator; import prefuse.action.assignment.ColorAction; import prefuse.action.assignment.FontAction; import prefuse.action.layout.CollapsedSubtreeLayout; import prefuse.action.layout.Layout; import prefuse.activity.SlowInSlowOutPacer; import prefuse.controls.FocusControl; import prefuse.controls.HoverActionControl; import prefuse.controls.PanControl; import prefuse.controls.WheelZoomControl; import prefuse.data.Graph; import prefuse.data.Node; import prefuse.data.Tuple; import prefuse.data.event.TupleSetListener; import prefuse.data.search.PrefixSearchTupleSet; import prefuse.data.search.SearchTupleSet; import prefuse.data.tuple.DefaultTupleSet; import prefuse.data.tuple.TupleSet; import prefuse.render.AbstractShapeRenderer; import prefuse.render.DefaultRendererFactory; import prefuse.render.LabelRenderer; import prefuse.util.ColorLib; import prefuse.util.FontLib; import prefuse.visual.VisualItem; import prefuse.visual.expression.InGroupPredicate; import prefuse.visual.sort.TreeDepthItemSorter; /** * Demonstration of a node-link tree viewer * * @version 1.0 * @author <a href="http://jheer.org">jeffrey heer</a> */ public class GraphJDepend extends Display { private RelationFilter filter = new RelationFilter(); public static final String tree = "tree"; public static final String treeNodes = "tree.nodes"; public static final String treeEdges = "tree.edges"; public static final String linear = "linear"; private LabelRenderer m_nodeRenderer; private JDependEdgeRenderer m_edgeRenderer; private String label; private MapData mapData; private GraphPanel graphPanel; private GraphPrinter printer; private HideVisualItemMgr hideVisualItemMgr; private LayoutMgr layoutMgr; public GraphJDepend(GraphPanel graphPanel, Collection<Relation> relations) { super(new Visualization()); this.graphPanel = graphPanel; mapData = new MapData(this.graphPanel.getGroup(), this.graphPanel.getCommand(), relations); hideVisualItemMgr = new HideVisualItemMgr(this); Graph g = CovertorUtil.getGraph(mapData); layoutMgr = new LayoutMgr(this); this.label = "label"; // initialize the display // setSize(1950, 1750); this.setSize(g); // -- set up visualization -- m_vis.add(tree, g); // m_vis.setInteractive(treeEdges, null, false); // -- set up renderers -- m_nodeRenderer = new LabelRenderer(label); m_nodeRenderer.setRenderType(AbstractShapeRenderer.RENDER_TYPE_FILL); m_nodeRenderer.setHorizontalAlignment(Constants.CENTER); m_nodeRenderer.setRoundedCorner(8, 8); if (this.filter.isRelationIntensity()) m_edgeRenderer = new JDependEdgeRenderer("weight"); else m_edgeRenderer = new JDependEdgeRenderer(); DefaultRendererFactory rf = new DefaultRendererFactory(m_nodeRenderer); rf.add(new InGroupPredicate(treeEdges), m_edgeRenderer); m_vis.setRendererFactory(rf); // -- set up processing actions -- // colors ItemAction nodeColor = new NodeColorAction(treeNodes); ItemAction textColor = new TextColorAction(treeNodes); m_vis.putAction("textColor", textColor); ItemAction edgeColor = new EdgeColorAction(treeEdges); FontAction fonts = null; if (this.filter.isElementIntensity()) fonts = new JDepnedFontAction(treeNodes, "Tahoma", "size"); else fonts = new FontAction(treeNodes, FontLib.getFont("Tahoma", 12)); fonts.add("ingroup('_focus_')", FontLib.getFont("Tahoma", 13)); // recolor ActionList recolor = new ActionList(); recolor.add(nodeColor); recolor.add(textColor); recolor.add(edgeColor); m_vis.putAction("recolor", recolor); // repaint ActionList repaint = new ActionList(); repaint.add(recolor); repaint.add(new RepaintAction()); m_vis.putAction("repaint", repaint); // animate paint change ActionList animatePaint = new ActionList(400); animatePaint.add(new ColorAnimator(treeNodes)); animatePaint.add(new RepaintAction()); m_vis.putAction("animatePaint", animatePaint); // create the tree layout action Layout layout = this.layoutMgr.getCurrentLayout().getLayout(); // treeLayout.setAngularBounds(Math.PI/2, Math.PI * 2); layout.setLayoutBounds(new Rectangle(this.getWidth(), this.getHeight())); m_vis.putAction("treeLayout", layout); CollapsedSubtreeLayout subLayout = new CollapsedSubtreeLayout(tree); m_vis.putAction("subLayout", subLayout); // create the filtering and layout ActionList filter = new ActionList(); filter.add(new TreeRootAction(tree)); filter.add(fonts); filter.add(layout); filter.add(subLayout); filter.add(textColor); filter.add(nodeColor); filter.add(edgeColor); m_vis.putAction("filter", filter); // animated transition ActionList animate = new ActionList(1250); animate.setPacingFunction(new SlowInSlowOutPacer()); animate.add(new QualityControlAnimator()); animate.add(new VisibilityAnimator(tree)); animate.add(new PolarLocationAnimator(treeNodes, linear)); animate.add(new ColorAnimator(treeNodes)); animate.add(new RepaintAction()); m_vis.putAction("animate", animate); m_vis.alwaysRunAfter("filter", "animate"); // ------------------------------------------------ setItemSorter(new TreeDepthItemSorter()); addControlListener(new JDependDragControl(this)); // addControlListener(new ZoomToFitControl()); addControlListener(new WheelZoomControl()); addControlListener(new PanControl()); if (this.filter.isMoveCenter()) { addControlListener(new FocusControl(1, "filter")); } addControlListener(new HoverActionControl("repaint")); addControlListener(new JDependHighlightControl()); addControlListener(new JDependEdgeControl(this)); addControlListener(new JDependNodeControl(this)); addControlListener(new MainMenuControl(this)); // ------------------------------------------------ // filter graph and perform layout m_vis.run("filter"); // maintain a set of items that should be interpolated linearly // this isn't absolutely necessary, but makes the animations nicer // the PolarLocationAnimator should read this set and act accordingly m_vis.addFocusGroup(linear, new DefaultTupleSet()); m_vis.getGroup(Visualization.FOCUS_ITEMS).addTupleSetListener( new TupleSetListener() { public void tupleSetChanged(TupleSet t, Tuple[] add, Tuple[] rem) { TupleSet linearInterp = m_vis.getGroup(linear); if (add.length < 1) return; linearInterp.clear(); for (Node n = (Node) add[0]; n != null; n = n .getParent()) linearInterp.addTuple(n); } }); SearchTupleSet search = createSearchTupleSet(); m_vis.addFocusGroup(Visualization.SEARCH_ITEMS, search); search.addTupleSetListener(new TupleSetListener() { public void tupleSetChanged(TupleSet t, Tuple[] add, Tuple[] rem) { m_vis.cancel("animatePaint"); m_vis.run("recolor"); m_vis.run("animatePaint"); } }); } private SearchTupleSet createSearchTupleSet() { PrefixSearchTupleSet search = new PrefixSearchTupleSet() { @Override public void index(Tuple t, String field) { try { String s; if (field == null || t == null || (s = t.getString(field)) == null) return; Method m = Class.forName( "prefuse.data.search.PrefixSearchTupleSet") .getDeclaredMethod("addString", String.class, Tuple.class); m.setAccessible(true); List<String> splits = new ArrayList<String>(); splits.add("\\."); splits.add("-"); for (String split : splits) { StringTokenizer st = new StringTokenizer(s, split); while (st.hasMoreTokens()) { String tok = st.nextToken(); m.invoke(this, tok, t); } } } catch (Exception e) { e.printStackTrace(); } } }; return search; } private void setSize(Graph g) { int nodes = g.getNodes().getTupleCount(); int minWidth = 600; int minHeight = 450; int maxWidth = 2175; int maxHeight = 1570; if (nodes * 70 < minWidth) { this.setSize(minWidth, minHeight); } else if (nodes * 70 > maxWidth) { this.setSize(maxWidth, maxHeight); } else { this.setSize(nodes * 70, nodes * 55); } } public String getLabel() { return label; } public Element getTheElement(String name) { return this.mapData.getTheElement(name); } public Collection<Relation> getRelations() { return this.mapData.getRelations(); } public GraphPrinter getPrinter() { return printer; } public void setPrinter(GraphPrinter printer) { this.printer = printer; } public GraphPanel getGraphPanel() { return graphPanel; } public HideVisualItemMgr getHideVisualItemMgr() { return hideVisualItemMgr; } public LayoutMgr getLayoutMgr() { return layoutMgr; } public MapData getMapData() { return mapData; } // ------------------------------------------------------------------------ /** * Switch the root of the tree by requesting a new spanning tree at the * desired root */ public static class TreeRootAction extends GroupAction { public TreeRootAction(String graphGroup) { super(graphGroup); } public void run(double frac) { TupleSet focus = m_vis.getGroup(Visualization.FOCUS_ITEMS); if (focus == null || focus.getTupleCount() == 0) return; Graph g = (Graph) m_vis.getGroup(m_group); Node f = null; Iterator tuples = focus.tuples(); while (tuples.hasNext() && !g.containsTuple(f = (Node) tuples.next())) { f = null; } if (f == null) return; g.getSpanningTree(f); } } /** * Set node fill colors */ public static class NodeColorAction extends ColorAction { public NodeColorAction(String group) { super(group, VisualItem.FILLCOLOR, ColorLib.rgba(255, 255, 255, 0)); add(VisualItem.HOVER, ColorLib.rgb(255, 200, 125)); add("ingroup('_search_')", ColorLib.rgb(255, 190, 190)); add("ingroup('_focus_')", ColorLib.rgb(198, 229, 229)); add(VisualItem.HIGHLIGHT, ColorLib.rgb(255, 200, 125)); add("CaColor", ColorLib.rgb(0, 255, 0)); add("CeColor", ColorLib.rgb(137, 0, 255)); add("mutualColor", ColorLib.rgb(140, 140, 140)); } } // end of inner class NodeColorAction /** * Set node text colors */ public static class TextColorAction extends ColorAction { public TextColorAction(String group) { super(group, VisualItem.TEXTCOLOR, ColorLib.gray(0)); add(VisualItem.HOVER, ColorLib.rgb(255, 0, 0)); } @Override public int getColor(VisualItem item) { Object o = lookup(item); if (o != null) { if (o instanceof ColorAction) { return ((ColorAction) o).getColor(item); } else if (o instanceof Integer) { return ((Integer) o).intValue(); } else { Logger.getLogger(this.getClass().getName()).warning( "Unrecognized Object from predicate chain."); } } // 处理外部分析单元颜色 if (item.getTable().getBoolean(item.getRow(), "isInner")) { return this.getDefaultColor(); } else { return ColorLib.gray(150); } } } // end of inner class TextColorAction /** * Set edge colors */ public static class EdgeColorAction extends ColorAction { public EdgeColorAction(String group) { super(group, VisualItem.STROKECOLOR, ColorLib.rgb(100, 200, 200)); add(VisualItem.HOVER, ColorLib.rgb(255, 200, 125)); add(VisualItem.HIGHLIGHT, ColorLib.rgb(255, 200, 125)); add("CaColor", ColorLib.rgb(0, 255, 0)); add("CeColor", ColorLib.rgb(137, 0, 255)); add("mutualColor", ColorLib.rgb(140, 140, 140)); } @Override public int getColor(VisualItem item) { Object o = lookup(item); if (o != null) { if (o instanceof ColorAction) { return ((ColorAction) o).getColor(item); } else if (o instanceof Integer) { return ((Integer) o).intValue(); } else { Logger.getLogger(this.getClass().getName()).warning( "Unrecognized Object from predicate chain."); } } // 处理关注类型 String attentionType = item.getTable().getString(item.getRow(), "attentionType"); if (attentionType.equals(Relation.ComponentLayerAttentionType)) { return ColorLib.rgba(255, 0, 0, 255); } else if (attentionType.equals(Relation.MutualDependAttentionType)) { return ColorLib.rgba(255, 0, 0, 120); } else if (attentionType.equals(Relation.SDPAttentionType)) { return ColorLib.rgba(255, 0, 0, 80); } else if (attentionType.equals(Relation.CycleDependAttentionType)) { return ColorLib.rgba(255, 0, 0, 40); } else { return this.getDefaultColor(); } } } // end of inner class TextColorAction } // end of class RadialGraphView