Mercurial > hg > heapstats
view analyzer/fx/src/main/java/jp/co/ntt/oss/heapstats/fx/plugin/builtin/snapshot/tabs/RefTreeController.java @ 272:dd85c1cbc8c8
Bug 3752: Migrate to OpenJFX 13
Reviewed-by: ykubota
https://github.com/HeapStats/heapstats/pull/144
author | Yasumasa Suenaga <yasuenag@gmail.com> |
---|---|
date | Fri, 27 Sep 2019 14:47:03 +0900 |
parents | |
children |
line wrap: on
line source
/* * Copyright (C) 2014-2019 Yasumasa Suenaga * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.tabs; import javafx.scene.layout.VBox; import jp.co.ntt.oss.heapstats.snapshot.ReferenceTracker; import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxGeometry; import com.mxgraph.model.mxICell; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.swing.util.mxGraphTransferable; import java.awt.Point; import java.awt.datatransfer.DataFlavor; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.OptionalInt; import java.util.ResourceBundle; import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.property.LongProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleLongProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.embed.swing.SwingNode; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.ButtonType; import javafx.scene.control.CheckBox; import javafx.scene.control.Label; import javafx.scene.control.RadioButton; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import jp.co.ntt.oss.heapstats.container.snapshot.ObjectData; import jp.co.ntt.oss.heapstats.container.snapshot.SnapShotHeader; import jp.co.ntt.oss.heapstats.fx.plugin.builtin.snapshot.SnapShotHeaderConverter; import jp.co.ntt.oss.heapstats.api.utils.HeapStatsUtils; /** * FXML Controller class of Reference Data tab. * * @author Yasumasa Suenaga */ public class RefTreeController implements Initializable, MouseListener { /** * Reference to the region for displaying the object. */ private ReferenceGraph graph; /** * Reference to the enclosing graph component. */ private mxGraphComponent graphComponent; @FXML private VBox topVBox; @FXML private Label snapshotLabel; @FXML private RadioButton radioParent; @FXML private RadioButton radioSize; @FXML private CheckBox rankCheckBox; @FXML private SwingNode graphNode; private ObjectProperty<SnapShotHeader> currentSnapShotHeader; private LongProperty currentObjectTag; private ResourceBundle resource; private void buildTab() { if ((currentSnapShotHeader.get() != null) && currentSnapShotHeader.get().hasReferenceData() && (currentObjectTag.get() != -1)){ SwingUtilities.invokeLater(() -> initializeSwingNode()); } else{ /* * Initialize SwingNode with dummy mxGraphComponent. * SwingNode seems to hook focus ungrab event. * If SwingNode is empty, IllegalArgumentException with "null source" is * thrown when another stage (e.g. about dialog) is shown. * To avoid this, dummy mxGraphComponent is set to SwingNode at first. */ SwingUtilities.invokeLater(() -> graphNode.setContent(new mxGraphComponent(new ReferenceGraph()))); } } /** * Initialize method for SwingNode. This method is called by Swing Event * Dispatcher Thread. */ private void initializeSwingNode() { graph = new ReferenceGraph(); graph.setCellsEditable(false); graph.getModel().beginUpdate(); { ObjectData data = currentSnapShotHeader.get().getSnapShot(HeapStatsUtils.getReplaceClassName()).get(currentObjectTag.get()); if (data == null) { throw new IllegalStateException(resource.getString("reftree.message.notselect")); } ReferenceCell cell = new ReferenceCell(data, true, false); graph.addCell(cell); } graph.getModel().endUpdate(); graph.setEdgeStyle(); graphComponent = new mxGraphComponent(graph); graphComponent.getGraphControl().addMouseListener(this); graphComponent.setToolTips(true); graphNode.setContent(graphComponent); /* * FIXME! * SwingNode is not shown immediately. * So I call repaint() method at last and request layout() call. */ graphComponent.repaint(); Platform.runLater(() -> topVBox.layout()); } /** * Initializes the controller class. */ @Override public void initialize(URL url, ResourceBundle rb) { resource = rb; ReferenceCell.initialize(); currentSnapShotHeader = new SimpleObjectProperty<>(); currentObjectTag = new SimpleLongProperty(); currentObjectTag.addListener((v, o, n) -> Optional.ofNullable(n).ifPresent(t -> buildTab())); snapshotLabel.textProperty().bind(Bindings.createStringBinding(() -> (new SnapShotHeaderConverter()).toString(currentSnapShotHeader.get()), currentSnapShotHeader)); /* * Initialize SwingNode with dummy mxGraphComponent. * SwingNode seems to hook focus ungrab event. * If SwingNode is empty, IllegalArgumentException with "null source" is * thrown when another stage (e.g. about dialog) is shown. * To avoid this, dummy mxGraphComponent is set to SwingNode at first. */ SwingUtilities.invokeLater(() -> { mxGraphComponent dummyGraphComponent = new mxGraphComponent(new ReferenceGraph()); graphNode.setContent(dummyGraphComponent); }); } /** * Add reference cells to graph. This method creates cell which represents * child, and connects to it from parent. * * @param parentCell Parent cell * @param child Child data. */ private void addReferenceCell(ReferenceCell parentCell, ObjectData objData) { ReferenceCell cell = null; mxCell tmp = (mxCell) parentCell.getParent(); for (int i = 0; i < tmp.getChildCount(); i++) { mxICell target = tmp.getChildAt(i); if (target.isVertex() && (((ReferenceCell) target).getTag() == objData.getTag())) { cell = (ReferenceCell) target; } } ReferenceCell edge = new ReferenceCell(objData, false, true); if (cell == null) { cell = new ReferenceCell(objData, false, false); graph.addCell(cell); } long size = objData.getTotalSize() / 1024; // KB edge.setValue((size == 0) ? objData.getCount() + "\n< 1" : objData.getCount() + "\n" + objData.getTotalSize() / 1024); // KB graph.addEdge(edge, graph.getDefaultParent(), parentCell, cell, 1); } /** * Displayed on the graph to get a child or of a cell object you clicked, * the information of the parent. * * @param parentCell Cell object you clicked */ private void drawMind(ReferenceCell parentCell) { if ((parentCell.getEdgeCount() > 1) || (parentCell.isRoot() && (parentCell.getEdgeCount() > 0)) || (parentCell.isEdge())) { return; } OptionalInt rankLevel = rankCheckBox.isSelected() ? OptionalInt.of(HeapStatsUtils.getRankLevel()) : OptionalInt.empty(); ReferenceTracker refTracker = new ReferenceTracker(currentSnapShotHeader.get().getSnapShot(HeapStatsUtils.getReplaceClassName()), rankLevel, Optional.empty()); List<ObjectData> objectList = radioParent.isSelected() ? refTracker.getParents(parentCell.getTag(), radioSize.isSelected()) : refTracker.getChildren(parentCell.getTag(), radioSize.isSelected()); if (objectList.isEmpty()) { return; } graph.getModel().beginUpdate(); { objectList.forEach(o -> addReferenceCell(parentCell, o)); if (mxGraphTransferable.dataFlavor == null) { try { mxGraphTransferable.dataFlavor = new DataFlavor( DataFlavor.javaJVMLocalObjectMimeType + "; class=com.mxgraph.swing.util.mxGraphTransferable", null, new mxGraphTransferable(null, null).getClass().getClassLoader()); } catch (ClassNotFoundException e) { // do nothing } } mxHierarchicalLayout layout = new mxHierarchicalLayout(graph, SwingConstants.WEST); layout.execute(graph.getDefaultParent()); } graph.getModel().endUpdate(); } @Override public void mouseClicked(MouseEvent e) { Object cell = graphComponent.getCellAt(e.getX(), e.getY()); if ((e.getButton() == MouseEvent.BUTTON1) && (cell != null) && (cell instanceof ReferenceCell)) { drawMind((ReferenceCell) cell); } graphComponent.repaint(); Platform.runLater(() -> topVBox.layout()); } @Override public void mousePressed(MouseEvent e) { /* Nothing to do */ } @Override public void mouseReleased(MouseEvent e) { /* Nothing to do */ } @Override public void mouseEntered(MouseEvent e) { /* Nothing to do */ } @Override public void mouseExited(MouseEvent e) { /* Nothing to do */ } /** * Event handler of OK button. * * @param event ActionEvent of this event. */ @FXML private void onOkClick(ActionEvent event) { if ((currentSnapShotHeader.get() == null) || (currentObjectTag.get() == -1)){ return; } else if (!currentSnapShotHeader.get().hasReferenceData()) { // Version 1.0.x or "collect_reftree=false" do not have reftree data Alert dialog = new Alert(AlertType.ERROR, resource.getString("reftree.nodata"), ButtonType.OK); dialog.show(); return; } mxCell cell = (mxCell) graph.getDefaultParent(); List<Object> removeCells = new ArrayList<>(); for (int i = 0; i < cell.getChildCount(); i++) { mxCell removeCell = (mxCell) cell.getChildAt(i); if (removeCell.isEdge() || !((ReferenceCell) removeCell).isRoot()) { removeCells.add(removeCell); } else { graph.removeSelectionCell(removeCell); mxGeometry geometry = ((ReferenceCell) removeCell).getGeometry(); geometry.setX(0); geometry.setY(0); ((ReferenceCell) removeCell).setGeometry(geometry); } } graph.removeCells(removeCells.toArray()); graph.refresh(); graphComponent.refresh(); graphComponent.getViewport().setViewPosition(new Point(0, 0)); } /** * Get property of current SnapShotHeader. * * @return Property of currentSnapShotHeader. */ public ObjectProperty<SnapShotHeader> currentSnapShotHeaderProperty() { return currentSnapShotHeader; } /** * Get property of current Object tag. * * @return Property of currentObjectTag. */ public LongProperty currentObjectTagProperty() { return currentObjectTag; } }