1 /*
2  * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  *
23  */
24 package com.sun.hotspot.igv.view;
25 
26 import com.sun.hotspot.igv.data.ChangedEvent;
27 import com.sun.hotspot.igv.data.ChangedListener;
28 import com.sun.hotspot.igv.data.InputNode;
29 import com.sun.hotspot.igv.data.Properties;
30 import com.sun.hotspot.igv.data.Properties.PropertyMatcher;
31 import com.sun.hotspot.igv.data.services.InputGraphProvider;
32 import com.sun.hotspot.igv.filter.FilterChain;
33 import com.sun.hotspot.igv.filter.FilterChainProvider;
34 import com.sun.hotspot.igv.graph.Diagram;
35 import com.sun.hotspot.igv.graph.Figure;
36 import com.sun.hotspot.igv.graph.services.DiagramProvider;
37 import com.sun.hotspot.igv.svg.BatikSVG;
38 import com.sun.hotspot.igv.util.LookupHistory;
39 import com.sun.hotspot.igv.util.RangeSlider;
40 import com.sun.hotspot.igv.view.actions.*;
41 import java.awt.*;
42 import java.awt.event.HierarchyBoundsListener;
43 import java.awt.event.HierarchyEvent;
44 import java.awt.event.KeyEvent;
45 import java.awt.event.KeyListener;
46 import java.beans.PropertyChangeEvent;
47 import java.beans.PropertyChangeListener;
48 import java.io.*;
49 import java.util.List;
50 import java.util.*;
51 import javax.swing.*;
52 import javax.swing.border.Border;
53 import org.openide.DialogDisplayer;
54 import org.openide.NotifyDescriptor;
55 import org.openide.actions.RedoAction;
56 import org.openide.actions.UndoAction;
57 import org.openide.awt.Toolbar;
58 import org.openide.awt.ToolbarPool;
59 import org.openide.awt.UndoRedo;
60 import org.openide.util.Lookup;
61 import org.openide.util.NbBundle;
62 import org.openide.util.Utilities;
63 import org.openide.util.actions.Presenter;
64 import org.openide.util.lookup.AbstractLookup;
65 import org.openide.util.lookup.InstanceContent;
66 import org.openide.util.lookup.ProxyLookup;
67 import org.openide.windows.Mode;
68 import org.openide.windows.TopComponent;
69 import org.openide.windows.WindowManager;
70 
71 /**
72  *
73  * @author Thomas Wuerthinger
74  */
75 public final class EditorTopComponent extends TopComponent implements PropertyChangeListener {
76 
77     private DiagramViewer scene;
78     private InstanceContent content;
79     private InstanceContent graphContent;
80     private EnableBlockLayoutAction blockLayoutAction;
81     private OverviewAction overviewAction;
82     private HideDuplicatesAction hideDuplicatesAction;
83     private PredSuccAction predSuccAction;
84     private SelectionModeAction selectionModeAction;
85     private PanModeAction panModeAction;
86     private boolean notFirstTime;
87     private JComponent satelliteComponent;
88     private JPanel centerPanel;
89     private CardLayout cardLayout;
90     private RangeSlider rangeSlider;
91     private JToggleButton overviewButton;
92     private JToggleButton hideDuplicatesButton;
93     private static final String PREFERRED_ID = "EditorTopComponent";
94     private static final String SATELLITE_STRING = "satellite";
95     private static final String SCENE_STRING = "scene";
96     private DiagramViewModel rangeSliderModel;
97     private ExportCookie exportCookie = new ExportCookie() {
98 
99         @Override
100         public void export(File f) {
101 
102             Graphics2D svgGenerator = BatikSVG.createGraphicsObject();
103 
104             if (svgGenerator == null) {
105                 NotifyDescriptor message = new NotifyDescriptor.Message("For export to SVG files the Batik SVG Toolkit must be intalled.", NotifyDescriptor.ERROR_MESSAGE);
106                 DialogDisplayer.getDefault().notifyLater(message);
107             } else {
108                 scene.paint(svgGenerator);
109                 FileOutputStream os = null;
110                 try {
111                     os = new FileOutputStream(f);
112                     Writer out = new OutputStreamWriter(os, "UTF-8");
113                     BatikSVG.printToStream(svgGenerator, out, true);
114                 } catch (FileNotFoundException e) {
115                     NotifyDescriptor message = new NotifyDescriptor.Message("For export to SVG files the Batik SVG Toolkit must be intalled.", NotifyDescriptor.ERROR_MESSAGE);
116                     DialogDisplayer.getDefault().notifyLater(message);
117 
118                 } catch (UnsupportedEncodingException e) {
119                 } finally {
120                     if (os != null) {
121                         try {
122                             os.close();
123                         } catch (IOException e) {
124                         }
125                     }
126                 }
127 
128             }
129         }
130     };
131 
132     private DiagramProvider diagramProvider = new DiagramProvider() {
133 
134         @Override
135         public Diagram getDiagram() {
136             return getModel().getDiagramToView();
137         }
138 
139         @Override
140         public ChangedEvent<DiagramProvider> getChangedEvent() {
141             return diagramChangedEvent;
142         }
143     };
144 
145     private ChangedEvent<DiagramProvider> diagramChangedEvent = new ChangedEvent<>(diagramProvider);
146 
147 
updateDisplayName()148     private void updateDisplayName() {
149         setDisplayName(getDiagram().getName());
150         setToolTipText(getDiagram().getGraph().getGroup().getName());
151     }
152 
EditorTopComponent(Diagram diagram)153     public EditorTopComponent(Diagram diagram) {
154 
155         LookupHistory.init(InputGraphProvider.class);
156         LookupHistory.init(DiagramProvider.class);
157         this.setFocusable(true);
158         FilterChain filterChain = null;
159         FilterChain sequence = null;
160         FilterChainProvider provider = Lookup.getDefault().lookup(FilterChainProvider.class);
161         if (provider == null) {
162             filterChain = new FilterChain();
163             sequence = new FilterChain();
164         } else {
165             filterChain = provider.getFilterChain();
166             sequence = provider.getSequence();
167         }
168 
169         setName(NbBundle.getMessage(EditorTopComponent.class, "CTL_EditorTopComponent"));
170         setToolTipText(NbBundle.getMessage(EditorTopComponent.class, "HINT_EditorTopComponent"));
171 
172         Action[] actions = new Action[]{
173             PrevDiagramAction.get(PrevDiagramAction.class),
174             NextDiagramAction.get(NextDiagramAction.class),
175             null,
176             ExtractAction.get(ExtractAction.class),
177             ShowAllAction.get(HideAction.class),
178             ShowAllAction.get(ShowAllAction.class),
179             null,
180             ZoomInAction.get(ZoomInAction.class),
181             ZoomOutAction.get(ZoomOutAction.class),
182         };
183 
184 
185         Action[] actionsWithSelection = new Action[]{
186             ExtractAction.get(ExtractAction.class),
187             ShowAllAction.get(HideAction.class),
188             null,
189             ExpandPredecessorsAction.get(ExpandPredecessorsAction.class),
190             ExpandSuccessorsAction.get(ExpandSuccessorsAction.class)
191         };
192 
193         initComponents();
194 
195         ToolbarPool.getDefault().setPreferredIconSize(16);
196         Toolbar toolBar = new Toolbar();
197         Border b = (Border) UIManager.get("Nb.Editor.Toolbar.border"); //NOI18N
198         toolBar.setBorder(b);
199         JPanel container = new JPanel();
200         this.add(container, BorderLayout.NORTH);
201         container.setLayout(new BorderLayout());
202         container.add(BorderLayout.NORTH, toolBar);
203 
204         rangeSliderModel = new DiagramViewModel(diagram.getGraph().getGroup(), filterChain, sequence);
205         rangeSlider = new RangeSlider();
206         rangeSlider.setModel(rangeSliderModel);
207         JScrollPane pane = new JScrollPane(rangeSlider, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
208         container.add(BorderLayout.CENTER, pane);
209 
210         scene = new DiagramScene(actions, actionsWithSelection, rangeSliderModel);
211         content = new InstanceContent();
212         graphContent = new InstanceContent();
213         this.associateLookup(new ProxyLookup(new Lookup[]{scene.getLookup(), new AbstractLookup(graphContent), new AbstractLookup(content)}));
214         content.add(exportCookie);
215         content.add(rangeSliderModel);
216         content.add(diagramProvider);
217 
218         rangeSliderModel.getDiagramChangedEvent().addListener(diagramChangedListener);
219         rangeSliderModel.selectGraph(diagram.getGraph());
220         rangeSliderModel.getViewPropertiesChangedEvent().addListener(new ChangedListener<DiagramViewModel>() {
221                 @Override
222                 public void changed(DiagramViewModel source) {
223                     hideDuplicatesButton.setSelected(getModel().getHideDuplicates());
224                     hideDuplicatesAction.setState(getModel().getHideDuplicates());
225                 }
226             });
227 
228 
229         toolBar.add(NextDiagramAction.get(NextDiagramAction.class));
230         toolBar.add(PrevDiagramAction.get(PrevDiagramAction.class));
231         toolBar.addSeparator();
232         toolBar.add(ExtractAction.get(ExtractAction.class));
233         toolBar.add(ShowAllAction.get(HideAction.class));
234         toolBar.add(ShowAllAction.get(ShowAllAction.class));
235         toolBar.addSeparator();
236         toolBar.add(ShowAllAction.get(ZoomInAction.class));
237         toolBar.add(ShowAllAction.get(ZoomOutAction.class));
238 
239         blockLayoutAction = new EnableBlockLayoutAction();
240         JToggleButton button = new JToggleButton(blockLayoutAction);
241         button.setSelected(false);
242         toolBar.add(button);
243         blockLayoutAction.addPropertyChangeListener(this);
244 
245         overviewAction = new OverviewAction();
246         overviewButton = new JToggleButton(overviewAction);
247         overviewButton.setSelected(false);
248         toolBar.add(overviewButton);
249         overviewAction.addPropertyChangeListener(this);
250 
251         predSuccAction = new PredSuccAction();
252         button = new JToggleButton(predSuccAction);
253         button.setSelected(true);
254         toolBar.add(button);
255         predSuccAction.addPropertyChangeListener(this);
256 
257         hideDuplicatesAction = new HideDuplicatesAction();
258         hideDuplicatesButton = new JToggleButton(hideDuplicatesAction);
259         hideDuplicatesButton.setSelected(false);
260         toolBar.add(hideDuplicatesButton);
261         hideDuplicatesAction.addPropertyChangeListener(this);
262 
263         toolBar.addSeparator();
264         toolBar.add(UndoAction.get(UndoAction.class));
265         toolBar.add(RedoAction.get(RedoAction.class));
266 
267         toolBar.addSeparator();
268         ButtonGroup interactionButtons = new ButtonGroup();
269 
270         panModeAction = new PanModeAction();
271         panModeAction.setSelected(true);
272         button = new JToggleButton(panModeAction);
273         button.setSelected(true);
274         interactionButtons.add(button);
275         toolBar.add(button);
276         panModeAction.addPropertyChangeListener(this);
277 
278         selectionModeAction = new SelectionModeAction();
279         button = new JToggleButton(selectionModeAction);
280         interactionButtons.add(button);
281         toolBar.add(button);
282         selectionModeAction.addPropertyChangeListener(this);
283 
284         toolBar.add(Box.createHorizontalGlue());
285         Action action = Utilities.actionsForPath("QuickSearchShadow").get(0);
286         Component quicksearch = ((Presenter.Toolbar) action).getToolbarPresenter();
287         try {
288             // (aw) workaround for disappearing search bar due to reparenting one shared component instance.
289             quicksearch = (Component) quicksearch.getClass().getConstructor(KeyStroke.class).newInstance(new Object[]{null});
290         } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) {
291         }
292         Dimension preferredSize = quicksearch.getPreferredSize();
293         preferredSize = new Dimension((int) preferredSize.getWidth() * 2, (int) preferredSize.getHeight());
294         quicksearch.setMinimumSize(preferredSize); // necessary for GTK LAF
295         quicksearch.setPreferredSize(preferredSize);
296         toolBar.add(quicksearch);
297 
298         centerPanel = new JPanel();
299         this.add(centerPanel, BorderLayout.CENTER);
300         cardLayout = new CardLayout();
301         centerPanel.setLayout(cardLayout);
302         centerPanel.add(SCENE_STRING, scene.getComponent());
303         centerPanel.setBackground(Color.WHITE);
304         satelliteComponent = scene.createSatelliteView();
305         satelliteComponent.setSize(200, 200);
306         centerPanel.add(SATELLITE_STRING, satelliteComponent);
307 
308         // TODO: Fix the hot key for entering the satellite view
309         this.addKeyListener(keyListener);
310 
311         scene.getComponent().addHierarchyBoundsListener(new HierarchyBoundsListener() {
312 
313             @Override
314             public void ancestorMoved(HierarchyEvent e) {
315             }
316 
317             @Override
318             public void ancestorResized(HierarchyEvent e) {
319                 if (!notFirstTime && scene.getComponent().getBounds().width > 0) {
320                     notFirstTime = true;
321                     SwingUtilities.invokeLater(new Runnable() {
322 
323                         @Override
324                         public void run() {
325                             EditorTopComponent.this.scene.initialize();
326                         }
327                     });
328                 }
329             }
330         });
331 
332         if (diagram.getGraph().getGroup().getGraphsCount() == 1) {
333             rangeSlider.setVisible(false);
334         }
335 
336         updateDisplayName();
337     }
338     private KeyListener keyListener = new KeyListener() {
339 
340         @Override
341         public void keyTyped(KeyEvent e) {
342         }
343 
344         @Override
345         public void keyPressed(KeyEvent e) {
346             if (e.getKeyCode() == KeyEvent.VK_S) {
347                 EditorTopComponent.this.overviewButton.setSelected(true);
348                 EditorTopComponent.this.overviewAction.setState(true);
349             }
350         }
351 
352         @Override
353         public void keyReleased(KeyEvent e) {
354             if (e.getKeyCode() == KeyEvent.VK_S) {
355                 EditorTopComponent.this.overviewButton.setSelected(false);
356                 EditorTopComponent.this.overviewAction.setState(false);
357             }
358         }
359     };
360 
getDiagramModel()361     public DiagramViewModel getDiagramModel() {
362         return rangeSliderModel;
363     }
364 
showSatellite()365     private void showSatellite() {
366         cardLayout.show(centerPanel, SATELLITE_STRING);
367         satelliteComponent.requestFocus();
368 
369     }
370 
showScene()371     private void showScene() {
372         cardLayout.show(centerPanel, SCENE_STRING);
373         scene.getComponent().requestFocus();
374     }
375 
zoomOut()376     public void zoomOut() {
377         scene.zoomOut();
378     }
379 
zoomIn()380     public void zoomIn() {
381         scene.zoomIn();
382     }
383 
showPrevDiagram()384     public void showPrevDiagram() {
385         int fp = getModel().getFirstPosition();
386         int sp = getModel().getSecondPosition();
387         if (fp != 0) {
388             fp--;
389             sp--;
390             getModel().setPositions(fp, sp);
391         }
392     }
393 
getModel()394     public DiagramViewModel getModel() {
395         return rangeSliderModel;
396     }
397 
getFilterChain()398     public FilterChain getFilterChain() {
399         return getModel().getFilterChain();
400     }
401 
getActive()402     public static EditorTopComponent getActive() {
403         Set<? extends Mode> modes = WindowManager.getDefault().getModes();
404         for (Mode m : modes) {
405             TopComponent tc = m.getSelectedTopComponent();
406             if (tc instanceof EditorTopComponent) {
407                 return (EditorTopComponent) tc;
408             }
409         }
410         return null;
411     }
412 
413     /** This method is called from within the constructor to
414      * initialize the form.
415      * WARNING: Do NOT modify this code. The content of this method is
416      * always regenerated by the Form Editor.
417      */
418         // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
initComponents()419         private void initComponents() {
420                 jCheckBox1 = new javax.swing.JCheckBox();
421 
422                 org.openide.awt.Mnemonics.setLocalizedText(jCheckBox1, "jCheckBox1");
423                 jCheckBox1.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
424                 jCheckBox1.setMargin(new java.awt.Insets(0, 0, 0, 0));
425 
426                 setLayout(new java.awt.BorderLayout());
427 
428         }// </editor-fold>//GEN-END:initComponents
429         // Variables declaration - do not modify//GEN-BEGIN:variables
430         private javax.swing.JCheckBox jCheckBox1;
431         // End of variables declaration//GEN-END:variables
432 
433     @Override
getPersistenceType()434     public int getPersistenceType() {
435         return TopComponent.PERSISTENCE_NEVER;
436     }
437 
438     @Override
componentOpened()439     public void componentOpened() {
440     }
441 
442     @Override
componentClosed()443     public void componentClosed() {
444         rangeSliderModel.close();
445     }
446 
447     @Override
preferredID()448     protected String preferredID() {
449         return PREFERRED_ID;
450     }
451 
452     private ChangedListener<DiagramViewModel> diagramChangedListener = new ChangedListener<DiagramViewModel>() {
453 
454         @Override
455         public void changed(DiagramViewModel source) {
456             updateDisplayName();
457             Collection<Object> list = new ArrayList<>();
458             list.add(new EditorInputGraphProvider(EditorTopComponent.this));
459             graphContent.set(list, null);
460             diagramProvider.getChangedEvent().fire();
461         }
462 
463     };
464 
showPredSucc()465     public boolean showPredSucc() {
466         return (Boolean) predSuccAction.getValue(PredSuccAction.STATE);
467     }
468 
setSelection(PropertyMatcher matcher)469     public void setSelection(PropertyMatcher matcher) {
470 
471         Properties.PropertySelector<Figure> selector = new Properties.PropertySelector<>(getModel().getDiagramToView().getFigures());
472         List<Figure> list = selector.selectMultiple(matcher);
473         setSelectedFigures(list);
474     }
475 
setSelectedFigures(List<Figure> list)476     public void setSelectedFigures(List<Figure> list) {
477         scene.setSelection(list);
478         scene.centerFigures(list);
479     }
480 
setSelectedNodes(Set<InputNode> nodes)481     public void setSelectedNodes(Set<InputNode> nodes) {
482 
483         List<Figure> list = new ArrayList<>();
484         Set<Integer> ids = new HashSet<>();
485         for (InputNode n : nodes) {
486             ids.add(n.getId());
487         }
488 
489         for (Figure f : getModel().getDiagramToView().getFigures()) {
490             for (InputNode n : f.getSource().getSourceNodes()) {
491                 if (ids.contains(n.getId())) {
492                     list.add(f);
493                     break;
494                 }
495             }
496         }
497 
498         setSelectedFigures(list);
499     }
500 
501     @Override
propertyChange(PropertyChangeEvent evt)502     public void propertyChange(PropertyChangeEvent evt) {
503         if (evt.getSource() == this.predSuccAction) {
504             boolean b = (Boolean) predSuccAction.getValue(PredSuccAction.STATE);
505             this.getModel().setShowNodeHull(b);
506         } else if (evt.getSource() == this.overviewAction) {
507             boolean b = (Boolean) overviewAction.getValue(OverviewAction.STATE);
508             if (b) {
509                 showSatellite();
510             } else {
511                 showScene();
512             }
513         } else if (evt.getSource() == this.blockLayoutAction) {
514             boolean b = (Boolean) blockLayoutAction.getValue(EnableBlockLayoutAction.STATE);
515             this.getModel().setShowBlocks(b);
516         } else if (evt.getSource() == this.hideDuplicatesAction) {
517             boolean b = (Boolean) hideDuplicatesAction.getValue(HideDuplicatesAction.STATE);
518             this.getModel().setHideDuplicates(b);
519         } else if (evt.getSource() == this.selectionModeAction || evt.getSource() == this.panModeAction) {
520             if (panModeAction.isSelected()) {
521                 scene.setInteractionMode(DiagramViewer.InteractionMode.PANNING);
522             } else if (selectionModeAction.isSelected()) {
523                 scene.setInteractionMode(DiagramViewer.InteractionMode.SELECTION);
524             }
525         } else {
526             assert false : "Unknown event source";
527         }
528     }
529 
extract()530     public void extract() {
531         getModel().showOnly(getModel().getSelectedNodes());
532     }
533 
hideNodes()534     public void hideNodes() {
535         Set<Integer> selectedNodes = this.getModel().getSelectedNodes();
536         HashSet<Integer> nodes = new HashSet<>(getModel().getHiddenNodes());
537         nodes.addAll(selectedNodes);
538         this.getModel().showNot(nodes);
539     }
540 
expandPredecessors()541     public void expandPredecessors() {
542         Set<Figure> oldSelection = getModel().getSelectedFigures();
543         Set<Figure> figures = new HashSet<>();
544 
545         for (Figure f : this.getDiagramModel().getDiagramToView().getFigures()) {
546             boolean ok = false;
547             if (oldSelection.contains(f)) {
548                 ok = true;
549             } else {
550                 for (Figure pred : f.getSuccessors()) {
551                     if (oldSelection.contains(pred)) {
552                         ok = true;
553                         break;
554                     }
555                 }
556             }
557 
558             if (ok) {
559                 figures.add(f);
560             }
561         }
562 
563         getModel().showAll(figures);
564     }
565 
expandSuccessors()566     public void expandSuccessors() {
567         Set<Figure> oldSelection = getModel().getSelectedFigures();
568         Set<Figure> figures = new HashSet<>();
569 
570         for (Figure f : this.getDiagramModel().getDiagramToView().getFigures()) {
571             boolean ok = false;
572             if (oldSelection.contains(f)) {
573                 ok = true;
574             } else {
575                 for (Figure succ : f.getPredecessors()) {
576                     if (oldSelection.contains(succ)) {
577                         ok = true;
578                         break;
579                     }
580                 }
581             }
582 
583             if (ok) {
584                 figures.add(f);
585             }
586         }
587 
588         getModel().showAll(figures);
589     }
590 
showAll()591     public void showAll() {
592         getModel().showNot(new HashSet<Integer>());
593     }
594 
getDiagram()595     public Diagram getDiagram() {
596         return getDiagramModel().getDiagramToView();
597     }
598 
599     @Override
componentHidden()600     protected void componentHidden() {
601         super.componentHidden();
602         scene.componentHidden();
603 
604     }
605 
606     @Override
componentShowing()607     protected void componentShowing() {
608         super.componentShowing();
609         scene.componentShowing();
610     }
611 
612     @Override
requestActive()613     public void requestActive() {
614         super.requestActive();
615         scene.getComponent().requestFocus();
616     }
617 
618     @Override
getUndoRedo()619     public UndoRedo getUndoRedo() {
620         return scene.getUndoRedo();
621     }
622 
623     @Override
writeReplace()624     protected Object writeReplace() throws ObjectStreamException {
625         throw new NotSerializableException();
626 }
627 }
628