diff --git a/.gitignore b/.gitignore index 28321b0496eb..1433d695556d 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,7 @@ bench-results.json jmh_result.json /vm/src/installer/dist/ visualizer/IdealGraphVisualizer/*/target/ +visualizer/C1Visualizer/*/target/ /.src-rev *.interp *.tokens diff --git a/compiler/mx.compiler/mx_graal_tools.py b/compiler/mx.compiler/mx_graal_tools.py index e1c1d355100c..5c6da176f592 100644 --- a/compiler/mx.compiler/mx_graal_tools.py +++ b/compiler/mx.compiler/mx_graal_tools.py @@ -26,11 +26,11 @@ from __future__ import print_function import os -import shutil import re +import shutil import sys -from os.path import join, exists from argparse import ArgumentParser, REMAINDER +from os.path import join, exists import mx @@ -41,7 +41,7 @@ _suite = mx.suite('compiler') -def run_netbeans_app(app_name, jdkhome, args=None, dist=None): +def run_netbeans_app(app_name, jdkhome, args=None, dist=None, launch_message=None): args = [] if args is None else args if dist is None: dist = app_name.upper() + '_DIST' @@ -75,12 +75,13 @@ def run_netbeans_app(app_name, jdkhome, args=None, dist=None): if mx.get_os() == 'linux': # Mitigates X server crashes on Linux launch.append('-J-Dsun.java2d.xrender=false') - print('Consider flag -J-Dsun.java2d.uiScale=2 if on a high resolution display') - print('Consider flag -J-Xms4g -J-Xmx8g if dealing with large graphs') + if launch_message: + launch_message() mx.run(launch+args) -def igv(args): - """run the Ideal Graph Visualizer + +def netbeans_docstring(fullname, mxname): + return f"""run the {fullname} The current version is based on NetBeans 26 which officially supports JDK 17 through JDK 24. A supported JDK will be chosen from the JDKs known to mx but it will fall back to whatever is @@ -90,19 +91,21 @@ def igv(args): You can directly control which JDK is used to launch IGV using - mx igv --jdkhome /path/to/java/home + mx {mxname} --jdkhome /path/to/java/home This will completely ignore any JAVA_HOME settings in mx. - Extra NetBeans specific options can be passed as well. mx igv --help will show the + Extra NetBeans specific options can be passed as well. mx {mxname} --help will show the help for the NetBeans launcher. """ + +def launch_netbeans_app(fullname, distname, mxname, args, launch_message=None): min_version = 17 max_version = 24 min_version_spec = mx.VersionSpec(str(min_version)) next_version_spec = mx.VersionSpec(str(max_version + 1)) - def _igvJdkVersionCheck(version): + def _netbeansJdkVersionCheck(version): return min_version_spec <= version < next_version_spec jdkhome = None @@ -111,29 +114,35 @@ def _do_not_abort(msg): pass # try to find a fully supported version first - jdk = mx.get_tools_jdk(versionCheck=_igvJdkVersionCheck, versionDescription=f'IGV prefers JDK {min_version} through JDK {max_version}', abortCallback=_do_not_abort) + jdk = mx.get_tools_jdk(versionCheck=_netbeansJdkVersionCheck, versionDescription=f'{fullname} prefers JDK {min_version} through JDK {max_version}', abortCallback=_do_not_abort) if jdk is None: # try any JDK jdk = mx.get_jdk() if jdk: jdkhome = jdk.home - mx.log(f'Launching IGV with {jdkhome}') - if not _igvJdkVersionCheck(jdk.version): - mx.warn(f'{jdk.home} is not an officially supported JDK for IGV.') + mx.log(f'Launching {fullname} with {jdkhome}') + if not _netbeansJdkVersionCheck(jdk.version): + mx.warn(f'{jdk.home} is not an officially supported JDK.') mx.warn(f'If you experience any problems try to use an LTS release between JDK {min_version} and JDK {max_version} instead.') - mx.warn(f'mx help igv provides more details.') + mx.warn(f'mx help {mxname} provides more details.') - run_netbeans_app('IdealGraphVisualizer', jdkhome, args=args, dist='IDEALGRAPHVISUALIZER_DIST') + run_netbeans_app(distname, jdkhome, args=args, dist=f'{distname.upper()}_DIST', launch_message=launch_message) + +def igv(args): + def help_message(): + print('Consider flag -J-Dsun.java2d.uiScale=2 if on a high resolution display') + print('Consider flag -J-Xms4g -J-Xmx8g if dealing with large graphs') + + launch_netbeans_app('Ideal Graph Visualizer', 'IdealGraphVisualizer', 'igv', args, launch_message=help_message) + +igv.__doc__ = netbeans_docstring('Ideal Graph Visualizer', 'igv') def c1visualizer(args): - """run the C1 Compiler Visualizer""" - v8u40 = mx.VersionSpec("1.8.0_40") - v12 = mx.VersionSpec("12") - def _c1vJdkVersionCheck(version): - return v8u40 <= version < v12 - jdkhome = mx.get_jdk(_c1vJdkVersionCheck, versionDescription='(JDK that is >= 1.8.0u40 and <= 11 which can be specified via EXTRA_JAVA_HOMES or --extra-java-homes)', purpose="running C1 Visualizer").home - run_netbeans_app('C1Visualizer', jdkhome, args() if callable(args) else args) + launch_netbeans_app('C1 Visualizer', 'C1Visualizer', 'c1visualizer', args) + +c1visualizer.__doc__ = netbeans_docstring('C1 Visualizer', 'c1visualizer') + def hsdis(args, copyToDir=None): """download the hsdis library and copy it to a specific dir or to the current JDK diff --git a/compiler/mx.compiler/suite.py b/compiler/mx.compiler/suite.py index 8a2fd4b32570..d7107c1f6d44 100644 --- a/compiler/mx.compiler/suite.py +++ b/compiler/mx.compiler/suite.py @@ -58,8 +58,8 @@ }, "C1VISUALIZER_DIST" : { - "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/graal-external-deps/c1visualizer/c1visualizer-1.10.zip"], - "digest" : "sha512:40c505dd03ca0bb102f1091b89b90672126922f290bd8370eef9a7afc5d9c1e7b5db08c448a0948ef46bf57d850e166813e2d68bf7b1c88a46256d839b6b0201", + "urls" : ["https://lafo.ssw.uni-linz.ac.at/pub/c1visualizer/c1visualizer-1.13-3413409cce0.zip"], + "digest" : "sha512:176dcef9447f1760f70ec4da50b2f742e786fc3db6af9db9d699c303ecfe0e470deb3bb32120123cb93a0073f4f31cecffde2a7860edcf514dce9894d6df25c4", "packedResource": True, }, diff --git a/visualizer/C1Visualizer.md b/visualizer/C1Visualizer.md new file mode 100644 index 000000000000..79b4d22aaa08 --- /dev/null +++ b/visualizer/C1Visualizer.md @@ -0,0 +1,32 @@ +# Readme for C1Visualizer + + +# Developing the C1Visualizer + +C1Visualizer is based on Netbeans 26 but can be often be worked on with later releases or with any +IDE that supports Maven. Care must be taken not to commmit anything that would keep it from working +with 26 so any automatic changes to property file updates by later NetBeans versions shouldn't be +pushed. The packaged product is built by: +``` +cd C1Visualizer +mvn package +``` + +# Regenerating the cfg file parser + +The cfg file parser is based on CoCo/R which hasn't been updated since 2018 and doesn't support +large files. For simplicity, it has been checked in and some bugs with handling of large files were +fixed. It can be regenerated by getting Coco.jar from https://ssw.jku.at/Research/Projects/Coco and +running the following command line. Note that still will overwrite any locals fixes but this should +never be necessary. + +``` +java -jar Coco.jar -o CompilationModel/src/main/java/at/ssw/visualizer/parser \ + -package at.ssw.visualizer.parser CompilationModel/src/main/java/at/ssw/visualizer/parser/CompilerOutput.atg +``` + +# Releasing a new version + +As this is now maven based, `mvn -B release:prepare` is used to update to new versions which will automatically bump the module versions. + +The resulting zip file will be in `C1Visualizer/application/target`. diff --git a/visualizer/C1Visualizer/BlockView/pom.xml b/visualizer/C1Visualizer/BlockView/pom.xml new file mode 100644 index 000000000000..1649a167cbdf --- /dev/null +++ b/visualizer/C1Visualizer/BlockView/pom.xml @@ -0,0 +1,97 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + BlockView + 1.14-SNAPSHOT + nbm + BlockView + + UTF-8 + + + + at.ssw.visualizer + VisualizerUI + ${project.version} + + + at.ssw.visualizer + CompilationModel + ${project.version} + + + org.netbeans.api + org-openide-explorer + ${netbeans.version} + + + org.netbeans.api + org-openide-loaders + ${netbeans.version} + + + org.netbeans.api + org-openide-nodes + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + org.netbeans.api + org-openide-windows + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.BlockView + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/BlockView/src/main/java/at/ssw/visualizer/block/view/BlockTableModel.java b/visualizer/C1Visualizer/BlockView/src/main/java/at/ssw/visualizer/block/view/BlockTableModel.java new file mode 100644 index 000000000000..8dab66ee7a06 --- /dev/null +++ b/visualizer/C1Visualizer/BlockView/src/main/java/at/ssw/visualizer/block/view/BlockTableModel.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.block.view; + +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import java.util.Collections; +import java.util.List; +import javax.swing.table.AbstractTableModel; + +/** + * TableModel containing the blocks of the currently selected view + * + * @author Bernhard Stiftner + * @author Christian Wimmer + */ +public class BlockTableModel extends AbstractTableModel { + + public static final int BLOCK_TABLE_NAME_COL_IDX = 0; + public static final int BLOCK_TABLE_BCI_COL_IDX = 1; + public static final int BLOCK_TABLE_FLAGS_COL_IDX = 2; + public static final int BLOCK_TABLE_LOOP_DEPTH_COL_IDX = 3; + public static final int BLOCK_TABLE_LOOP_INDEX_COL_IDX = 4; + public static final int BLOCK_TABLE_DOMINATOR_COL_IDX = 5; + public static final int BLOCK_TABLE_PREDECESSORS_COL_IDX = 6; + public static final int BLOCK_TABLE_SUCCESSORS_COL_IDX = 7; + public static final int BLOCK_TABLE_XHANDLERS_COL_IDX = 8; + public static final int BLOCK_TABLE_PROBABILITY_COL_IDX = 9; + + public static final String[] COLUMN_NAMES = new String[]{"Name", "BCI", "Flags", "Loop Depth", "Loop Index", "Dominator", "Predecessors", "Successors", "XHandlers", "Probability"}; + public static final int[] COLUMN_WIDTHS = new int[]{60, 60, 60, 80, 80, 60, 120, 120, 120}; + + private List blocks = Collections.emptyList(); + + public void setControlFlowGraph(ControlFlowGraph cfg) { + if (cfg == null) { + blocks = Collections.emptyList(); + } else { + blocks = cfg.getBasicBlocks(); + } + fireTableDataChanged(); + } + + public int getRowCount() { + return blocks.size(); + } + + public int getColumnCount() { + return COLUMN_NAMES.length; + } + + @Override + public String getColumnName(int column) { + return COLUMN_NAMES[column]; + } + + public Object getValueAt(int row, int column) { + BasicBlock block = blocks.get(row); + switch (column) { + case BLOCK_TABLE_NAME_COL_IDX: + return block.getName(); + case BLOCK_TABLE_BCI_COL_IDX: + return "[" + block.getFromBci() + ", " + block.getToBci() + "]"; + case BLOCK_TABLE_FLAGS_COL_IDX: + return formatFlags(block.getFlags()); + case BLOCK_TABLE_LOOP_DEPTH_COL_IDX: + return Integer.toString(block.getLoopDepth()); + case BLOCK_TABLE_LOOP_INDEX_COL_IDX: + return block.getLoopDepth() > 0 ? Integer.toString(block.getLoopIndex()) : ""; + case BLOCK_TABLE_DOMINATOR_COL_IDX: + return block.getDominator() != null ? block.getDominator().getName() : ""; + case BLOCK_TABLE_PREDECESSORS_COL_IDX: + return formatBlocks(block.getPredecessors()); + case BLOCK_TABLE_SUCCESSORS_COL_IDX: + return formatBlocks(block.getSuccessors()); + case BLOCK_TABLE_XHANDLERS_COL_IDX: + return formatBlocks(block.getXhandlers()); + case BLOCK_TABLE_PROBABILITY_COL_IDX: + return Double.isNaN(block.getProbability()) ? "" : (Double)block.getProbability(); + default: + throw new Error("invalid column"); + } + } + + @Override + public Class getColumnClass(int columnIndex) { + if (blocks.isEmpty()) { + return Object.class; + } + return getValueAt(0, columnIndex).getClass(); + } + + private String formatFlags(List flags) { + StringBuilder sb = new StringBuilder(); + String prefix = ""; + for (String flag : flags) { + sb.append(prefix).append(flag); + prefix = ", "; + } + return sb.toString(); + } + + private String formatBlocks(List blocks) { + StringBuilder sb = new StringBuilder(); + String prefix = ""; + for (BasicBlock block : blocks) { + sb.append(prefix).append(block.getName()); + prefix = ", "; + } + return sb.toString(); + } +} diff --git a/visualizer/C1Visualizer/BlockView/src/main/java/at/ssw/visualizer/block/view/BlockViewTopComponent.java b/visualizer/C1Visualizer/BlockView/src/main/java/at/ssw/visualizer/block/view/BlockViewTopComponent.java new file mode 100644 index 000000000000..8e0109534fda --- /dev/null +++ b/visualizer/C1Visualizer/BlockView/src/main/java/at/ssw/visualizer/block/view/BlockViewTopComponent.java @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.block.view; + +import at.ssw.visualizer.core.selection.Selection; +import at.ssw.visualizer.core.selection.SelectionManager; +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import java.awt.BorderLayout; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.swing.BorderFactory; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.TableModel; +import javax.swing.table.TableRowSorter; +import org.openide.windows.TopComponent; +import org.openide.windows.WindowManager; + +/** + * TopComponent which displays the BlockView. + * + * @author Bernhard Stiftner + * @author Christian Wimmer + */ +final class BlockViewTopComponent extends TopComponent { + private ControlFlowGraph curCFG; + private BasicBlock[] curBlocks; + + private JTable blockTable; + private BlockTableModel tableModel; + private boolean selectionUpdating; + + private BlockViewTopComponent() { + setName("Blocks"); + setToolTipText("List of Blocks"); + + tableModel = new BlockTableModel(); + blockTable = new JTable(tableModel); + blockTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + blockTable.setRowMargin(0); + blockTable.getColumnModel().setColumnMargin(0); + blockTable.setShowGrid(false); + blockTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + for (int i = 0; i < BlockTableModel.COLUMN_WIDTHS.length; i++) { + blockTable.getColumnModel().getColumn(i).setPreferredWidth(BlockTableModel.COLUMN_WIDTHS[i]); + } + blockTable.getSelectionModel().addListSelectionListener(listSelectionListener); + + // sorting + TableRowSorter sorter = new TableRowSorter(blockTable.getModel()); + sorter.setComparator(BlockTableModel.BLOCK_TABLE_NAME_COL_IDX, new Comparator() { + + @Override + public int compare(String block1, String block2) { + if (block1.charAt(0) == 'B' && block2.charAt(0)=='B') { + try { + return Integer.parseUnsignedInt(block1.substring(1)) - Integer.parseUnsignedInt(block2.substring(1)); + } catch(NumberFormatException e) { + // fall-back to string + } + } + return block1.compareTo(block2); + } + }); + blockTable.setRowSorter(sorter); + + JScrollPane scrollPane = new JScrollPane(blockTable); + scrollPane.setViewportBorder(BorderFactory.createEmptyBorder()); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + setLayout(new BorderLayout()); + add(scrollPane); + } + + @Override + protected void componentShowing() { + super.componentShowing(); + SelectionManager.getDefault().addChangeListener(selectionChangeListener); + updateContent(); + } + + @Override + protected void componentHidden() { + super.componentHidden(); + SelectionManager.getDefault().removeChangeListener(selectionChangeListener); + selectionUpdating = true; + curCFG = null; + curBlocks = null; + tableModel.setControlFlowGraph(null); + selectionUpdating = false; + } + + + private ChangeListener selectionChangeListener = new ChangeListener() { + public void stateChanged(ChangeEvent event) { + updateContent(); + } + }; + + protected void updateContent() { + if (selectionUpdating) { + return; + } + selectionUpdating = true; + Selection selection = SelectionManager.getDefault().getCurSelection(); + ControlFlowGraph newCFG = selection.get(ControlFlowGraph.class); + BasicBlock[] newBlocks = selection.get(BasicBlock[].class); + + if (curCFG != newCFG) { + // This resets a user-defined sorting. + tableModel.setControlFlowGraph(newCFG); + curBlocks = null; + } + + if (newBlocks != null) { + if(newBlocks.length == 0) { + blockTable.clearSelection(); + } else if (!Arrays.equals(curBlocks, newBlocks)) { + Map blockNames = new HashMap(); + for (BasicBlock block : newBlocks) { + blockNames.put(block.getName(), block); + } + + blockTable.clearSelection(); + for (int i = blockTable.getModel().getRowCount() - 1; i >= 0; i--) { + BasicBlock block = blockNames.get(blockTable.getValueAt(i, 0)); + if (block != null) { + blockTable.addRowSelectionInterval(i, i); + blockTable.scrollRectToVisible(blockTable.getCellRect(i, 0, true)); + } + } + } + } + curCFG = newCFG; + curBlocks = newBlocks; + selectionUpdating = false; + } + + + private ListSelectionListener listSelectionListener = new ListSelectionListener() { + public void valueChanged(ListSelectionEvent event) { + updateSelection(); + } + }; + + private void updateSelection() { + if (selectionUpdating) { + return; + } + selectionUpdating = true; + + List blocks = new ArrayList(); + for (int i = 0; i < blockTable.getModel().getRowCount(); i++) { + if (blockTable.getSelectionModel().isSelectedIndex(i)) { + blocks.add(curCFG.getBasicBlockByName((String) blockTable.getValueAt(i, 0))); + } + } + + curBlocks = blocks.toArray(new BasicBlock[blocks.size()]); + Selection selection = SelectionManager.getDefault().getCurSelection(); + selection.put(curBlocks); + + selectionUpdating = false; + } + + // + private static final String PREFERRED_ID = "BlockViewTopComponent"; + private static BlockViewTopComponent instance; + + public static synchronized BlockViewTopComponent getDefault() { + if (instance == null) { + instance = new BlockViewTopComponent(); + } + return instance; + } + + public static synchronized BlockViewTopComponent findInstance() { + return (BlockViewTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID); + } + + @Override + public int getPersistenceType() { + return TopComponent.PERSISTENCE_ALWAYS; + } + + @Override + protected String preferredID() { + return PREFERRED_ID; + } + + @Override + public Object writeReplace() { + return new ResolvableHelper(); + } + + static final class ResolvableHelper implements Serializable { + private static final long serialVersionUID = 1L; + public Object readResolve() { + return BlockViewTopComponent.getDefault(); + } + } + // +} diff --git a/visualizer/C1Visualizer/BlockView/src/main/java/at/ssw/visualizer/block/view/ShowBlockViewAction.java b/visualizer/C1Visualizer/BlockView/src/main/java/at/ssw/visualizer/block/view/ShowBlockViewAction.java new file mode 100644 index 000000000000..053509a9a84a --- /dev/null +++ b/visualizer/C1Visualizer/BlockView/src/main/java/at/ssw/visualizer/block/view/ShowBlockViewAction.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.block.view; + +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; +import org.openide.windows.TopComponent; + +/** + * Action which shows BlockView component. + * + * @author Bernhard Stiftner + */ +public class ShowBlockViewAction extends AbstractAction { + public ShowBlockViewAction() { + super("Blocks View"); + } + + public void actionPerformed(ActionEvent event) { + TopComponent win = BlockViewTopComponent.findInstance(); + win.open(); + win.requestActive(); + } +} diff --git a/visualizer/C1Visualizer/BlockView/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/BlockView/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..e57c53fa1021 --- /dev/null +++ b/visualizer/C1Visualizer/BlockView/src/main/nbm/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.block.view +OpenIDE-Module-Layer: at/ssw/visualizer/block/view/layer.xml +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/block/view/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/BlockView/src/main/resources/at/ssw/visualizer/block/view/BlockViewTopComponentSettings.xml b/visualizer/C1Visualizer/BlockView/src/main/resources/at/ssw/visualizer/block/view/BlockViewTopComponentSettings.xml new file mode 100644 index 000000000000..e9cb6e95d844 --- /dev/null +++ b/visualizer/C1Visualizer/BlockView/src/main/resources/at/ssw/visualizer/block/view/BlockViewTopComponentSettings.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/visualizer/C1Visualizer/BlockView/src/main/resources/at/ssw/visualizer/block/view/BlockViewTopComponentWstcref.xml b/visualizer/C1Visualizer/BlockView/src/main/resources/at/ssw/visualizer/block/view/BlockViewTopComponentWstcref.xml new file mode 100644 index 000000000000..66cb2b958926 --- /dev/null +++ b/visualizer/C1Visualizer/BlockView/src/main/resources/at/ssw/visualizer/block/view/BlockViewTopComponentWstcref.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/visualizer/C1Visualizer/BlockView/src/main/resources/at/ssw/visualizer/block/view/Bundle.properties b/visualizer/C1Visualizer/BlockView/src/main/resources/at/ssw/visualizer/block/view/Bundle.properties new file mode 100644 index 000000000000..01077adde15c --- /dev/null +++ b/visualizer/C1Visualizer/BlockView/src/main/resources/at/ssw/visualizer/block/view/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=Block View diff --git a/visualizer/C1Visualizer/BlockView/src/main/resources/at/ssw/visualizer/block/view/layer.xml b/visualizer/C1Visualizer/BlockView/src/main/resources/at/ssw/visualizer/block/view/layer.xml new file mode 100644 index 000000000000..e0243e78c1f7 --- /dev/null +++ b/visualizer/C1Visualizer/BlockView/src/main/resources/at/ssw/visualizer/block/view/layer.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/BytecodeEditor/pom.xml b/visualizer/C1Visualizer/BytecodeEditor/pom.xml new file mode 100644 index 000000000000..dcb904953196 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeEditor/pom.xml @@ -0,0 +1,128 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + BytecodeEditor + 1.14-SNAPSHOT + nbm + BytecodeEditor + + UTF-8 + + + + at.ssw.visualizer + VisualizerUI + ${project.version} + + + at.ssw.visualizer + BytecodeModel + ${project.version} + + + at.ssw.visualizer + CompilationModel + ${project.version} + + + at.ssw.visualizer + TextEditor + ${project.version} + + + org.netbeans.api + org-netbeans-modules-editor + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-fold + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-lib + ${netbeans.version} + + + org.netbeans.api + org-openide-awt + ${netbeans.version} + + + org.netbeans.api + org-openide-nodes + ${netbeans.version} + + + org.netbeans.api + org-openide-text + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + org.netbeans.api + org-openide-util-ui + ${netbeans.version} + + + org.netbeans.api + org-openide-windows + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.bc + at.ssw.visualizer.bc.model + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/BCEditor.java b/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/BCEditor.java new file mode 100644 index 000000000000..b68c04331d0f --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/BCEditor.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc; + +import at.ssw.visualizer.texteditor.Editor; +import org.openide.windows.CloneableTopComponent; + +/** + * Editor associated to the BCEditorSupport. + * + * @author Alexander Reder + * @author Christian Wimmer + */ +public class BCEditor extends Editor { + + /** + * Creates a new CloneableEditor for the ClonableEditorSupport. + * + * @param support ClonableEditor to attach + */ + public BCEditor(BCEditorSupport support) { + super(support); + } + + @Override + protected CloneableTopComponent createClonedObject() { + BCEditor editor = new BCEditor((BCEditorSupport) cloneableEditorSupport()); + editor.setActivatedNodes(getActivatedNodes()); + return editor; + } +} diff --git a/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/BCEditorKit.java b/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/BCEditorKit.java new file mode 100644 index 000000000000..fd6fc653b1f1 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/BCEditorKit.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc; + +import at.ssw.visualizer.bc.model.BCScanner; +import at.ssw.visualizer.texteditor.EditorKit; +import javax.swing.text.Document; +import org.netbeans.editor.Syntax; + +/** + * A EditorKit for the bytecode, mime type text/x-compilation-bc, providing + * the syntax for the bytecode. + * + * @author Alexander Reder + */ +public class BCEditorKit extends EditorKit { + + @Override + public Syntax createSyntax(Document document) { + return new BCScanner(); + } + + /** + * Returns the mime type which identifies the bytecode for this EditorKit. + * + * @return the mime type from the EditorSupport (text/x-compilation-bc) + */ + @Override + public String getContentType() { + return BCEditorSupport.MIME_TYPE; + } +} diff --git a/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/BCEditorSupport.java b/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/BCEditorSupport.java new file mode 100644 index 000000000000..2f174f3f42b8 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/BCEditorSupport.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc; + +import at.ssw.visualizer.bc.icons.Icons; +import at.ssw.visualizer.bc.model.BCTextBuilder; +import at.ssw.visualizer.model.bc.Bytecodes; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.texteditor.EditorSupport; +import javax.swing.text.EditorKit; +import javax.swing.text.StyledDocument; +import org.openide.text.CloneableEditor; +import org.openide.util.ImageUtilities; + +/** + * Associates an Editor to a StyledDocument. + * + * @author Alexander Reder + * @author Christian Wimmer + */ +public class BCEditorSupport extends EditorSupport { + + public static final String MIME_TYPE = "text/x-compilation-bc"; + + private Bytecodes bytecodes; + + public BCEditorSupport(Bytecodes bytecodes) { + super(bytecodes.getControlFlowGraph()); + this.bytecodes = bytecodes; + this.text = new BCTextBuilder().buildDocument(cfg); + } + + public String getMimeType() { + return MIME_TYPE; + } + + public Bytecodes getBytecodes() { + return bytecodes; + } + + /** + * Returns the Editor associated to the EditorSupport. + * + * @return a new BCEditor + */ + @Override + protected CloneableEditor createCloneableEditor() { + return new BCEditor(this); + } + + /** + * Returns the document associated to the EditorSupport and the EditorKit. + * + * @param kit the EditorKit + * @return the new created styled document + */ + @Override + protected StyledDocument createStyledDocument(EditorKit kit) { + StyledDocument doc = super.createStyledDocument(kit); + doc.putProperty(Bytecodes.class, bytecodes); + doc.putProperty(ControlFlowGraph.class, bytecodes.getControlFlowGraph()); + return doc; + } + + /** + * Returns the name of the method which will be displayed on top of the + * editor window. + * + * @return the method name + */ + @Override + public String messageName() { + return bytecodes.getControlFlowGraph().getShortName(); + } + + @Override + protected String messageToolTip() { + return bytecodes.getControlFlowGraph().getName(); + } + + @Override + protected void initializeCloneableEditor(CloneableEditor editor) { + super.initializeCloneableEditor(editor); + editor.setIcon(ImageUtilities.loadImage(Icons.BYTECODE)); + } + +} diff --git a/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/action/ShowBCEditorAction.java b/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/action/ShowBCEditorAction.java new file mode 100644 index 000000000000..633f12424599 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/action/ShowBCEditorAction.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc.action; + +import at.ssw.visualizer.bc.BCEditor; +import at.ssw.visualizer.bc.BCEditorSupport; +import at.ssw.visualizer.bc.icons.Icons; +import at.ssw.visualizer.bc.model.BytecodeModel; +import at.ssw.visualizer.core.focus.Focus; +import at.ssw.visualizer.model.bc.Bytecodes; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import javax.swing.JOptionPane; +import org.openide.nodes.Node; +import org.openide.util.HelpCtx; +import org.openide.util.Lookup; +import org.openide.util.actions.CookieAction; + +/** + * This class provides methods for the action to open the BytecodeEditor + * for an entry of the compilation menu. + * + * @author Alexander Reder + * @author Christian Wimmer + */ +public final class ShowBCEditorAction extends CookieAction { + /** + * Opens the BCEditor for the control flow graph of the active node. + * + * @param activatedNodes Nodes that are selected + */ + protected void performAction(Node[] activatedNodes) { + BytecodeModel bcModel = Lookup.getDefault().lookup(BytecodeModel.class); + ControlFlowGraph cfg = activatedNodes[0].getLookup().lookup(ControlFlowGraph.class); + Bytecodes bytecodes = bcModel.getBytecodes(cfg); + if (bytecodes == null) { + JOptionPane.showMessageDialog(null, bcModel.noBytecodesMsg(cfg), "No bytecodes available", JOptionPane.INFORMATION_MESSAGE); + return; + } + + if (!Focus.findEditor(BCEditor.class, bytecodes.getControlFlowGraph())) { + BCEditorSupport editor = new BCEditorSupport(bytecodes); + editor.open(); + } + } + + /** + * Returns how often an action can be performed on a selected node. + * + * @return it can be performed only one time. + */ + protected int mode() { + return CookieAction.MODE_EXACTLY_ONE; + } + + /** + * When should the BCEditor be available. + * + * @return returns true if exactely one BlockListBuilder node is selected + * or exactly one After Generation of HIR node is selected and there is + * only one BlockListerBuilder node, false otherwise. + */ + @Override + protected boolean enable(Node[] activatedNodes) { + if (!super.enable(activatedNodes)) { + return false; + } + BytecodeModel bcModel = Lookup.getDefault().lookup(BytecodeModel.class); + ControlFlowGraph cfg = activatedNodes[0].getLookup().lookup(ControlFlowGraph.class); + return bcModel.hasBytecodes(cfg); + } + + /** + * Returns the name of the action. + * + * @return name of the action + */ + public String getName() { + return "Open Bytecodes"; + } + + /** + * Returns the icon of the action. + * + * @return icon of the action + */ + @Override + protected String iconResource() { + return Icons.BYTECODE; + } + + /** + * Specfies the cookie classes where this action should be available. + * + * @return ControlFlowGraph.class + */ + protected Class[] cookieClasses() { + return new Class[]{ControlFlowGraph.class}; + } + + /** + * Returns the help context for this action. + * + * @return null, no help context available + */ + public HelpCtx getHelpCtx() { + return null; + } + + /** + * How this action should be performed. + * + * @return false, this action will be performed immediatly + */ + @Override + protected boolean asynchronous() { + return false; + } +} diff --git a/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/icons/Icons.java b/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/icons/Icons.java new file mode 100644 index 000000000000..99e696668ac0 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/icons/Icons.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc.icons; + +/** + * + * @author Christian Wimmer + */ +public class Icons { + private static final String PATH = "at/ssw/visualizer/bc/icons/"; + + public static final String BYTECODE = PATH + "bytecode.gif"; +} diff --git a/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/model/BCScanner.java b/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/model/BCScanner.java new file mode 100644 index 000000000000..0adfc419777a --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/model/BCScanner.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc.model; + +import at.ssw.visualizer.texteditor.model.Scanner; +import org.netbeans.editor.TokenID; + +/** + * This class is used for parsing the bytecode text. + * + * @author Alexander Reder + * @author Christian Wimmer + */ +public class BCScanner extends Scanner { + public BCScanner() { + super("\n\r\t ,:", BCTokenContext.contextPath); + } + + private boolean isBlock() { + return expectChar('B') && expectChar(DIGIT) && expectChars(DIGIT) && expectEnd(); + } + + private boolean isBciDef() { + return expectChar(DIGIT) && expectChars(DIGIT) && expectEnd(':'); + } + + private boolean isBciRef() { + return expectChar('#') && expectChar(DIGIT) && expectChars(DIGIT) && expectEnd(); + } + + private boolean isVarRef() { + return expectChar('%') && expectChar(DIGIT) && expectChars(DIGIT) && expectEnd(); + } + + private boolean isBcDescription() { + return beforeChar(':') && expectChar(LETTER) && expectChars(LETTER_DIGIT) && expectEnd(); + } + + @Override + protected TokenID parseToken() { + findTokenBegin(); + if (ch == EOF) { + return BCTokenContext.EOF_TOKEN; + } else if (isWhitespace()) { + return BCTokenContext.WHITESPACE_TOKEN; + } else if (isBlock()) { + return BCTokenContext.BLOCK_TOKEN; + } else if (isBciDef() || isBciRef()) { + return BCTokenContext.BCI_TOKEN; + } else if (isVarRef()) { + return BCTokenContext.VAR_REFERENCE_TOKEN; + } else if (isBcDescription()) { + return BCTokenContext.BC_DESCRIPTION_TOKEN; + } else { + readToWhitespace(); + return BCTokenContext.OTHER_TOKEN; + } + } + +} diff --git a/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/model/BCTextBuilder.java b/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/model/BCTextBuilder.java new file mode 100644 index 000000000000..d3787a02f818 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/model/BCTextBuilder.java @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc.model; + +import at.ssw.visualizer.model.bc.Bytecodes; +import at.ssw.visualizer.bc.BCEditorSupport; +import at.ssw.visualizer.model.Compilation; +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.texteditor.model.BlockRegion; +import at.ssw.visualizer.texteditor.model.FoldingRegion; +import at.ssw.visualizer.texteditor.model.HoverParser; +import at.ssw.visualizer.texteditor.model.Text; +import at.ssw.visualizer.texteditor.model.TextBuilder; +import at.ssw.visualizer.texteditor.model.TextRegion; +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.netbeans.api.editor.fold.FoldType; +import org.netbeans.editor.TokenID; +import org.openide.util.Lookup; + +/** + * Builds the text containing the bytecode. + * + * @author Alexander Reder + */ +public class BCTextBuilder extends TextBuilder { + + private static final FoldType KIND_BLOCK = new FoldType("..."); + + private static final String BLOCK_LIST_PREFIX = "BlockListBuilder "; + + public BCTextBuilder() { + super(); + scanner = new BCScanner(); + } + + @Override + public Text buildDocument(ControlFlowGraph cfg) { + BytecodeModel bcModel = Lookup.getDefault().lookup(BytecodeModel.class); + Bytecodes bytecodes = bcModel.getBytecodes(cfg); + bytecodes.parseBytecodes(); + + Compilation compilation = cfg.getCompilation(); + DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM); + + String name = cfg.getName(); + if (name.startsWith(BLOCK_LIST_PREFIX)) { + name = name.substring(BLOCK_LIST_PREFIX.length()); + } + text.append(name).append("\n"); + text.append(dateFormat.format(compilation.getDate())).append("\n\n"); + + BasicBlock[] sortedBlocks = sortBlocks(cfg.getBasicBlocks()); + if (sortedBlocks.length == 0) { + appendAll(bytecodes); + } else { + for(int i = 0; i < sortedBlocks.length; i++) { + appendBlock(sortedBlocks, bytecodes, i); + } + } + text.append("\n\n").append(bytecodes.getEpilogue()); + return buildText(cfg, BCEditorSupport.MIME_TYPE); + } + + public String buildView(ControlFlowGraph cfg, BasicBlock[] basicBlocks) { + BytecodeModel bcModel = Lookup.getDefault().lookup(BytecodeModel.class); + Bytecodes bytecodes = bcModel.getBytecodes(cfg); + if(bytecodes == null) { + return "No bytecodes available\n"; + } + StringBuilder view = new StringBuilder(1024); + buildDocument(cfg); + for(BasicBlock b : basicBlocks) { + BlockRegion br = blocks.get(b); + view.append(text.substring(br.getStart(), br.getEnd())); + } + return view.toString(); + } + + private void appendAll(Bytecodes bytecodes) { + int fromBCI = 0; + int toBCI = Integer.MAX_VALUE; + String[] bcs = bytecodes.getBytecodes(fromBCI, toBCI).split("\n"); + for(String s : bcs) { + appendBytecode(null, s); + } + } + + private void appendBlock(BasicBlock[] basicBlocks, Bytecodes bytecodes, int index) { + int start = text.length(); + int fromBCI = basicBlocks[index].getFromBci(); + int toBCI = Integer.MAX_VALUE; + if(basicBlocks.length > (index + 1) && basicBlocks[index + 1].getFromBci() >= 0) { + toBCI = basicBlocks[index + 1].getFromBci(); + } + appendBlockDetails(basicBlocks[index]); + text.append("\n"); + int bytecodesStart = text.length(); + String[] bcs = bytecodes.getBytecodes(fromBCI, toBCI).split("\n"); + for(String s : bcs) { + appendBytecode(basicBlocks[index], s); + } + blocks.put(basicBlocks[index], + new BlockRegion(basicBlocks[index], start, text.length(), start, start + basicBlocks[index].getName().length())); + foldingRegions.add(new FoldingRegion(KIND_BLOCK, bytecodesStart - 1, text.length() - 1, false)); + hyperlinks.put(basicBlocks[index].getName(), new TextRegion(start, start + basicBlocks[index].getName().length())); + } + + private void appendBytecode(BasicBlock block, String bytecode) { + int start = text.length(); + HoverParser p = new HoverParser(bytecode); + while (p.hasNext()) { + int pstart = text.length(); + text.append(p.next()); + if (p.getHover() != null) { + regionHovers.put(new TextRegion(pstart, text.length()), p.getHover()); + } + } + text.append("\n"); + scanner.setText(bytecode, 0, bytecode.length()); + TokenID token = scanner.nextToken(); + String tokenString; + while(token != null && token.getNumericID() != BCTokenContext.EOF_TOKEN_ID) { + tokenString = scanner.getTokenString(); + if(token.getNumericID() == BCTokenContext.BCI_TOKEN_ID) { + if(scanner.getTokenString().startsWith("#")) { + addReference(block, tokenString, bytecode); + addReference(block, tokenString.substring(1, tokenString.length()), bytecode); + } else { + hoverKeys.add(tokenString); + hoverKeys.add('#' + tokenString); + addDefinition(block, tokenString, bytecode); + addDefinition(block, '#' + tokenString, bytecode); + hyperlinks.put('#' + tokenString, new TextRegion(start + scanner.getTokenOffset(), start + scanner.getOffset())); + } + } else if(token.getNumericID() == BCTokenContext.VAR_REFERENCE_TOKEN_ID) { + hoverKeys.add(tokenString); + addReference(block, tokenString, bytecode); + } + token = scanner.nextToken(); + } + } + + private void addReference(BasicBlock block, String key, String s) { + if (block == null) { + return; + } + if(!hoverReferences.containsKey(key)) { + hoverReferences.put(key, new ArrayList()); + } + hoverReferences.get(key).add(block.getName() + ":\t" + s); + } + + private void addDefinition(BasicBlock block, String key, String s) { + if (block == null) { + return; + } + hoverDefinitions.put(key, block.getName() + ":\t" + s); + } + + @Override + protected void buildHighlighting() { + scanner.setText(text.toString(), 0, text.length()); + TokenID token = scanner.nextToken(); + Map> highlightings = new HashMap>(); + while(token != null && token.getNumericID() != BCTokenContext.EOF_TOKEN_ID) { + String key = scanner.getTokenString(); + switch(token.getNumericID()) { + case BCTokenContext.BCI_TOKEN_ID: + if(key.startsWith("#")) { + key = key.substring(1, key.length()); + } + List tr = new ArrayList(); + if(!highlightings.containsKey(key)) { + highlightings.put(key, tr); + highlightings.put('#' + key, tr); + } + highlightings.get(key).add(new TextRegion(scanner.getTokenOffset(), scanner.getOffset())); + break; + case BCTokenContext.BLOCK_TOKEN_ID: + case BCTokenContext.VAR_REFERENCE_TOKEN_ID: + if(!highlightings.containsKey(key)) { + highlightings.put(key, new ArrayList()); + } + highlightings.get(key).add(new TextRegion(scanner.getTokenOffset(), scanner.getOffset())); + break; + } + token = scanner.nextToken(); + } + for(String key : highlightings.keySet()) { + List regions = highlightings.get(key); + highlighting.put(key, regions.toArray(new TextRegion[regions.size()])); + } + } + + /** + * Sorts the blocks on the basis of the starting BCI. BCIs < 0 will be + * placed at the end of the array. + * + * @param blocks array containing the unsorted blocks + * @return sorted array of blocks + */ + protected BasicBlock[] sortBlocks(List blocks) { + List blockList = new ArrayList(blocks); + Collections.sort(blockList, new Comparator() { + + public int compare(BasicBlock b1, BasicBlock b2) { + if (b1.getFromBci() >= 0 && b2.getFromBci() < 0) { + return -1; + } + if (b1.getFromBci() < 0 && b2.getFromBci() >= 0) { + return 1; + } + if(b1 != b2 && b1.getFromBci() == b2.getFromBci()) { + return -1; + } + return b1.getFromBci() - b2.getFromBci(); + } + }); + return blockList.toArray(new BasicBlock[blocks.size()]); + } + +} diff --git a/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/model/BCTokenContext.java b/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/model/BCTokenContext.java new file mode 100644 index 000000000000..718051890be9 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeEditor/src/main/java/at/ssw/visualizer/bc/model/BCTokenContext.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc.model; + +import org.netbeans.editor.BaseTokenID; +import org.netbeans.editor.TokenContext; +import org.netbeans.editor.TokenContextPath; +import org.netbeans.editor.TokenID; + +/** + * The token context for the scanner. + * + * @author Alexander Reder + */ +public class BCTokenContext extends TokenContext { + + public static final int EOF_TOKEN_ID = -1; + public static final int WHITESPACE_TOKEN_ID = -2; + public static final int OTHER_TOKEN_ID = -3; + public static final int BCI_TOKEN_ID = 4; + public static final int BC_DESCRIPTION_TOKEN_ID = 5; + public static final int VAR_REFERENCE_TOKEN_ID = 6; + public static final int BLOCK_TOKEN_ID = 7; + + public static final TokenID EOF_TOKEN = new BaseTokenID("eof", EOF_TOKEN_ID); + public static final TokenID WHITESPACE_TOKEN = new BaseTokenID("whitespace", WHITESPACE_TOKEN_ID); + public static final TokenID OTHER_TOKEN = new BaseTokenID("other", OTHER_TOKEN_ID); + public static final TokenID BCI_TOKEN = new BaseTokenID("bci", BCI_TOKEN_ID); + public static final TokenID BC_DESCRIPTION_TOKEN = new BaseTokenID("bytecode_description", BC_DESCRIPTION_TOKEN_ID); + public static final TokenID VAR_REFERENCE_TOKEN = new BaseTokenID("var_reference", VAR_REFERENCE_TOKEN_ID); + public static final TokenID BLOCK_TOKEN = new BaseTokenID("block", BLOCK_TOKEN_ID); + + public static final BCTokenContext context = new BCTokenContext(); + public static final TokenContextPath contextPath = context.getContextPath(); + + /** + * Initializes the token context with the prefix "bc-" and all available + * tokens. + */ + private BCTokenContext() { + super("bc-"); + addTokenID(WHITESPACE_TOKEN); + addTokenID(OTHER_TOKEN); + addTokenID(BCI_TOKEN); + addTokenID(BC_DESCRIPTION_TOKEN); + addTokenID(VAR_REFERENCE_TOKEN); + addTokenID(BLOCK_TOKEN); + } +} diff --git a/visualizer/C1Visualizer/BytecodeEditor/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/BytecodeEditor/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..6dc884109add --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeEditor/src/main/nbm/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.bc +OpenIDE-Module-Layer: at/ssw/visualizer/bc/layer.xml +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/bc/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/BytecodeEditor/src/main/resources/at/ssw/visualizer/bc/Bundle.properties b/visualizer/C1Visualizer/BytecodeEditor/src/main/resources/at/ssw/visualizer/bc/Bundle.properties new file mode 100644 index 000000000000..e1b692d69fb8 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeEditor/src/main/resources/at/ssw/visualizer/bc/Bundle.properties @@ -0,0 +1,12 @@ +OpenIDE-Module-Name=Bytecode Editor + +text/x-compilation-bc=Bytecodes +bc-bci=Bytecode index +bc-bci_reference=Bytecode index reference +bc-bytecode_description=Bytecode description +bc-var_reference=Variable reference +bc-block=Block +bc-block_reference=Block reference +bc-whitespace=Whitespace +bc-other=Other +bc-default=Default diff --git a/visualizer/C1Visualizer/BytecodeEditor/src/main/resources/at/ssw/visualizer/bc/icons/bytecode.gif b/visualizer/C1Visualizer/BytecodeEditor/src/main/resources/at/ssw/visualizer/bc/icons/bytecode.gif new file mode 100644 index 000000000000..daa9302b4d10 Binary files /dev/null and b/visualizer/C1Visualizer/BytecodeEditor/src/main/resources/at/ssw/visualizer/bc/icons/bytecode.gif differ diff --git a/visualizer/C1Visualizer/BytecodeEditor/src/main/resources/at/ssw/visualizer/bc/layer.xml b/visualizer/C1Visualizer/BytecodeEditor/src/main/resources/at/ssw/visualizer/bc/layer.xml new file mode 100644 index 000000000000..6ac977f8e289 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeEditor/src/main/resources/at/ssw/visualizer/bc/layer.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/BytecodeEditor/src/main/resources/at/ssw/visualizer/bc/model/NetBeans-BC-fontsColors.xml b/visualizer/C1Visualizer/BytecodeEditor/src/main/resources/at/ssw/visualizer/bc/model/NetBeans-BC-fontsColors.xml new file mode 100644 index 000000000000..9067fc09e5a7 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeEditor/src/main/resources/at/ssw/visualizer/bc/model/NetBeans-BC-fontsColors.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/BytecodeModel/pom.xml b/visualizer/C1Visualizer/BytecodeModel/pom.xml new file mode 100644 index 000000000000..22db187991f5 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeModel/pom.xml @@ -0,0 +1,97 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + BytecodeModel + 1.14-SNAPSHOT + nbm + BytecodeModel + + UTF-8 + + + + at.ssw.visualizer + CompilationModel + ${project.version} + + + org.apache.bcel + bcel + 5.2 + + + org.netbeans.api + org-netbeans-modules-options-api + ${netbeans.version} + + + org.netbeans.api + org-openide-awt + ${netbeans.version} + + + org.netbeans.api + org-openide-filesystems + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + org.netbeans.api + org-openide-util-ui + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.bc.model + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/model/BytecodeModel.java b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/model/BytecodeModel.java new file mode 100644 index 000000000000..8f5c6f1285ad --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/model/BytecodeModel.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc.model; + +import at.ssw.visualizer.model.bc.Bytecodes; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; + +/** + * Entry point for the public bytecodes data model. + * + * @author Christian Wimmer + */ +public interface BytecodeModel { + /** + * Checks if bytecodes can be available for a control flow graph, i.e. if + * the control flow graph is either from the early bytecode parsing phase + * of the compiler, or no method inlining was performed. + * + * This does not imply that {link #getBytecodes()} returns true for this + * control flow graph. It is possible that the class is not in the + * classpath specified in the visualizer options. + * + * @return returns true if a BlockListBuilder node is selected + * or a After Generation of HIR node is selected and there is + * only one BlockListerBuilder node, false otherwise. + */ + public boolean hasBytecodes(ControlFlowGraph cfg); + + /** + * Gets the bytecodes for a control flow graph. + * + * @return the bytecodes, or null if not available. + */ + public Bytecodes getBytecodes(ControlFlowGraph cfg); + + /** + * Returns a human-readable message that explains why no bytecodes are + * available for the specified control flow graph. + */ + public String noBytecodesMsg(ControlFlowGraph cfg); +} diff --git a/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/modelimpl/BytecodeModelImpl.java b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/modelimpl/BytecodeModelImpl.java new file mode 100644 index 000000000000..a36468c84826 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/modelimpl/BytecodeModelImpl.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc.modelimpl; + +import at.ssw.visualizer.bc.model.BytecodeModel; +import at.ssw.visualizer.model.bc.Bytecodes; +import at.ssw.visualizer.model.CompilationElement; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JOptionPane; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.Repository; + +/** + * This class holds the classpaths and the method bytecodes. + * + * @author Alexander Reder + * @author Christian Wimmer + */ +public class BytecodeModelImpl implements BytecodeModel { + + private String[] classPaths; + private Map loadedBytecodes; + + /** + * Initializes a BytecodeModel. + */ + public BytecodeModelImpl() { + loadClassPaths(); + + // Use a synchronized map to avoid all possible threading issues. + // Use a weak map because elements are never removed explicitly. + loadedBytecodes = Collections.synchronizedMap(new WeakHashMap()); + } + + /** The prefix of all control flow graphs in the early bytecode parsing phase of the compiler. */ + private static final String BLOCK_LIST_PREFIX = "BlockListBuilder "; + + private ControlFlowGraph getBlockListCFG(ControlFlowGraph cfg) { + if (cfg.getBytecodes() != null) { + return cfg; + } + if (cfg.getName().startsWith(BLOCK_LIST_PREFIX)) { + return cfg; + } + + ControlFlowGraph result = null; + for (CompilationElement ce : cfg.getCompilation().getElements()) { + if (ce instanceof ControlFlowGraph && ce.getName().startsWith(BLOCK_LIST_PREFIX)) { + if (result == null) { + result = (ControlFlowGraph) ce; + if (result.getElements().size() > 0) { + // Child elements are present, so methods were inlined. + return null; + } + } else { + // More than one BlockListBuilder, so methods were inlined. + return null; + } + } + } + return result; + } + + private String getMethodName(ControlFlowGraph cfg) { + assert cfg.getName().startsWith(BLOCK_LIST_PREFIX); + return cfg.getName().substring(BLOCK_LIST_PREFIX.length()); + } + + + public boolean hasBytecodes(ControlFlowGraph cfg) { + return getBlockListCFG(cfg) != null; + } +/* + public boolean bytecodesLoaded(ControlFlowGraph cfg) { + return getBytecodes(cfg) != null; + } +*/ + public String noBytecodesMsg(ControlFlowGraph cfg) { + StringBuilder result = new StringBuilder(); + result.append("No bytecodes available for ").append(cfg.getCompilation().getName()).append(" - ").append(cfg.getName()); + if (!hasBytecodes(cfg)) { + result.append("\nThe compiler inlined methods during compilation, so there is no single method to take the bytecodes from."); + } else { + result.append("\nThe method is not present in the classpath. Configure the claspath using Tools->Options."); + } + return result.toString(); + } + + public Bytecodes getBytecodes(ControlFlowGraph cfg) { + Bytecodes result = loadedBytecodes.get(cfg); + if (result != null) { + return result; + } + + cfg = getBlockListCFG(cfg); + if (cfg == null) { + return null; + } + + if (cfg.getBytecodes() != null) { + return cfg.getBytecodes(); + } + + String methodName = getMethodName(cfg); + List mbcs = BytecodesParser.readMethod(cfg, methodName, classPaths); + if (mbcs.size() == 0) { + return null; + } else if (mbcs.size() == 1) { + result = mbcs.get(0); + } else { + result = (Bytecodes) JOptionPane.showInputDialog(null, "Ambiguous methodname: \n" + methodName, "Select method", JOptionPane.QUESTION_MESSAGE, null, mbcs.toArray(), mbcs.get(0)); + if (result == null) { + return null; + } + } + + loadedBytecodes.put(cfg, result); + return result; + } + + private FileObject getOptionLocation() { + return Repository.getDefault().getDefaultFileSystem().findResource("at-ssw-visualizer-bc/classpaths"); + } + + /** + * Loads the classpath information from the system filesystem. + */ + private void loadClassPaths() { + List data = new ArrayList(); + try { + InputStream stream = getOptionLocation().getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + + String cp = reader.readLine(); + while (cp != null) { + data.add(cp); + cp = reader.readLine(); + } + reader.close(); + } catch (IOException ex) { + Logger log = Logger.getLogger(BytecodeModel.class.getName()); + log.log(Level.SEVERE, ex.getMessage(), ex); + } + + if (data.size() > 0) { + classPaths = data.toArray(new String[data.size()]); + } else { + classPaths = getDefaultClassPaths(); + } + } + + /** + * Stores the classpath infotmation to the system filesystem. + */ + private void saveClassPaths() { + try { + OutputStream stream = getOptionLocation().getOutputStream(); + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stream)); + + for (String cp : classPaths) { + writer.write(cp); + writer.newLine(); + } + writer.close(); + } catch (IOException ex) { + Logger log = Logger.getLogger(BytecodeModel.class.getName()); + log.log(Level.SEVERE, ex.getMessage(), ex); + } + } + + public String[] getDefaultClassPaths() { + return System.getProperty("sun.boot.class.path", "").split(System.getProperty("path.separator")); + } + + /** + * Returns an array of all classpaths stored in the model. + * + * @return String array with all classpaths + */ + public String[] getClassPaths() { + return classPaths; + } + + public void setClassPaths(String[] classPaths) { + this.classPaths = classPaths; + + loadedBytecodes.clear(); + saveClassPaths(); + } +} diff --git a/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/modelimpl/BytecodesImpl.java b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/modelimpl/BytecodesImpl.java new file mode 100644 index 000000000000..96415af3bcfa --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/modelimpl/BytecodesImpl.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc.modelimpl; + +import at.ssw.visualizer.model.bc.Bytecodes; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import java.util.SortedMap; + +/** + * This class holds the bytecode of a method and provides severel methods + * accessing the details. + * + * @author Alexander Reder + * @author Christian Wimmer + */ +public class BytecodesImpl implements Bytecodes { + + private ControlFlowGraph controlFlowGraph; + private String name; + private String shortName; + + private String prolog; + private String attributes; + private SortedMap byteCodes; + + public BytecodesImpl(ControlFlowGraph controlFlowGraph, String name, String shortName, String prolog, String attributes, SortedMap byteCodes) { + this.controlFlowGraph = controlFlowGraph; + this.name = name; + this.shortName = shortName; + this.prolog = prolog; + this.attributes = attributes; + this.byteCodes = byteCodes; + } + + @Override + public ControlFlowGraph getControlFlowGraph() { + return controlFlowGraph; + } + + @Override + public void parseBytecodes() { + // Nothing to parse + } + + @Override + public String getBytecodes(int fromBCI, int toBCI) { + StringBuilder sb = new StringBuilder(); + for (Integer key : byteCodes.subMap(fromBCI, toBCI).keySet()) { + String keyString = Integer.toString(key); + sb.append(keyString).append(":"); + sb.append(" ".substring(Math.min(keyString.length(), 4))); + sb.append(byteCodes.get(key)); + sb.append("\n"); + } + return sb.toString(); + } + + @Override + public String getEpilogue() { + return attributes; + } + + @Override + public String toString() { + return name; + } +} diff --git a/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/modelimpl/BytecodesParser.java b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/modelimpl/BytecodesParser.java new file mode 100644 index 000000000000..accdc8b9d3f2 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/modelimpl/BytecodesParser.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc.modelimpl; + +import at.ssw.visualizer.model.bc.Bytecodes; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import org.apache.bcel.classfile.ClassParser; +import org.apache.bcel.classfile.Code; +import org.apache.bcel.classfile.JavaClass; +import org.apache.bcel.classfile.Method; +import org.apache.bcel.generic.Type; + +/** + * + * @author Alexander Reder + * @author Christian Wimmer + */ +public class BytecodesParser { + + private BytecodesParser() { + } + + /** + * Trys to read the bytecode of the given method name. + * + * @param method full name of the method + */ + public static List readMethod(ControlFlowGraph cfg, String method, String[] classPaths) { + List result = new ArrayList(); + + MethodName methodName = MethodName.parse(method); + if (methodName == null) { + return result; + } + + for (String classPath : classPaths) { + try { + ClassParser cp = locateClass(methodName, classPath); + if (cp != null) { + JavaClass c = cp.parse(); + for (Method m : c.getMethods()) { + if (methodName.matches(m)) { + result.add(readBytecodes(cfg, methodName, m)); + } + } + } + } catch (IOException ex) { + Logger log = Logger.getLogger(BytecodesParser.class.getName()); + log.log(Level.INFO, ex.getMessage(), ex); + } + } + return result; + } + + private static ClassParser locateClass(MethodName methodName, String classPath) throws IOException { + File baseFile = new File(classPath); + if (!baseFile.exists()) { + return null; + } + + if (baseFile.isDirectory()) { + String className = methodName.className.replace('.', File.separatorChar) + ".class"; + File classFile = new File(baseFile, className); + if (classFile.exists()) { + return new ClassParser(new FileInputStream(classFile), className); + } + + } else { + String className = methodName.className.replace('.', '/') + ".class"; + ZipFile zipFile = new ZipFile(baseFile); + ZipEntry zipEntry = zipFile.getEntry(className); + if (zipEntry != null) { + return new ClassParser(zipFile.getInputStream(zipEntry), className); + } + } + return null; + } + + /** + * Parses the code string and generates the prolog, attributeslist and the + * hashmap for the bytecode. + */ + private static Bytecodes readBytecodes(ControlFlowGraph cfg, MethodName methodName, Method method) { + Code code = method.getCode(); + Scanner bcScanner = new Scanner(code.toString()); + + String prolog = ""; + String attributes = ""; + SortedMap byteCodes = new TreeMap(); + + bcScanner.useDelimiter("\n"); + while (bcScanner.hasNext()) { + String codeLine = bcScanner.next(); + if (codeLine.startsWith("Code")) { + prolog = codeLine; + } else if (codeLine.length() > 0 && codeLine.charAt(0) >= '0' && codeLine.charAt(0) <= '9' && codeLine.indexOf(':') > 0) { + Scanner codeLineScanner = new Scanner(codeLine); + codeLineScanner.useDelimiter(":\\s*"); +// try { + int key = Integer.parseInt(codeLineScanner.next()); + byteCodes.put(key, codeLineScanner.next()); +/* } catch (NumberFormatException ex) { + System.err.println(codeLine); + } catch (NoSuchElementException ex) { + System.err.println(codeLine); + } + */ + codeLineScanner.close(); + } else if (codeLine.length() > 0 && codeLine.charAt(0) != ' ') { + attributes = attributes + codeLine + "\n"; + } + } + bcScanner.close(); + + StringBuilder sb = new StringBuilder(); + sb.append(method.getReturnType()).append(" "); + sb.append(methodName.className); + sb.append(".").append(method.getName()).append("("); + boolean sep = false; + for (Type t : method.getArgumentTypes()) { + if (sep) { + sb.append(", "); + } + sb.append(t); + sep = true; + } + sb.append(")"); + String name = sb.toString(); + + sb = new StringBuilder(); + int point = methodName.className.lastIndexOf('.'); + if (point > 0) { + sb.append(methodName.className.substring(point + 1)); + } else { + sb.append(methodName.className); + } + sb.append(".").append(method.getName()); + String shortName = sb.toString(); + + return new BytecodesImpl(cfg, name, shortName, prolog, attributes, byteCodes); + } +} diff --git a/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/modelimpl/MethodName.java b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/modelimpl/MethodName.java new file mode 100644 index 000000000000..8d3267f29063 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/modelimpl/MethodName.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc.modelimpl; + +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.apache.bcel.classfile.Method; +import org.apache.bcel.generic.Type; + +/** + * This class holds informations like package, class, arguments, return values + * and name of a method. + * + * @author Alexander Reder + * @author Christian Wimmer + */ +public class MethodName { + + public String methodModifier; + public Type returnValType; + public String className; + public String methodName; + public List arguments; + + private MethodName() { + // Prevent creation from outside. + } + + /** + * Initializes a new MethodName and parses the full name. + * + * @param name full name of the method + */ + public static MethodName parse(String name) { + Scanner methodScanner = new Scanner(name); + methodScanner.useDelimiter("\\s|\\,\\s|\\(|\\)$"); + try { + MethodName result = new MethodName(); + result.methodModifier = methodScanner.next(); + result.returnValType = getTypeOf(methodScanner.next()); + String[] classMethodName = methodScanner.next().split("\\."); + StringBuilder pkgClass = new StringBuilder(); + pkgClass.append(classMethodName[0]); + for(int i = 1; i < classMethodName.length - 1; i++) { + pkgClass.append("."); + pkgClass.append(classMethodName[i]); + } + result.className = pkgClass.toString(); + result.methodName = classMethodName[classMethodName.length - 1]; + result.arguments = new ArrayList(); + String argument; + while(methodScanner.hasNext()) { + argument = methodScanner.next(); + if(!argument.equals("")) { + result.arguments.add(getTypeOf(argument)); + } + } + return result; + } catch (Exception ex) { + Logger log = Logger.getLogger(BytecodesParser.class.getName()); + log.log(Level.WARNING, ex.getMessage(), ex); + return null; + } finally { + methodScanner.close(); + } + } + + /** + * Checks if the specified method has the same signature as defined by this + * parsed method name. + */ + public boolean matches(Method m) { + if (!methodName.equals(m.getName())) { + return false; + } + if (returnValType != Type.UNKNOWN && returnValType != m.getReturnType()) { + return false; + } + if (arguments.size() != m.getArgumentTypes().length) { + return false; + } + for(int i = 0; i < arguments.size(); i++) { + if (arguments.get(i) != Type.UNKNOWN && arguments.get(i) != m.getArgumentTypes()[i]) { + return false; + } + } + return true; + } + + /** + * Translates the String representation of a type into a Type. + * + * @param type String representation of the type + * @return Type of the Stringrepresentation + */ + private static Type getTypeOf(String type) { + if(type.equals("void")) { + return Type.VOID; + } else if(type.equals("jint")) { + return Type.INT; + } else if(type.equals("jshort")) { + return Type.SHORT; + } else if(type.equals("jlong")) { + return Type.LONG; + } else if(type.equals("jchar")) { + return Type.CHAR; + } else if(type.equals("jboolean")) { + return Type.BOOLEAN; + } else if(type.equals("jdouble")) { + return Type.DOUBLE; + } else if(type.equals("jfloat")) { + return Type.FLOAT; + } else { + return Type.UNKNOWN; + } + } +} diff --git a/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/options/BCOptionPanel.form b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/options/BCOptionPanel.form new file mode 100644 index 000000000000..de6db176c838 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/options/BCOptionPanel.form @@ -0,0 +1,102 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/options/BCOptionPanel.java b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/options/BCOptionPanel.java new file mode 100644 index 000000000000..fa9e46680b59 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/options/BCOptionPanel.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc.options; + +import at.ssw.visualizer.bc.model.BytecodeModel; +import at.ssw.visualizer.bc.modelimpl.BytecodeModelImpl; +import javax.swing.DefaultListModel; +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileNameExtensionFilter; +import org.openide.util.Lookup; + +/** + * The panel for setting the classpaths. + * + * @author Alexander Reder + * @author Christian Wimmer + */ +final class BCOptionPanel extends javax.swing.JPanel { + + private final BCOptionsPanelController controller; + private final BytecodeModelImpl bytecodeModel; + private final DefaultListModel listModel; + + public BCOptionPanel(BCOptionsPanelController controller) { + this.controller = controller; + initComponents(); + + bytecodeModel = (BytecodeModelImpl) Lookup.getDefault().lookup(BytecodeModel.class); + listModel = new DefaultListModel(); + classpathList.setModel(listModel); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + + classpathScrollPane = new javax.swing.JScrollPane(); + classpathList = new javax.swing.JList(); + addPathButton = new javax.swing.JButton(); + removeButton = new javax.swing.JButton(); + addJarZipButton = new javax.swing.JButton(); + addDefaultClasspath = new javax.swing.JButton(); + + classpathList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); + classpathScrollPane.setViewportView(classpathList); + + org.openide.awt.Mnemonics.setLocalizedText(addPathButton, "Add folder"); + addPathButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + addPathButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(removeButton, "Remove"); + removeButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + removeButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(addJarZipButton, "Add Jar/Zip file"); + addJarZipButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + addJarZipButtonActionPerformed(evt); + } + }); + + org.openide.awt.Mnemonics.setLocalizedText(addDefaultClasspath, "Add default classpath"); + addDefaultClasspath.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + addDefaultClasspathActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addComponent(classpathScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 309, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(addJarZipButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(addDefaultClasspath, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(removeButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(addPathButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()) + ); + + layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {addDefaultClasspath, addJarZipButton, removeButton}); + + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(addPathButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(addJarZipButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(addDefaultClasspath) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(removeButton)) + .addComponent(classpathScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 202, Short.MAX_VALUE)) + .addContainerGap()) + ); + }// //GEN-END:initComponents + + private void addDefaultClasspathActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addDefaultClasspathActionPerformed + for (String cp : bytecodeModel.getDefaultClassPaths()) { + listModel.addElement(cp); + controller.changed(); + classpathList.setSelectedIndex(listModel.getSize() - 1); + } + }//GEN-LAST:event_addDefaultClasspathActionPerformed + + private void addJarZipButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addJarZipButtonActionPerformed + JFileChooser chooser = new JFileChooser(); + chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + chooser.setFileFilter(new FileNameExtensionFilter("Bytecode archives", "jar", "zip")); + if (chooser.showOpenDialog(getParent()) == JFileChooser.APPROVE_OPTION) { + listModel.addElement(chooser.getSelectedFile().getAbsolutePath()); + controller.changed(); + classpathList.setSelectedIndex(listModel.getSize() - 1); + } + }//GEN-LAST:event_addJarZipButtonActionPerformed + + private void removeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeButtonActionPerformed + int idx = classpathList.getSelectedIndex(); + if (idx >= 0 && idx < listModel.getSize()) { + listModel.remove(idx); + controller.changed(); + if (listModel.size() > 0) { + classpathList.setSelectedIndex(Math.max(0, idx - 1)); + } + } + }//GEN-LAST:event_removeButtonActionPerformed + + private void addPathButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addPathButtonActionPerformed + JFileChooser chooser = new JFileChooser(); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + if (chooser.showOpenDialog(getParent()) == JFileChooser.APPROVE_OPTION) { + listModel.addElement(chooser.getSelectedFile().getAbsolutePath()); + controller.changed(); + classpathList.setSelectedIndex(listModel.getSize() - 1); + } + }//GEN-LAST:event_addPathButtonActionPerformed + + public void update() { + listModel.clear(); + for (String cp : bytecodeModel.getClassPaths()) { + listModel.addElement(cp); + } + } + + public void applyChanges() { + String[] data = new String[listModel.getSize()]; + listModel.copyInto(data); + bytecodeModel.setClassPaths(data); + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton addDefaultClasspath; + private javax.swing.JButton addJarZipButton; + private javax.swing.JButton addPathButton; + private javax.swing.JList classpathList; + private javax.swing.JScrollPane classpathScrollPane; + private javax.swing.JButton removeButton; + // End of variables declaration//GEN-END:variables +} diff --git a/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/options/BCOptions.java b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/options/BCOptions.java new file mode 100644 index 000000000000..0a1aa80864e1 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/options/BCOptions.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc.options; + +import at.ssw.visualizer.bc.options.icons.Icons; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import org.netbeans.spi.options.OptionsCategory; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.ImageUtilities; + +/** + * This class configures the OptionPanel. + * + * @author Alexander Reder + */ +public final class BCOptions extends OptionsCategory { + + public OptionsPanelController create() { + return new BCOptionsPanelController(); + } + + public String getCategoryName() { + return "Bytecodes"; + } + + public String getTitle() { + return "Bytecodes"; + } + + @Override + public Icon getIcon() { + return new ImageIcon(ImageUtilities.loadImage(Icons.BC_OPTION)); + } +} diff --git a/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/options/BCOptionsPanelController.java b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/options/BCOptionsPanelController.java new file mode 100644 index 000000000000..d023bce84249 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/options/BCOptionsPanelController.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc.options; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import javax.swing.JComponent; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.HelpCtx; +import org.openide.util.Lookup; + +/** + * Controlls the communication. + * + * @author Alexander Reder + * @author Christian Wimmer + */ +final class BCOptionsPanelController extends OptionsPanelController { + + private BCOptionPanel panel; + private boolean changed; + private PropertyChangeSupport prop; + + public BCOptionsPanelController() { + prop = new PropertyChangeSupport(this); + panel = new BCOptionPanel(this); + panel.update(); + } + + public void changed() { + if (!changed) { + changed = true; + prop.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true); + } + } + + public void update() { + panel.update(); + changed = false; + } + + public void applyChanges() { + panel.applyChanges(); + changed = false; + } + + public void cancel() { + // Nothing to do. + } + + public boolean isValid() { + return true; + } + + public boolean isChanged() { + return changed; + } + + public HelpCtx getHelpCtx() { + return null; + } + + public JComponent getComponent(Lookup masterLookup) { + return panel; + } + + public void addPropertyChangeListener(PropertyChangeListener l) { + prop.addPropertyChangeListener(l); + } + + public void removePropertyChangeListener(PropertyChangeListener l) { + prop.removePropertyChangeListener(l); + } +} diff --git a/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/options/icons/Icons.java b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/options/icons/Icons.java new file mode 100644 index 000000000000..3831fdf952ff --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeModel/src/main/java/at/ssw/visualizer/bc/options/icons/Icons.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc.options.icons; + +/** + * + * @author Christian Wimmer + */ +public class Icons { + + private static final String PATH = "at/ssw/visualizer/bc/options/icons/"; + + public static final String BC_OPTION = PATH + "package.gif"; +} diff --git a/visualizer/C1Visualizer/BytecodeModel/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/BytecodeModel/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..58cd5a764430 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeModel/src/main/nbm/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.bc.model +OpenIDE-Module-Layer: at/ssw/visualizer/bc/model/layer.xml +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/bc/model/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/BytecodeModel/src/main/resources/META-INF/services/at.ssw.visualizer.bc.model.BytecodeModel b/visualizer/C1Visualizer/BytecodeModel/src/main/resources/META-INF/services/at.ssw.visualizer.bc.model.BytecodeModel new file mode 100644 index 000000000000..595e968b5da9 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeModel/src/main/resources/META-INF/services/at.ssw.visualizer.bc.model.BytecodeModel @@ -0,0 +1 @@ +at.ssw.visualizer.bc.modelimpl.BytecodeModelImpl diff --git a/visualizer/C1Visualizer/BytecodeModel/src/main/resources/at/ssw/visualizer/bc/model/Bundle.properties b/visualizer/C1Visualizer/BytecodeModel/src/main/resources/at/ssw/visualizer/bc/model/Bundle.properties new file mode 100644 index 000000000000..4100074e983f --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeModel/src/main/resources/at/ssw/visualizer/bc/model/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=Bytecode Model diff --git a/visualizer/C1Visualizer/BytecodeModel/src/main/resources/at/ssw/visualizer/bc/model/layer.xml b/visualizer/C1Visualizer/BytecodeModel/src/main/resources/at/ssw/visualizer/bc/model/layer.xml new file mode 100644 index 000000000000..98fb647182a8 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeModel/src/main/resources/at/ssw/visualizer/bc/model/layer.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/BytecodeModel/src/main/resources/at/ssw/visualizer/bc/options/icons/package.gif b/visualizer/C1Visualizer/BytecodeModel/src/main/resources/at/ssw/visualizer/bc/options/icons/package.gif new file mode 100644 index 000000000000..ced7996a76b0 Binary files /dev/null and b/visualizer/C1Visualizer/BytecodeModel/src/main/resources/at/ssw/visualizer/bc/options/icons/package.gif differ diff --git a/visualizer/C1Visualizer/BytecodeView/pom.xml b/visualizer/C1Visualizer/BytecodeView/pom.xml new file mode 100644 index 000000000000..7e964ddccf19 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeView/pom.xml @@ -0,0 +1,117 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + BytecodeView + 1.14-SNAPSHOT + nbm + BytecodeView + + UTF-8 + + + + at.ssw.visualizer + VisualizerUI + ${project.version} + + + at.ssw.visualizer + BytecodeEditor + ${project.version} + + + at.ssw.visualizer + BytecodeModel + ${project.version} + + + at.ssw.visualizer + CompilationModel + ${project.version} + + + at.ssw.visualizer + TextEditor + ${project.version} + + + org.netbeans.api + org-netbeans-modules-editor + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-lib + ${netbeans.version} + + + org.netbeans.api + org-openide-loaders + ${netbeans.version} + + + org.netbeans.api + org-openide-nodes + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + org.netbeans.api + org-openide-windows + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.BytecodeView + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/BytecodeView/src/main/java/at/ssw/visualizer/bc/view/BCViewTopComponent.java b/visualizer/C1Visualizer/BytecodeView/src/main/java/at/ssw/visualizer/bc/view/BCViewTopComponent.java new file mode 100644 index 000000000000..25b95b5580bd --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeView/src/main/java/at/ssw/visualizer/bc/view/BCViewTopComponent.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc.view; + +import at.ssw.visualizer.bc.BCEditorKit; +import at.ssw.visualizer.bc.model.BCTextBuilder; +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.texteditor.view.AbstractTextViewTopComponent; +import java.io.Serializable; +import org.openide.windows.TopComponent; +import org.openide.windows.WindowManager; + +/** + * Top component which displays the bytecode for a selected block. + * + * @author Alexander Reder + * @author Christian Wimmer + */ +final class BCViewTopComponent extends AbstractTextViewTopComponent { + + private BCViewTopComponent() { + super(new BCEditorKit()); + setName("Bytecodes"); + setToolTipText("Bytecodes"); + } + + @Override + protected String getContent(ControlFlowGraph cfg, BasicBlock[] blocks) { + BCTextBuilder builder = new BCTextBuilder(); + return builder.buildView(cfg, blocks); + } + + // + private static final String PREFERRED_ID = "BCViewTopComponent"; + private static BCViewTopComponent instance; + + public static synchronized BCViewTopComponent getDefault() { + if (instance == null) { + instance = new BCViewTopComponent(); + } + return instance; + } + + public static synchronized BCViewTopComponent findInstance() { + return (BCViewTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID); + } + + @Override + public int getPersistenceType() { + return TopComponent.PERSISTENCE_ALWAYS; + } + + @Override + protected String preferredID() { + return PREFERRED_ID; + } + + @Override + public Object writeReplace() { + return new ResolvableHelper(); + } + + static final class ResolvableHelper implements Serializable { + private static final long serialVersionUID = 1L; + public Object readResolve() { + return BCViewTopComponent.getDefault(); + } + } + // +} diff --git a/visualizer/C1Visualizer/BytecodeView/src/main/java/at/ssw/visualizer/bc/view/ShowBCViewAction.java b/visualizer/C1Visualizer/BytecodeView/src/main/java/at/ssw/visualizer/bc/view/ShowBCViewAction.java new file mode 100644 index 000000000000..a0fc80984833 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeView/src/main/java/at/ssw/visualizer/bc/view/ShowBCViewAction.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.bc.view; + +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; +import org.openide.windows.TopComponent; + +/** + * Action which shows BCView component. + * + * @author Alexander Reder + */ +public class ShowBCViewAction extends AbstractAction { + public ShowBCViewAction() { + super("Bytecodes"); + } + + public void actionPerformed(ActionEvent evt) { + TopComponent win = BCViewTopComponent.findInstance(); + win.open(); + win.requestActive(); + } +} diff --git a/visualizer/C1Visualizer/BytecodeView/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/BytecodeView/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..46ad11a92fab --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeView/src/main/nbm/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.bc.view +OpenIDE-Module-Layer: at/ssw/visualizer/bc/view/layer.xml +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/bc/view/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/BytecodeView/src/main/resources/at/ssw/visualizer/bc/view/BCViewTopComponentSettings.xml b/visualizer/C1Visualizer/BytecodeView/src/main/resources/at/ssw/visualizer/bc/view/BCViewTopComponentSettings.xml new file mode 100644 index 000000000000..85c9b6995abc --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeView/src/main/resources/at/ssw/visualizer/bc/view/BCViewTopComponentSettings.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/visualizer/C1Visualizer/BytecodeView/src/main/resources/at/ssw/visualizer/bc/view/BCViewTopComponentWstcref.xml b/visualizer/C1Visualizer/BytecodeView/src/main/resources/at/ssw/visualizer/bc/view/BCViewTopComponentWstcref.xml new file mode 100644 index 000000000000..7b4d85758d7b --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeView/src/main/resources/at/ssw/visualizer/bc/view/BCViewTopComponentWstcref.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/visualizer/C1Visualizer/BytecodeView/src/main/resources/at/ssw/visualizer/bc/view/Bundle.properties b/visualizer/C1Visualizer/BytecodeView/src/main/resources/at/ssw/visualizer/bc/view/Bundle.properties new file mode 100644 index 000000000000..f1df38cceb82 --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeView/src/main/resources/at/ssw/visualizer/bc/view/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=Bytecode View diff --git a/visualizer/C1Visualizer/BytecodeView/src/main/resources/at/ssw/visualizer/bc/view/layer.xml b/visualizer/C1Visualizer/BytecodeView/src/main/resources/at/ssw/visualizer/bc/view/layer.xml new file mode 100644 index 000000000000..2710385d20bf --- /dev/null +++ b/visualizer/C1Visualizer/BytecodeView/src/main/resources/at/ssw/visualizer/bc/view/layer.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/CompilationModel/pom.xml b/visualizer/C1Visualizer/CompilationModel/pom.xml new file mode 100644 index 000000000000..894e98598682 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/pom.xml @@ -0,0 +1,99 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + CompilationModel + 1.14-SNAPSHOT + nbm + CompilationModel + + UTF-8 + + + + org.netbeans.api + org-netbeans-api-progress + ${netbeans.version} + + + org.netbeans.api + org-netbeans-api-progress-nb + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.model + at.ssw.visualizer.model.bc + at.ssw.visualizer.model.nc + at.ssw.visualizer.model.cfg + at.ssw.visualizer.model.interval + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.5.0 + + + generate-sources + + add-source + + + + ${project.build.directory}/generated-sources/cocor + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/Compilation.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/Compilation.java new file mode 100644 index 000000000000..151cb6eba296 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/Compilation.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.model; + +import java.util.Date; + +/** + * + * @author Christian Wimmer + */ +public interface Compilation extends CompilationElement { + public CompilationModel getCompilationModel(); + + public String getMethod(); + + public Date getDate(); +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/CompilationElement.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/CompilationElement.java new file mode 100644 index 000000000000..40a28ceb7872 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/CompilationElement.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.model; + +import java.util.List; + +/** + * + * @author Christian Wimmer + */ +public interface CompilationElement { + public Compilation getCompilation(); + + public CompilationElement getParent(); + + public List getElements(); + + public String getShortName(); + + public String getName(); +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/CompilationModel.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/CompilationModel.java new file mode 100644 index 000000000000..c208e3c54ec8 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/CompilationModel.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.model; + +import java.util.List; +import javax.swing.event.ChangeListener; + +/** + * + * @author Christian Wimmer + */ +public interface CompilationModel { + public List getCompilations(); + + public String parseInputFile(String fileName); + + public void removeCompilation(Compilation compilation); + + public void clear(); + + public void addChangedListener(ChangeListener listener); + + public void removeChangedListener(ChangeListener listener); +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/bc/Bytecodes.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/bc/Bytecodes.java new file mode 100644 index 000000000000..944d8f36830a --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/bc/Bytecodes.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.model.bc; + +import at.ssw.visualizer.model.cfg.ControlFlowGraph; + +/** + * This class holds the bytecode of a method and provides severel methods + * accessing the details. + * + * @author Alexander Reder + * @author Christian Wimmer + */ +public interface Bytecodes { + /** + * Back-link to the control flow graph where the bytecodes were loaded from. + */ + public ControlFlowGraph getControlFlowGraph(); + + /** + * Called before the first call of getBytecodes() or getEpilogue(). Can be called multiple times. + */ + public void parseBytecodes(); + + /** + * The bytecodes of the method in the given bytecode range. + * + * @param fromBCI starting BCI (including this bci) + * @param toBCI ending BCI (not including this bci) + * @return string representation of the bytecodes + */ + public String getBytecodes(int fromBCI, int toBCI); + + public String getEpilogue(); +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/cfg/BasicBlock.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/cfg/BasicBlock.java new file mode 100644 index 000000000000..2091934f3f11 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/cfg/BasicBlock.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.model.cfg; + +import java.util.List; + +/** + * + * @author Christian Wimmer + */ +public interface BasicBlock { + public ControlFlowGraph getParent(); + + public String getName(); + + public int getFromBci(); + + public int getToBci(); + + public List getPredecessors(); + + public List getSuccessors(); + + public List getXhandlers(); + + public List getFlags(); + + public BasicBlock getDominator(); + + public int getLoopIndex(); + + public int getLoopDepth(); + + public int getFirstLirId(); + + public int getLastLirId(); + + public double getProbability(); + + public boolean hasState(); + + public List getStates(); + + public boolean hasHir(); + + public List getHirInstructions(); + + public boolean hasLir(); + + public List getLirOperations(); +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/cfg/ControlFlowGraph.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/cfg/ControlFlowGraph.java new file mode 100644 index 000000000000..4eb63b25affd --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/cfg/ControlFlowGraph.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.model.cfg; + +import at.ssw.visualizer.model.CompilationElement; +import at.ssw.visualizer.model.bc.Bytecodes; +import at.ssw.visualizer.model.nc.NativeMethod; +import java.util.List; + +/** + * + * @author Christian Wimmer + */ +public interface ControlFlowGraph extends CompilationElement { + public List getBasicBlocks(); + + public BasicBlock getBasicBlockByName(String name); + + public Bytecodes getBytecodes(); + + public NativeMethod getNativeMethod(); + + public boolean hasState(); + + public boolean hasHir(); + + public boolean hasLir(); +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/cfg/IRInstruction.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/cfg/IRInstruction.java new file mode 100644 index 000000000000..4efff579aa95 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/cfg/IRInstruction.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.model.cfg; + +import java.util.Collection; + +/** + * + * @author Christian Wimmer + */ +public interface IRInstruction { + public static String HIR_NAME = "tid"; + public static String HIR_TEXT = "instruction"; + public static String HIR_OPERAND = "result"; + + public static String LIR_NUMBER = "nr"; + public static String LIR_TEXT = "instruction"; + + public Collection getNames(); + public String getValue(String name); +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/cfg/State.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/cfg/State.java new file mode 100644 index 000000000000..63ff4772ad8d --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/cfg/State.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.model.cfg; + +import java.util.List; + +/** + * + * @author Christian Wimmer + */ +public interface State { + public String getKind(); + + public int getSize(); + + public String getMethod(); + + public List getEntries(); +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/cfg/StateEntry.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/cfg/StateEntry.java new file mode 100644 index 000000000000..d893177cfbdc --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/cfg/StateEntry.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.model.cfg; + +import java.util.List; + +/** + * + * @author Christian Wimmer + */ +public interface StateEntry { + public int getIndex(); + + public String getName(); + + public boolean hasPhiOperands(); + + public List getPhiOperands(); + + public String getOperand(); +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/interval/ChildInterval.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/interval/ChildInterval.java new file mode 100644 index 000000000000..2aff9f6395c1 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/interval/ChildInterval.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.model.interval; + +import java.util.List; + +/** + * + * @author Christian Wimmer + */ +public interface ChildInterval { + public Interval getParent(); + + public String getRegNum(); + + public String getType(); + + public String getOperand(); + + public String getSpillState(); + + public ChildInterval getRegisterHint(); + + public List getRanges(); + + public List getUsePositions(); + + public int getFrom(); + + public int getTo(); +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/interval/Interval.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/interval/Interval.java new file mode 100644 index 000000000000..e3bebdd9101f --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/interval/Interval.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.model.interval; + +import java.util.List; + +/** + * + * @author Christian Wimmer + */ +public interface Interval { + public IntervalList getParent(); + + public List getChildren(); + + public String getRegNum(); + + public int getFrom(); + + public int getTo(); +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/interval/IntervalList.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/interval/IntervalList.java new file mode 100644 index 000000000000..afc96422b622 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/interval/IntervalList.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.model.interval; + +import at.ssw.visualizer.model.CompilationElement; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import java.util.List; + +/** + * + * @author Christian Wimmer + */ +public interface IntervalList extends CompilationElement { + public List getIntervals(); + + public ControlFlowGraph getControlFlowGraph(); + + public int getNumLIROperations(); +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/interval/Range.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/interval/Range.java new file mode 100644 index 000000000000..9e2e60c3c149 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/interval/Range.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.model.interval; + +/** + * + * @author Christian Wimmer + */ +public interface Range { + public int getFrom(); + + public int getTo(); +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/interval/UsePosition.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/interval/UsePosition.java new file mode 100644 index 000000000000..a36a7a8ed2b6 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/interval/UsePosition.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.model.interval; + +/** + * + * @author Christian Wimmer + */ +public interface UsePosition { + public char getKind(); + + public int getPosition(); +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/nc/NativeMethod.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/nc/NativeMethod.java new file mode 100644 index 000000000000..75fc0e89bc55 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/model/nc/NativeMethod.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.model.nc; + +import at.ssw.visualizer.model.cfg.ControlFlowGraph; + +/** + * + * @author Alexander Reder + */ +public interface NativeMethod { + + public ControlFlowGraph getControlFlowGraph(); + + public String getMethodText(); + +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/CompilationElementImpl.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/CompilationElementImpl.java new file mode 100644 index 000000000000..57a58a28160d --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/CompilationElementImpl.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.modelimpl; + +import at.ssw.visualizer.model.Compilation; +import at.ssw.visualizer.model.CompilationElement; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * + * @author Christian Wimmer + */ +public class CompilationElementImpl implements CompilationElement { + private Compilation compilation; + private CompilationElement parent; + private CompilationElement[] elements; + private String shortName; + private String name; + + public CompilationElementImpl(String shortName, String name) { + this.shortName = shortName; + this.name = name; + this.elements = new CompilationElement[0]; + } + + public Compilation getCompilation() { + return compilation; + } + + public CompilationElement getParent() { + return parent; + } + + public List getElements() { + return Collections.unmodifiableList(Arrays.asList(elements)); + } + + public void setElements(CompilationElementImpl[] elements, Compilation compilation) { + this.elements = elements; + for (CompilationElementImpl ce : elements) { + ce.parent = this; + ce.compilation = compilation; + } + } + + public String getShortName() { + return shortName; + } + + public String getName() { + return name; + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/CompilationImpl.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/CompilationImpl.java new file mode 100644 index 000000000000..25c151a7886e --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/CompilationImpl.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.modelimpl; + +import at.ssw.visualizer.model.Compilation; +import at.ssw.visualizer.model.CompilationElement; +import at.ssw.visualizer.model.CompilationModel; +import java.util.Date; + +/** + * + * @author Christian Wimmer + */ +public class CompilationImpl extends CompilationElementImpl implements Compilation { + private CompilationModel compilationModel; + private String method; + private Date date; + + public CompilationImpl(String shortName, String name, String method, Date date) { + super(shortName, name); + this.method = method; + this.date = date; + } + + public CompilationModel getCompilationModel() { + return compilationModel; + } + + public void setCompilationModel(CompilationModelImpl compilationModel) { + this.compilationModel = compilationModel; + } + + public String getMethod() { + return method; + } + + public Date getDate() { + return date; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append(" Name: ").append(getName()); + result.append("\n Method: ").append(method); + result.append("\n Date: ").append(date); + result.append("\n Elements: ").append(getElements().size()); + result.append("\n"); + for (CompilationElement element : getElements()) { + result.append(element); + } + return result.toString(); + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/CompilationModelImpl.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/CompilationModelImpl.java new file mode 100644 index 000000000000..296f23deab6d --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/CompilationModelImpl.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.modelimpl; + +import at.ssw.visualizer.model.Compilation; +import at.ssw.visualizer.model.CompilationModel; +import at.ssw.visualizer.parser.CompilationParser; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +/** + * + * @author Christian Wimmer + */ +public class CompilationModelImpl implements CompilationModel { + private List compilations = new ArrayList(); + private List listeners = new ArrayList(); + + private boolean parsing; + + public List getCompilations() { + return Collections.unmodifiableList(compilations); + } + + public String parseInputFile(String fileName) { + parsing = true; + String result; + try { + result = CompilationParser.parseInputFile(fileName, this); + } finally { + parsing = false; + notifyListeners(); + } + return result; + } + + public void addCompilation(CompilationImpl compilation) { + this.compilations.add(compilation); + compilation.setCompilationModel(this); + + if (!parsing || this.compilations.size() % 40 == 0) { + notifyListeners(); + } + } + + public void removeCompilation(Compilation compilation) { + compilations.remove(compilation); + notifyListeners(); + } + + public void clear() { + compilations.clear(); + notifyListeners(); + } + + + public void addChangedListener(ChangeListener listener) { + listeners.add(listener); + } + + public void removeChangedListener(ChangeListener listener) { + listeners.remove(listener); + } + + private void notifyListeners() { + ChangeEvent event = new ChangeEvent(this); + for (ChangeListener listener : listeners) { + listener.stateChanged(event); + } + } + + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append("Compilations: "); + result.append(compilations.size()); + result.append("\n"); + for (Compilation compilation : compilations) { + result.append(compilation); + } + return result.toString(); + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/bc/BytecodesImpl.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/bc/BytecodesImpl.java new file mode 100644 index 000000000000..5bf2fae4bf85 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/bc/BytecodesImpl.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.modelimpl.bc; + +import at.ssw.visualizer.model.bc.Bytecodes; +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.modelimpl.cfg.BasicBlockImpl; +import java.util.Arrays; + +/** + * This class holds the bytecode of a method and provides severel methods + * accessing the details. + * + * @author Christian Wimmer + */ +public class BytecodesImpl implements Bytecodes { + private ControlFlowGraph controlFlowGraph; + private String bytecodeString; + + private String[] bytecodes; + private String epilogue; + + + public BytecodesImpl(ControlFlowGraph controlFlowGraph, String bytecodeString) { + this.controlFlowGraph = controlFlowGraph; + this.bytecodeString = bytecodeString; + } + + public void parseBytecodes() { + String[] lines = bytecodeString.split("\n"); + + boolean inPrologue = true; + String[] result = new String[lines.length * 3]; + int lastBci = -1; + int lnr = 0; + for (; lnr < lines.length; lnr++) { + String line = lines[lnr]; + + line = line.trim(); + if (line.startsWith("[")) { + int end = line.indexOf(']'); + if (end != -1) { + line = line.substring(end + 1, line.length()); + } + } + + line = line.trim(); + int space1 = line.indexOf(' '); + if (space1 <= 0) { + if (inPrologue) { + continue; + } else { + break; + } + } + String bciStr = line.substring(0, space1); + if (bciStr.endsWith(":")) { + bciStr = bciStr.substring(0, bciStr.length() - 1); + } + + int bci; + try { + bci = Integer.parseInt(bciStr); + } catch (NumberFormatException ex) { + // Ignore invalid lines. + if (inPrologue) { + continue; + } else { + break; + } + } + + String opcode = line.substring(space1 + 1); + String params = ""; + int space2 = opcode.indexOf(' '); + if (space2 > 0) { + params = opcode.substring(space2 + 1).trim(); + opcode = opcode.substring(0, space2); + } + String tail = ""; + int space3 = params.indexOf('|'); + if (space3 >= 0) { + tail = params.substring(space3); + params = params.substring(0, space3); + } + +// if (!"ldc".equals(opcode) || !params.startsWith("\"")) { +// // Separate packages with "." instead of "/" +// params = params.replace('/', '.'); +// } + + String printLine = bciStr + ":" + " ".substring(Math.min(bciStr.length(), 3)) + + opcode + " ".substring(Math.min(opcode.length(), 13)) + + params + " ".substring(Math.min(params.length(), 8)) + + tail; + + + if (bci >= result.length) { + result = Arrays.copyOf(result, Math.max(bci + 1, result.length * 2)); + } + result[bci] = printLine; + inPrologue = false; + lastBci = Math.max(lastBci, bci); + } + + StringBuilder epilogueBuilder = new StringBuilder(); + for (; lnr < lines.length; lnr++) { + epilogueBuilder.append(lines[lnr]).append("\n"); + } + epilogue = epilogueBuilder.toString(); + bytecodes = Arrays.copyOf(result, lastBci + 1); + + + BasicBlockImpl[] blocks = new BasicBlockImpl[bytecodes.length]; + for (BasicBlock b : controlFlowGraph.getBasicBlocks()) { + if (b instanceof BasicBlockImpl) { + BasicBlockImpl block = (BasicBlockImpl) b; + if (block.getToBci() != -1) { + // Do not override existing values. + return; + } + if (block.getFromBci() >= 0 && block.getFromBci() < blocks.length) { + blocks[block.getFromBci()] = block; + } + } + } + + int curToBci = -1; + for (int i = blocks.length - 1; i >= 0; i--) { + if (bytecodes[i] != null && curToBci == -1) { + curToBci = i; + } + if (blocks[i] != null) { + blocks[i].setToBci(curToBci); + curToBci = -1; + } + } + } + + public ControlFlowGraph getControlFlowGraph() { + return controlFlowGraph; + } + + public String getBytecodes(int fromBCI, int toBCI) { + if (fromBCI < 0) { + return ""; + } + toBCI = Math.min(toBCI, bytecodes.length); + StringBuilder sb = new StringBuilder(); + for (int i = fromBCI; i < toBCI; i++) { + if (bytecodes[i] != null) { + sb.append(bytecodes[i]).append("\n"); + } + } + return sb.toString(); + } + + @Override + public String getEpilogue() { + return epilogue; + } + + @Override + public String toString() { + return "Bytecodes " + getControlFlowGraph().getName(); + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/cfg/BasicBlockImpl.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/cfg/BasicBlockImpl.java new file mode 100644 index 000000000000..83f6f2c2e4be --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/cfg/BasicBlockImpl.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.modelimpl.cfg; + +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.model.cfg.IRInstruction; +import at.ssw.visualizer.model.cfg.State; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * + * @author Christian Wimmer + */ +public class BasicBlockImpl implements BasicBlock { + + private ControlFlowGraph parent; + private String name; + private int fromBci; + private int toBci; + private BasicBlock[] predecessors; + private BasicBlock[] successors; + private BasicBlock[] xhandlers; + private String[] flags; + private BasicBlock dominator; + private int loopIndex; + private int loopDepth; + private int firstLirId; + private int lastLirId; + private double probability; + private State[] states; + private IRInstruction[] hirInstructions; + private IRInstruction[] lirOperations; + + public void setValues(String name, int fromBci, int toBci, BasicBlock[] predecessors, BasicBlock[] successors, BasicBlock[] xhandlers, String[] flags, BasicBlock dominator, int loopIndex, int loopDepth, int firstLirId, int lastLirId, double probability, State[] states, IRInstruction[] hirInstructions, IRInstruction[] lirOperations) { + this.name = name; + this.fromBci = fromBci; + this.toBci = toBci; + + this.predecessors = predecessors; + this.successors = successors; + this.xhandlers = xhandlers; + + this.flags = flags; + this.dominator = dominator; + this.loopIndex = loopIndex; + this.loopDepth = loopDepth; + this.firstLirId = firstLirId; + this.lastLirId = lastLirId; + this.probability = probability; + + this.states = states; + this.hirInstructions = hirInstructions; + this.lirOperations = lirOperations; + } + + public ControlFlowGraph getParent() { + return parent; + } + + protected void setParent(ControlFlowGraphImpl parent) { + this.parent = parent; + } + + public String getName() { + return name; + } + + public int getFromBci() { + return fromBci; + } + + public int getToBci() { + return toBci; + } + + public void setToBci(int toBci) { + this.toBci = toBci; + } + + public List getPredecessors() { + return Collections.unmodifiableList(Arrays.asList(predecessors)); + } + + public List getSuccessors() { + return Collections.unmodifiableList(Arrays.asList(successors)); + } + + public List getXhandlers() { + return Collections.unmodifiableList(Arrays.asList(xhandlers)); + } + + public List getFlags() { + return Collections.unmodifiableList(Arrays.asList(flags)); + } + + public BasicBlock getDominator() { + return dominator; + } + + public int getLoopIndex() { + return loopIndex; + } + + public int getLoopDepth() { + return loopDepth; + } + + public int getFirstLirId() { + return firstLirId; + } + + public int getLastLirId() { + return lastLirId; + } + + public double getProbability() { + return probability; + } + + public boolean hasState() { + return states != null; + } + + public List getStates() { + return Collections.unmodifiableList(Arrays.asList(states)); + } + + public boolean hasHir() { + return hirInstructions != null; + } + + public List getHirInstructions() { + return Collections.unmodifiableList(Arrays.asList(hirInstructions)); + } + + public boolean hasLir() { + return lirOperations != null; + } + + public List getLirOperations() { + return Collections.unmodifiableList(Arrays.asList(lirOperations)); + } + + @Override + public String toString() { + return name; + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/cfg/ControlFlowGraphImpl.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/cfg/ControlFlowGraphImpl.java new file mode 100644 index 000000000000..c87d9974a194 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/cfg/ControlFlowGraphImpl.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.modelimpl.cfg; + +import at.ssw.visualizer.model.bc.Bytecodes; +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.model.nc.NativeMethod; +import at.ssw.visualizer.modelimpl.CompilationElementImpl; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * + * @author Christian Wimmer + */ +public class ControlFlowGraphImpl extends CompilationElementImpl implements ControlFlowGraph { + private BasicBlock[] basicBlocks; + private Map blockNames; + private Bytecodes bytecodes; + private NativeMethod nativeMethod; + private boolean hasState; + private boolean hasHir; + private boolean hasLir; + + public ControlFlowGraphImpl(String shortName, String name, BasicBlockImpl[] basicBlocks) { + super(shortName, name); + this.basicBlocks = basicBlocks; + + blockNames = new HashMap(basicBlocks.length); + nativeMethod = null; + for (BasicBlockImpl block : basicBlocks) { + block.setParent(this); + blockNames.put(block.getName(), block); + hasState |= block.hasState(); + hasHir |= block.hasHir(); + hasLir |= block.hasLir(); + } + } + + public List getBasicBlocks() { + return Collections.unmodifiableList(Arrays.asList(basicBlocks)); + } + + public BasicBlock getBasicBlockByName(String name) { + return blockNames.get(name); + } + + public Bytecodes getBytecodes() { + return bytecodes; + } + + public void setBytecodes(Bytecodes bytecodes) { + this.bytecodes = bytecodes; + } + + public NativeMethod getNativeMethod() { + return nativeMethod; + } + + public void setNativeMethod(NativeMethod nativeMethod) { + this.nativeMethod = nativeMethod; + } + + public boolean hasState() { + return hasState; + } + + public boolean hasHir() { + return hasHir; + } + + public boolean hasLir() { + return hasLir; + } + + @Override + public String toString() { + return " CFG \"" + getName() + "\": " + basicBlocks.length + " blocks\n"; + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/cfg/IRInstructionImpl.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/cfg/IRInstructionImpl.java new file mode 100644 index 000000000000..31927e3b9413 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/cfg/IRInstructionImpl.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.modelimpl.cfg; + +import at.ssw.visualizer.model.cfg.IRInstruction; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map.Entry; + +public class IRInstructionImpl implements IRInstruction { + + private final String[] keys; + private final String[] values; + + public IRInstructionImpl(LinkedHashMap data) { + String[] skeys = new String[data.size()]; + String[] svalues = new String[data.size()]; + int index = 0; + for (Entry e : data.entrySet()) { + skeys[index] = e.getKey().intern(); + svalues[index++] = e.getValue().intern(); + } + if (Arrays.equals(skeys, twoKeys)) { + keys = twoKeys; + } else { + keys = skeys; + } + values = svalues; + } + + public IRInstructionImpl(String pinned, int bci, int useCount, String name, String text, String operand) { + final String p = checkIntern(pinned); + final String b = Integer.toString(bci).intern(); + final String u = Integer.toString(useCount).intern(); + final String n = checkIntern(name); + final String i = checkIntern(text); + if (operand != null) { + keys = withOperandKeys; + values = new String[]{p, b, u, n, checkIntern(operand), i}; + } else { + keys = noOperandKeys; + values = new String[]{p, b, u, n, i}; + } + } + + public IRInstructionImpl(int number, String text) { + keys = twoKeys; + values = new String[]{Integer.toString(number).intern(), checkIntern(text)}; + } + + static final String[] twoKeys = new String[]{LIR_NUMBER, LIR_TEXT}; + static final String[] noOperandKeys = new String[]{"p", "bci", "use", HIR_NAME, HIR_TEXT}; + static final String[] withOperandKeys = new String[]{"p", "bci", "use", HIR_NAME, HIR_OPERAND, HIR_TEXT}; + + public Collection getNames() { + return Collections.unmodifiableList(Arrays.asList(keys)); + } + + public String getValue(String name) { + for (int i = 0; i < keys.length; i++) { + if (keys[i].equals(name)) { + return values[i]; + } + } + return null; + } + + private String checkIntern(String s) { + if (s != s.intern()) { + throw new InternalError("non-interned String passed to IRInstructionImpl constructor"); + } + return s; + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/cfg/StateEntryImpl.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/cfg/StateEntryImpl.java new file mode 100644 index 000000000000..2446fa1e4b95 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/cfg/StateEntryImpl.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.modelimpl.cfg; + +import at.ssw.visualizer.model.cfg.StateEntry; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * + * @author Christian Wimmer + */ +public class StateEntryImpl implements StateEntry { + private int index; + private String name; + private String[] phiOperands; + private String operand; + + public StateEntryImpl(int index, String name, String[] phiOperands, String operand) { + this.index = index; + this.name = name; + this.phiOperands = phiOperands; + this.operand = operand; + } + + public int getIndex() { + return index; + } + + public String getName() { + return name; + } + + public boolean hasPhiOperands() { + return phiOperands != null; + } + + public List getPhiOperands() { + return Collections.unmodifiableList(Arrays.asList(phiOperands)); + } + + public String getOperand() { + return operand; + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/cfg/StateImpl.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/cfg/StateImpl.java new file mode 100644 index 000000000000..64b328f4f5f7 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/cfg/StateImpl.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.modelimpl.cfg; + +import at.ssw.visualizer.model.cfg.State; +import at.ssw.visualizer.model.cfg.StateEntry; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * + * @author Christian Wimmer + */ +public class StateImpl implements State { + private String kind; + private int size; + private String method; + private StateEntry[] entries; + + public StateImpl(String kind, int size, String method, StateEntryImpl[] entries) { + this.kind = kind; + this.size = size; + this.method = method; + this.entries = entries; + } + + public String getKind() { + return kind; + } + + public int getSize() { + return size; + } + + public String getMethod() { + return method; + } + + public List getEntries() { + return Collections.unmodifiableList(Arrays.asList(entries)); + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/interval/ChildIntervalImpl.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/interval/ChildIntervalImpl.java new file mode 100644 index 000000000000..4c2f40459b79 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/interval/ChildIntervalImpl.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.modelimpl.interval; + +import at.ssw.visualizer.model.interval.ChildInterval; +import at.ssw.visualizer.model.interval.Interval; +import at.ssw.visualizer.model.interval.Range; +import at.ssw.visualizer.model.interval.UsePosition; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * + * @author Christian Wimmer + */ +public class ChildIntervalImpl implements ChildInterval, Comparable { + private Interval parent; + private String regNum; + private String type; + private String operand; + private String spillState; + private ChildInterval registerHint; + private Range[] ranges; + private UsePosition[] usePositions; + + public void setValues(String regNum, String type, String operand, String spillState, ChildInterval registerHint, Range[] ranges, UsePosition[] usePositions) { + this.regNum = regNum; + this.type = type; + this.operand = operand; + this.spillState = spillState; + this.registerHint = registerHint; + this.ranges = ranges; + this.usePositions = usePositions; + } + + public Interval getParent() { + return parent; + } + + protected void setParent(IntervalImpl parent) { + this.parent = parent; + } + + public String getRegNum() { + return regNum; + } + + public String getType() { + return type; + } + + public String getOperand() { + return operand; + } + + public String getSpillState() { + return spillState; + } + + public ChildInterval getRegisterHint() { + return registerHint; + } + + public List getRanges() { + return Collections.unmodifiableList(Arrays.asList(ranges)); + } + + public List getUsePositions() { + return Collections.unmodifiableList(Arrays.asList(usePositions)); + } + + + public int getFrom() { + return ranges[0].getFrom(); + } + + public int getTo() { + return ranges[ranges.length - 1].getTo(); + } + + + public int compareTo(ChildIntervalImpl other) { + return getFrom() - other.getFrom(); + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append(regNum); + result.append(": "); + result.append(getType()); + result.append(", "); + result.append(getOperand()); + result.append(", "); + if (registerHint != null) { + result.append(registerHint.getRegNum()); + } else { + result.append("null"); + } + + result.append(" "); + for (int i = 0; i < ranges.length; i++) { + if (i > 0) { + result.append(", "); + } + result.append(ranges[i]); + } + + result.append(" "); + for (int i = 0; i < usePositions.length; i++) { + if (i > 0) { + result.append(", "); + } + result.append(usePositions[i]); + } + + return result.toString(); + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/interval/IntervalImpl.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/interval/IntervalImpl.java new file mode 100644 index 000000000000..fc7c3d2d4a1b --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/interval/IntervalImpl.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.modelimpl.interval; + +import at.ssw.visualizer.model.interval.ChildInterval; +import at.ssw.visualizer.model.interval.Interval; +import at.ssw.visualizer.model.interval.IntervalList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * + * @author Christian Wimmer + */ +public class IntervalImpl implements Interval { + private IntervalList parent; + private ChildInterval[] children; + + public IntervalImpl(ChildIntervalImpl[] children) { + this.children = children; + for (ChildIntervalImpl child : children) { + child.setParent(this); + } + } + + public IntervalList getParent() { + return parent; + } + + protected void setParent(IntervalListImpl parent) { + this.parent = parent; + } + + public List getChildren() { + return Collections.unmodifiableList(Arrays.asList(children)); + } + + public String getRegNum() { + return children[0].getRegNum(); + } + + public int getFrom() { + return children[0].getFrom(); + } + + public int getTo() { + return children[children.length - 1].getTo(); + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + for (int i = 0; i < children.length; i++) { + if (i > 0) { + result.append("\n "); + } + result.append(children[i]); + } + return result.toString(); + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/interval/IntervalListImpl.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/interval/IntervalListImpl.java new file mode 100644 index 000000000000..819294eba126 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/interval/IntervalListImpl.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.modelimpl.interval; + +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.model.interval.Interval; +import at.ssw.visualizer.model.interval.IntervalList; +import at.ssw.visualizer.modelimpl.CompilationElementImpl; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * + * @author Christian Wimmer + */ +public class IntervalListImpl extends CompilationElementImpl implements IntervalList { + private Interval[] intervals; + private ControlFlowGraph controlFlowGraph; + private int numLIROperations; + + public IntervalListImpl(String shortName, String name, IntervalImpl[] intervals, ControlFlowGraph controlFlowGraph) { + super(shortName, name); + this.intervals = intervals; + this.controlFlowGraph = controlFlowGraph; + + for (IntervalImpl interval : intervals) { + interval.setParent(this); + numLIROperations = Math.max(numLIROperations, interval.getTo()); + } + for (BasicBlock basicBlock : controlFlowGraph.getBasicBlocks()) { + numLIROperations = Math.max(numLIROperations, basicBlock.getLastLirId() + 2); + } + } + + + public List getIntervals() { + return Collections.unmodifiableList(Arrays.asList(intervals)); + } + + public ControlFlowGraph getControlFlowGraph() { + return controlFlowGraph; + } + + public int getNumLIROperations() { + return numLIROperations; + } + + + @Override + public String toString() { + return " Intervals \"" + getName() + "\": " + intervals.length + " intervals\n"; + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/interval/RangeImpl.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/interval/RangeImpl.java new file mode 100644 index 000000000000..9d39e551ae35 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/interval/RangeImpl.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.modelimpl.interval; + +import at.ssw.visualizer.model.interval.Range; + +/** + * + * @author Christian Wimmer + */ +public class RangeImpl implements Range, Comparable { + private int from; + private int to; + + public RangeImpl(int from, int to) { + this.from = from; + this.to = to; + } + + + public int getFrom() { + return from; + } + + public int getTo() { + return to; + } + + + public int compareTo(RangeImpl other) { + return getFrom() - other.getFrom(); + } + + @Override + public String toString() { + return "[" + from + ", " + to + "]"; + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/interval/UsePositionImpl.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/interval/UsePositionImpl.java new file mode 100644 index 000000000000..2e9a1e6719a3 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/interval/UsePositionImpl.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.modelimpl.interval; + +import at.ssw.visualizer.model.interval.UsePosition; + +/** + * + * @author Christian Wimmer + */ +public class UsePositionImpl implements UsePosition, Comparable { + private int position; + private char kind; + + public UsePositionImpl(int position, char kind) { + this.position = position; + this.kind = kind; + } + + + public char getKind() { + return kind; + } + + public int getPosition() { + return position; + } + + + public int compareTo(UsePositionImpl other) { + return getPosition() - other.getPosition(); + } + + @Override + public String toString() { + return position + "(" + kind + ")"; + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/nc/NativeMethodImpl.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/nc/NativeMethodImpl.java new file mode 100644 index 000000000000..523fdbbb7bd1 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/modelimpl/nc/NativeMethodImpl.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.modelimpl.nc; + +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.model.nc.NativeMethod; + +/** + * + * @author Alexander Reder + */ +public class NativeMethodImpl implements NativeMethod { + + private ControlFlowGraph controlFlowGraph; + private String methodText; + + public NativeMethodImpl(ControlFlowGraph controlFlowGraph, String methodText) { + this.controlFlowGraph = controlFlowGraph; + this.methodText = methodText; + } + + public ControlFlowGraph getControlFlowGraph() { + return controlFlowGraph; + } + + public String getMethodText() { + return methodText; + } + +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/BBHelper.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/BBHelper.java new file mode 100644 index 000000000000..955e804cb12d --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/BBHelper.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.parser; + +import java.util.ArrayList; +import java.util.List; +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.IRInstruction; +import at.ssw.visualizer.model.cfg.State; +import at.ssw.visualizer.modelimpl.cfg.BasicBlockImpl; + +/** + * + * @author Christian Wimmer + */ +public class BBHelper { + + protected String name; + protected int fromBci; + protected int toBci; + protected String[] predecessors; + protected String[] successors; + protected String[] xhandlers; + protected String[] flags; + protected String dominator; + protected int loopIndex; + protected int loopDepth; + protected int firstLirId; + protected int lastLirId; + protected double probability = Double.NaN; + protected List states = new ArrayList(); + protected List hirInstructions = new ArrayList(); + protected List lirOperations = new ArrayList(); + protected BasicBlockImpl basicBlock = new BasicBlockImpl(); + protected List defPredecessorsList = new ArrayList(); + protected List calcPredecessorsList = new ArrayList(); + protected List successorsList = new ArrayList(); + protected List xhandlersList = new ArrayList(); +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/CFGHelper.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/CFGHelper.java new file mode 100644 index 000000000000..c6ed49ec5d9b --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/CFGHelper.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.parser; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.IRInstruction; +import at.ssw.visualizer.model.cfg.State; +import at.ssw.visualizer.modelimpl.cfg.BasicBlockImpl; +import at.ssw.visualizer.modelimpl.cfg.ControlFlowGraphImpl; + +/** + * + * @author Christian Wimmer + */ +public class CFGHelper { + + protected String shortName; + protected String name; + protected int id; + protected int parentId; + private Map helpers = new HashMap(); + private List helpersList = new ArrayList(); + protected List elements = new ArrayList(); + protected ControlFlowGraphImpl resolved; + + public void add(BBHelper helper) { + helpers.put(helper.name, helper); + helpersList.add(helper); + } + + public ControlFlowGraphImpl resolve(CompilationHelper lastComp, Parser parser) { + for (BBHelper helper : helpersList) { + for (String predecessorName : helper.predecessors) { + BBHelper predecessor = helpers.get(predecessorName); + if (predecessor != null) { + helper.defPredecessorsList.add(predecessor.basicBlock); + } else { + // ignore + // parser.SemErr("Undefined predecessor: " + predecessorName); + } + } + + for (String successorName : helper.successors) { + BBHelper successor = helpers.get(successorName); + if (successor != null) { + helper.successorsList.add(successor.basicBlock); + successor.calcPredecessorsList.add(helper.basicBlock); + } else { + // ignore + // parser.SemErr("Undefined successor: " + successorName); + } + } + + for (String xhandlerName : helper.xhandlers) { + BBHelper xhandler = helpers.get(xhandlerName); + if (xhandler != null) { + helper.xhandlersList.add(xhandler.basicBlock); + xhandler.calcPredecessorsList.add(helper.basicBlock); + } else { + // ignore + // parser.SemErr("Undefined xhandler: " + xhandlerName); + } + } + } + + BasicBlockImpl[] basicBlocks = new BasicBlockImpl[helpersList.size()]; + int idx = 0; + for (BBHelper helper : helpersList) { + basicBlocks[idx++] = helper.basicBlock; + + List predecessorsList; + if (helper.defPredecessorsList.size() > 0) { + // check if defined and calculated predecessors are equal + if (helper.defPredecessorsList.size() != helper.calcPredecessorsList.size()) { + parser.SemErr("Defined and calculated predecessors size different: " + helper.name); + } else { + for (BasicBlock block : helper.defPredecessorsList) { + if (!helper.calcPredecessorsList.remove(block)) { + parser.SemErr("Defined and calculated predecessors not matching: " + helper.name); + } + } + if (helper.calcPredecessorsList.size() > 0) { + // should never come here, but just checking... + parser.SemErr("Defined and calculated predecessors not matching: " + helper.name); + } + } + predecessorsList = helper.defPredecessorsList; + } else { + predecessorsList = helper.calcPredecessorsList; + } + + BasicBlock[] predecessors = predecessorsList.toArray(new BasicBlock[predecessorsList.size()]); + BasicBlock[] successors = helper.successorsList.toArray(new BasicBlock[helper.successorsList.size()]); + BasicBlock[] xhandlers = helper.xhandlersList.toArray(new BasicBlock[helper.xhandlersList.size()]); + + BasicBlock dominator = null; + if (helpers.get(helper.dominator) != null) { + dominator = helpers.get(helper.dominator).basicBlock; + } + + State[] states = null; + if (helper.states.size() > 0) { + states = helper.states.toArray(new State[helper.states.size()]); + } + + IRInstruction[] hirInstructions = null; + if (helper.hirInstructions.size() > 0) { + hirInstructions = helper.hirInstructions.toArray(new IRInstruction[helper.hirInstructions.size()]); + } + IRInstruction[] lirOperations = null; + if (helper.lirOperations.size() > 0) { + lirOperations = helper.lirOperations.toArray(new IRInstruction[helper.lirOperations.size()]); + + if (helper.firstLirId == 0) { + try { + helper.firstLirId = Integer.parseInt(lirOperations[0].getValue(IRInstruction.LIR_NUMBER)); + } catch (NumberFormatException ex) { + // Silently ignore invalid numbers. + } + } + if (helper.lastLirId == 0) { + try { + helper.lastLirId = Integer.parseInt(lirOperations[lirOperations.length - 1].getValue(IRInstruction.LIR_NUMBER)); + } catch (NumberFormatException ex) { + // Silently ignore invalid numbers. + } + } + } + + helper.basicBlock.setValues(helper.name, helper.fromBci, helper.toBci, predecessors, successors, xhandlers, helper.flags, dominator, helper.loopIndex, helper.loopDepth, helper.firstLirId, helper.lastLirId, helper.probability, states, hirInstructions, lirOperations); + } + + resolved = new ControlFlowGraphImpl(shortName, name, basicBlocks); + + if (parentId == 0) { + lastComp.elements.add(resolved); + } else { + CFGHelper parent = lastComp.idToCFG.get(parentId); + if (parent != null) { + parent.elements.add(resolved); + } else { + parser.SemErr("Undefined compilation id: " + parentId); + } + } + if (id != 0) { + lastComp.idToCFG.put(id, this); + } + + return resolved; + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/CompilationHelper.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/CompilationHelper.java new file mode 100644 index 000000000000..d9ad0ee362ab --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/CompilationHelper.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.parser; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import at.ssw.visualizer.modelimpl.CompilationElementImpl; +import at.ssw.visualizer.modelimpl.CompilationImpl; +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author Christian Wimmer + */ +public class CompilationHelper { + protected String shortName; + protected String name; + protected String method; + protected Date date; + protected List elements = new ArrayList(); + + protected Map idToCFG = new HashMap(); + + protected CompilationImpl resolve() { + CompilationImpl compilation = new CompilationImpl(shortName, name, method, date); + + compilation.setElements(elements.toArray(new CompilationElementImpl[elements.size()]), compilation); + for (CFGHelper cfg : idToCFG.values()) { + cfg.resolved.setElements(cfg.elements.toArray(new CompilationElementImpl[cfg.elements.size()]), compilation); + } + + return compilation; + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/CompilationParser.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/CompilationParser.java new file mode 100644 index 000000000000..1b698a886de6 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/CompilationParser.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.parser; + +import at.ssw.visualizer.modelimpl.CompilationModelImpl; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.api.progress.ProgressHandleFactory; +import org.openide.util.Cancellable; + +/** + * + * @author Christian Wimmer + */ +public class CompilationParser { + public static String parseInputFile(String fileName, CompilationModelImpl compilationData) { + ProgressHandle progressHandle = ProgressHandleFactory.createHandle("Parsing input file \"" + fileName + "\"", new CancelParsing()); + Scanner scanner = null; + try { + scanner = new Scanner(fileName, progressHandle); + Parser parser = new Parser(scanner); + parser.setCompilationModel(compilationData); + parser.Parse(); + + if (parser.hasErrors()) { + return parser.getErrors(); + } else { + return null; + } + } catch (UserCanceledError ex) { + // user canceled parsing, so report no error + return null; + } catch (Error ex) { + return ex.getMessage(); + } catch (Throwable ex) { + // catch everything else that might happen + return ex.getClass().getName() + (ex.getMessage() != null ? ": " + ex.getMessage() : ""); + } finally { + progressHandle.finish(); + } + } + + private static class CancelParsing implements Cancellable { + public boolean cancel() { + throw new UserCanceledError(); + } + } + + private static class UserCanceledError extends Error { + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/CompilerOutput.atg b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/CompilerOutput.atg new file mode 100644 index 000000000000..3929bbd11eae --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/CompilerOutput.atg @@ -0,0 +1,357 @@ +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.Date; +import java.util.List; + +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.modelimpl.CompilationImpl; +import at.ssw.visualizer.modelimpl.CompilationModelImpl; +import at.ssw.visualizer.modelimpl.cfg.ControlFlowGraphImpl; +import at.ssw.visualizer.modelimpl.cfg.IRInstructionImpl; +import at.ssw.visualizer.modelimpl.cfg.StateImpl; +import at.ssw.visualizer.modelimpl.cfg.StateEntryImpl; +import at.ssw.visualizer.modelimpl.interval.IntervalListImpl; +import at.ssw.visualizer.modelimpl.interval.RangeImpl; +import at.ssw.visualizer.modelimpl.interval.UsePositionImpl; +import at.ssw.visualizer.modelimpl.nc.NativeMethodImpl; +import at.ssw.visualizer.modelimpl.bc.BytecodesImpl; + +COMPILER InputFile + +private CompilationModelImpl compilationModel; + +public void setCompilationModel(CompilationModelImpl compilationModel) { + this.compilationModel = compilationModel; +} + +private String simpleName(String className) { + int index = className.lastIndexOf('.'); + if (index < 0) { + return className; + } + return className.substring(index + 1); +} + +private String shortName(String name) { + name = longName(name); + String params = ""; + + int openParam = name.indexOf('('); + if (openParam >= 0) { + int closeParam = name.indexOf(')', openParam); + if (closeParam >= 0) { + String[] parts = name.substring(openParam + 1, closeParam).split(", *"); + for (int i = 0; i < parts.length; i++) { + if (!params.isEmpty()) { + params += ","; + } + params += simpleName(parts[i]); + } + params = "(" + params + ")"; + } + name = name.substring(0, openParam); + } + + int methodPoint = name.lastIndexOf("."); + if (methodPoint < 0) { + return name + params; + } + int classPoint = name.lastIndexOf(".", methodPoint - 1); + if (classPoint < 0) { + return name + params; + } + return name.substring(classPoint + 1) + params; +} + +private String longName(String name) { + return name.replace("::", "."); +} + +CHARACTERS + identCh = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-|:*". + cr = '\r'. + lf = '\n'. + +TOKENS + ident = identCh { identCh }. + +IGNORE cr + lf + +/*-------------------------------------------------------------------------*/ + +PRODUCTIONS + + +InputFile = (. CompilationHelper lastComp = null; + ControlFlowGraphImpl lastCFG = null; + IntervalListImpl lastIL = null; .) +{ + Compilation (. if (lastComp != null) compilationModel.addCompilation(lastComp.resolve()); + lastComp = curComp; .) +| + CFG +| + IntervalList (. lastComp.elements.add(lastIL); .) +| + NativeMethod +| + Bytecodes +} (. if (lastComp != null) compilationModel.addCompilation(lastComp.resolve()); .) + +. + + +Compilation = (. helper = new CompilationHelper(); .) +"begin_compilation" + "name" StringValue (. helper.name = longName(name); helper.shortName = shortName(name); .) + "method" StringValue (. helper.method = longName(method); .) + "date" DateValue +"end_compilation". + + + +CFG = (. CFGHelper helper = new CFGHelper(); .) +"begin_cfg" + "name" StringValue (. helper.name = longName(name); helper.shortName = shortName(name); .) + [ + "id" IntegerValue + "caller_id" IntegerValue + ] + { + BasicBlock (. helper.add(basicBlock); .) + } (. res = helper.resolve(lastComp, this); .) +"end_cfg". + + +BasicBlock = (. helper = new BBHelper(); .) +"begin_block" + "name" StringValue + "from_bci" IntegerValue + "to_bci" IntegerValue + + "predecessors" StringList + "successors" StringList + "xhandlers" StringList + "flags" StringList + + [ "dominator" StringValue ] + [ "loop_index" IntegerValue ] + [ "loop_depth" IntegerValue ] + [ "first_lir_id" IntegerValue ] + [ "last_lir_id" IntegerValue ] + [ "probability" DoubleValue ] + + [ StateList ] + [ HIR ] + [ LIR ] + { IR } +"end_block". + + +StateList = +"begin_states" + { + "begin_stack" + State (. helper.states.add(state); .) + "end_stack" + | + "begin_locks" + State (. helper.states.add(state); .) + "end_locks" + | + "begin_locals" + State (. helper.states.add(state); .) + "end_locals" + } +"end_states". + + +State = (. String method = ""; ArrayList entries = new ArrayList(); .) + "size" IntegerValue + [ "method" StringValue ] + { + StateEntry (. entries.add(entry); .) + } (. res = new StateImpl(kind, size, longName(method), entries.toArray(new StateEntryImpl[entries.size()])); .) +. + +StateEntry = (. String[] operands = null; String operand = null; .) + IntegerValue + HIRName + [ + "[" (. ArrayList operandsList = new ArrayList(); .) + { + HIRName (. operandsList.add(opd); .) + } + "]" (. operands = operandsList.toArray(new String[operandsList.size()]); .) + ] + [ + StringValue + ] (. res = new StateEntryImpl(index, name, operands, operand); .) +. + + +HIR = +"begin_HIR" + { + HIRInstruction (. helper.hirInstructions.add(ins); .) + } +"end_HIR". + + +HIRInstruction = (. String pinned = ""; String operand = null; .) + [ + "." (. pinned = "."; .) + ] + IntegerValue + IntegerValue + [ + StringValue + ] + HIRName + FreeValue (. res = new IRInstructionImpl(pinned, bci, useCount, name, text, operand); .) +. + +HIRName = + IdentValue (. if (res.charAt(0) >= '0' && res.charAt(0) <= '9') { res = "v" + res; res = res.intern(); } .) +. + + +LIR = +"begin_LIR" + { + LIROperation (. helper.lirOperations.add(op); .) + } +"end_LIR". + + +LIROperation = + IntegerValue + FreeValue (. res = new IRInstructionImpl(number, text); .) +. + +IR = +"begin_IR" +( + "HIR" + { + IRInstruction (. helper.hirInstructions.add(op); .) + } +| + "LIR" + { + IRInstruction (. helper.lirOperations.add(op); .) + } +) +"end_IR". + +IRInstruction = (. LinkedHashMap data = new LinkedHashMap(); .) + { + IdentValue + FreeValue (. data.put(name.intern(), value.intern()); .) + } + "<|@" (. res = new IRInstructionImpl(data); .) +. + + + +IntervalList = + (. IntervalListHelper helper = new IntervalListHelper(); IntervalHelper interval; + if (controlFlowGraph == null) SemErr("must have CFG before intervals"); .) +"begin_intervals" + "name" StringValue (. helper.name = longName(name); helper.shortName = shortName(name); .) + { + Interval (. helper.add(interval); .) + } (. res = helper.resolve(this, controlFlowGraph); .) +"end_intervals". + + +Interval = (. helper = new IntervalHelper(); RangeImpl range; UsePositionImpl usePosition; .) + IdentValue + IdentValue + [ StringValue ] + IdentValue + IdentValue + + Range (. helper.ranges.add(range); .) + { + Range (. helper.ranges.add(range); .) + } + { + UsePosition (. helper.usePositions.add(usePosition); .) + } + + StringValue +. + + +Range = + "[" IntegerValue + "," IntegerValue "[" (. res = new RangeImpl(from, to); .) +. + + +UsePosition = + IntegerValue + IdentValue (. res = new UsePositionImpl(position, kindStr.charAt(0)); .) +. + + +NativeMethod = +"begin_nmethod" + NoTrimFreeValue (. cfg.setNativeMethod(new NativeMethodImpl(cfg, res)); .) +"end_nmethod" +. + + +Bytecodes = +"begin_bytecodes" + FreeValue (. cfg.setBytecodes(new BytecodesImpl(cfg, res)); .) +"end_bytecodes" +. + + +StringList = (. ArrayList list = new ArrayList(); String item; .) + { + StringValue (. list.add(item); .) + } (. res = list.toArray(new String[list.size()]); .) +. + + +StringValue = + "\"" (. int beg = la.pos; .) + { ANY } (. res = scanner.buffer.GetString(beg, la.pos).trim().intern(); .) + "\"" +. + + +IdentValue = + ident (. res = t.val.trim().intern(); .) +. + + +IntegerValue = (. res = 0; .) + ident (. try { res = Integer.parseInt(t.val); } catch (NumberFormatException ex) { SemErr(t.val); } .) +. + + +DoubleValue = (. res = Double.NaN; .) + ident (. try { res = Double.longBitsToDouble(Long.parseLong(t.val)); } catch (NumberFormatException ex) { SemErr(t.val); } .) +. + + +DateValue = (. res = null; .) + ident (. try { res = new Date(Long.parseLong(t.val)); } catch (NumberFormatException ex) { SemErr(t.val); } .) +. + + +FreeValue = (. int beg = la.pos; .) + { ANY } (. res = scanner.buffer.GetString(beg, la.pos).trim(); if (res.indexOf('\r') != -1) { res = res.replace("\r\n", "\n"); } res = res.intern(); .) + "<|@" +. + +NoTrimFreeValue = (. int beg = la.pos; .) + { ANY } (. res = scanner.buffer.GetString(beg, la.pos); if (res.indexOf('\r') != -1) { res = res.replace("\r\n", "\n"); } res = res.intern(); .) + "<|@" +. + +END InputFile. diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/IntervalHelper.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/IntervalHelper.java new file mode 100644 index 000000000000..77402a43e8f5 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/IntervalHelper.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.parser; + +import java.util.SortedSet; +import java.util.TreeSet; +import at.ssw.visualizer.model.interval.Range; +import at.ssw.visualizer.model.interval.UsePosition; +import at.ssw.visualizer.modelimpl.interval.ChildIntervalImpl; + +/** + * + * @author Christian Wimmer + */ +public class IntervalHelper implements Comparable { + protected String regNum; + protected String type; + protected String operand = ""; // avoid null values if not defined + protected String splitParent; + protected String spillState; + protected String registerHint; + + protected SortedSet ranges = new TreeSet(); + protected SortedSet usePositions = new TreeSet(); + + protected ChildIntervalImpl childInterval = new ChildIntervalImpl(); + protected SortedSet splitChildren = new TreeSet(); + + public int compareTo(IntervalHelper other) { + return ranges.first().getFrom() - other.ranges.first().getFrom(); + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/IntervalListHelper.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/IntervalListHelper.java new file mode 100644 index 000000000000..118d1478856e --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/IntervalListHelper.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.parser; + +import java.util.Collection; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.model.interval.ChildInterval; +import at.ssw.visualizer.model.interval.Range; +import at.ssw.visualizer.model.interval.UsePosition; +import at.ssw.visualizer.modelimpl.interval.ChildIntervalImpl; +import at.ssw.visualizer.modelimpl.interval.IntervalImpl; +import at.ssw.visualizer.modelimpl.interval.IntervalListImpl; +import java.util.ArrayList; +import java.util.LinkedHashMap; + +/** + * + * @author Christian Wimmer + */ +public class IntervalListHelper { + protected String shortName; + protected String name; + protected LinkedHashMap helpers = new LinkedHashMap(); + + public void add(IntervalHelper helper) { + helpers.put(helper.regNum, helper); + } + + + public IntervalListImpl resolve(Parser parser, ControlFlowGraph controlFlowGraph) { + for (IntervalHelper helper : helpers.values()) { + ChildInterval registerHint = null; + if (helpers.containsKey(helper.registerHint)) { + registerHint = helpers.get(helper.registerHint).childInterval; + } + Range[] ranges = helper.ranges.toArray(new Range[helper.ranges.size()]); + UsePosition[] usePositions = helper.usePositions.toArray(new UsePosition[helper.usePositions.size()]); + + helper.childInterval.setValues(helper.regNum, helper.type, helper.operand, helper.spillState, registerHint, ranges, usePositions); + + + IntervalHelper parent = helpers.get(helper.splitParent); + if (parent != null) { + parent.splitChildren.add(helper.childInterval); + } else { + parser.SemErr("Unknown split parent: " + helper.splitParent); + } + } + + Collection intervals = new ArrayList(); + for (IntervalHelper helper : helpers.values()) { + if (helper.splitChildren.size() > 0) { + ChildIntervalImpl[] children = helper.splitChildren.toArray(new ChildIntervalImpl[helper.splitChildren.size()]); + intervals.add(new IntervalImpl(children)); + } + } + + return new IntervalListImpl(shortName, name, intervals.toArray(new IntervalImpl[intervals.size()]), controlFlowGraph); + } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/Parser.frame b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/Parser.frame new file mode 100644 index 000000000000..638d40c26e97 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/Parser.frame @@ -0,0 +1,179 @@ +/*------------------------------------------------------------------------- +Compiler Generator Coco/R, +Copyright (c) 1990, 2004 Hanspeter Moessenboeck, University of Linz +extended by M. Loeberbauer & A. Woess, Univ. of Linz +ported from C# to Java by Wolfgang Ahorner +with improvements by Pat Terry, Rhodes University + +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, 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., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +As an exception, it is allowed to write an extension of Coco/R that is +used as a plugin in non-free software. + +If not otherwise stated, any source code generated by Coco/R (other than +Coco/R itself) does not fall under the GNU General Public License. +------------------------------------------------------------------------*/ +-->begin + +public class Parser { +-->constants + static final boolean _T = true; + static final boolean _x = false; + static final int minErrDist = 2; + + public Token t; // last recognized token + public Token la; // lookahead token + int errDist = minErrDist; + + public Scanner scanner; + public Errors errors; + + public boolean hasErrors() { + return errors.errors.size() > 0; + } + + public String getErrors() { + StringBuilder sb = new StringBuilder(); + for (String s : errors.errors) { + sb.append(s); + sb.append('\n'); + } + return sb.toString(); + } + + + -->declarations + + public Parser(Scanner scanner) { + this.scanner = scanner; + errors = new Errors(); + } + + void SynErr (int n) { + if (errDist >= minErrDist) errors.SynErr(la.line, la.col, n); + errDist = 0; + } + + public void SemErr (String msg) { + if (errDist >= minErrDist) errors.SemErr(t.line, t.col, msg); + errDist = 0; + } + + void Get () { + for (;;) { + t = la; + la = scanner.Scan(); + if (la.kind <= maxT) { ++errDist; break; } +-->pragmas + la = t; + } + } + + void Expect (int n) { + if (la.kind==n) Get(); else { SynErr(n); } + } + + boolean StartOf (int s) { + return set[s][la.kind]; + } + + void ExpectWeak (int n, int follow) { + if (la.kind == n) Get(); + else { + SynErr(n); + while (!StartOf(follow)) Get(); + } + } + + boolean WeakSeparator (int n, int syFol, int repFol) { + int kind = la.kind; + if (kind == n) { Get(); return true; } + else if (StartOf(repFol)) return false; + else { + SynErr(n); + while (!(set[syFol][kind] || set[repFol][kind] || set[0][kind])) { + Get(); + kind = la.kind; + } + return StartOf(syFol); + } + } + +-->productions + + public void Parse() { + la = new Token(); + la.val = ""; + Get(); +-->parseRoot + Expect(0); + } + + private boolean[][] set = { +-->initialization + }; +} // end Parser + + +class Errors { + public String errMsgFormat = "-- line {0} col {1}: {2}"; // 0=line, 1=column, 2=text + public ArrayList errors = new ArrayList(); + + protected void printMsg(int line, int column, String msg) { + StringBuffer b = new StringBuffer(errMsgFormat); + int pos = b.indexOf("{0}"); + if (pos >= 0) { b.delete(pos, pos+3); b.insert(pos, line); } + pos = b.indexOf("{1}"); + if (pos >= 0) { b.delete(pos, pos+3); b.insert(pos, column); } + pos = b.indexOf("{2}"); + if (pos >= 0) b.replace(pos, pos+3, msg); + printMsg(b.toString()); + } + + protected void printMsg(String msg) { + if (errors.size() < 10) { + errors.add(msg); + } + } + + public void SynErr (int line, int col, int n) { + String s; + switch (n) {-->errors + default: s = "error " + n; break; + } + printMsg(line, col, s); + } + + public void SemErr (int line, int col, String s) { + printMsg(line, col, s); + } + + public void SemErr (String s) { + printMsg(s); + } + + public void Warning (int line, int col, String s) { + printMsg(line, col, s); + } + + public void Warning (String s) { + printMsg(s); + } +} // Errors + + +class FatalError extends RuntimeException { + public FatalError(String s) { super(s); } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/Parser.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/Parser.java new file mode 100644 index 000000000000..38b1ba9de1ae --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/Parser.java @@ -0,0 +1,721 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.parser; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.Date; +import java.util.List; + +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.modelimpl.CompilationImpl; +import at.ssw.visualizer.modelimpl.CompilationModelImpl; +import at.ssw.visualizer.modelimpl.cfg.ControlFlowGraphImpl; +import at.ssw.visualizer.modelimpl.cfg.IRInstructionImpl; +import at.ssw.visualizer.modelimpl.cfg.StateImpl; +import at.ssw.visualizer.modelimpl.cfg.StateEntryImpl; +import at.ssw.visualizer.modelimpl.interval.IntervalListImpl; +import at.ssw.visualizer.modelimpl.interval.RangeImpl; +import at.ssw.visualizer.modelimpl.interval.UsePositionImpl; +import at.ssw.visualizer.modelimpl.nc.NativeMethodImpl; +import at.ssw.visualizer.modelimpl.bc.BytecodesImpl; + + + +public class Parser { + public static final int _EOF = 0; + public static final int _ident = 1; + public static final int maxT = 54; + + static final boolean _T = true; + static final boolean _x = false; + static final int minErrDist = 2; + + public Token t; // last recognized token + public Token la; // lookahead token + int errDist = minErrDist; + + public Scanner scanner; + public Errors errors; + + public boolean hasErrors() { + return errors.errors.size() > 0; + } + + public String getErrors() { + StringBuilder sb = new StringBuilder(); + for (String s : errors.errors) { + sb.append(s); + sb.append('\n'); + } + return sb.toString(); + } + + + private CompilationModelImpl compilationModel; + +public void setCompilationModel(CompilationModelImpl compilationModel) { + this.compilationModel = compilationModel; +} + +private String simpleName(String className) { + int index = className.lastIndexOf('.'); + if (index < 0) { + return className; + } + return className.substring(index + 1); +} + +private String shortName(String name) { + name = longName(name); + String params = ""; + + int openParam = name.indexOf('('); + if (openParam >= 0) { + int closeParam = name.indexOf(')', openParam); + if (closeParam >= 0) { + String[] parts = name.substring(openParam + 1, closeParam).split(", *"); + for (int i = 0; i < parts.length; i++) { + if (!params.isEmpty()) { + params += ","; + } + params += simpleName(parts[i]); + } + params = "(" + params + ")"; + } + name = name.substring(0, openParam); + } + + int methodPoint = name.lastIndexOf("."); + if (methodPoint < 0) { + return name + params; + } + int classPoint = name.lastIndexOf(".", methodPoint - 1); + if (classPoint < 0) { + return name + params; + } + return name.substring(classPoint + 1) + params; +} + +private String longName(String name) { + return name.replace("::", "."); +} + + + + public Parser(Scanner scanner) { + this.scanner = scanner; + errors = new Errors(); + } + + void SynErr (int n) { + if (errDist >= minErrDist) errors.SynErr(la.line, la.col, n); + errDist = 0; + } + + public void SemErr (String msg) { + if (errDist >= minErrDist) errors.SemErr(t.line, t.col, msg); + errDist = 0; + } + + void Get () { + for (;;) { + t = la; + la = scanner.Scan(); + if (la.kind <= maxT) { ++errDist; break; } + + la = t; + } + } + + void Expect (int n) { + if (la.kind==n) Get(); else { SynErr(n); } + } + + boolean StartOf (int s) { + return set[s][la.kind]; + } + + void ExpectWeak (int n, int follow) { + if (la.kind == n) Get(); + else { + SynErr(n); + while (!StartOf(follow)) Get(); + } + } + + boolean WeakSeparator (int n, int syFol, int repFol) { + int kind = la.kind; + if (kind == n) { Get(); return true; } + else if (StartOf(repFol)) return false; + else { + SynErr(n); + while (!(set[syFol][kind] || set[repFol][kind] || set[0][kind])) { + Get(); + kind = la.kind; + } + return StartOf(syFol); + } + } + + void InputFile() { + CompilationHelper lastComp = null; + ControlFlowGraphImpl lastCFG = null; + IntervalListImpl lastIL = null; + while (StartOf(1)) { + if (la.kind == 2) { + CompilationHelper curComp = Compilation(); + if (lastComp != null) compilationModel.addCompilation(lastComp.resolve()); + lastComp = curComp; + } else if (la.kind == 7) { + lastCFG = CFG(lastComp); + } else if (la.kind == 46) { + lastIL = IntervalList(lastCFG); + lastComp.elements.add(lastIL); + } else if (la.kind == 49) { + NativeMethod(lastCFG); + } else { + Bytecodes(lastCFG); + } + } + if (lastComp != null) compilationModel.addCompilation(lastComp.resolve()); + } + + CompilationHelper Compilation() { + CompilationHelper helper; + helper = new CompilationHelper(); + Expect(2); + Expect(3); + String name = StringValue(); + helper.name = longName(name); helper.shortName = shortName(name); + Expect(4); + String method = StringValue(); + helper.method = longName(method); + Expect(5); + helper.date = DateValue(); + Expect(6); + return helper; + } + + ControlFlowGraphImpl CFG(CompilationHelper lastComp) { + ControlFlowGraphImpl res; + CFGHelper helper = new CFGHelper(); + Expect(7); + Expect(3); + String name = StringValue(); + helper.name = longName(name); helper.shortName = shortName(name); + if (la.kind == 8) { + Get(); + helper.id = IntegerValue(); + Expect(9); + helper.parentId = IntegerValue(); + } + while (la.kind == 11) { + BBHelper basicBlock = BasicBlock(); + helper.add(basicBlock); + } + res = helper.resolve(lastComp, this); + Expect(10); + return res; + } + + IntervalListImpl IntervalList(ControlFlowGraph controlFlowGraph) { + IntervalListImpl res; + IntervalListHelper helper = new IntervalListHelper(); IntervalHelper interval; + if (controlFlowGraph == null) SemErr("must have CFG before intervals"); + Expect(46); + Expect(3); + String name = StringValue(); + helper.name = longName(name); helper.shortName = shortName(name); + while (la.kind == 1) { + interval = Interval(); + helper.add(interval); + } + res = helper.resolve(this, controlFlowGraph); + Expect(47); + return res; + } + + void NativeMethod(ControlFlowGraphImpl cfg) { + Expect(49); + String res = NoTrimFreeValue(); + cfg.setNativeMethod(new NativeMethodImpl(cfg, res)); + Expect(50); + } + + void Bytecodes(ControlFlowGraphImpl cfg) { + Expect(51); + String res = FreeValue(); + cfg.setBytecodes(new BytecodesImpl(cfg, res)); + Expect(52); + } + + String StringValue() { + String res; + Expect(53); + long beg = la.pos; + while (StartOf(2)) { + Get(); + } + res = scanner.buffer.GetString(beg, la.pos).trim().intern(); + Expect(53); + return res; + } + + Date DateValue() { + Date res; + res = null; + Expect(1); + try { res = new Date(Long.parseLong(t.val)); } catch (NumberFormatException ex) { SemErr(t.val); } + return res; + } + + int IntegerValue() { + int res; + res = 0; + Expect(1); + try { res = Integer.parseInt(t.val); } catch (NumberFormatException ex) { SemErr(t.val); } + return res; + } + + BBHelper BasicBlock() { + BBHelper helper; + helper = new BBHelper(); + Expect(11); + Expect(3); + helper.name = StringValue(); + Expect(12); + helper.fromBci = IntegerValue(); + Expect(13); + helper.toBci = IntegerValue(); + Expect(14); + helper.predecessors = StringList(); + Expect(15); + helper.successors = StringList(); + Expect(16); + helper.xhandlers = StringList(); + Expect(17); + helper.flags = StringList(); + if (la.kind == 18) { + Get(); + helper.dominator = StringValue(); + } + if (la.kind == 19) { + Get(); + helper.loopIndex = IntegerValue(); + } + if (la.kind == 20) { + Get(); + helper.loopDepth = IntegerValue(); + } + if (la.kind == 21) { + Get(); + helper.firstLirId = IntegerValue(); + } + if (la.kind == 22) { + Get(); + helper.lastLirId = IntegerValue(); + } + if (la.kind == 23) { + Get(); + helper.probability = DoubleValue(); + } + if (la.kind == 25) { + StateList(helper); + } + if (la.kind == 36) { + HIR(helper); + } + if (la.kind == 39) { + LIR(helper); + } + while (la.kind == 41) { + IR(helper); + } + Expect(24); + return helper; + } + + String[] StringList() { + String[] res; + ArrayList list = new ArrayList(); String item; + while (la.kind == 53) { + item = StringValue(); + list.add(item); + } + res = list.toArray(new String[list.size()]); + return res; + } + + double DoubleValue() { + double res; + res = Double.NaN; + Expect(1); + try { res = Double.longBitsToDouble(Long.parseLong(t.val)); } catch (NumberFormatException ex) { SemErr(t.val); } + return res; + } + + void StateList(BBHelper helper) { + Expect(25); + while (la.kind == 26 || la.kind == 28 || la.kind == 30) { + if (la.kind == 26) { + Get(); + StateImpl state = State("Operands"); + helper.states.add(state); + Expect(27); + } else if (la.kind == 28) { + Get(); + StateImpl state = State("Locks"); + helper.states.add(state); + Expect(29); + } else { + Get(); + StateImpl state = State("Locals"); + helper.states.add(state); + Expect(31); + } + } + Expect(32); + } + + void HIR(BBHelper helper) { + Expect(36); + while (la.kind == 1 || la.kind == 38) { + IRInstructionImpl ins = HIRInstruction(); + helper.hirInstructions.add(ins); + } + Expect(37); + } + + void LIR(BBHelper helper) { + Expect(39); + while (la.kind == 1) { + IRInstructionImpl op = LIROperation(); + helper.lirOperations.add(op); + } + Expect(40); + } + + void IR(BBHelper helper) { + Expect(41); + if (la.kind == 42) { + Get(); + while (la.kind == 1 || la.kind == 45) { + IRInstructionImpl op = IRInstruction(); + helper.hirInstructions.add(op); + } + } else if (la.kind == 43) { + Get(); + while (la.kind == 1 || la.kind == 45) { + IRInstructionImpl op = IRInstruction(); + helper.lirOperations.add(op); + } + } else SynErr(55); + Expect(44); + } + + StateImpl State(String kind) { + StateImpl res; + String method = ""; ArrayList entries = new ArrayList(); + Expect(33); + int size = IntegerValue(); + if (la.kind == 4) { + Get(); + method = StringValue(); + } + while (la.kind == 1) { + StateEntryImpl entry = StateEntry(); + entries.add(entry); + } + res = new StateImpl(kind, size, longName(method), entries.toArray(new StateEntryImpl[entries.size()])); + return res; + } + + StateEntryImpl StateEntry() { + StateEntryImpl res; + String[] operands = null; String operand = null; + int index = IntegerValue(); + String name = HIRName(); + if (la.kind == 34) { + Get(); + ArrayList operandsList = new ArrayList(); + while (la.kind == 1) { + String opd = HIRName(); + operandsList.add(opd); + } + Expect(35); + operands = operandsList.toArray(new String[operandsList.size()]); + } + if (la.kind == 53) { + operand = StringValue(); + } + res = new StateEntryImpl(index, name, operands, operand); + return res; + } + + String HIRName() { + String res; + res = IdentValue(); + if (res.charAt(0) >= '0' && res.charAt(0) <= '9') { res = "v" + res; res = res.intern(); } + return res; + } + + IRInstructionImpl HIRInstruction() { + IRInstructionImpl res; + String pinned = ""; String operand = null; + if (la.kind == 38) { + Get(); + pinned = "."; + } + int bci = IntegerValue(); + int useCount = IntegerValue(); + if (la.kind == 53) { + operand = StringValue(); + } + String name = HIRName(); + String text = FreeValue(); + res = new IRInstructionImpl(pinned, bci, useCount, name, text, operand); + return res; + } + + String FreeValue() { + String res; + long beg = la.pos; + while (StartOf(3)) { + Get(); + } + res = scanner.buffer.GetString(beg, la.pos).trim(); if (res.indexOf('\r') != -1) { res = res.replace("\r\n", "\n"); } res = res.intern(); + Expect(45); + return res; + } + + String IdentValue() { + String res; + Expect(1); + res = t.val.trim().intern(); + return res; + } + + IRInstructionImpl LIROperation() { + IRInstructionImpl res; + int number = IntegerValue(); + String text = FreeValue(); + res = new IRInstructionImpl(number, text); + return res; + } + + IRInstructionImpl IRInstruction() { + IRInstructionImpl res; + LinkedHashMap data = new LinkedHashMap(); + while (la.kind == 1) { + String name = IdentValue(); + String value = FreeValue(); + data.put(name.intern(), value.intern()); + } + Expect(45); + res = new IRInstructionImpl(data); + return res; + } + + IntervalHelper Interval() { + IntervalHelper helper; + helper = new IntervalHelper(); RangeImpl range; UsePositionImpl usePosition; + helper.regNum = IdentValue(); + helper.type = IdentValue(); + if (la.kind == 53) { + helper.operand = StringValue(); + } + helper.splitParent = IdentValue(); + helper.registerHint = IdentValue(); + range = Range(); + helper.ranges.add(range); + while (la.kind == 34) { + range = Range(); + helper.ranges.add(range); + } + while (la.kind == 1) { + usePosition = UsePosition(); + helper.usePositions.add(usePosition); + } + helper.spillState = StringValue(); + return helper; + } + + RangeImpl Range() { + RangeImpl res; + Expect(34); + int from = IntegerValue(); + Expect(48); + int to = IntegerValue(); + Expect(34); + res = new RangeImpl(from, to); + return res; + } + + UsePositionImpl UsePosition() { + UsePositionImpl res; + int position = IntegerValue(); + String kindStr = IdentValue(); + res = new UsePositionImpl(position, kindStr.charAt(0)); + return res; + } + + String NoTrimFreeValue() { + String res; + long beg = la.pos; + while (StartOf(3)) { + Get(); + } + res = scanner.buffer.GetString(beg, la.pos); if (res.indexOf('\r') != -1) { res = res.replace("\r\n", "\n"); } res = res.intern(); + Expect(45); + return res; + } + + + + public void Parse() { + la = new Token(); + la.val = ""; + Get(); + InputFile(); + Expect(0); + + Expect(0); + } + + private boolean[][] set = { + {_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x}, + {_x,_x,_T,_x, _x,_x,_x,_T, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_T,_x, _x,_T,_x,_T, _x,_x,_x,_x}, + {_x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_T,_x}, + {_x,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_T,_T,_T, _T,_x,_T,_T, _T,_T,_T,_T, _T,_T,_T,_x} + + }; +} // end Parser + + +class Errors { + public String errMsgFormat = "-- line {0} col {1}: {2}"; // 0=line, 1=column, 2=text + public ArrayList errors = new ArrayList(); + + protected void printMsg(int line, int column, String msg) { + StringBuffer b = new StringBuffer(errMsgFormat); + int pos = b.indexOf("{0}"); + if (pos >= 0) { b.delete(pos, pos+3); b.insert(pos, line); } + pos = b.indexOf("{1}"); + if (pos >= 0) { b.delete(pos, pos+3); b.insert(pos, column); } + pos = b.indexOf("{2}"); + if (pos >= 0) b.replace(pos, pos+3, msg); + printMsg(b.toString()); + } + + protected void printMsg(String msg) { + if (errors.size() < 10) { + errors.add(msg); + } + } + + public void SynErr (int line, int col, int n) { + String s; + switch (n) { + case 0: s = "EOF expected"; break; + case 1: s = "ident expected"; break; + case 2: s = "\"begin_compilation\" expected"; break; + case 3: s = "\"name\" expected"; break; + case 4: s = "\"method\" expected"; break; + case 5: s = "\"date\" expected"; break; + case 6: s = "\"end_compilation\" expected"; break; + case 7: s = "\"begin_cfg\" expected"; break; + case 8: s = "\"id\" expected"; break; + case 9: s = "\"caller_id\" expected"; break; + case 10: s = "\"end_cfg\" expected"; break; + case 11: s = "\"begin_block\" expected"; break; + case 12: s = "\"from_bci\" expected"; break; + case 13: s = "\"to_bci\" expected"; break; + case 14: s = "\"predecessors\" expected"; break; + case 15: s = "\"successors\" expected"; break; + case 16: s = "\"xhandlers\" expected"; break; + case 17: s = "\"flags\" expected"; break; + case 18: s = "\"dominator\" expected"; break; + case 19: s = "\"loop_index\" expected"; break; + case 20: s = "\"loop_depth\" expected"; break; + case 21: s = "\"first_lir_id\" expected"; break; + case 22: s = "\"last_lir_id\" expected"; break; + case 23: s = "\"probability\" expected"; break; + case 24: s = "\"end_block\" expected"; break; + case 25: s = "\"begin_states\" expected"; break; + case 26: s = "\"begin_stack\" expected"; break; + case 27: s = "\"end_stack\" expected"; break; + case 28: s = "\"begin_locks\" expected"; break; + case 29: s = "\"end_locks\" expected"; break; + case 30: s = "\"begin_locals\" expected"; break; + case 31: s = "\"end_locals\" expected"; break; + case 32: s = "\"end_states\" expected"; break; + case 33: s = "\"size\" expected"; break; + case 34: s = "\"[\" expected"; break; + case 35: s = "\"]\" expected"; break; + case 36: s = "\"begin_HIR\" expected"; break; + case 37: s = "\"end_HIR\" expected"; break; + case 38: s = "\".\" expected"; break; + case 39: s = "\"begin_LIR\" expected"; break; + case 40: s = "\"end_LIR\" expected"; break; + case 41: s = "\"begin_IR\" expected"; break; + case 42: s = "\"HIR\" expected"; break; + case 43: s = "\"LIR\" expected"; break; + case 44: s = "\"end_IR\" expected"; break; + case 45: s = "\"<|@\" expected"; break; + case 46: s = "\"begin_intervals\" expected"; break; + case 47: s = "\"end_intervals\" expected"; break; + case 48: s = "\",\" expected"; break; + case 49: s = "\"begin_nmethod\" expected"; break; + case 50: s = "\"end_nmethod\" expected"; break; + case 51: s = "\"begin_bytecodes\" expected"; break; + case 52: s = "\"end_bytecodes\" expected"; break; + case 53: s = "\"\\\"\" expected"; break; + case 54: s = "??? expected"; break; + case 55: s = "invalid IR"; break; + default: s = "error " + n; break; + } + printMsg(line, col, s); + } + + public void SemErr (int line, int col, String s) { + printMsg(line, col, s); + } + + public void SemErr (String s) { + printMsg(s); + } + + public void Warning (int line, int col, String s) { + printMsg(line, col, s); + } + + public void Warning (String s) { + printMsg(s); + } +} // Errors + + +class FatalError extends RuntimeException { + public FatalError(String s) { super(s); } +} diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/Scanner.frame b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/Scanner.frame new file mode 100644 index 000000000000..fd8793afda2c --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/Scanner.frame @@ -0,0 +1,463 @@ +/*------------------------------------------------------------------------- +Compiler Generator Coco/R, +Copyright (c) 1990, 2004 Hanspeter Moessenboeck, University of Linz +extended by M. Loeberbauer & A. Woess, Univ. of Linz +ported from C# to Java by Wolfgang Ahorner +with improvements by Pat Terry, Rhodes University + +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, 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., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +As an exception, it is allowed to write an extension of Coco/R that is +used as a plugin in non-free software. + +If not otherwise stated, any source code generated by Coco/R (other than +Coco/R itself) does not fall under the GNU General Public License. +------------------------------------------------------------------------*/ +-->begin +import java.io.InputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Arrays; +import java.util.Map; +import java.util.HashMap; +import org.netbeans.api.progress.ProgressHandle; + +class Token { + public int kind; // token kind + public int pos; // token position in bytes in the source text (starting at 0) + public int charPos; // token position in characters in the source text (starting at 0) + public int col; // token column (starting at 1) + public int line; // token line (starting at 1) + public String val; // token value + public Token next; // ML 2005-03-11 Peek tokens are kept in linked list +} + +//----------------------------------------------------------------------------------- +// Buffer +//----------------------------------------------------------------------------------- +class Buffer { + // This Buffer supports the following cases: + // 1) seekable stream (file) + // a) whole stream in buffer + // b) part of stream in buffer + // 2) non seekable stream (network, console) + + public static final int EOF = Character.MAX_VALUE + 1; + private static final int MIN_BUFFER_LENGTH = 1024; // 1KB + private static final int MAX_BUFFER_LENGTH = MIN_BUFFER_LENGTH * 64; // 64KB + private byte[] buf; // input buffer + private int bufStart; // position of first byte in buffer relative to input stream + private int bufLen; // length of buffer + private int fileLen; // length of input stream (may change if stream is no file) + private int bufPos; // current position in buffer + private RandomAccessFile file; // input stream (seekable) + private InputStream stream; // growing input stream (e.g.: console, network) + ProgressHandle progressHandle; + int maxSetPosValue; + + public Buffer(InputStream s) { + stream = s; + fileLen = bufLen = bufStart = bufPos = 0; + buf = new byte[MIN_BUFFER_LENGTH]; + } + + public Buffer(String fileName, ProgressHandle progressHandle) { + this.progressHandle = progressHandle; + try { + file = new RandomAccessFile(fileName, "r"); + fileLen = (int) file.length(); + if (progressHandle != null) { + progressHandle.start((int)(fileLen / MAX_BUFFER_LENGTH)); + } + bufLen = Math.min(fileLen, MAX_BUFFER_LENGTH); + buf = new byte[bufLen]; + bufStart = Integer.MAX_VALUE; // nothing in buffer so far + if (fileLen > 0) setPos(0); // setup buffer to position 0 (start) + else bufPos = 0; // index 0 is already after the file, thus setPos(0) is invalid + if (bufLen == fileLen) Close(); + } catch (IOException e) { + throw new FatalError("Could not open file " + fileName); + } + } + + // don't use b after this call anymore + // called in UTF8Buffer constructor + protected Buffer(Buffer b) { + buf = b.buf; + bufStart = b.bufStart; + bufLen = b.bufLen; + fileLen = b.fileLen; + bufPos = b.bufPos; + file = b.file; + stream = b.stream; + // keep finalize from closing the file + b.file = null; + } + + protected void finalize() throws Throwable { + super.finalize(); + Close(); + } + + protected void Close() { + if (file != null) { + try { + file.close(); + file = null; + } catch (IOException e) { + throw new FatalError(e.getMessage()); + } + } + } + + public int Read() { + if (bufPos < bufLen) { + return buf[bufPos++] & 0xff; // mask out sign bits + } else if (getPos() < fileLen) { + setPos(getPos()); // shift buffer start to pos + return buf[bufPos++] & 0xff; // mask out sign bits + } else if (stream != null && ReadNextStreamChunk() > 0) { + return buf[bufPos++] & 0xff; // mask out sign bits + } else { + return EOF; + } + } + + public int Peek() { + int curPos = getPos(); + int ch = Read(); + setPos(curPos); + return ch; + } + + // beg .. begin, zero-based, inclusive, in byte + // end .. end, zero-based, exclusive, in byte + public String GetString(int beg, int end) { + int len = 0; + char[] buf = new char[end - beg]; + int oldPos = getPos(); + setPos(beg); + while (getPos() < end) buf[len++] = (char) Read(); + setPos(oldPos); + return new String(buf, 0, len); + } + + public int getPos() { + return bufPos + bufStart; + } + + public void setPos(int value) { + if (value >= fileLen && stream != null) { + // Wanted position is after buffer and the stream + // is not seek-able e.g. network or console, + // thus we have to read the stream manually till + // the wanted position is in sight. + while (value >= fileLen && ReadNextStreamChunk() > 0); + } + + if (value < 0 || value > fileLen) { + throw new FatalError("buffer out of bounds access, position: " + value); + } + + if (value >= bufStart && value < bufStart + bufLen) { // already in buffer + bufPos = value - bufStart; + } else if (file != null) { // must be swapped in + try { + file.seek(value); + bufLen = file.read(buf); + bufStart = value; bufPos = 0; + } catch(IOException e) { + throw new FatalError(e.getMessage()); + } + int newPosValue = (int)(value / MAX_BUFFER_LENGTH); + if (progressHandle != null && newPosValue > maxSetPosValue) { + progressHandle.progress(newPosValue); + maxSetPosValue = newPosValue; + } + } else { + // set the position to the end of the file, Pos will return fileLen. + bufPos = fileLen - bufStart; + } + } + + // Read the next chunk of bytes from the stream, increases the buffer + // if needed and updates the fields fileLen and bufLen. + // Returns the number of bytes read. + private int ReadNextStreamChunk() { + int free = buf.length - bufLen; + if (free == 0) { + // in the case of a growing input stream + // we can neither seek in the stream, nor can we + // foresee the maximum length, thus we must adapt + // the buffer size on demand. + byte[] newBuf = new byte[bufLen * 2]; + System.arraycopy(buf, 0, newBuf, 0, bufLen); + buf = newBuf; + free = bufLen; + } + + int read; + try { read = stream.read(buf, bufLen, free); } + catch (IOException ioex) { throw new FatalError(ioex.getMessage()); } + + if (read > 0) { + fileLen = bufLen = (bufLen + read); + return read; + } + // end of stream reached + return 0; + } +} + +//----------------------------------------------------------------------------------- +// UTF8Buffer +//----------------------------------------------------------------------------------- +class UTF8Buffer extends Buffer { + UTF8Buffer(Buffer b) { super(b); } + + public int Read() { + int ch; + do { + ch = super.Read(); + // until we find a utf8 start (0xxxxxxx or 11xxxxxx) + } while ((ch >= 128) && ((ch & 0xC0) != 0xC0) && (ch != EOF)); + if (ch < 128 || ch == EOF) { + // nothing to do, first 127 chars are the same in ascii and utf8 + // 0xxxxxxx or end of file character + } else if ((ch & 0xF0) == 0xF0) { + // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + int c1 = ch & 0x07; ch = super.Read(); + int c2 = ch & 0x3F; ch = super.Read(); + int c3 = ch & 0x3F; ch = super.Read(); + int c4 = ch & 0x3F; + ch = (((((c1 << 6) | c2) << 6) | c3) << 6) | c4; + } else if ((ch & 0xE0) == 0xE0) { + // 1110xxxx 10xxxxxx 10xxxxxx + int c1 = ch & 0x0F; ch = super.Read(); + int c2 = ch & 0x3F; ch = super.Read(); + int c3 = ch & 0x3F; + ch = (((c1 << 6) | c2) << 6) | c3; + } else if ((ch & 0xC0) == 0xC0) { + // 110xxxxx 10xxxxxx + int c1 = ch & 0x1F; ch = super.Read(); + int c2 = ch & 0x3F; + ch = (c1 << 6) | c2; + } + return ch; + } +} + +//----------------------------------------------------------------------------------- +// StartStates -- maps characters to start states of tokens +//----------------------------------------------------------------------------------- +class StartStates { + private static class Elem { + public int key, val; + public Elem next; + public Elem(int key, int val) { this.key = key; this.val = val; } + } + + private Elem[] tab = new Elem[128]; + + public void set(int key, int val) { + Elem e = new Elem(key, val); + int k = key % 128; + e.next = tab[k]; tab[k] = e; + } + + public int state(int key) { + Elem e = tab[key % 128]; + while (e != null && e.key != key) e = e.next; + return e == null ? 0: e.val; + } +} + +//----------------------------------------------------------------------------------- +// Scanner +//----------------------------------------------------------------------------------- +public class Scanner { + static final char EOL = '\n'; + static final int eofSym = 0; +-->declarations + + public Buffer buffer; // scanner buffer + + Token t; // current token + int ch; // current input character + int pos; // byte position of current character + int charPos; // position by unicode characters starting with 0 + int col; // column number of current character + int line; // line number of current character + int oldEols; // EOLs that appeared in a comment; + static final StartStates start; // maps initial token character to start state + static final Map literals; // maps literal strings to literal kinds + static int maxLiteral; + static boolean[] literalFirstChar; + + Token tokens; // list of tokens already peeked (first token is a dummy) + Token pt; // current peek token + + char[] tval = new char[16]; // token text used in NextToken(), dynamically enlarged + int tlen; // length of current token + + + static { + start = new StartStates(); + literals = new HashMap(); +-->initialization + literalFirstChar = new boolean[0]; + for (String literal : literals.keySet()) { + maxLiteral = Math.max(literal.length(), maxLiteral); + char c = literal.charAt(0); + if (c >= literalFirstChar.length) { + literalFirstChar = Arrays.copyOf(literalFirstChar, c + 1); + } + literalFirstChar[c] = true; + } + maxLiteral = 0; + literalFirstChar = null; + } + + public Scanner (String fileName, ProgressHandle progressHandle) { + buffer = new Buffer(fileName, progressHandle); + Init(); + } + + public Scanner(InputStream s) { + buffer = new Buffer(s); + Init(); + } + + void Init () { + pos = -1; line = 1; col = 0; charPos = -1; + oldEols = 0; + NextCh(); + if (ch == 0xEF) { // check optional byte order mark for UTF-8 + NextCh(); int ch1 = ch; + NextCh(); int ch2 = ch; + if (ch1 != 0xBB || ch2 != 0xBF) { + throw new FatalError("Illegal byte order mark at start of file"); + } + buffer = new UTF8Buffer(buffer); col = 0; charPos = -1; + NextCh(); + } + pt = tokens = new Token(); // first token is a dummy + } + + void NextCh() { + if (oldEols > 0) { ch = EOL; oldEols--; } + else { + pos = buffer.getPos(); + // buffer reads unicode chars, if UTF8 has been detected + ch = buffer.Read(); col++; charPos++; + // replace isolated '\r' by '\n' in order to make + // eol handling uniform across Windows, Unix and Mac + if (ch == '\r' && buffer.Peek() != '\n') ch = EOL; + if (ch == EOL) { line++; col = 0; } + } +-->casing + } + + void AddCh() { + if (tlen >= tval.length) { + char[] newBuf = new char[2 * tval.length]; + System.arraycopy(tval, 0, newBuf, 0, tval.length); + tval = newBuf; + } + if (ch != Buffer.EOF) { +-->casing2 + NextCh(); + } + + } + +-->comments + + void CheckLiteral() { + String val = t.val; +-->casing3 +// if (maxLiteral == 0 || val.length() <= maxLiteral && val.length() > 0) { +// char c = val.charAt(0); +// if (literalFirstChar == null || c < literalFirstChar.length && literalFirstChar[c]) { + Object kind = literals.get(val); + if (kind != null) { + t.kind = ((Integer) kind).intValue(); + } +// } +// } + } + + Token NextToken() { + while (ch == ' ' || +-->scan1 + ) NextCh(); +-->scan2 + int recKind = noSym; + int recEnd = pos; + t = new Token(); + t.pos = pos; t.col = col; t.line = line; t.charPos = charPos; + int state = start.state(ch); + tlen = 0; AddCh(); + + loop: for (;;) { + switch (state) { + case -1: { t.kind = eofSym; break loop; } // NextCh already done + case 0: { + if (recKind != noSym) { + tlen = recEnd - t.pos; + SetScannerBehindT(); + } + t.kind = recKind; break loop; + } // NextCh already done +-->scan3 + } + } + t.val = new String(tval, 0, tlen); + return t; + } + + private void SetScannerBehindT() { + buffer.setPos(t.pos); + NextCh(); + line = t.line; col = t.col; charPos = t.charPos; + for (int i = 0; i < tlen; i++) NextCh(); + } + + // get the next token (possibly a token already seen during peeking) + public Token Scan () { + if (tokens.next == null) { + return NextToken(); + } else { + pt = tokens = tokens.next; + return tokens; + } + } + + // get the next token, ignore pragmas + public Token Peek () { + do { + if (pt.next == null) { + pt.next = NextToken(); + } + pt = pt.next; + } while (pt.kind > maxT); // skip pragmas + + return pt; + } + + // make sure that peeking starts at current scan position + public void ResetPeek () { pt = tokens; } + +} // end Scanner diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/Scanner.java b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/Scanner.java new file mode 100644 index 000000000000..3d312d3b811c --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/java/at/ssw/visualizer/parser/Scanner.java @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.parser; + +import java.io.InputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Arrays; +import java.util.Map; +import java.util.HashMap; +import org.netbeans.api.progress.ProgressHandle; + +class Token { + public int kind; // token kind + public long pos; // token position in bytes in the source text (starting at 0) + public long charPos; // token position in characters in the source text (starting at 0) + public int col; // token column (starting at 1) + public int line; // token line (starting at 1) + public String val; // token value + public Token next; // ML 2005-03-11 Peek tokens are kept in linked list +} + +//----------------------------------------------------------------------------------- +// Buffer +//----------------------------------------------------------------------------------- +class Buffer { + // This Buffer supports the following cases: + // 1) seekable stream (file) + // a) whole stream in buffer + // b) part of stream in buffer + // 2) non seekable stream (network, console) + + public static final int EOF = Character.MAX_VALUE + 1; + private static final int MIN_BUFFER_LENGTH = 1024; // 1KB + private static final int MAX_BUFFER_LENGTH = MIN_BUFFER_LENGTH * 64; // 64KB + private byte[] buf; // input buffer + private long bufStart; // position of first byte in buffer relative to input stream + private int bufLen; // length of buffer + private long fileLen; // length of input stream (may change if stream is no file) + private int bufPos; // current position in buffer + private RandomAccessFile file; // input stream (seekable) + private InputStream stream; // growing input stream (e.g.: console, network) + ProgressHandle progressHandle; + int maxSetPosValue; + + public Buffer(InputStream s) { + stream = s; + fileLen = 0; + bufLen = 0; + bufStart = 0; + bufPos = 0; + buf = new byte[MIN_BUFFER_LENGTH]; + } + + public Buffer(String fileName, ProgressHandle progressHandle) { + this.progressHandle = progressHandle; + try { + file = new RandomAccessFile(fileName, "r"); + fileLen = file.length(); + if (progressHandle != null) { + progressHandle.start((int)(fileLen / MAX_BUFFER_LENGTH)); + } + bufLen = (int) Math.min(fileLen, MAX_BUFFER_LENGTH); + buf = new byte[bufLen]; + bufStart = Long.MAX_VALUE; // nothing in buffer so far + if (fileLen > 0) setPos(0); // setup buffer to position 0 (start) + else bufPos = 0; // index 0 is already after the file, thus setPos(0) is invalid + if (bufLen == fileLen) Close(); + } catch (IOException e) { + throw new FatalError("Could not open file " + fileName); + } + } + + // don't use b after this call anymore + // called in UTF8Buffer constructor + protected Buffer(Buffer b) { + buf = b.buf; + bufStart = b.bufStart; + bufLen = b.bufLen; + fileLen = b.fileLen; + bufPos = b.bufPos; + file = b.file; + stream = b.stream; + // keep finalize from closing the file + b.file = null; + } + + protected void finalize() throws Throwable { + super.finalize(); + Close(); + } + + protected void Close() { + if (file != null) { + try { + file.close(); + file = null; + } catch (IOException e) { + throw new FatalError(e.getMessage()); + } + } + } + + public int Read() { + if (bufPos < bufLen) { + return buf[bufPos++] & 0xff; // mask out sign bits + } else if (getPos() < fileLen) { + setPos(getPos()); // shift buffer start to pos + return buf[bufPos++] & 0xff; // mask out sign bits + } else if (stream != null && ReadNextStreamChunk() > 0) { + return buf[bufPos++] & 0xff; // mask out sign bits + } else { + return EOF; + } + } + + public int Peek() { + long curPos = getPos(); + int ch = Read(); + setPos(curPos); + return ch; + } + + // beg .. begin, zero-based, inclusive, in byte + // end .. end, zero-based, exclusive, in byte + public String GetString(long beg, long end) { + int len = 0; + char[] buf = new char[(int) (end - beg)]; + long oldPos = getPos(); + setPos(beg); + while (getPos() < end) buf[len++] = (char) Read(); + setPos(oldPos); + return new String(buf, 0, len); + } + + public long getPos() { + return bufPos + bufStart; + } + + public void setPos(long value) { + if (value >= fileLen && stream != null) { + // Wanted position is after buffer and the stream + // is not seek-able e.g. network or console, + // thus we have to read the stream manually till + // the wanted position is in sight. + while (value >= fileLen && ReadNextStreamChunk() > 0); + } + + if (value < 0 || value > fileLen) { + throw new FatalError("buffer out of bounds access, position: " + value); + } + + if (value >= bufStart && value < bufStart + bufLen) { // already in buffer + bufPos = (int) (value - bufStart); + } else if (file != null) { // must be swapped in + try { + file.seek(value); + bufLen = file.read(buf); + bufStart = value; bufPos = 0; + } catch(IOException e) { + throw new FatalError(e.getMessage()); + } + int newPosValue = (int)(value / MAX_BUFFER_LENGTH); + if (progressHandle != null && newPosValue > maxSetPosValue) { + progressHandle.progress(newPosValue); + maxSetPosValue = newPosValue; + } + } else { + // set the position to the end of the file, Pos will return fileLen. + throw new InternalError(); + } + } + + // Read the next chunk of bytes from the stream, increases the buffer + // if needed and updates the fields fileLen and bufLen. + // Returns the number of bytes read. + private int ReadNextStreamChunk() { + int free = buf.length - bufLen; + if (free == 0) { + // in the case of a growing input stream + // we can neither seek in the stream, nor can we + // foresee the maximum length, thus we must adapt + // the buffer size on demand. + byte[] newBuf = new byte[bufLen * 2]; + System.arraycopy(buf, 0, newBuf, 0, bufLen); + buf = newBuf; + free = bufLen; + } + + int read; + try { read = stream.read(buf, bufLen, free); } + catch (IOException ioex) { throw new FatalError(ioex.getMessage()); } + + if (read > 0) { + fileLen = bufLen = (bufLen + read); + return read; + } + // end of stream reached + return 0; + } +} + +//----------------------------------------------------------------------------------- +// UTF8Buffer +//----------------------------------------------------------------------------------- +class UTF8Buffer extends Buffer { + UTF8Buffer(Buffer b) { super(b); } + + public int Read() { + int ch; + do { + ch = super.Read(); + // until we find a utf8 start (0xxxxxxx or 11xxxxxx) + } while ((ch >= 128) && ((ch & 0xC0) != 0xC0) && (ch != EOF)); + if (ch < 128 || ch == EOF) { + // nothing to do, first 127 chars are the same in ascii and utf8 + // 0xxxxxxx or end of file character + } else if ((ch & 0xF0) == 0xF0) { + // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + int c1 = ch & 0x07; ch = super.Read(); + int c2 = ch & 0x3F; ch = super.Read(); + int c3 = ch & 0x3F; ch = super.Read(); + int c4 = ch & 0x3F; + ch = (((((c1 << 6) | c2) << 6) | c3) << 6) | c4; + } else if ((ch & 0xE0) == 0xE0) { + // 1110xxxx 10xxxxxx 10xxxxxx + int c1 = ch & 0x0F; ch = super.Read(); + int c2 = ch & 0x3F; ch = super.Read(); + int c3 = ch & 0x3F; + ch = (((c1 << 6) | c2) << 6) | c3; + } else if ((ch & 0xC0) == 0xC0) { + // 110xxxxx 10xxxxxx + int c1 = ch & 0x1F; ch = super.Read(); + int c2 = ch & 0x3F; + ch = (c1 << 6) | c2; + } + return ch; + } +} + +//----------------------------------------------------------------------------------- +// StartStates -- maps characters to start states of tokens +//----------------------------------------------------------------------------------- +class StartStates { + private static class Elem { + public int key, val; + public Elem next; + public Elem(int key, int val) { this.key = key; this.val = val; } + } + + private Elem[] tab = new Elem[128]; + + public void set(int key, int val) { + Elem e = new Elem(key, val); + int k = key % 128; + e.next = tab[k]; tab[k] = e; + } + + public int state(int key) { + Elem e = tab[key % 128]; + while (e != null && e.key != key) e = e.next; + return e == null ? 0: e.val; + } +} + +//----------------------------------------------------------------------------------- +// Scanner +//----------------------------------------------------------------------------------- +public class Scanner { + static final char EOL = '\n'; + static final int eofSym = 0; + static final int maxT = 54; + static final int noSym = 54; + + + public Buffer buffer; // scanner buffer + + Token t; // current token + int ch; // current input character + long pos; // byte position of current character + long charPos; // position by unicode characters starting with 0 + int col; // column number of current character + int line; // line number of current character + int oldEols; // EOLs that appeared in a comment; + static final StartStates start; // maps initial token character to start state + static final Map literals; // maps literal strings to literal kinds + static int maxLiteral; + static boolean[] literalFirstChar; + + Token tokens; // list of tokens already peeked (first token is a dummy) + Token pt; // current peek token + + char[] tval = new char[16]; // token text used in NextToken(), dynamically enlarged + int tlen; // length of current token + + + static { + start = new StartStates(); + literals = new HashMap(); + for (int i = 42; i <= 42; ++i) start.set(i, 1); + for (int i = 45; i <= 45; ++i) start.set(i, 1); + for (int i = 48; i <= 58; ++i) start.set(i, 1); + for (int i = 65; i <= 90; ++i) start.set(i, 1); + for (int i = 95; i <= 95; ++i) start.set(i, 1); + for (int i = 97; i <= 122; ++i) start.set(i, 1); + for (int i = 124; i <= 124; ++i) start.set(i, 1); + start.set(91, 2); + start.set(93, 3); + start.set(46, 4); + start.set(60, 5); + start.set(44, 8); + start.set(34, 9); + start.set(Buffer.EOF, -1); + literals.put("begin_compilation", 2); + literals.put("name", 3); + literals.put("method", 4); + literals.put("date", 5); + literals.put("end_compilation", 6); + literals.put("begin_cfg", 7); + literals.put("id", 8); + literals.put("caller_id", 9); + literals.put("end_cfg", 10); + literals.put("begin_block", 11); + literals.put("from_bci", 12); + literals.put("to_bci", 13); + literals.put("predecessors", 14); + literals.put("successors", 15); + literals.put("xhandlers", 16); + literals.put("flags", 17); + literals.put("dominator", 18); + literals.put("loop_index", 19); + literals.put("loop_depth", 20); + literals.put("first_lir_id", 21); + literals.put("last_lir_id", 22); + literals.put("probability", 23); + literals.put("end_block", 24); + literals.put("begin_states", 25); + literals.put("begin_stack", 26); + literals.put("end_stack", 27); + literals.put("begin_locks", 28); + literals.put("end_locks", 29); + literals.put("begin_locals", 30); + literals.put("end_locals", 31); + literals.put("end_states", 32); + literals.put("size", 33); + literals.put("begin_HIR", 36); + literals.put("end_HIR", 37); + literals.put("begin_LIR", 39); + literals.put("end_LIR", 40); + literals.put("begin_IR", 41); + literals.put("HIR", 42); + literals.put("LIR", 43); + literals.put("end_IR", 44); + literals.put("begin_intervals", 46); + literals.put("end_intervals", 47); + literals.put("begin_nmethod", 49); + literals.put("end_nmethod", 50); + literals.put("begin_bytecodes", 51); + literals.put("end_bytecodes", 52); + + literalFirstChar = new boolean[0]; + for (String literal : literals.keySet()) { + maxLiteral = Math.max(literal.length(), maxLiteral); + char c = literal.charAt(0); + if (c >= literalFirstChar.length) { + literalFirstChar = Arrays.copyOf(literalFirstChar, c + 1); + } + literalFirstChar[c] = true; + } + maxLiteral = 0; + literalFirstChar = null; + } + + public Scanner (String fileName, ProgressHandle progressHandle) { + buffer = new Buffer(fileName, progressHandle); + Init(); + } + + public Scanner(InputStream s) { + buffer = new Buffer(s); + Init(); + } + + void Init () { + pos = -1; line = 1; col = 0; charPos = -1; + oldEols = 0; + NextCh(); + if (ch == 0xEF) { // check optional byte order mark for UTF-8 + NextCh(); int ch1 = ch; + NextCh(); int ch2 = ch; + if (ch1 != 0xBB || ch2 != 0xBF) { + throw new FatalError("Illegal byte order mark at start of file"); + } + buffer = new UTF8Buffer(buffer); col = 0; charPos = -1; + NextCh(); + } + pt = tokens = new Token(); // first token is a dummy + } + + void NextCh() { + if (oldEols > 0) { ch = EOL; oldEols--; } + else { + pos = buffer.getPos(); + // buffer reads unicode chars, if UTF8 has been detected + ch = buffer.Read(); col++; charPos++; + // replace isolated '\r' by '\n' in order to make + // eol handling uniform across Windows, Unix and Mac + if (ch == '\r' && buffer.Peek() != '\n') ch = EOL; + if (ch == EOL) { line++; col = 0; } + } + + } + + void AddCh() { + if (tlen >= tval.length) { + char[] newBuf = new char[2 * tval.length]; + System.arraycopy(tval, 0, newBuf, 0, tval.length); + tval = newBuf; + } + if (ch != Buffer.EOF) { + tval[tlen++] = (char)ch; + + NextCh(); + } + + } + + + + void CheckLiteral() { + String val = t.val; + +// if (maxLiteral == 0 || val.length() <= maxLiteral && val.length() > 0) { +// char c = val.charAt(0); +// if (literalFirstChar == null || c < literalFirstChar.length && literalFirstChar[c]) { + Object kind = literals.get(val); + if (kind != null) { + t.kind = ((Integer) kind).intValue(); + } +// } +// } + } + + Token NextToken() { + while (ch == ' ' || + ch == 10 || ch == 13 + ) NextCh(); + + int recKind = noSym; + long recEnd = pos; + t = new Token(); + t.pos = pos; t.col = col; t.line = line; t.charPos = charPos; + int state = start.state(ch); + tlen = 0; AddCh(); + + loop: for (;;) { + switch (state) { + case -1: { t.kind = eofSym; break loop; } // NextCh already done + case 0: { + if (recKind != noSym) { + tlen = Math.toIntExact(recEnd - t.pos); + SetScannerBehindT(); + } + t.kind = recKind; break loop; + } // NextCh already done + case 1: + recEnd = pos; recKind = 1; + if (ch == '*' || ch == '-' || ch >= '0' && ch <= ':' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z' || ch == '|') {AddCh(); state = 1; break;} + else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;} + case 2: + {t.kind = 34; break loop;} + case 3: + {t.kind = 35; break loop;} + case 4: + {t.kind = 38; break loop;} + case 5: + if (ch == '|') {AddCh(); state = 6; break;} + else {state = 0; break;} + case 6: + if (ch == '@') {AddCh(); state = 7; break;} + else {state = 0; break;} + case 7: + {t.kind = 45; break loop;} + case 8: + {t.kind = 48; break loop;} + case 9: + {t.kind = 53; break loop;} + + } + } + t.val = new String(tval, 0, tlen); + return t; + } + + private void SetScannerBehindT() { + buffer.setPos(t.pos); + NextCh(); + line = t.line; col = t.col; charPos = t.charPos; + for (int i = 0; i < tlen; i++) NextCh(); + } + + // get the next token (possibly a token already seen during peeking) + public Token Scan () { + if (tokens.next == null) { + return NextToken(); + } else { + pt = tokens = tokens.next; + return tokens; + } + } + + // get the next token, ignore pragmas + public Token Peek () { + do { + if (pt.next == null) { + pt.next = NextToken(); + } + pt = pt.next; + } while (pt.kind > maxT); // skip pragmas + + return pt; + } + + // make sure that peeking starts at current scan position + public void ResetPeek () { pt = tokens; } + +} // end Scanner diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/CompilationModel/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..9a47908b4c88 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/nbm/manifest.mf @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.model +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/model/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/resources/META-INF/services/at.ssw.visualizer.model.CompilationModel b/visualizer/C1Visualizer/CompilationModel/src/main/resources/META-INF/services/at.ssw.visualizer.model.CompilationModel new file mode 100644 index 000000000000..5c61a429118c --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/resources/META-INF/services/at.ssw.visualizer.model.CompilationModel @@ -0,0 +1 @@ +at.ssw.visualizer.modelimpl.CompilationModelImpl \ No newline at end of file diff --git a/visualizer/C1Visualizer/CompilationModel/src/main/resources/at/ssw/visualizer/model/Bundle.properties b/visualizer/C1Visualizer/CompilationModel/src/main/resources/at/ssw/visualizer/model/Bundle.properties new file mode 100644 index 000000000000..876abe05faf5 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationModel/src/main/resources/at/ssw/visualizer/model/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=Compilation Model diff --git a/visualizer/C1Visualizer/CompilationView/pom.xml b/visualizer/C1Visualizer/CompilationView/pom.xml new file mode 100644 index 000000000000..97286b56757b --- /dev/null +++ b/visualizer/C1Visualizer/CompilationView/pom.xml @@ -0,0 +1,137 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + CompilationView + 1.14-SNAPSHOT + nbm + CompilationView + + UTF-8 + + + + at.ssw.visualizer + VisualizerUI + ${project.version} + + + at.ssw.visualizer + CompilationModel + ${project.version} + + + org.netbeans.api + org-netbeans-modules-editor-mimelookup + ${netbeans.version} + + + org.netbeans.modules + org-netbeans-modules-editor-mimelookup-impl + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-sendopts + ${netbeans.version} + + + org.netbeans.api + org-openide-actions + ${netbeans.version} + + + org.netbeans.api + org-openide-awt + ${netbeans.version} + + + org.netbeans.api + org-openide-dialogs + ${netbeans.version} + + + org.netbeans.api + org-openide-explorer + ${netbeans.version} + + + org.netbeans.api + org-openide-filesystems + ${netbeans.version} + + + org.netbeans.api + org-openide-loaders + ${netbeans.version} + + + org.netbeans.api + org-openide-nodes + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + org.netbeans.api + org-openide-util-ui + ${netbeans.version} + + + org.netbeans.api + org-openide-windows + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.CompilationView + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/CompilationModelNode.java b/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/CompilationModelNode.java new file mode 100644 index 000000000000..5acb276c769b --- /dev/null +++ b/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/CompilationModelNode.java @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.compilation.view; + +import at.ssw.visualizer.compilation.view.icons.Icons; +import at.ssw.visualizer.model.Compilation; +import at.ssw.visualizer.model.CompilationElement; +import at.ssw.visualizer.model.CompilationModel; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import java.awt.Image; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import javax.swing.Action; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.util.ImageUtilities; +import org.openide.util.lookup.Lookups; + +/** + * + * @author Christian Wimmer + */ +public class CompilationModelNode extends AbstractNode { + protected Image ImageFolder = ImageUtilities.loadImage(Icons.FOLDER); + protected Image ImageCfg = ImageUtilities.loadImage(Icons.CFG); + protected Image ImageIntervals = ImageUtilities.loadImage(Icons.INTERVALS); + protected boolean sortCompilations; + protected boolean filterCfg; + protected boolean shortNames; + + public CompilationModelNode(CompilationModel model) { + super(Children.LEAF); + setChildren(new CompilationModelChildren(model)); + } + + public void doRefresh(boolean sortCompilations, boolean filterCfg, boolean shortNames) { + this.sortCompilations = sortCompilations; + this.filterCfg = filterCfg; + this.shortNames = shortNames; + + ((CompilationModelChildren) getChildren()).doRefresh(); + } + + @Override + public Action[] getActions(boolean context) { + return new Action[0]; + } + + class CompilationModelChildren extends Children.Keys { + private CompilationModel model; + private ChangeListener changeListener = new ChangeListener() { + public void stateChanged(ChangeEvent event) { + addNotify(); + } + + }; + + public CompilationModelChildren(CompilationModel model) { + this.model = model; + model.addChangedListener(changeListener); + } + + public void doRefresh() { + addNotify(); + for (Node n : getNodes()) { + ((CompilationNode) n).doRefresh(); + } + } + + @Override + protected void addNotify() { + Compilation[] compilations = model.getCompilations().toArray(new Compilation[0]); + if (sortCompilations) { + Arrays.sort(compilations, new Comparator() { + public int compare(Compilation o1, Compilation o2) { + if (shortNames) { + return o1.getShortName().compareToIgnoreCase(o2.getShortName()); + } + return o1.getName().compareToIgnoreCase(o2.getName()); + } + + }); + } + setKeys(compilations); + } + + protected Node[] createNodes(Compilation key) { + return new Node[]{new CompilationNode(key)}; + } + + } + + class CompilationElementNode extends AbstractNode { + private CompilationElement element; + + public CompilationElementNode(CompilationElement element) { + super(element.getElements().size() > 0 ? new CompilationElementChildren(element) : Children.LEAF, Lookups.singleton(element)); + this.element = element; + } + + public void doRefresh() { + fireNameChange(null, null); + if (getChildren() instanceof CompilationElementChildren) { + ((CompilationElementChildren) getChildren()).doRefresh(); + } + } + + @Override + public String getName() { + if (shortNames) { + return element.getShortName(); + } + return element.getName(); + } + + @Override + public String getShortDescription() { + return element.getName(); + } + + @Override + public Image getIcon(int type) { + return element instanceof ControlFlowGraph ? ImageCfg : ImageIntervals; + } + + @Override + public Image getOpenedIcon(int type) { + return getIcon(type); + } + + @Override + public Action[] getActions(boolean context) { + String path = element instanceof ControlFlowGraph ? CompilationViewTopComponent.ACTIONS_CFG : CompilationViewTopComponent.ACTIONS_INTERVALS; + return Lookups.forPath(path).lookupAll(Action.class).toArray(new Action[0]); + } + + @Override + public Action getPreferredAction() { + for (Action action : getActions(false)) { + if (action.isEnabled()) { + return action; + } + } + return null; + } + + } + + class CompilationNode extends CompilationElementNode { + public CompilationNode(Compilation compilation) { + super(compilation); + } + + @Override + public Image getIcon(int type) { + return ImageFolder; + } + + @Override + public Action[] getActions(boolean context) { + return Lookups.forPath(CompilationViewTopComponent.ACTIONS_COMPILATION).lookupAll(Action.class).toArray(new Action[0]); + } + + @Override + public Action getPreferredAction() { + return null; + } + + } + + class CompilationElementChildren extends Children.Keys { + private CompilationElement compilation; + + public CompilationElementChildren(CompilationElement compilation) { + this.compilation = compilation; + } + + public void doRefresh() { + addNotify(); + for (Node n : getNodes()) { + ((CompilationElementNode) n).doRefresh(); + } + } + + @Override + protected void addNotify() { + List elements = new ArrayList(); + for (CompilationElement element : compilation.getElements()) { + if (!filterCfg || element.getCompilation() != element.getParent() || !(element instanceof ControlFlowGraph) || ((ControlFlowGraph) element).hasHir() || ((ControlFlowGraph) element).hasLir()|| ((ControlFlowGraph) element).getNativeMethod() != null) { + elements.add(element); + } + } + setKeys(elements); + } + + protected Node[] createNodes(CompilationElement key) { + return new Node[]{new CompilationElementNode(key)}; + } + + } +} diff --git a/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/CompilationViewTopComponent.java b/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/CompilationViewTopComponent.java new file mode 100644 index 000000000000..e96743d23bb9 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/CompilationViewTopComponent.java @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.compilation.view; + +import at.ssw.visualizer.compilation.view.action.OpenCompilationAction; +import at.ssw.visualizer.compilation.view.icons.Icons; +import at.ssw.visualizer.model.CompilationModel; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyVetoException; +import java.io.Serializable; +import java.util.Collection; +import javax.swing.Action; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JToggleButton; +import javax.swing.JToolBar; +import javax.swing.UIManager; +import javax.swing.border.Border; +import org.openide.awt.Toolbar; +import org.openide.awt.ToolbarPool; +import org.openide.explorer.ExplorerManager; +import org.openide.explorer.ExplorerUtils; +import org.openide.explorer.view.BeanTreeView; +import org.openide.nodes.Node; +import org.openide.util.ImageUtilities; +import org.openide.util.Lookup; +import org.openide.util.actions.Presenter; +import org.openide.util.lookup.Lookups; +import org.openide.windows.TopComponent; +import org.openide.windows.WindowManager; + +/** + * Explorer-like view of all compiled methods. + * + * @author Bernhard Stiftner + * @author Christian Wimmer + */ +public final class CompilationViewTopComponent extends TopComponent implements ExplorerManager.Provider { + + public static final String ACTIONS_COMPILATION = "at-ssw-visualizer-actions-compilation"; + public static final String ACTIONS_CFG = "at-ssw-visualizer-actions-cfg"; + public static final String ACTIONS_INTERVALS = "at-ssw-visualizer-actions-intervals"; + protected ExplorerManager manager; + protected BeanTreeView view; + protected CompilationModelNode rootNode; + protected JToggleButton sortButton; + protected JToggleButton filterButton; + protected JToggleButton packageButton; + + private CompilationViewTopComponent() { + setName("Compiled Methods"); + setToolTipText("List of Compiled Methods"); + setIcon(ImageUtilities.loadImage(Icons.COMPILATIONS)); + + manager = new ExplorerManager(); + view = new BeanTreeView(); + view.setRootVisible(false); + + setLayout(new BorderLayout()); + add(createToolbar(), BorderLayout.NORTH); + add(view, BorderLayout.CENTER); + + CompilationModel model = Lookup.getDefault().lookup(CompilationModel.class); + rootNode = new CompilationModelNode(model); + manager.setRootContext(rootNode); + associateLookup(ExplorerUtils.createLookup(manager, getActionMap())); + + configButtonListener.actionPerformed(null); + } + + private Toolbar createToolbar() { + ToolbarPool.getDefault().setPreferredIconSize(16); + + Toolbar toolBar = new Toolbar(); + toolBar.setBorder((Border) UIManager.get("Nb.Editor.Toolbar.border")); + + toolBar.add(new OpenCompilationAction()); + toolBar.addSeparator(); + addElements(toolBar, Lookups.forPath(CompilationViewTopComponent.ACTIONS_COMPILATION).lookupAll(Action.class)); + toolBar.addSeparator(); + addElements(toolBar, Lookups.forPath(CompilationViewTopComponent.ACTIONS_CFG).lookupAll(Action.class)); + toolBar.addSeparator(); + addElements(toolBar, Lookups.forPath(CompilationViewTopComponent.ACTIONS_INTERVALS).lookupAll(Action.class)); + + toolBar.addSeparator(); + sortButton = new JToggleButton(new ImageIcon(ImageUtilities.loadImage(Icons.SORT)), false); + sortButton.setToolTipText("Sort List of Compilations"); + sortButton.addActionListener(configButtonListener); + toolBar.add(sortButton); + filterButton = new JToggleButton(new ImageIcon(ImageUtilities.loadImage(Icons.FILTER)), true); + filterButton.addActionListener(configButtonListener); + filterButton.setToolTipText("Filter Control Flow Graphs that have no IR (generated during bytecode parsing phase)"); + toolBar.add(filterButton); + packageButton = new JToggleButton(new ImageIcon(ImageUtilities.loadImage(Icons.PACKAGE)), false); + packageButton.addActionListener(configButtonListener); + packageButton.setToolTipText("Show Package Names"); + toolBar.add(packageButton); + toolBar.addSeparator(); + JButton collapseAllButton = new JButton(new ImageIcon(ImageUtilities.loadImage(Icons.COLLAPSE_ALL))); + collapseAllButton.addActionListener(collapseAllListener); + collapseAllButton.setToolTipText("Collapse All"); + toolBar.add(collapseAllButton); + + return toolBar; + } + + private void addElements(JToolBar toolBar, Collection actions) { + for (Action action : actions) { + if (action instanceof Presenter.Toolbar) { + toolBar.add(((Presenter.Toolbar) action).getToolbarPresenter()); + } else { + toolBar.add(action); + } + } + } + + @Override + public Dimension getMinimumSize() { + return new Dimension(0, 0); + } + + public ExplorerManager getExplorerManager() { + return manager; + } + private ActionListener configButtonListener = new ActionListener() { + + public void actionPerformed(ActionEvent event) { + org.openide.nodes.Node[] selectedNodes = manager.getSelectedNodes(); + rootNode.doRefresh(sortButton.isSelected(), filterButton.isSelected(), !packageButton.isSelected()); + try { + manager.setSelectedNodes(selectedNodes); + } catch (PropertyVetoException ex) { + throw new Error(ex); + } + } + }; + private ActionListener collapseAllListener = new ActionListener() { + + public void actionPerformed(ActionEvent event) { + for (Node n : rootNode.getChildren().getNodes()) { + view.collapseNode(n); + } + } + }; + // + private static final String PREFERRED_ID = "CompilationViewTopComponent"; + private static CompilationViewTopComponent instance; + + public static synchronized CompilationViewTopComponent getDefault() { + if (instance == null) { + instance = new CompilationViewTopComponent(); + } + return instance; + } + + public static synchronized CompilationViewTopComponent findInstance() { + return (CompilationViewTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID); + } + + @Override + public int getPersistenceType() { + return PERSISTENCE_ALWAYS; + } + + @Override + protected String preferredID() { + return PREFERRED_ID; + } + + @Override + public Object writeReplace() { + return new ResolvableHelper(); + } + + static final class ResolvableHelper implements Serializable { + + private static final long serialVersionUID = 1L; + + public Object readResolve() { + return CompilationViewTopComponent.getDefault(); + } + } + // +} diff --git a/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/ShowCompilationViewAction.java b/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/ShowCompilationViewAction.java new file mode 100644 index 000000000000..aff1a42082a2 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/ShowCompilationViewAction.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.compilation.view; + +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; +import org.openide.windows.TopComponent; + +/** + * Action which shows Compilation component. + * + * @author Bernhard Stiftner + */ +public class ShowCompilationViewAction extends AbstractAction { + public ShowCompilationViewAction() { + super("Compiled Methods"); + } + + public void actionPerformed(ActionEvent event) { + TopComponent win = CompilationViewTopComponent.findInstance(); + win.open(); + win.requestActive(); + } +} diff --git a/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/action/C1VisualizerDataObject.java b/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/action/C1VisualizerDataObject.java new file mode 100644 index 000000000000..37adb7d10508 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/action/C1VisualizerDataObject.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2025, 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. + * + * 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 at.ssw.visualizer.compilation.view.action; + +import at.ssw.visualizer.model.CompilationModel; +import org.netbeans.api.actions.Openable; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; +import org.openide.cookies.OpenCookie; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.filesystems.MIMEResolver; +import org.openide.loaders.DataObject; +import org.openide.loaders.DataObjectExistsException; +import org.openide.loaders.MultiDataObject; +import org.openide.loaders.MultiFileLoader; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.openide.util.RequestProcessor; + +import java.io.File; + +/** + * DataObject that represents a .CFG file. This object is NOT designed for presentation + * as is usual for NB dataobjects. It just serves to provide Open file from commandline + * feature, nothing more. + */ +@NbBundle.Messages( + "LBL_GraalVMC1VisualizerFile=GraalVM C1Visualizer File" +) +@MIMEResolver.ExtensionRegistration( + displayName = "#LBL_GraalVMC1VisualizerFile", + mimeType = C1VisualizerDataObject.GRAAL_CFG_MIME_TYPE, + position = 3232, + extension = {"cfg"} +) +@DataObject.Registration( + displayName = "LBL_GraalVMC1VisualizerFile", + mimeType = C1VisualizerDataObject.GRAAL_CFG_MIME_TYPE, + position = 400 +) +public class C1VisualizerDataObject extends MultiDataObject { + /** + * MIME type for CFG dumps + */ + public static final String GRAAL_CFG_MIME_TYPE = "application/x-c1visualizer"; // NOI18N + + public C1VisualizerDataObject(FileObject fo, MultiFileLoader loader) throws DataObjectExistsException { + super(fo, loader); + getCookieSet().assign(Openable.class, new OpenImpl()); + } + + private class OpenImpl implements OpenCookie { + @Override + public void open() { + importPrimaryFile(); + } + } + + public void importPrimaryFile() { + FileObject fo = getPrimaryFile(); + File f = FileUtil.toFile(fo); + if (f == null) { + return; + } + + final String fileName = f.getAbsolutePath(); + RequestProcessor.getDefault().post(new Runnable() { + public void run() { + CompilationModel model = Lookup.getDefault().lookup(CompilationModel.class); + String errorMsg = model.parseInputFile(fileName); + if (errorMsg != null) { + NotifyDescriptor d = new NotifyDescriptor.Message("Errors while parsing input:\n" + errorMsg, NotifyDescriptor.ERROR_MESSAGE); + DialogDisplayer.getDefault().notify(d); + } + } + }); + } +} + diff --git a/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/action/OpenCompilationAction.java b/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/action/OpenCompilationAction.java new file mode 100644 index 000000000000..204e4af25f40 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/action/OpenCompilationAction.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.compilation.view.action; + +import at.ssw.visualizer.compilation.view.icons.Icons; +import at.ssw.visualizer.model.CompilationModel; +import java.awt.event.ActionEvent; +import java.io.File; +import java.util.prefs.Preferences; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ImageIcon; +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileNameExtensionFilter; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; +import org.openide.util.ImageUtilities; +import org.openide.util.Lookup; +import org.openide.util.RequestProcessor; +import org.openide.windows.WindowManager; + +/** + * Action opening a CFG file and loading it into the workspace. + * + * @author Bernhard Stiftner + * @author Christian Wimmer + */ +public final class OpenCompilationAction extends AbstractAction { + public OpenCompilationAction() { + super("&Open Compiled Methods...", new ImageIcon(ImageUtilities.loadImage(Icons.OPEN))); + putValue(Action.SHORT_DESCRIPTION, "Open Compiled Methods"); + } + + public void actionPerformed(ActionEvent event) { + JFileChooser ch = new JFileChooser(); + ch.setCurrentDirectory(getLastDirectory()); + ch.setFileFilter(new FileNameExtensionFilter("Compiled methods (*.cfg)", "cfg")); + if (ch.showOpenDialog(WindowManager.getDefault().getMainWindow()) != JFileChooser.APPROVE_OPTION || ch.getSelectedFile() == null) { + return; + } + setLastDirectory(ch.getSelectedFile()); + + final String fileName = ch.getSelectedFile().getAbsolutePath(); + RequestProcessor.getDefault().post(new Runnable() { + public void run() { + CompilationModel model = Lookup.getDefault().lookup(CompilationModel.class); + String errorMsg = model.parseInputFile(fileName); + if (errorMsg != null) { + NotifyDescriptor d = new NotifyDescriptor.Message("Errors while parsing input:\n" + errorMsg, NotifyDescriptor.ERROR_MESSAGE); + DialogDisplayer.getDefault().notify(d); + } + } + }); + } + + private File getLastDirectory() { + Preferences prefs = Preferences.userNodeForPackage(getClass()); + String fileName = prefs.get("lastDirectory", null); + File file = null; + if (fileName != null) { + file = new File(fileName); + } + if (file == null || !file.exists()) { + file = new File(System.getProperty("user.home")); + } + return file; + } + + private void setLastDirectory(File file) { + Preferences prefs = Preferences.userNodeForPackage(getClass()); + if (!file.isDirectory()) { + file = file.getParentFile(); + } + prefs.put("lastDirectory", file.getPath()); + } +} diff --git a/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/action/RemoveAllCompilationsAction.java b/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/action/RemoveAllCompilationsAction.java new file mode 100644 index 000000000000..1b8539e43697 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/action/RemoveAllCompilationsAction.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.compilation.view.action; + +import at.ssw.visualizer.compilation.view.icons.Icons; +import at.ssw.visualizer.model.CompilationModel; +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ImageIcon; +import org.openide.util.ImageUtilities; +import org.openide.util.Lookup; + +/** + * Action for removing all compilations from the workspace. + * + * @author Bernhard Stiftner + * @author Christian Wimmer + */ +public final class RemoveAllCompilationsAction extends AbstractAction { + public RemoveAllCompilationsAction() { + super("Remove All Methods", new ImageIcon(ImageUtilities.loadImage(Icons.REMOVE_ALL))); + putValue(Action.SHORT_DESCRIPTION, "Remove All Methods"); + } + + public void actionPerformed(ActionEvent event) { + CompilationModel model = Lookup.getDefault().lookup(CompilationModel.class); + model.clear(); + } +} diff --git a/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/action/RemoveCompilationAction.java b/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/action/RemoveCompilationAction.java new file mode 100644 index 000000000000..24c6c3f20f0a --- /dev/null +++ b/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/action/RemoveCompilationAction.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.compilation.view.action; + +import at.ssw.visualizer.compilation.view.icons.Icons; +import at.ssw.visualizer.model.Compilation; +import org.openide.nodes.Node; +import org.openide.util.HelpCtx; +import org.openide.util.actions.CookieAction; + +/** + * Action for removing the currently selected compilation from the workspace. + * + * @author Bernhard Stiftner + * @author Christian Wimmer + */ +public final class RemoveCompilationAction extends CookieAction { + protected void performAction(Node[] activatedNodes) { + Compilation compilation = activatedNodes[0].getLookup().lookup(Compilation.class); + compilation.getCompilationModel().removeCompilation(compilation); + } + + public String getName() { + return "Remove Method"; + } + + @Override + protected String iconResource() { + return Icons.REMOVE; + } + + protected int mode() { + return CookieAction.MODE_EXACTLY_ONE; + } + + protected Class[] cookieClasses() { + return new Class[]{Compilation.class}; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + protected boolean asynchronous() { + return false; + } +} diff --git a/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/icons/Icons.java b/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/icons/Icons.java new file mode 100644 index 000000000000..ebcbd7cf05ff --- /dev/null +++ b/visualizer/C1Visualizer/CompilationView/src/main/java/at/ssw/visualizer/compilation/view/icons/Icons.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.compilation.view.icons; + +/** + * + * @author Christian Wimmer + */ +public class Icons { + private static final String PATH = "at/ssw/visualizer/compilation/view/icons/"; + + public static final String OPEN = PATH + "open.gif"; + public static final String REMOVE = PATH + "remove.gif"; + public static final String REMOVE_ALL = PATH + "removeall.gif"; + + public static final String FOLDER = PATH + "folder.gif"; + public static final String COMPILATIONS = PATH + "compilations.gif"; + public static final String CFG = PATH + "cfg.gif"; + public static final String INTERVALS = PATH + "intervals.gif"; + + public static final String FILTER = PATH + "filter.gif"; + public static final String SORT = PATH + "sort.gif"; + public static final String PACKAGE = PATH + "package.gif"; + public static final String COLLAPSE_ALL = PATH + "collapseall.gif"; + + private Icons() { + } +} diff --git a/visualizer/C1Visualizer/CompilationView/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/CompilationView/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..fe26eac40b36 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationView/src/main/nbm/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.compilation.view +OpenIDE-Module-Layer: at/ssw/visualizer/compilation/view/layer.xml +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/compilation/view/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/Bundle.properties b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/Bundle.properties new file mode 100644 index 000000000000..c6f874ec5fbb --- /dev/null +++ b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=Compilation View diff --git a/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/CompilationViewTopComponentSettings.xml b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/CompilationViewTopComponentSettings.xml new file mode 100644 index 000000000000..0b20f777ba36 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/CompilationViewTopComponentSettings.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/CompilationViewTopComponentWstcref.xml b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/CompilationViewTopComponentWstcref.xml new file mode 100644 index 000000000000..f725aff40389 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/CompilationViewTopComponentWstcref.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/cfg.gif b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/cfg.gif new file mode 100644 index 000000000000..695e5a5cfa5b Binary files /dev/null and b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/cfg.gif differ diff --git a/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/collapseall.gif b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/collapseall.gif new file mode 100644 index 000000000000..a2d80a9044f3 Binary files /dev/null and b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/collapseall.gif differ diff --git a/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/compilations.gif b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/compilations.gif new file mode 100644 index 000000000000..983932fcccdf Binary files /dev/null and b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/compilations.gif differ diff --git a/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/filter.gif b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/filter.gif new file mode 100644 index 000000000000..6fe6f0e10a11 Binary files /dev/null and b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/filter.gif differ diff --git a/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/folder.gif b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/folder.gif new file mode 100644 index 000000000000..51e703b1b9c6 Binary files /dev/null and b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/folder.gif differ diff --git a/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/intervals.gif b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/intervals.gif new file mode 100644 index 000000000000..5ef0ed7f13c7 Binary files /dev/null and b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/intervals.gif differ diff --git a/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/open.gif b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/open.gif new file mode 100644 index 000000000000..7aea894d0b60 Binary files /dev/null and b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/open.gif differ diff --git a/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/package.gif b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/package.gif new file mode 100644 index 000000000000..b80590bb7be2 Binary files /dev/null and b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/package.gif differ diff --git a/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/remove.gif b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/remove.gif new file mode 100644 index 000000000000..2cd9c544436c Binary files /dev/null and b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/remove.gif differ diff --git a/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/removeall.gif b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/removeall.gif new file mode 100644 index 000000000000..28a3785aaca7 Binary files /dev/null and b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/removeall.gif differ diff --git a/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/sort.gif b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/sort.gif new file mode 100644 index 000000000000..6311cc00f80d Binary files /dev/null and b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/icons/sort.gif differ diff --git a/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/layer.xml b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/layer.xml new file mode 100644 index 000000000000..c70bed252de8 --- /dev/null +++ b/visualizer/C1Visualizer/CompilationView/src/main/resources/at/ssw/visualizer/compilation/view/layer.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/ControlFlowEditor/pom.xml b/visualizer/C1Visualizer/ControlFlowEditor/pom.xml new file mode 100644 index 000000000000..e91c6baae053 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/pom.xml @@ -0,0 +1,139 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + ControlFlowEditor + 1.14-SNAPSHOT + nbm + ControlFlowEditor + + UTF-8 + + + + at.ssw.visualizer + VisualizerUI + ${project.version} + + + at.ssw.visualizer + CompilationModel + ${project.version} + + + org.eclipse + draw2d + 3.2.100-v20070529 + + + + org.apache.xmlgraphics + batik-dom + ${batik.version} + + + org.apache.xmlgraphics + batik-svggen + ${batik.version} + + + + org.netbeans.api + org-netbeans-api-visual + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-options-api + ${netbeans.version} + + + org.netbeans.api + org-openide-actions + ${netbeans.version} + + + org.netbeans.api + org-openide-awt + ${netbeans.version} + + + org.netbeans.api + org-openide-dialogs + ${netbeans.version} + + + org.netbeans.api + org-openide-loaders + ${netbeans.version} + + + org.netbeans.api + org-openide-nodes + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + org.netbeans.api + org-openide-util-ui + ${netbeans.version} + + + org.netbeans.api + org-openide-windows + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.ControlFlowEditor + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/CfgEditorContext.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/CfgEditorContext.java new file mode 100644 index 000000000000..ced6077b8069 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/CfgEditorContext.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg; + +public abstract class CfgEditorContext { + + public static final int LAYOUT_HIERARCHICALNODELAYOUT = 1; + public static final int LAYOUT_HIERARCHICALCOMPOUNDLAYOUT = 2; + + public static final int ROUTING_DIRECTLINES = 1; + public static final int ROUTING_BEZIER = 2; + + public static final int MAX_AUTOEDGESVISIBLE = 8; +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/AbstractCfgEditorAction.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/AbstractCfgEditorAction.java new file mode 100644 index 000000000000..3a7306d0b2a7 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/AbstractCfgEditorAction.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.action; + + +import at.ssw.visualizer.cfg.graph.CfgEventListener; +import at.ssw.visualizer.cfg.editor.CfgEditorTopComponent; +import at.ssw.visualizer.cfg.graph.CfgScene; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JMenuItem; +import org.openide.util.actions.CallableSystemAction; +import org.openide.windows.TopComponent; + +/** + * The common superclass of all concrete actions related to the CFG visualizer. + * + * @author Bernhard Stiftner + * @author Rumpfhuber Stefan + */ +public abstract class AbstractCfgEditorAction extends CallableSystemAction implements CfgEventListener , PropertyChangeListener { + + CfgEditorTopComponent topComponent = null; + + public AbstractCfgEditorAction() { + TopComponent.getRegistry().addPropertyChangeListener(this); + setEnabled(false); + + } + + protected CfgEditorTopComponent getEditor() { + return topComponent; + } + + protected void setEditor(CfgEditorTopComponent newTopComponent) { + CfgEditorTopComponent oldTopComponent = getEditor(); + if(newTopComponent != oldTopComponent){ + if(oldTopComponent != null) { + oldTopComponent.getCfgScene().removeCfgEventListener(this); + } + this.topComponent = newTopComponent; + if (newTopComponent != null) { + newTopComponent.getCfgScene().addCfgEventListener(this); + selectionChanged(newTopComponent.getCfgScene()); + } + this.setEnabled(newTopComponent!=null); + } + } + + + @Override + public JMenuItem getMenuPresenter() { + return new JMenuItem(this); + } + + + @Override + public JComponent getToolbarPresenter() { + JButton b = new JButton(this); + if (getIcon() != null) { + b.setText(null); + b.setToolTipText(getName()); + } + return b; + } + + + @Override + public JMenuItem getPopupPresenter() { + return new JMenuItem(this); + } + + + @Override + protected boolean asynchronous() { + return false; + } + + + public void propertyChange(PropertyChangeEvent e) { + if ( e.getPropertyName().equals(TopComponent.Registry.PROP_ACTIVATED)) { + if(e.getNewValue() instanceof CfgEditorTopComponent){ + CfgEditorTopComponent tc = (CfgEditorTopComponent)e.getNewValue(); + setEditor(tc); + selectionChanged(tc.getCfgScene()); + } + } + } + + + public void selectionChanged(CfgScene scene) { + } + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/AbstractRouterAction.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/AbstractRouterAction.java new file mode 100644 index 000000000000..e5fa947df6be --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/AbstractRouterAction.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.action; + +import at.ssw.visualizer.cfg.editor.CfgEditorTopComponent; +import javax.swing.JComponent; +import javax.swing.JMenuItem; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.JToggleButton; +import org.openide.util.actions.Presenter; + +/** + * Common superclass for all actions which set the link router. + * + * @author Bernhard Stiftner + * @author Rumpfhuber Stefan + */ +public abstract class AbstractRouterAction extends AbstractCfgEditorAction implements Presenter.Menu, Presenter.Popup, Presenter.Toolbar { + + public void performAction() { + CfgEditorTopComponent tc = getEditor(); + if (tc != null) { + setLinkRouter(tc); + } + } + + protected abstract void setLinkRouter(CfgEditorTopComponent editor); + + @Override + public JMenuItem getMenuPresenter() { + JMenuItem presenter = new MenuPresenter(); + presenter.setToolTipText(getName()); + return presenter; + } + + @Override + public JMenuItem getPopupPresenter() { + return getMenuPresenter(); + } + + @Override + public JComponent getToolbarPresenter() { + ToolbarPresenter presenter = new ToolbarPresenter(); + presenter.setToolTipText(getName()); + return presenter; + } + + class MenuPresenter extends JRadioButtonMenuItem { + + public MenuPresenter() { + super(AbstractRouterAction.this); + setIcon(null); + } + } + + class ToolbarPresenter extends JToggleButton { + + public ToolbarPresenter() { + super(AbstractRouterAction.this); + setText(null); + } + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ColorAction.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ColorAction.java new file mode 100644 index 000000000000..a2126791ca0f --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ColorAction.java @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.action; + +import at.ssw.visualizer.cfg.graph.CfgEventListener; +import at.ssw.visualizer.cfg.editor.CfgEditorTopComponent; +import at.ssw.visualizer.cfg.graph.CfgScene; +import at.ssw.visualizer.cfg.model.CfgNode; +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.image.BufferedImage; +import java.util.Set; +import javax.swing.AbstractAction; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.SwingConstants; +import javax.swing.plaf.basic.BasicArrowButton; +import org.openide.util.HelpCtx; +import org.openide.util.actions.Presenter; + +/** + * Changes the background color of a node. + * + * @author Bernhard Stiftner + * @author Rumpfhuber Stefan + */ +public class ColorAction extends AbstractCfgEditorAction implements Presenter.Menu, Presenter.Popup, Presenter.Toolbar { + + public static final int IMAGE_WIDTH = 16; + public static final int IMAGE_HEIGHT = 16; + + + /** Names of colors shown in color select lists. */ + private static final String[] COLOR_NAMES = {"White", "Light Gray", "Dark Gray", "Light Yellow", "Dark Yellow", "Light Green", "Dark Green", "Light Cyan", "Dark Cyan", "Light Blue", "Dark Blue", "Light Magenta", "Dark Magenta", "Light Red", "Dark Red"}; + + /** Values of colors shown in color select lists. */ + private static final Color[] COLORS = { + new Color(0xFFFFFF), new Color(0xD4D0C8), new Color(0xA4A098), new Color(0xF0F0B0), new Color(0xE0E040), + new Color(0xB0F0B0), new Color(0x40E040), new Color(0xB0F0F0), new Color(0x40E0E0), new Color(0xB0B0F0), + new Color(0x4040E0), new Color(0xF0B0F0), new Color(0xE040E0), new Color(0xF0B0B0), new Color(0xE04040) + }; + + public void performAction() { + // nothing to do here, the presenters are supposed to call + // performAction(Color) + } + + protected void performAction(Color color) { + CfgEditorTopComponent tc = getEditor(); + if (tc != null) { + tc.getCfgScene().setSelectedNodesColor(color); + } + } + + public String getName() { + return "Change NodeColor"; + } + + @Override + protected String iconResource() { + return "at/ssw/visualizer/cfg/icons/color.gif"; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + public JMenuItem getMenuPresenter() { + return new MenuPresenter(); + } + + @Override + public JMenuItem getPopupPresenter() { + return new MenuPresenter(); + } + + @Override + public JComponent getToolbarPresenter() { + return new ToolbarPresenter(); + } + + class MenuPresenter extends JMenu { + + public MenuPresenter() { + super(ColorAction.this); + initGUI(); + } + + protected void initGUI() { + CfgEditorTopComponent tc = getEditor(); + if( tc != null && tc.getCfgScene().getSelectedNodes().size()==0) { + //no node selected + setEnabled(false); + } else { + add(new SetColorAction(null, "Automatic")); + for (int i = 0; i < COLORS.length; i++) { + add(new SetColorAction(COLORS[i], COLOR_NAMES[i])); + } + } + } + } + + class ToolbarPresenter extends JButton implements CfgEventListener, MouseListener { + + final int arrowSize = 5; + final int arrowMargin = 3; + JPopupMenu popup; + + public ToolbarPresenter() { + setIcon(createIcon()); + setToolTipText(ColorAction.this.getName()); + + popup = new JPopupMenu(); + popup.add(new SetColorAction(null, "Automatic")); + for (int i = 0; i < COLORS.length; i++) { + popup.add(new SetColorAction(COLORS[i], COLOR_NAMES[i])); + } + addMouseListener(this); + } + + public Icon createIcon() { + BasicArrowButton arrow = new BasicArrowButton(SwingConstants.SOUTH); + BufferedImage img = new BufferedImage(IMAGE_WIDTH + arrowSize + 2 * arrowMargin, IMAGE_HEIGHT, BufferedImage.TYPE_INT_ARGB); + Graphics g = img.getGraphics(); + ColorAction.this.getIcon().paintIcon(this, g, 0, 0); + arrow.paintTriangle(g, IMAGE_WIDTH + arrowMargin + arrowSize / 2, IMAGE_HEIGHT / 2 - arrowSize / 2, arrowSize, SwingConstants.SOUTH, true); + return new ImageIcon(img); + } + + + public void selectionChanged(CfgScene scene) { + Set nodes = scene.getSelectedNodes(); + setEnabled(nodes.size() > 0); + } + + public void mouseClicked(MouseEvent e) { + if (e.getX() < getInsets().left + IMAGE_WIDTH + arrowMargin) { + performAction(null); + } else { + popup.show(this, 0, getSize().height); + } + } + + public void mouseEntered(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + } + + public void mousePressed(MouseEvent e) { + } + + public void mouseReleased(MouseEvent e) { + } + } + + protected Icon createIcon(Color color) { + if (color == null) { + return ColorAction.this.getIcon(); + } else { + return new ColorIcon(color); + } + } + + class ColorIcon implements Icon { + + Color color; + + public ColorIcon(Color color) { + this.color = color; + } + + public int getIconWidth() { + return IMAGE_WIDTH; + } + + public int getIconHeight() { + return IMAGE_HEIGHT; + } + + public void paintIcon(Component c, Graphics g, int x, int y) { + Color oldColor = g.getColor(); + g.setColor(color); + g.fillRect(x, y, IMAGE_WIDTH, IMAGE_HEIGHT); + g.setColor(oldColor); + } + } + + class SetColorAction extends AbstractAction { + + Color color; + String name; + Icon icon; + + public SetColorAction(Color color, String name) { + super(name, createIcon(color)); + this.color = color; + this.name = name; + icon = (Icon) getValue(AbstractAction.SMALL_ICON); + } + + public Color getColor() { + return color; + } + + public String getName() { + return name; + } + + public Icon getIcon() { + return icon; + } + + public void actionPerformed(ActionEvent e) { + performAction(color); + } + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ExportAction.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ExportAction.java new file mode 100644 index 000000000000..c363364f3c14 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ExportAction.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.action; + +import at.ssw.visualizer.cfg.editor.CfgEditorTopComponent; +import at.ssw.visualizer.cfg.graph.CfgScene; +import java.io.File; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileNameExtensionFilter; +import org.openide.DialogDescriptor; +import org.openide.DialogDisplayer; +import org.openide.util.HelpCtx; +import org.apache.batik.dom.GenericDOMImplementation; +import org.apache.batik.svggen.SVGGeneratorContext; +import org.apache.batik.svggen.SVGGraphics2D; +import org.w3c.dom.DOMImplementation; +import java.io.*; +import java.awt.Graphics2D; + +/** + * Exports Scene to various Graphics Formats + * + * @author Rumpfhuber Stefan + */ +public class ExportAction extends AbstractCfgEditorAction { + + public static final String DESCRIPTION_SVG = "Scaleable Vector Format (.svg)"; + public static final String EXT_SVG = "svg"; + + String lastDirectory = null; + + @Override + public void performAction() { + CfgEditorTopComponent tc = this.getEditor(); + CfgScene scene = tc.getCfgScene(); + JComponent view = scene.getView(); + + JFileChooser chooser = new JFileChooser (); + chooser.setAcceptAllFileFilterUsed(false); + chooser.setDialogTitle (getName()); + chooser.setDialogType (JFileChooser.SAVE_DIALOG); + chooser.setMultiSelectionEnabled (false); + chooser.setFileSelectionMode (JFileChooser.FILES_ONLY); + chooser.addChoosableFileFilter(new FileNameExtensionFilter(DESCRIPTION_SVG, EXT_SVG)); + if(lastDirectory != null) + chooser.setCurrentDirectory(new File(lastDirectory)); + chooser.setSelectedFile(new File(tc.getName())); + + + if (chooser.showSaveDialog (tc) != JFileChooser.APPROVE_OPTION) + return; + + File file = chooser.getSelectedFile (); + + if(file == null) + return; + + FileNameExtensionFilter filter = (FileNameExtensionFilter) chooser.getFileFilter(); + String fn = file.getAbsolutePath().toLowerCase(); + String ext = filter.getExtensions()[0]; + if(!fn.endsWith("." + ext)){ + file = new File( file.getParentFile(), file.getName() + "." + ext); + } + + if (file.exists ()) { + DialogDescriptor descriptor = new DialogDescriptor ( + "File (" + file.getAbsolutePath () + ") already exists. Do you want to overwrite it?", + "File Exists", true, DialogDescriptor.YES_NO_OPTION, DialogDescriptor.NO_OPTION, null); + DialogDisplayer.getDefault ().createDialog (descriptor).setVisible (true); + if (descriptor.getValue () != DialogDescriptor.YES_OPTION) + return; + } + + lastDirectory = chooser.getCurrentDirectory().getAbsolutePath(); + + if(ext.equals(EXT_SVG)){ + DOMImplementation dom = GenericDOMImplementation.getDOMImplementation(); + org.w3c.dom.Document document = dom.createDocument("http://www.w3.org/2000/svg", "svg", null); + SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(document); + ctx.setEmbeddedFontsOn(true); + Graphics2D svgGenerator = new SVGGraphics2D(ctx, true); + scene.paint(svgGenerator); + FileOutputStream os = null; + try { + os = new FileOutputStream(file); + Writer out = new OutputStreamWriter(os, "UTF-8"); + assert svgGenerator instanceof SVGGraphics2D; + SVGGraphics2D svgGraphics = (SVGGraphics2D)svgGenerator; + svgGraphics.stream(out, true); + } catch (IOException e) { + // NotifyDescriptor message = new NotifyDescriptor.Message( + // Bundle.EXPORT_BATIK_ErrorExportingSVG(e.getLocalizedMessage()), NotifyDescriptor.ERROR_MESSAGE); + // DialogDisplayer.getDefault().notifyLater(message); + } finally { + if (os != null) { + try { + os.close(); + } catch (IOException e) { + } + } + } + } + } + + + @Override + public String getName() { + return "Export CFG"; + } + + @Override + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + protected String iconResource() { + return "at/ssw/visualizer/cfg/icons/disk.gif"; + } + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/HideEdgesAction.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/HideEdgesAction.java new file mode 100644 index 000000000000..253e9166cba0 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/HideEdgesAction.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.action; + + +import at.ssw.visualizer.cfg.editor.CfgEditorTopComponent; +import at.ssw.visualizer.cfg.graph.CfgScene; +import at.ssw.visualizer.cfg.graph.EdgeWidget; +import at.ssw.visualizer.cfg.model.CfgEdge; +import at.ssw.visualizer.cfg.model.CfgNode; +import org.openide.util.HelpCtx; + +/** + * Hides all edges connected to the selected node. + * + * @author Bernhard Stiftner + * @author Rumpfhuber Stefan + */ +public class HideEdgesAction extends AbstractCfgEditorAction { + + public void performAction() { + CfgEditorTopComponent tc = getEditor(); + if (tc != null) { + tc.getCfgScene().setSelectedEdgesVisibility(false); + } + } + + public String getName() { + return "Hide Edges"; + } + + @Override + protected String iconResource() { + return "at/ssw/visualizer/cfg/icons/hideedges.gif"; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + public void selectionChanged(CfgScene scene) { + for (CfgNode n : scene.getSelectedNodes()) { + for (CfgEdge e : scene.findNodeEdges(n, true, true) ){ + EdgeWidget ew = (EdgeWidget) scene.findWidget(e); + if(ew.isVisible()) { + setEnabled(true); + return; + } + } + } + setEnabled(false); + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/HierarchicalCompoundLayoutAction.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/HierarchicalCompoundLayoutAction.java new file mode 100644 index 000000000000..11cc54ed7219 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/HierarchicalCompoundLayoutAction.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.action; + +import at.ssw.visualizer.cfg.CfgEditorContext; +import at.ssw.visualizer.cfg.editor.CfgEditorTopComponent; +import at.ssw.visualizer.cfg.graph.CfgScene; +import org.openide.util.HelpCtx; + + +public class HierarchicalCompoundLayoutAction extends AbstractCfgEditorAction { + + public void performAction() { + CfgEditorTopComponent tc = getEditor(); + if (tc != null) { + CfgScene scene = tc.getCfgScene(); + scene.setSceneLayout(CfgEditorContext.LAYOUT_HIERARCHICALCOMPOUNDLAYOUT); + scene.applyLayout(); + } + } + + public String getName() { + return "Hierarchical Compound Layout"; + } + + + @Override + protected String iconResource() { + return "at/ssw/visualizer/cfg/icons/arrangeloop.gif"; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/HierarchicalNodeLayoutAction.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/HierarchicalNodeLayoutAction.java new file mode 100644 index 000000000000..a6c7563fdb4c --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/HierarchicalNodeLayoutAction.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.action; + +import at.ssw.visualizer.cfg.CfgEditorContext; +import at.ssw.visualizer.cfg.editor.CfgEditorTopComponent; +import at.ssw.visualizer.cfg.graph.CfgScene; +import org.openide.util.HelpCtx; + + +public class HierarchicalNodeLayoutAction extends AbstractCfgEditorAction { + + @Override + public void performAction() { + CfgEditorTopComponent tc = getEditor(); + if (tc != null) { + CfgScene scene = tc.getCfgScene(); + scene.setSceneLayout(CfgEditorContext.LAYOUT_HIERARCHICALNODELAYOUT); + scene.applyLayout(); + } + } + + public String getName() { + return "Hierarchical Node Layout"; + } + + + @Override + protected String iconResource() { + return "at/ssw/visualizer/cfg/icons/arrangehier.gif"; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ShowAllAction.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ShowAllAction.java new file mode 100644 index 000000000000..fdae5ce5c843 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ShowAllAction.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.action; + +import at.ssw.visualizer.cfg.editor.CfgEditorTopComponent; +import at.ssw.visualizer.cfg.graph.CfgScene; +import org.openide.util.HelpCtx; + + +/** + * Adjusts the Zoom factor of the Scene to the bounds of Scroll panel + * to get a clean view on the whole graph. + * + */ +public class ShowAllAction extends AbstractCfgEditorAction { + + @Override + public void performAction() { + CfgEditorTopComponent tc = getEditor(); + if (tc != null) { + CfgScene scene = tc.getCfgScene(); + scene.zoomScene(); + + } + } + + @Override + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + public String getName() { + return "Fit Scene to Window"; + } + + @Override + protected String iconResource() { + return "at/ssw/visualizer/cfg/icons/autosize.gif"; + } + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ShowCFGEditorAction.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ShowCFGEditorAction.java new file mode 100644 index 000000000000..3e85928871a7 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ShowCFGEditorAction.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.action; + +import at.ssw.visualizer.cfg.editor.CfgEditorSupport; +import at.ssw.visualizer.cfg.editor.CfgEditorTopComponent; +import at.ssw.visualizer.cfg.icons.Icons; +import at.ssw.visualizer.core.focus.Focus; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import org.openide.nodes.Node; +import org.openide.util.HelpCtx; +import org.openide.util.actions.CookieAction; + +/** + * Shows the CFG visualizer for the currently selected compilation. + * + * @author Bernhard Stiftner + * @author Christian Wimmer + */ +public final class ShowCFGEditorAction extends CookieAction { + + protected void performAction(Node[] activatedNodes) { + ControlFlowGraph cfg = activatedNodes[0].getLookup().lookup(ControlFlowGraph.class); + if (!Focus.findEditor(CfgEditorTopComponent.class, cfg)) { + CfgEditorSupport editor = new CfgEditorSupport(cfg); + editor.open(); + } + } + + @Override + protected boolean enable(Node[] activatedNodes) { + if (!super.enable(activatedNodes)) { + return false; + } + ControlFlowGraph cfg = activatedNodes[0].getLookup().lookup(ControlFlowGraph.class); + return cfg.getBasicBlocks().size() > 0; + } + + public String getName() { + return "Open Control Flow Graph"; + } + + @Override + protected String iconResource() { + return Icons.CFG; + } + + protected int mode() { + return CookieAction.MODE_EXACTLY_ONE; + } + + protected Class[] cookieClasses() { + return new Class[]{ControlFlowGraph.class}; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + protected boolean asynchronous() { + return false; + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ShowEdgesAction.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ShowEdgesAction.java new file mode 100644 index 000000000000..f0c2c7d89212 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ShowEdgesAction.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.action; + +import at.ssw.visualizer.cfg.editor.CfgEditorTopComponent; +import at.ssw.visualizer.cfg.graph.CfgScene; +import at.ssw.visualizer.cfg.graph.EdgeWidget; +import at.ssw.visualizer.cfg.model.CfgEdge; +import at.ssw.visualizer.cfg.model.CfgNode; +import org.openide.util.HelpCtx; + +/** + * Shows all edges connected to the selected node. + * + * @author Bernhard Stiftner + * @author Rumpfhuber Stefan + */ +public class ShowEdgesAction extends AbstractCfgEditorAction { + + public void performAction() { + CfgEditorTopComponent tc = getEditor(); + if (tc != null) { + tc.getCfgScene().setSelectedEdgesVisibility(true); + } + } + + public String getName() { + return "Show edges"; + } + + + @Override + protected String iconResource() { + return "at/ssw/visualizer/cfg/icons/showedges.gif"; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + public void selectionChanged(CfgScene scene) { + for (CfgNode n : scene.getSelectedNodes()) { + for (CfgEdge e : scene.findNodeEdges(n, true, true) ){ + EdgeWidget ew = (EdgeWidget) scene.findWidget(e); + if(!ew.isVisible()) { + setEnabled(true); + return; + } + } + } + setEnabled(false); + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/SwitchLoopClustersAction.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/SwitchLoopClustersAction.java new file mode 100644 index 000000000000..9a2f7f23019b --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/SwitchLoopClustersAction.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.action; + +import at.ssw.visualizer.cfg.editor.CfgEditorTopComponent; +import at.ssw.visualizer.cfg.graph.CfgScene; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import javax.swing.JComponent; +import javax.swing.JToggleButton; +import org.openide.util.HelpCtx; +import org.openide.util.actions.Presenter; + +public class SwitchLoopClustersAction extends AbstractCfgEditorAction implements Presenter.Toolbar { + + @Override + public void performAction() { + CfgEditorTopComponent tc = getEditor(); + if (tc != null) { + CfgScene scene = tc.getCfgScene(); + boolean visible = scene.isLoopClusterVisible(); + scene.setLoopWidgets(!visible); + } + } + + @Override + public String getName() { + return "Enable/Disable Loop Clusters"; + } + + @Override + protected String iconResource() { + return "at/ssw/visualizer/cfg/icons/cluster.gif"; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + + @Override + public JComponent getToolbarPresenter() { + return new ToolbarPresenter(); + } + + class ToolbarPresenter extends JToggleButton { + private static final String TOOLTIP_ENABLE = "Enable LoopClusters"; + private static final String TOOLTIP_DISABLE = "Disable LoopClusters"; + + public ToolbarPresenter() { + super(SwitchLoopClustersAction.this); + setText(null); + this.setToolTipText(TOOLTIP_DISABLE); + this.setSelected(true); + + this.addItemListener(new ItemListener(){ + public void itemStateChanged(ItemEvent e) { + if(isSelected()){ + setToolTipText(TOOLTIP_DISABLE); + } else { + setToolTipText(TOOLTIP_ENABLE); + } + } + }); + } + } + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/UseBezierRouterAction.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/UseBezierRouterAction.java new file mode 100644 index 000000000000..67317a526af3 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/UseBezierRouterAction.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.action; + +import at.ssw.visualizer.cfg.CfgEditorContext; +import at.ssw.visualizer.cfg.editor.CfgEditorTopComponent; +import org.openide.util.HelpCtx; + + +public class UseBezierRouterAction extends AbstractRouterAction { + + @Override + protected void setLinkRouter(CfgEditorTopComponent editor) { + editor.getCfgScene().setRouter(CfgEditorContext.ROUTING_BEZIER); + } + + @Override + public String getName() { + return "Use Bezier Router"; + } + + @Override + protected String iconResource() { + return "at/ssw/visualizer/cfg/icons/bezierrouter.gif"; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/UseDirectLineRouterAction.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/UseDirectLineRouterAction.java new file mode 100644 index 000000000000..220b5ee1ee5e --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/UseDirectLineRouterAction.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.action; + +import at.ssw.visualizer.cfg.CfgEditorContext; +import at.ssw.visualizer.cfg.editor.CfgEditorTopComponent; +import org.openide.util.HelpCtx; + + +public class UseDirectLineRouterAction extends AbstractRouterAction { + + @Override + protected void setLinkRouter(CfgEditorTopComponent editor) { + editor.getCfgScene().setRouter(CfgEditorContext.ROUTING_DIRECTLINES); + } + + @Override + public String getName() { + return "User Direct Router"; + } + + @Override + protected String iconResource() { + return "at/ssw/visualizer/cfg/icons/fanrouter.gif"; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ZoominAction.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ZoominAction.java new file mode 100644 index 000000000000..3d01c5389328 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ZoominAction.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.action; + +import at.ssw.visualizer.cfg.editor.CfgEditorTopComponent; +import at.ssw.visualizer.cfg.graph.CfgScene; +import org.openide.util.HelpCtx; + + +public class ZoominAction extends AbstractCfgEditorAction { + + @Override + public void performAction() { + CfgEditorTopComponent tc = getEditor(); + if (tc != null) { + CfgScene scene = tc.getCfgScene(); + scene.animateZoom(1.1); + } + } + + @Override + protected String iconResource() { + return "at/ssw/visualizer/cfg/icons/zoomin.gif"; + } + + @Override + public String getName() { + return "Zoomin"; + } + + @Override + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ZoomoutAction.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ZoomoutAction.java new file mode 100644 index 000000000000..fbfd30f5c591 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/action/ZoomoutAction.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.action; + +import at.ssw.visualizer.cfg.editor.CfgEditorTopComponent; +import at.ssw.visualizer.cfg.graph.CfgScene; +import org.openide.util.HelpCtx; + +public class ZoomoutAction extends AbstractCfgEditorAction { + + @Override + public void performAction() { + CfgEditorTopComponent tc = getEditor(); + if (tc != null) { + CfgScene scene = tc.getCfgScene(); + scene.animateZoom(0.9); + } + } + + @Override + protected String iconResource() { + return "at/ssw/visualizer/cfg/icons/zoomout.gif"; + } + + @Override + public String getName() { + return "Zoomout"; + } + + @Override + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/editor/CfgEditorSupport.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/editor/CfgEditorSupport.java new file mode 100644 index 000000000000..9231210a56ec --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/editor/CfgEditorSupport.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.editor; + +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.beans.VetoableChangeListener; +import java.beans.VetoableChangeSupport; +import java.io.IOException; +import org.openide.cookies.OpenCookie; +import org.openide.windows.CloneableOpenSupport; +import org.openide.windows.CloneableTopComponent; + + +public class CfgEditorSupport extends CloneableOpenSupport implements OpenCookie { + private ControlFlowGraph cfg; + + public CfgEditorSupport(ControlFlowGraph cfg) { + super(new Env()); + ((Env) env).editorSupport = this; + this.cfg = cfg; + } + + protected CloneableTopComponent createCloneableTopComponent() { + return new CfgEditorTopComponent(cfg); + } + + public String messageOpened() { + return "Opened " + cfg.getCompilation().getMethod() + " - " + cfg.getName(); + } + + public String messageOpening() { + return "Opening " + cfg.getCompilation().getMethod() + " - " + cfg.getName(); + } + + + public static class Env implements CloneableOpenSupport.Env { + private PropertyChangeSupport prop = new PropertyChangeSupport(this); + private VetoableChangeSupport veto = new VetoableChangeSupport(this); + private CfgEditorSupport editorSupport; + + public boolean isValid() { + return true; + } + + public boolean isModified() { + return false; + } + + public void markModified() throws IOException { + throw new IOException("Editor is readonly"); + } + + public void unmarkModified() { + // Nothing to do. + } + + public CloneableOpenSupport findCloneableOpenSupport() { + return editorSupport; + } + + public void addPropertyChangeListener(PropertyChangeListener l) { + prop.addPropertyChangeListener(l); + } + + public void removePropertyChangeListener(PropertyChangeListener l) { + prop.removePropertyChangeListener(l); + } + + public void addVetoableChangeListener(VetoableChangeListener l) { + veto.addVetoableChangeListener(l); + } + + public void removeVetoableChangeListener(VetoableChangeListener l) { + veto.removeVetoableChangeListener(l); + } + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/editor/CfgEditorTopComponent.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/editor/CfgEditorTopComponent.java new file mode 100644 index 000000000000..1974ded48f1d --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/editor/CfgEditorTopComponent.java @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.editor; + +import at.ssw.visualizer.cfg.action.ShowAllAction; +import at.ssw.visualizer.cfg.action.ColorAction; +import at.ssw.visualizer.cfg.action.ExportAction; +import at.ssw.visualizer.cfg.action.HideEdgesAction; +import at.ssw.visualizer.cfg.action.HierarchicalCompoundLayoutAction; +import at.ssw.visualizer.cfg.action.HierarchicalNodeLayoutAction; +import at.ssw.visualizer.cfg.action.ShowEdgesAction; +import at.ssw.visualizer.cfg.action.SwitchLoopClustersAction; +import at.ssw.visualizer.cfg.action.UseBezierRouterAction; +import at.ssw.visualizer.cfg.action.UseDirectLineRouterAction; +import at.ssw.visualizer.cfg.action.ZoominAction; +import at.ssw.visualizer.cfg.action.ZoomoutAction; +import at.ssw.visualizer.cfg.graph.CfgEventListener; +import at.ssw.visualizer.cfg.graph.CfgScene; +import at.ssw.visualizer.cfg.graph.EdgeWidget; +import at.ssw.visualizer.cfg.graph.NodeWidget; +import at.ssw.visualizer.cfg.model.CfgEdge; +import at.ssw.visualizer.cfg.model.CfgNode; +import at.ssw.visualizer.cfg.preferences.CfgPreferences; +import at.ssw.visualizer.cfg.preferences.FlagsSetting; +import javax.swing.ScrollPaneConstants; +import at.ssw.visualizer.core.selection.Selection; +import at.ssw.visualizer.core.selection.SelectionManager; +import at.ssw.visualizer.core.selection.SelectionProvider; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import java.awt.BorderLayout; +import java.awt.Color; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.JComponent; +import javax.swing.JScrollPane; +import javax.swing.JToggleButton; +import javax.swing.UIManager; +import javax.swing.border.Border; +import org.netbeans.api.visual.widget.Widget; +import org.openide.awt.Toolbar; +import org.openide.util.ImageUtilities; +import org.openide.windows.CloneableTopComponent; +import org.openide.windows.TopComponent; +import org.openide.util.actions.SystemAction; + +public class CfgEditorTopComponent extends CloneableTopComponent implements PropertyChangeListener, SelectionProvider { + + private CfgScene scene; + private JScrollPane jScrollPane; + private ControlFlowGraph cfg; + private JComponent myView; + private Selection selection; + + public CfgEditorTopComponent(ControlFlowGraph cfg) { + this.cfg = cfg; + + setIcon(ImageUtilities.loadImage("at/ssw/visualizer/cfg/icons/cfg.gif")); + setName(cfg.getParent().getShortName()); + setToolTipText(cfg.getCompilation().getMethod() + " - " + cfg.getName()); + + //panel setup + this.jScrollPane = new JScrollPane(); + this.jScrollPane.setOpaque(true); + this.jScrollPane.setBorder(BorderFactory.createEmptyBorder()); + this.jScrollPane.setViewportBorder(BorderFactory.createEmptyBorder()); + this.scene = new CfgScene(this); + this.myView = scene.createView(); + this.jScrollPane.setViewportView(myView); + this.setLayout(new BorderLayout()); + this.add(createToolbar(), BorderLayout.NORTH); + this.add(jScrollPane, BorderLayout.CENTER); + jScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + jScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); + jScrollPane.getVerticalScrollBar().setEnabled(true); + jScrollPane.getHorizontalScrollBar().setEnabled(true); + + //setup enviroment,register listeners + selection = new Selection(); + selection.put(cfg); + selection.put(scene); + selection.addChangeListener(scene); + + scene.validate(); + scene.applyLayout(); + } + + public Selection getSelection() { + return selection; + } + + public ControlFlowGraph getCfg() { + return cfg; + } + + public JScrollPane getJScrollPanel() { + return jScrollPane; + } + + public CfgScene getCfgScene() { + return scene; + } + + @Override + public int getPersistenceType() { + return TopComponent.PERSISTENCE_NEVER; + } + + @Override + protected void componentOpened() { + super.componentOpened(); + CfgPreferences.getInstance().addPropertyChangeListener(this); + } + + @Override + protected void componentActivated() { + super.componentActivated(); + SelectionManager.getDefault().setSelection(selection); + this.getCfgScene().updateGlobalSelection(); + this.getCfgScene().fireSelectionChanged(); + } + + @Override + protected void componentClosed() { + super.componentClosed(); + SelectionManager.getDefault().removeSelection(selection); + CfgPreferences.getInstance().removePropertyChangeListener(this); + } + + @Override + protected CloneableTopComponent createClonedObject() { + CfgEditorTopComponent component = new CfgEditorTopComponent(cfg); + component.setActivatedNodes(getActivatedNodes()); + return component; + } + + public void propertyChange(PropertyChangeEvent evt) { + if (this.scene != null) { + + String propName = evt.getPropertyName(); + CfgPreferences prefs = CfgPreferences.getInstance(); + if (propName.equals(CfgPreferences.PROP_BACKGROUND_COLOR)) { + scene.setBackground(prefs.getBackgroundColor()); + scene.revalidate(); + } else if (propName.equals(CfgPreferences.PROP_NODE_COLOR)) { + for (NodeWidget nw : scene.getNodeWidgets()) { + //only change the node color if its not a custom color + if (!nw.isNodeColorCustomized()) { + nw.setNodeColor(prefs.getNodeColor(), false); + } + } + } else if (propName.equals(CfgPreferences.PROP_EDGE_COLOR)) { + for (CfgEdge e : scene.getEdges()) { + if (!e.isBackEdge() && !e.isXhandler()) { + EdgeWidget w = (EdgeWidget) scene.findWidget(e); + w.setLineColor(prefs.getEdgeColor()); + } + } + } else if (propName.equals(CfgPreferences.PROP_BACK_EDGE_COLOR)) { + for (CfgEdge e : scene.getEdges()) { + if (e.isBackEdge()) { + EdgeWidget w = (EdgeWidget) scene.findWidget(e); + w.setLineColor(prefs.getBackedgeColor()); + } + } + } else if (propName.equals(CfgPreferences.PROP_EXCEPTION_EDGE_COLOR)) { + for (CfgEdge e : scene.getEdges()) { + if (e.isXhandler()) { + EdgeWidget w = (EdgeWidget) scene.findWidget(e); + w.setLineColor(prefs.getExceptionEdgeColor()); + } + } + } else if (propName.equals(CfgPreferences.PROP_BORDER_COLOR)) { + for (CfgNode n : scene.getNodes()) { + NodeWidget nw = (NodeWidget) scene.findWidget(n); + nw.setBorderColor(prefs.getBorderColor()); + } + } else if (propName.equals(CfgPreferences.PROP_TEXT_FONT)) { + for (CfgNode n : scene.getNodes()) { + NodeWidget nw = (NodeWidget) scene.findWidget(n); + nw.adjustFont(prefs.getTextFont()); + } + } else if (propName.equals(CfgPreferences.PROP_TEXT_COLOR)) { + for (CfgNode n : scene.getNodes()) { + NodeWidget nw = (NodeWidget) scene.findWidget(n); + nw.setForeground(prefs.getTextColor()); + } + } else if (propName.equals(CfgPreferences.PROP_FLAGS)) { + FlagsSetting fs = CfgPreferences.getInstance().getFlagsSetting(); + for (CfgNode n : scene.getNodes()) { + NodeWidget nw = (NodeWidget) scene.findWidget(n); + Color nodeColor = fs.getColor(n.getBasicBlock().getFlags()); + if (nodeColor != null) { + nw.setNodeColor(nodeColor, true); + } else { + nw.setNodeColor(CfgPreferences.getInstance().getNodeColor(), false); + } + } + } else if (propName.equals(CfgPreferences.PROP_SELECTION_COLOR_BG) || propName.equals(CfgPreferences.PROP_SELECTION_COLOR_FG)) { + for (CfgNode n : scene.getNodes()) { + Widget w = scene.findWidget(n); + w.revalidate(); + } + } + scene.validate(); + } + + } + + private Toolbar createToolbar() { + Toolbar tb = new Toolbar("CfgToolbar"); + + tb.setBorder((Border) UIManager.get("Nb.Editor.Toolbar.border")); + + //zoomin/zoomout buttons + tb.add(SystemAction.get(ZoominAction.class).getToolbarPresenter()); + tb.add(SystemAction.get(ZoomoutAction.class).getToolbarPresenter()); + tb.addSeparator(); + + //router buttons + ButtonGroup routerButtons = new ButtonGroup(); + UseDirectLineRouterAction direct = SystemAction.get(UseDirectLineRouterAction.class); + UseBezierRouterAction bezier = SystemAction.get(UseBezierRouterAction.class); + JToggleButton button = (JToggleButton) direct.getToolbarPresenter(); + button.getModel().setGroup(routerButtons); + button.setSelected(true); + tb.add(button); + button = (JToggleButton) bezier.getToolbarPresenter(); + button.getModel().setGroup(routerButtons); + tb.add(button); + tb.addSeparator(); + + //layout buttons + tb.add(SystemAction.get(HierarchicalNodeLayoutAction.class).getToolbarPresenter()); + tb.add(SystemAction.get(HierarchicalCompoundLayoutAction.class).getToolbarPresenter()); + + tb.addSeparator(); + tb.add(SystemAction.get(ShowAllAction.class).getToolbarPresenter()); + tb.addSeparator(); + + //cluster button + tb.add(SystemAction.get(SwitchLoopClustersAction.class).getToolbarPresenter()); + tb.addSeparator(); + + //show/hide edge button + tb.add(SystemAction.get(ShowEdgesAction.class).getToolbarPresenter()); + tb.add(SystemAction.get(HideEdgesAction.class).getToolbarPresenter()); + tb.addSeparator(); + + //color button + JComponent colorButton = SystemAction.get(ColorAction.class).getToolbarPresenter(); + getCfgScene().addCfgEventListener((CfgEventListener) colorButton); + tb.add(colorButton); + + //export button + tb.add(SystemAction.get(ExportAction.class).getToolbarPresenter()); + tb.doLayout(); + + return tb; + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/CfgEventListener.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/CfgEventListener.java new file mode 100644 index 000000000000..0071fa38eb8e --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/CfgEventListener.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.graph; + +import java.util.EventListener; + + +public interface CfgEventListener extends EventListener { + + /** + * the node or the edge selection got changed + */ + public void selectionChanged(CfgScene scene); + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/CfgScene.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/CfgScene.java new file mode 100644 index 000000000000..a5a4233011ef --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/CfgScene.java @@ -0,0 +1,856 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.graph; + + +import at.ssw.visualizer.cfg.model.CfgEdge; +import at.ssw.visualizer.cfg.CfgEditorContext; +import at.ssw.visualizer.cfg.action.ColorAction; +import at.ssw.visualizer.cfg.action.HideEdgesAction; +import at.ssw.visualizer.cfg.action.ShowEdgesAction; +import at.ssw.visualizer.cfg.editor.CfgEditorTopComponent; +import at.ssw.visualizer.cfg.graph.layout.HierarchicalCompoundLayout; +import at.ssw.visualizer.cfg.graph.layout.HierarchicalNodeLayout; +import at.ssw.visualizer.cfg.model.CfgEnv; +import at.ssw.visualizer.cfg.model.CfgNode; +import at.ssw.visualizer.cfg.model.LoopInfo; +import at.ssw.visualizer.cfg.preferences.CfgPreferences; +import at.ssw.visualizer.cfg.visual.PolylineRouter; +import at.ssw.visualizer.cfg.visual.PolylineRouterV2; +import at.ssw.visualizer.cfg.visual.WidgetCollisionCollector; +import at.ssw.visualizer.core.selection.Selection; +import at.ssw.visualizer.core.selection.SelectionManager; +import at.ssw.visualizer.model.cfg.BasicBlock; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.swing.AbstractAction; +import javax.swing.JComponent; +import javax.swing.JMenu; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.EventListenerList; +import org.netbeans.api.visual.action.ActionFactory; +import org.netbeans.api.visual.action.MoveProvider; +import org.netbeans.api.visual.action.PopupMenuProvider; +import org.netbeans.api.visual.action.RectangularSelectDecorator; +import org.netbeans.api.visual.action.RectangularSelectProvider; +import org.netbeans.api.visual.action.WidgetAction; +import org.netbeans.api.visual.anchor.Anchor; +import org.netbeans.api.visual.anchor.AnchorFactory; +import org.netbeans.api.visual.graph.GraphScene; +import org.netbeans.api.visual.graph.layout.GraphLayout; +import org.netbeans.api.visual.layout.LayoutFactory; +import org.netbeans.api.visual.layout.SceneLayout; +import org.netbeans.api.visual.router.Router; +import org.netbeans.api.visual.router.Router.*; +import org.netbeans.api.visual.router.RouterFactory; +import org.netbeans.api.visual.widget.ConnectionWidget; +import org.netbeans.api.visual.widget.LayerWidget; +import org.netbeans.api.visual.widget.Widget; +import org.openide.util.actions.SystemAction; + + +public class CfgScene extends GraphScene implements ChangeListener { + private LayerWidget mainLayer = new LayerWidget(this); + private LayerWidget connectionLayer = new LayerWidget(this); + private LayerWidget interractionLayer = new LayerWidget(this); + private LayerWidget clusterLayer = new LayerWidget(this); + private Set selectedNodes = Collections.emptySet(); + private Map loopidx2clusterwidget = new HashMap(); + private Map inputSwitches = new HashMap(); + private Map outputSwitches = new HashMap(); + private WidgetAction moveAction = ActionFactory.createMoveAction (ActionFactory.createFreeMoveStrategy(), this.createMoveProvider()); + private SceneLayout sceneLayout; + private CfgEnv env; + private int currentLayout=-1; + private int currentRouter=-1; + private CfgEditorTopComponent cfgtc; + private EventListenerList listenerList = new EventListenerList(); + private WidgetAction contextPopupAction = this.createContextMenuAction(this); + private List nodeWidgets=null; + private boolean loopClustersVisible = true; + + + public CfgScene(final CfgEditorTopComponent cfgtc){ + addChild(clusterLayer); + addChild(mainLayer); + addChild(interractionLayer); + addChild(connectionLayer); + this.loadDefaults(); + this.cfgtc=cfgtc; + this.loadModel(new CfgEnv(cfgtc.getCfg())); + this.setSceneLayout(CfgEditorContext.LAYOUT_HIERARCHICALNODELAYOUT);//default + this.getInputBindings().setZoomActionModifiers(0); + this.getActions().addAction(ActionFactory.createMouseCenteredZoomAction(1.1)); + this.getActions().addAction(ActionFactory.createPanAction()); + this.getActions().addAction(ActionFactory.createRectangularSelectAction( + this.createSelectDecorator(this), + interractionLayer, + this.createRectangularSelectProvider()) + ); + this.getActions().addAction(this.contextPopupAction); + this.addSceneListener(createSceneListener(this)); + this.validate(); + } + + private void loadModel(CfgEnv cfgenv) { + this.env = cfgenv; + for(CfgNode n : env.getNodes()) { + addNode(n); + } + for(CfgEdge e : env.getEdges()) { + addEdge(e); + setEdgeSource(e, e.getSourceNode()); + setEdgeTarget(e, e.getTargetNode()); + } + this.stackLoops(cfgenv.getLoopMap()); + this.autoHideEdges(); + } + + public void loadDefaults() { + this.setRouter(CfgEditorContext.ROUTING_DIRECTLINES); + CfgPreferences prefs = CfgPreferences.getInstance(); + this.setBackground(prefs.getBackgroundColor()); + } + + //sets the parent Widget of all LoopClusterWidgets + private void stackLoops(Map map) { + this.clusterLayer.removeChildren(); + + Set cache = new HashSet(); + for(LoopInfo info : map.values()){ + if(cache.contains(info)) continue; + LoopClusterWidget widget = this.loopidx2clusterwidget.get(info.getLoopIndex()); + LoopInfo parent = info.getParent(); + while(parent != null){ + LoopClusterWidget parentWidget = this.loopidx2clusterwidget.get(parent.getLoopIndex()); + assert parentWidget != null; + if(widget.getParentWidget()!=null) + widget.removeFromParent(); + parentWidget.addChild(widget); + widget=parentWidget; + parent = parent.getParent(); + } + widget.removeFromParent(); + this.clusterLayer.addChild(widget);//parent == null => parent is clusterlayer + } + } + + //hide in|output edges + private void autoHideEdges(){ + for(CfgNode n : this.getNodes()){ + int fanin = n.getInputEdges().length; + int fanout = n.getOutputEdges().length; + if (fanin > CfgEditorContext.MAX_AUTOEDGESVISIBLE){ + assert(inputSwitches.containsKey(n)); + if(this.inputSwitches.containsKey(n)){ + EdgeSwitchWidget esw = this.inputSwitches.get(n); + esw.changeEdgeVisibility(false); + } + } + if(fanout > CfgEditorContext.MAX_AUTOEDGESVISIBLE){ + if(this.outputSwitches.containsKey(n)){ + EdgeSwitchWidget esw = this.outputSwitches.get(n); + esw.changeEdgeVisibility(false); + } + } + } + + } + + //apply current cfggraphscene layout + public void applyLayout(){ + this.sceneLayout.invokeLayoutImmediately(); + } + + //returns a Set with the currently selected Nodes + public Set getSelectedNodes() { + return Collections.unmodifiableSet(selectedNodes); + } + + + public Map getLoopidx2clusterwidget() { + return loopidx2clusterwidget; + } + + /** + * Sets the color of the currently selected Nodes + * If the supplied color is null the default color will be used + */ + public void setSelectedNodesColor(Color color) { + if(color == null) { //set default color + CfgPreferences prefs = CfgPreferences.getInstance(); + boolean customized=false; + for(CfgNode n : this.selectedNodes){ + color=null; + color = prefs.getFlagsSetting().getColor(n.getBasicBlock().getFlags()); + customized = (color!=null); + NodeWidget nw = (NodeWidget) this.findWidget(n); + nw.setNodeColor((customized) ? color : prefs.getNodeColor(), customized); + } + } else { + for(CfgNode n : this.selectedNodes){ + NodeWidget nw = (NodeWidget) this.findWidget(n); + nw.setNodeColor(color, true); + } + } + this.validate(); + } + + public void setSelectedEdgesVisibility(boolean visible) { + for(CfgNode n : this.selectedNodes){ + EdgeSwitchWidget in = this.inputSwitches.get(n); + EdgeSwitchWidget out = this.outputSwitches.get(n); + if(in != null) in.changeEdgeVisibility(visible); + if(out != null) out.changeEdgeVisibility(visible); + } + this.fireSelectionChanged(); + this.validate(); + } + + public EdgeSwitchWidget getInputSwitch(CfgNode n){ + return this.inputSwitches.get(n); + } + public EdgeSwitchWidget getOutputSwitch(CfgNode n){ + return this.outputSwitches.get(n); + } + + public CfgEnv getCfgEnv() { + return env; + } + + public boolean isLoopClusterVisible() { + return loopClustersVisible; + } + + public void setLoopWidgets(boolean visible) { + for(Widget w : this.loopidx2clusterwidget.values()){ + w.setVisible(visible); + w.revalidate(); + } + this.loopClustersVisible=visible; + this.validate(); + } + + public void setRouter(int newRouter){ + if(newRouter == this.currentRouter) return; + + this.currentRouter=newRouter; + + Router router; + + switch (newRouter) { + case CfgEditorContext.ROUTING_BEZIER: + router = new PolylineRouterV2(new WidgetCollisionCollector() { + public void collectCollisions(List collisions) { + collisions.addAll(getNodeWidgets()); + } + }); + break; + case CfgEditorContext.ROUTING_DIRECTLINES: + router = RouterFactory.createDirectRouter(); + break; + default: + throw new IllegalStateException ("Unknown Router ID: " + newRouter); // NOI18N + } + + for(CfgEdge e : this.getEdges()){ + EdgeWidget ew = (EdgeWidget) this.findWidget(e); + ew.setRouter(router); + } + this.validate(); + } + + + + public Collection getNodeWidgets() { + if(nodeWidgets != null && nodeWidgets.size()==this.getNodes().size()) return nodeWidgets; + + List widgets = new ArrayList(); + for(CfgNode n : this.getNodes()){ + NodeWidget w = (NodeWidget) this.findWidget(n); + widgets.add(w); + } + + nodeWidgets = Collections.unmodifiableList(widgets); + return widgets; + } + + + + public void setSceneLayout(int newLayout){ + + if(currentLayout == newLayout) return; + + GraphLayout graphLayout=null; + + switch (newLayout) { + case CfgEditorContext.LAYOUT_HIERARCHICALNODELAYOUT: + graphLayout = new HierarchicalNodeLayout(this); + break; + + case CfgEditorContext.LAYOUT_HIERARCHICALCOMPOUNDLAYOUT: + graphLayout = new HierarchicalCompoundLayout(this); + break; + } + + this.currentLayout=newLayout; + if(graphLayout != null) + this.sceneLayout=LayoutFactory.createSceneGraphLayout(this, graphLayout); + } + + + @Override + protected void attachEdgeSourceAnchor(CfgEdge edge, CfgNode oldSourceNode, CfgNode sourceNode) { + Anchor sourceAnchor; + EdgeWidget edgeWidget = (EdgeWidget) findWidget (edge); + Widget sourceWidget = findWidget(sourceNode); + + if (edge.isSymmetric()) { + sourceAnchor = new SymmetricAnchor(sourceWidget, true, true); + } else { + sourceAnchor = AnchorFactory.createRectangularAnchor(sourceWidget); + } + edgeWidget.setSourceAnchor (sourceAnchor); + } + + @Override + protected void attachEdgeTargetAnchor(CfgEdge edge, CfgNode oldtarget, CfgNode targetNode) { + Anchor targetAnchor; + ConnectionWidget edgeWidget = (ConnectionWidget) findWidget (edge); + Widget targetWidget = findWidget(targetNode); + + if (edge.isSymmetric()) { + targetAnchor = new SymmetricAnchor(targetWidget, true, false); + } else { + targetAnchor = AnchorFactory.createRectangularAnchor(targetWidget); + } + edgeWidget.setTargetAnchor (targetAnchor); + } + + + @Override + protected Widget attachEdgeWidget(CfgEdge edge) { + EdgeWidget widget = new EdgeWidget(this, edge); + connectionLayer.addChild(widget); + attachSourceSwitchWidget(edge); + attachTargetSwitchWidget(edge); + return widget; + } + + + + @Override + protected Widget attachNodeWidget(CfgNode node) { + this.nodeWidgets = null; + + NodeWidget nw = new NodeWidget(this,node); + WidgetAction.Chain actions = nw.getActions(); + actions.addAction(this.contextPopupAction); + actions.addAction(this.moveAction); + actions.addAction(this.createObjectHoverAction()); + + if ( node.isLoopMember() ) { + LoopClusterWidget loopWidget = this.attachLoopMember(node); + loopWidget.addMember(nw); + } + mainLayer.addChild(nw); + return nw; + } + + + private LoopClusterWidget attachLoopMember(CfgNode node) { + LoopClusterWidget lw = this.loopidx2clusterwidget.get(node.getLoopIndex()); + if(lw == null) { + lw = new LoopClusterWidget(this, node.getLoopDepth(), node.getLoopIndex()); + this.loopidx2clusterwidget.put(node.getLoopIndex(), lw); + this.clusterLayer.addChild(lw); + } + return lw; + } + + + private boolean detachLoopMember(CfgNode node, NodeWidget nodeWidget) { + LoopClusterWidget rm = this.loopidx2clusterwidget.get(node.getLoopIndex()); + if( rm == null) return false;//not added + + if ( rm.removeMember(nodeWidget) ) { + if(rm.getMembers().size() == 0){ + this.loopidx2clusterwidget.remove(rm.getLoopIndex()); + List childs = new ArrayList(rm.getChildren()); + for (Widget w : childs){//append stacked loopwidgets + w.removeFromParent(); + rm.getParentWidget().addChild(w); + } + rm.removeFromParent(); + } + return true; + } + return false; + } + + //this function is not invoked by any class of the module + //however to ensure that the edge switches are treatet corretly + //when a future version removes nodes it was implemented too. + + @Override + protected void detachNodeWidget(CfgNode node, Widget nodeWidget) { + if(node.isLoopMember() && nodeWidget instanceof NodeWidget ) { + this.detachLoopMember(node,(NodeWidget)nodeWidget); + } + super.detachNodeWidget(node, nodeWidget); + assert nodeWidget.getParentWidget()== null; + if(this.inputSwitches.containsKey(node)) { + EdgeSwitchWidget esw = this.inputSwitches.remove(node); + this.connectionLayer.removeChild(esw); + } + if(this.outputSwitches.containsKey(node)){ + EdgeSwitchWidget esw = this.outputSwitches.remove(node); + this.connectionLayer.removeChild(esw); + } + } + + protected EdgeSwitchWidget attachSourceSwitchWidget(CfgEdge e){ + CfgNode sourceNode = e.getSourceNode(); + NodeWidget sourceWidget = (NodeWidget) this.findWidget(sourceNode); + EdgeSwitchWidget out = outputSwitches.get(sourceNode); + if (out==null) { + out = new EdgeSwitchWidget(this, sourceWidget, true); + this.connectionLayer.addChild(out); + outputSwitches.put(sourceNode, out); + } + return out; + } + + + protected EdgeSwitchWidget attachTargetSwitchWidget(CfgEdge e){ + CfgNode targetNode = e.getTargetNode(); + NodeWidget targetWidget = (NodeWidget) this.findWidget(targetNode); + EdgeSwitchWidget in = inputSwitches.get(targetNode); + if (in==null) { + in = new EdgeSwitchWidget(this, targetWidget, false); + this.connectionLayer.addChild(in); + inputSwitches.put(targetNode, in); + } + return in; + } + + //resets the selection state of all NodeWidgets + private void cleanNodeSelection(){ + if( this.selectedNodes.size() != 0) { + this.userSelectionSuggested(Collections.emptySet(), false); + this.selectedNodes = Collections.emptySet(); + this.fireSelectionChanged(); + this.validate(); + } + } + + + //sets the scene & global node selection + public void setNodeSelection(Set newSelection){ + this.setSceneSelection(newSelection); + this.updateGlobalSelection(); + } + + //sets the scene selection + private void setSceneSelection(Set newSelection){ + if(newSelection.equals(selectedNodes)) return; + + this.selectedNodes=newSelection; + + Set selectedObjects = new HashSet(); + + for(CfgNode n : newSelection){ + selectedObjects.addAll(this.findNodeEdges(n, true, true)); + } + selectedObjects.addAll(newSelection); + + //if the selection gets updated from a change in the block view + //the scene will be centered + if(selectionUpdating) + this.centerSelection(); + + this.userSelectionSuggested(selectedObjects, false); + this.fireSelectionChanged(); + this.validate(); + } + + //updates selection of Block View + public void updateGlobalSelection() { + Selection selection = SelectionManager.getDefault().getCurSelection(); + ArrayList newBlocks = new ArrayList(); + for (CfgNode n : this.selectedNodes) { + newBlocks.add(n.getBasicBlock()); + } + BasicBlock[] curBlocks = newBlocks.toArray(new BasicBlock[newBlocks.size()]); + selection.put(curBlocks); + } + + private boolean selectionUpdating = false; + + //change of blockview selection + public void stateChanged(ChangeEvent event) { + if (selectionUpdating) { + return; + } + + selectionUpdating = true; + + Object source = event.getSource(); + if(source instanceof Selection){ + Selection selection=(Selection) source; + Set newSelection = new HashSet(); + BasicBlock[] newBlocks = selection.get(BasicBlock[].class); + if (newBlocks != null) { + for(BasicBlock b : newBlocks){ + for(CfgNode n : this.getNodes()){ + if(n.getBasicBlock() == b) newSelection.add(n); + } + } + this.setSceneSelection(newSelection); + } + } + selectionUpdating = false; + } + + //centers the viewport on the currently selected nodewidgets + private void centerSelection(){ + Point sceneCenter = null; + Collection nodes = this.selectedNodes; + if(nodes.size()==0) { + nodes = this.getNodes(); + } + + for(CfgNode n : nodes) { + if(sceneCenter==null) { + sceneCenter = this.findWidget(n).getLocation(); + continue; + } + Point location = this.findWidget(n).getLocation(); + sceneCenter.x = (location.x+sceneCenter.x)/2; + sceneCenter.y = (location.y+sceneCenter.y)/2; + } + + JComponent view = this.getView (); + if (view != null) { + Rectangle viewBounds = view.getVisibleRect (); + + Point viewCenter = this.convertSceneToView (sceneCenter); + + view.scrollRectToVisible (new Rectangle ( + viewCenter.x - viewBounds.width / 2, + viewCenter.y - viewBounds.height / 2, + viewBounds.width, + viewBounds.height + )); + } + } + + //animated scene Zoom to the max bounds of current viewport + public void zoomScene(){ + JScrollPane pane = this.cfgtc.getJScrollPanel(); + + Rectangle prefBounds = this.getPreferredBounds(); + Dimension viewDim = pane.getViewportBorderBounds().getSize(); + + double realwidth = (double)prefBounds.width*this.getZoomFactor(); + double realheight = (double)prefBounds.height*this.getZoomFactor(); + + double zoomX = (double)viewDim.width / realwidth; + double zoomY = (double)viewDim.height / realheight; + double zoomFactor = Math.min(zoomX, zoomY); + + this.animateZoom(zoomFactor*0.9); + } + + + //animated animateZoom function for scene animateZoom factor + public void animateZoom(double zoomfactor) { + this.getSceneAnimator().animateZoomFactor(this.getZoomFactor() * zoomfactor); + } + + public void addCfgEventListener(CfgEventListener l) { + listenerList.add(CfgEventListener.class, l); + } + + public void removeCfgEventListener(CfgEventListener l) { + listenerList.remove(CfgEventListener.class, l); + } + + public void fireSelectionChanged() { + Object[] listeners = listenerList.getListenerList(); + for (int i = listeners.length-2; i>=0; i-=2) { + if (listeners[i]==CfgEventListener.class) { + ((CfgEventListener)listeners[i+1]).selectionChanged(this); + } + } + } + + + //Enables Antialiasing + @Override + public void paintChildren () { + Object anti = getGraphics ().getRenderingHint (RenderingHints.KEY_ANTIALIASING); + Object textAnti = getGraphics ().getRenderingHint (RenderingHints.KEY_TEXT_ANTIALIASING); + + getGraphics ().setRenderingHint ( + RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + getGraphics ().setRenderingHint ( + RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + super.paintChildren (); + + getGraphics ().setRenderingHint (RenderingHints.KEY_ANTIALIASING, anti); + getGraphics ().setRenderingHint (RenderingHints.KEY_TEXT_ANTIALIASING, textAnti); + } + + + //select provider for node selection + private RectangularSelectProvider createRectangularSelectProvider() { + return new RectangularSelectProvider() { + public void performSelection(Rectangle rectangle) { + HashSet set=new HashSet(); + + //change to a rectangle with (x,offsetY) at the top left + if(rectangle.width < 0){ + rectangle.x=rectangle.x+rectangle.width; + rectangle.width*=-1; + } + if(rectangle.height < 0){ + rectangle.y=rectangle.y+rectangle.height; + rectangle.height*=-1; + } + + for(NodeWidget n: getNodeWidgets()) { + Point p = n.getLocation(); + if (p != null && rectangle.contains(p)) { + set.add(n.getNodeModel()); + } + } + setNodeSelection(set); + } + }; + } + + //select decorator for node selection + private RectangularSelectDecorator createSelectDecorator(final CfgScene scene){ + return new RectangularSelectDecorator() { + public Widget createSelectionWidget() { + scene.cleanNodeSelection();//unselected all nodes + scene.revalidate(); + return new SelectionWidget(getScene()); + } + }; + } + + + private MoveProvider createMoveProvider() { + return new MoveProvider(){ + private HashMap originals = new HashMap(); + + private Point original=null; + + public void movementStarted(Widget widget) { + originals.clear(); + NodeWidget nw = (NodeWidget) widget; + if(selectedNodes.contains(nw.getNodeModel())) {//move current selection + for(CfgNode n : selectedNodes){ + Widget w = findWidget(n); + originals.put(w, w.getLocation()); + } + } else {//a newly-selected node will be moved + CfgNode n = nw.getNodeModel(); + HashSet selectedNode = new HashSet(1); + selectedNode.add(n); + setNodeSelection(selectedNode); + originals.put(widget, widget.getPreferredLocation()); + widget.revalidate(); + validate(); + + } + } + + public void movementFinished(Widget widget) { + NodeWidget nw = (NodeWidget) widget; + if(selectedNodes.contains(nw.getNodeModel())) { + return;//to be able to move the current selection + } + + HashSet selectedNode = new HashSet(1); + selectedNode.add(nw.getNodeModel()); + setNodeSelection(selectedNode); + originals.clear (); + original = null; + + } + + public Point getOriginalLocation(Widget widget) { + if(original==null) + original = widget.getLocation(); + + return original; + } + //todo : find a cache algorithm which only routes edges + //which are intersected by bounds of the moved rectangle + public void setNewLocation(Widget widget, Point location) { + Point org = getOriginalLocation(widget); + int dx = location.x - org.x; + int dy = location.y - org.y; + for (Map.Entry entry : originals.entrySet ()) { + Point point = entry.getValue (); + entry.getKey ().setPreferredLocation (new Point (point.x + dx, point.y + dy)); + } + for(CfgEdge e : getEdges()) { + EdgeWidget ew = (EdgeWidget) findWidget(e); + if(ew.isVisible()) + ew.reroute(); + } + } + }; + } + + + private WidgetAction createContextMenuAction(final CfgScene scene) { + return ActionFactory.createPopupMenuAction(new PopupMenuProvider() { + public JPopupMenu getPopupMenu(Widget widget, Point point) { + JPopupMenu menu = new JPopupMenu(); + NodeWidget nw = null; + if(widget instanceof NodeWidget) { + nw = (NodeWidget) widget; + if(!selectedNodes.contains(nw.getNodeModel())){ + HashSet selectedNode = new HashSet(1); + selectedNode.add(nw.getNodeModel()); + setNodeSelection(selectedNode); + } + } else if (scene.getSelectedNodes().size() == 1) { + nw = (NodeWidget) scene.findWidget(scene.getSelectedNodes().iterator().next()); + } + + if(nw != null){ + CfgNode node = nw.getNodeModel(); + ArrayList successors = new ArrayList(); + ArrayList predecessors = new ArrayList(); + for(CfgEdge e : node.getOutputEdges()){ + successors.add(e.getTargetNode()); + } + for(CfgEdge e : node.getInputEdges()){ + predecessors.add(e.getSourceNode()); + } + + if(predecessors.size()>0){ + Collections.sort(predecessors, new NodeNameComparator()); + JMenu predmenu = new JMenu("Go to predecessor"); + for (CfgNode n : predecessors) { + GotoNodeAction action = new GotoNodeAction(n); + predmenu.add(action); + } + menu.add(predmenu); + } + if(successors.size()>0){ + Collections.sort(successors, new NodeNameComparator()); + JMenu succmenu = new JMenu("Go to successor"); + for (CfgNode n : successors) { + GotoNodeAction action = new GotoNodeAction(n); + succmenu.add(action); + } + menu.add(succmenu); + } + if ( successors.size() > 0 || predecessors.size() > 0) + menu.addSeparator(); + } + + menu.add(SystemAction.get(ShowEdgesAction.class)); + menu.add(SystemAction.get(HideEdgesAction.class)); + menu.addSeparator(); + menu.add(SystemAction.get(ColorAction.class).getPopupPresenter()); + return menu; + } + }); + } + + private class NodeNameComparator implements Comparator { + public int compare(CfgNode node1, CfgNode node2) { + String name1 = node1.getBasicBlock().getName().substring(1); + String name2 = node2.getBasicBlock().getName().substring(1); + Integer blocknum1 = Integer.parseInt(name1); + Integer blocknum2 = Integer.parseInt(name2); + return blocknum1.compareTo(blocknum2); + } + } + + private class GotoNodeAction extends AbstractAction { + CfgNode node ; + + GotoNodeAction(CfgNode node){ + super(node.getBasicBlock().getName()); + this.node = node; + } + public void actionPerformed(ActionEvent e) { + Set nodes = new HashSet(1); + nodes.add(node); + setNodeSelection(nodes); + centerSelection(); + } + } + + private SceneListener createSceneListener(final CfgScene scene){ + return new SceneListener() { + + public void sceneRepaint() { + } + + public void sceneValidating() { + } + + public void sceneValidated() { + if (scene.isLoopClusterVisible()){ //update only if visible + for(LoopClusterWidget cw : getLoopidx2clusterwidget().values()){ + cw.updateClusterBounds(); + } + } + for(EdgeSwitchWidget esw : inputSwitches.values()){ + esw.updatePosition(); + } + + for(EdgeSwitchWidget esw : outputSwitches.values()){ + esw.updatePosition(); + } + } + }; + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/EdgeSwitchWidget.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/EdgeSwitchWidget.java new file mode 100644 index 000000000000..eeab28034966 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/EdgeSwitchWidget.java @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.graph; + +import at.ssw.visualizer.cfg.model.CfgEdge; +import at.ssw.visualizer.cfg.model.CfgNode; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.util.ArrayList; +import java.util.Collection; +import org.netbeans.api.visual.action.ActionFactory; +import org.netbeans.api.visual.action.SelectProvider; +import org.netbeans.api.visual.action.TwoStateHoverProvider; +import org.netbeans.api.visual.action.WidgetAction; +import org.netbeans.api.visual.model.ObjectState; +import org.netbeans.api.visual.widget.Widget; + + +public class EdgeSwitchWidget extends Widget { + private final static Color color_enabled = Color.gray; + private final static Color color_hover = Color.lightGray; + private float width=1; + private float height=1; + private CfgScene scene; + private NodeWidget nodeWidget; + private boolean output; + private WidgetAction hoverAction; + private static final String TT_HIDE_EDGES = "Hide Edges"; + private static final String TT_SHOW_EDGES = "Show Edges"; + private static SelectProvider selectProvider = createSelectProvider(); + + + public EdgeSwitchWidget(final CfgScene scene, NodeWidget nodeWidget, boolean output) { + super(scene); + this.scene = scene; + this.output = output; + this.nodeWidget = nodeWidget; + + this.getActions().addAction(ActionFactory.createSelectAction(selectProvider)); + TwoStateHoverProvider ts = new TsHover(this); + WidgetAction wa = ActionFactory.createHoverAction(ts); + this.hoverAction = wa; + this.getActions().addAction(wa); + scene.getActions().addAction(wa); + this.setToolTipText(TT_HIDE_EDGES); + this.setForeground(color_enabled); + this.setState(ObjectState.createNormal()); + } + + + @Override + protected Rectangle calculateClientArea() { + if (this.nodeWidget.getBounds() == null) return new Rectangle(0, 0, 1, 1); + int hw = (int) (this.width / 2); + int hh = (int) (this.height /2); + + return new Rectangle(-hw, -hh, 2*hw, 2*hh); + } + + + public void updatePosition() { + if (this.nodeWidget.getBounds() != null) { + this.width = nodeWidget.getBounds().width*9; + this.width /=10; + this.height = nodeWidget.getBounds().height/4; + int offset=(int)(2 * (height / 3)); + + Rectangle bounds = nodeWidget.getBounds(); + Point location = nodeWidget.getLocation(); + + Point newLoc = new Point(); + newLoc.x = location.x; + + if(output) { + newLoc.y = +location.y + bounds.height/2+offset; + }else { + newLoc.y = location.y - bounds.height/2-offset; + } + this.setPreferredLocation(newLoc); + } + } + + private Collection getEdges(){ + Collection edges; + CfgNode node = nodeWidget.getNodeModel(); + if (output) { + edges = scene.findNodeEdges(node, true, false); + } else { + edges = scene.findNodeEdges(node, false, true); + } + return edges; + } + + //change visibility for all Edges + public void changeEdgeVisibility(boolean visible){ + Collection edges = this.getEdges(); + + for(CfgEdge e: edges) { + EdgeWidget ew = (EdgeWidget) scene.findWidget(e); + if(visible != ew.isEdgeVisible()){ + ew.setEdgeVisible(visible); + if(output){ + scene.getInputSwitch(e.getTargetNode()).updateStatus(); + } else { + scene.getOutputSwitch(e.getSourceNode()).updateStatus(); + } + } + } + if(visible) + this.setToolTipText(TT_HIDE_EDGES); + else + this.setToolTipText(TT_SHOW_EDGES); + + this.setForeground(color_enabled); + this.bringToBack(); + ObjectState os = this.getState(); + this.setState(os.deriveSelected(!visible)); + } + + /** + * Update the status of the switch to the current state of the edges + * usually needed when the opposit switch changes the state + */ + private void updateStatus(){ + Collection edges = this.getEdges(); + boolean hiddenFound=false; + for(CfgEdge e: edges) { + EdgeWidget ew = (EdgeWidget) scene.findWidget(e); + if(!ew.isVisible()) { + hiddenFound=true; + break; + } + } + ObjectState os = this.getState(); + if(os.isSelected() && !hiddenFound) { + this.setState(os.deriveSelected(false)); + setToolTipText(TT_HIDE_EDGES); + } else if (!os.isSelected() && hiddenFound) { + this.setState(os.deriveSelected(true)); + setToolTipText(TT_SHOW_EDGES); + } + this.revalidate(); + } + + + public void startPreview() { + ObjectState os = this.getState(); + + for(CfgEdge e : getEdges()) { + EdgeWidget ew = (EdgeWidget) scene.findWidget(e); + if(!os.isSelected() || !ew.isVisible()){ + ObjectState edgeState = ew.getState(); + ew.setState(edgeState.deriveHighlighted(true)); + } + } + } + + public void endPreview(){ + for(CfgEdge e : getEdges()) { + EdgeWidget ew = (EdgeWidget) scene.findWidget(e); + ObjectState os = ew.getState(); + ew.setState(os.deriveHighlighted(false)); + } + } + + /** + * shows or hides the edges of the switch + */ + public void switchEdges() { + endPreview(); + ObjectState os = this.getState(); + Collection edges = this.getEdges(); + ArrayList updates = new ArrayList(); + boolean visible=os.isSelected(); + this.setState(os.deriveSelected(!visible)); + for(CfgEdge e: edges) { + EdgeWidget ew = (EdgeWidget) scene.findWidget(e); + if(ew.isEdgeVisible() != visible){ + updates.add(e); + ew.setEdgeVisible(visible); + if(output){ + scene.getInputSwitch(e.getTargetNode()).updateStatus(); + } else { + scene.getOutputSwitch(e.getSourceNode()).updateStatus(); + } + } + } + if(visible) + this.setToolTipText(TT_HIDE_EDGES); + else + this.setToolTipText(TT_SHOW_EDGES); + + scene.fireSelectionChanged();//updates Edge visibility for context action + revalidate(); + } + + + + + private class TsHover implements TwoStateHoverProvider { + EdgeSwitchWidget tw; + + TsHover(EdgeSwitchWidget tw) { + this.tw = tw; + } + + public void unsetHovering(Widget w) { + w.setForeground(color_enabled); + ObjectState state = w.getState(); + w.setState(state.deriveWidgetHovered(false)); + w.bringToBack(); + endPreview(); + } + + public void setHovering(Widget w) { + ObjectState state = w.getState(); + w.setState(state.deriveWidgetHovered(true)); + w.setForeground(color_hover); + w.bringToFront(); + nodeWidget.bringToFront(); + startPreview(); + } + } + + @Override + public void paintWidget() { + ObjectState os = this.getState(); + if(!os.isHovered() && !os.isSelected()) return; //all previewEdges visible and not hovering, + //no need to paint the switch + float hw = width/2; + Polygon pol = new Polygon(); + pol.addPoint(0,(int) -height/2); + pol.addPoint((int)hw,(int) height/2); + pol.addPoint((int)-hw,(int) height/2); + Graphics2D gr = getGraphics(); + gr.setColor(this.getForeground()); + BasicStroke bs = new BasicStroke(2.0f, BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND); + gr.setStroke(bs); + AffineTransform previousTransform; + previousTransform = gr.getTransform (); + if(output) { + if(os.isSelected() ){//hidden + gr.scale(1.0, -1.0); + } + } else { //input switch + if(os.isHovered() && !os.isSelected()){ + gr.scale(1.0, -1.0); + } + } + gr.fillPolygon(pol); + gr.setTransform(previousTransform); + + } + + + + //the constructor adds the hover WidgetAction to the scene + //the action is removed from the scene when the object gets destroyed + @Override + protected void finalize() throws Throwable { + this.getScene().getActions().removeAction(hoverAction); + this.getActions().removeAction(hoverAction); + } + + @Override + public String toString(){ + return "EDGESWITCH("+this.nodeWidget.getNodeModel().toString()+")"; + } + + private static SelectProvider createSelectProvider() { + return new SelectProvider(){ + public boolean isAimingAllowed(Widget arg0, Point arg1, boolean arg2) { + return false; + } + + public boolean isSelectionAllowed(Widget arg0, Point arg1, boolean arg2) { + return true; + } + + public void select(Widget w, Point arg1, boolean arg2) { + if(w instanceof EdgeSwitchWidget){ + EdgeSwitchWidget tw = (EdgeSwitchWidget) w; + tw.switchEdges(); + } + } + }; + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/EdgeWidget.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/EdgeWidget.java new file mode 100644 index 000000000000..1e79de09b170 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/EdgeWidget.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.graph; + +import at.ssw.visualizer.cfg.model.CfgEdge; +import at.ssw.visualizer.cfg.preferences.CfgPreferences; +import at.ssw.visualizer.cfg.visual.BezierWidget; +import at.ssw.visualizer.cfg.visual.SplineConnectionWidget; +import org.netbeans.api.visual.anchor.AnchorShapeFactory; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Stroke; +import org.netbeans.api.visual.anchor.AnchorShape; +import org.netbeans.api.visual.model.ObjectState; + +//public class EdgeWidget extends BezierWidget { +public class EdgeWidget extends SplineConnectionWidget { + + private boolean visible=true;//to store the visible state when entering the preview + protected static final Stroke selectedStroke = new BasicStroke(3.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); + protected static final Stroke defaultStroke = new BasicStroke(1.5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); + protected static final Stroke previewStroke = new BasicStroke( + 0.5f, // Width + BasicStroke.CAP_SQUARE, // End cap + BasicStroke.JOIN_MITER, // Join style + 10.0f, // Miter limit + new float[] {5.0f, 5.0f}, // Dash pattern + 0.0f); + + + + public EdgeWidget(CfgScene scene, CfgEdge edge) { + super(scene); + Color lineColor; + CfgPreferences prefs = CfgPreferences.getInstance(); + + if(edge.isBackEdge()) + lineColor = prefs.getBackedgeColor(); + else if (edge.isXhandler()) + lineColor = prefs.getExceptionEdgeColor(); + else + lineColor = prefs.getEdgeColor(); + + setLineColor(lineColor); + AnchorShape as; + if(edge.isReflexive())//small Arrow + as = AnchorShapeFactory.createTriangleAnchorShape(6, true, false, 5); + else + as =AnchorShapeFactory.createTriangleAnchorShape(10, true, false, 9); + + setTargetAnchorShape(as); + setToolTipText(edge.toString()); + } + + public CfgEdge getEdgeModel() { + CfgScene scene = (CfgScene) this.getScene(); + return (CfgEdge) scene.findObject(this); + } + + public void setEdgeVisible(boolean visible) { + this.visible=visible; + this.setVisible(visible); + this.reroute(); + this.revalidate(); + } + + + public boolean isEdgeVisible(){ + return visible; + } + + + @Override + public void notifyStateChanged(ObjectState oldState, ObjectState newState) { + setForeground (getLineColor()); + + if(newState.isHighlighted() && !oldState.isHighlighted()){ + this.setStroke(previewStroke); + this.setVisible(true); + } else { + if(newState.isSelected()){ + this.setStroke(selectedStroke); + } else { + this.setStroke(defaultStroke); + } + if(this.isEdgeVisible()){ + this.setVisible(true); + } else { + this.setVisible(false); + } + } + } + + + @Override + public String toString(){ + return "EdgeWidget[" + getEdgeModel().toString() + "]"; + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/LoopClusterWidget.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/LoopClusterWidget.java new file mode 100644 index 000000000000..3bd8d95634b3 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/LoopClusterWidget.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.graph; + +import at.ssw.visualizer.cfg.model.LoopInfo; +import java.awt.Color; +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import org.netbeans.api.visual.action.ActionFactory; +import org.netbeans.api.visual.action.EditProvider; +import org.netbeans.api.visual.border.BorderFactory; +import org.netbeans.api.visual.widget.Widget; + +public class LoopClusterWidget extends Widget implements Comparable { + private static final int INSET = 10;//min space between node members and cluster border + private static final int DASHSIZE = 10; + private Color color = Color.BLUE; + private int loopIndex; + private int loopDepth; + private CfgScene cfgscene; + private ArrayList members = new ArrayList(); + + public LoopClusterWidget(CfgScene scene, int loopdepth, final int loopindex) { + super(scene); + this.cfgscene = scene; + this.loopIndex = loopindex; + this.loopDepth = loopdepth; + this.setBorder(BorderFactory.createDashedBorder(color, DASHSIZE, DASHSIZE/2, true)); + this.getActions().addAction(ActionFactory.createEditAction( new EditProvider() { //double click action + public void edit(Widget w) { + if(w instanceof LoopClusterWidget){ + for(LoopInfo info : cfgscene.getCfgEnv().getLoopMap().values()){ + if(info.getLoopIndex() == loopindex){ + cfgscene.setNodeSelection(info.getMembers()); + break; + } + } + } + } + })); + + } + + public List getMembers() { + return members; + } + + public int getLoopIndex() { + return loopIndex; + } + + public void addMember(NodeWidget nw) { + assert(!this.members.contains(nw)); + members.add(nw); + } + + public boolean removeMember(NodeWidget nw) { + if(this.members.contains(nw)){ + members.remove(nw); + return true; + } + return false; + } + + public void setrandomColor(){ + if(this.loopDepth == 0 ) return; + Random rand = new Random(); + Color randColor = Color.getHSBColor(rand.nextFloat()%360,0.1f,1.0f); + this.setBackground(randColor); + } + + //updates the bounds of the widget, + //and revalidates the widget if a membernode changed the scene position + public void updateClusterBounds(){ + Rectangle boundRect=null; + + for(NodeWidget nw : this.members){ + if(boundRect==null){ + boundRect = nw.convertLocalToScene(nw.getBounds()); + } else { + boundRect = boundRect.union(nw.convertLocalToScene(nw.getBounds())); + } + } + if(boundRect==null) return; + for(Widget w : this.getChildren()) { + if(w instanceof LoopClusterWidget) { + LoopClusterWidget lc = (LoopClusterWidget)w; + lc.updateClusterBounds(); + boundRect = boundRect.union(w.convertLocalToScene(w.getBounds())); + } + } + + boundRect.grow(INSET, INSET); + this.setPreferredBounds(boundRect); + } + + + public int compareTo(LoopClusterWidget o) { + return new Integer(this.loopDepth).compareTo(o.loopDepth); + } + + + @Override + public String toString(){ + return "LoopCluster: [DEPTH "+this.loopDepth+ "] [INDEX "+this.loopIndex+"]"; + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/NodeWidget.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/NodeWidget.java new file mode 100644 index 000000000000..8080a05348df --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/NodeWidget.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.graph; + +import at.ssw.visualizer.cfg.model.CfgNode; +import at.ssw.visualizer.cfg.preferences.CfgPreferences; +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.font.FontRenderContext; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import org.netbeans.api.visual.border.BorderFactory; +import org.netbeans.api.visual.model.ObjectState; +import org.netbeans.api.visual.widget.Widget; + + +public class NodeWidget extends Widget { + + + //basic block node dimension + private final int halfheight=12; + private final int halfwidth=17; + + private final int height=halfheight*2+1; + private final int width=halfwidth*2+1; + private final int arcHeight=(halfheight/4)*3; + private final int arcWidth = arcHeight; + private final int FONT_MAXSIZE=18; + + private int borderWidth; + private boolean selected=false; + private boolean nodeColorCustomized; + private String text; + private Rectangle2D fontRect; + private Color nodeColor; + + protected static final Color HOVER_BACKGROUND = new Color(0xEEEEEE); + protected static final Color HOVER_FOREGROUND = new Color(0xCDCDCD); + + public NodeWidget(CfgScene scene, CfgNode nodeModel ){ + super(scene); + this.setToolTipText("" + nodeModel.getDescription().replaceAll("\n", "
") + ""); + this.selected=false; + this.text = nodeModel.getBasicBlock().getName(); + this.borderWidth = nodeModel.getLoopDepth()+1; + this.setBorder(BorderFactory.createRoundedBorder(arcWidth+borderWidth, arcHeight+borderWidth, borderWidth, borderWidth, Color.BLACK, Color.BLACK)); + CfgPreferences prefs = CfgPreferences.getInstance(); + Color color = prefs.getFlagsSetting().getColor(nodeModel.getBasicBlock().getFlags()); + this.nodeColorCustomized = (color!=null); + this.nodeColor = (nodeColorCustomized) ? color : prefs.getNodeColor(); + this.adjustFont(null); + } + + public void setBorderColor(Color color){ + this.setBorder(BorderFactory.createRoundedBorder(arcWidth+borderWidth, arcHeight+borderWidth, borderWidth, borderWidth, color, color)); + } + + public boolean isNodeColorCustomized() { + return nodeColorCustomized; + } + + //sets a customColor node color + public void setNodeColor(Color color, boolean customColor) { + this.nodeColorCustomized=customColor; + this.nodeColor=color; + this.revalidate(); + } + + public Color getNodeColor() { + return this.nodeColor; + } + + public CfgNode getNodeModel() { + CfgScene scene = (CfgScene) this.getScene(); + return (CfgNode) scene.findObject(this); + } + + + @Override + public void notifyStateChanged(ObjectState oldState, ObjectState newState) { + if(!oldState.equals(newState)) + this.revalidate(); + if(!oldState.isSelected() && newState.isSelected()) + this.bringToFront(); + } + + @Override + protected Rectangle calculateClientArea() { + return new Rectangle(-(halfwidth+1), -(1+halfheight), width+1, height+1);//add border + } + + public void adjustFont(Font font){ + if(font==null) + font = CfgPreferences.getInstance().getTextFont(); + if(font.getSize()>FONT_MAXSIZE){ + font = new Font(font.getFamily(), font.getStyle(), FONT_MAXSIZE); + } + int size=font.getSize(); + int fontStyle = font.getStyle(); + String fontName = font.getFamily(); + FontRenderContext frc = new FontRenderContext(new AffineTransform(), false, false); + Rectangle2D bounds = font.getStringBounds(text, frc); + while(size > 1 && bounds.getWidth() > width) { + font = new Font(fontName, fontStyle, --size); + bounds = font.getStringBounds(text, frc); + } + this.fontRect=bounds; + this.setFont(font); + } + + @Override + protected void paintWidget() { + Graphics2D gr = getGraphics(); + gr.setColor(nodeColor); + Insets borderInsets = this.getBorder().getInsets(); + RoundRectangle2D.Float innerRect = new RoundRectangle2D.Float(-(halfwidth+1), -(halfheight+1), width+1, height+1,arcWidth-1, arcHeight-1); + gr.fill(innerRect); + gr.setColor(getForeground()); + gr.setFont(getFont()); + float textX = (float)( - fontRect.getCenterX()); + float textY = (float)( - fontRect.getCenterY()); + gr.drawString(text, textX, textY); + + RoundRectangle2D.Float outerRect = new RoundRectangle2D.Float(-(halfwidth+borderInsets.left + 1), -(halfheight+borderInsets.top + 1), + width+borderInsets.left + borderInsets.right + 1, height + borderInsets.top + borderInsets.bottom + 1, + arcWidth + borderWidth, arcHeight + borderWidth); + + ObjectState os =this.getState(); + if(os.isSelected()){ + Composite composite = gr.getComposite(); + gr.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.5f)); + gr.setColor(CfgPreferences.getInstance().getSelectionColorForeground()); + gr.fill(outerRect); + gr.setColor(CfgPreferences.getInstance().getSelectionColorBackground()); + gr.setComposite(composite); + } + if(os.isHovered()){ + Composite composite = gr.getComposite(); + gr.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.5f)); + gr.setColor(HOVER_FOREGROUND); + gr.fill(outerRect); + gr.setColor(HOVER_BACKGROUND); + gr.setComposite(composite); + } + } + + @Override + public String toString() { + return "NodeWidget[" + getNodeModel().getBasicBlock().getName() + "]"; + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/SelectionWidget.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/SelectionWidget.java new file mode 100644 index 000000000000..30de3bd57e9d --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/SelectionWidget.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.graph; + +import at.ssw.visualizer.cfg.preferences.CfgPreferences; +import java.awt.AlphaComposite; +import java.awt.Composite; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import org.netbeans.api.visual.widget.Scene; +import org.netbeans.api.visual.widget.Widget; + + +public class SelectionWidget extends Widget { + public SelectionWidget(Scene scene) { + super(scene); + } + + public static void renderSelectedRect(Graphics2D gr, Rectangle rect) { + if (rect == null) { + return; + } + gr.setColor(CfgPreferences.getInstance().getSelectionColorBackground()); + gr.fillRect(rect.x, rect.y, rect.width, rect.height); + gr.setColor(CfgPreferences.getInstance().getSelectionColorForeground()); + } + + public void renderSelectionRectangle(Graphics2D gr, Rectangle selectionRectangle) { + Composite composite = gr.getComposite(); + gr.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.5f)); + + renderSelectedRect(gr, selectionRectangle); + gr.setComposite(composite); + } + + @Override + public void paintWidget(){ + this.renderSelectionRectangle(this.getGraphics(), this.getBounds()); + } + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/SymmetricAnchor.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/SymmetricAnchor.java new file mode 100644 index 000000000000..3e20272830be --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/SymmetricAnchor.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.graph; + +import org.netbeans.api.visual.widget.Widget; +import org.netbeans.api.visual.anchor.Anchor; +import java.awt.*; + + +/** + * This Anchor can be used with symmetric edges to create parallel Edges. + * Two Directed Edges are symmetric if they are connecting the same Nodes in different directions. + * e.g. Nodes (1, 2) Edges(a , b) with (1)-a->(2) , (2)-b->(1) + * Start-/End positions are calculated with a fixed offset to prevent edges from overlapping. + * If the edges are drawn as straight lines they will appear as parallel edges. + */ + +public final class SymmetricAnchor extends Anchor { + + private final static int OFFSET = 10; + private boolean includeBorders; + private int offx; + private int offy; + + public SymmetricAnchor (Widget widget, boolean includeBorders, boolean source) { + super (widget); + this.includeBorders = includeBorders; + if (source) { + offx = OFFSET; + offy = OFFSET; + } + else { + offx = -OFFSET; + offy = -OFFSET; + } + } + + public Result compute (Entry entry) { + Point relatedLocation = //center of the widget + getRelatedSceneLocation (); + Point oppositeLocation = //center of the widget + getOppositeSceneLocation (entry); + + Widget widget = getRelatedWidget (); + Rectangle bounds = widget.getBounds (); + if (! includeBorders) { + Insets insets = widget.getBorder ().getInsets (); + bounds.x += insets.left; + bounds.y += insets.top; + bounds.width -= insets.left + insets.right; + bounds.height -= insets.top + insets.bottom; + } + + bounds = widget.convertLocalToScene (bounds); + + if (bounds.isEmpty () || relatedLocation.equals (oppositeLocation)) + return new Anchor.Result (relatedLocation, Anchor.DIRECTION_ANY); + + float dx //distance x-axis + = oppositeLocation.x - relatedLocation.x; + float dy //distance y-axis + = oppositeLocation.y - relatedLocation.y; + + + float ddx + = Math.abs (dx) / (float) bounds.width; + float ddy = + Math.abs (dy) / (float) bounds.height; + + Anchor.Direction direction; + + + if (ddx >= ddy) { + if(dx >= 0.0f){ + direction = Direction.RIGHT; + relatedLocation.y -= offy; + } else { + direction = Direction.LEFT; + relatedLocation.y += offy; + } + } else { + if(dy >= 0.0f){ + direction = Direction.BOTTOM; + relatedLocation.x += offx; + } else { + direction = Direction.TOP; + relatedLocation.x -= offx; + } + } + + + float scale = 0.5f / Math.max (ddx, ddy); + + float ex = scale * dx; + float ey = scale * dy; + + Point point = new Point (Math.round (relatedLocation.x + ex), Math.round (relatedLocation.y + ey)); + + if(direction == Direction.RIGHT) { + int top = bounds.y;//-bounds.height;// left y of the widget + int bottom = bounds.y + bounds.height;// right y of the widget + if(point.y < top) {//above the widget + int cor = top-point.y; + point.x -= cor; + point.y += cor; + } else if ( point.y > bottom) { + int cor = point.y-bottom; + point.x -= cor; + point.y -= cor; + } + + } else if (direction == Direction.LEFT) { + int top = bounds.y;//-bounds.height;// left y of the widget + int bottom = bounds.y + bounds.height;// right y of the widget + + + + if(point.y < top) {//above the widget + int cor = top-point.y; + point.x += cor; + point.y += cor; + } else if ( point.y > bottom) { + int cor = bottom-point.y; + point.x -= cor; + point.y += cor; + } + + + } else if (direction == Direction.BOTTOM) { + int left = bounds.x;//-bounds.height;// left y of the widget + int right = bounds.x + bounds.width;// right y of the widget + if(point.x < left) {//above the widget + int cor = left-point.x; + point.x += cor; + point.y -= cor; + } else if ( point.x > right) { + int cor = point.x- right; + point.x -= cor; + point.y -= cor; + } + + } else if (direction == Direction.TOP) { + int left = bounds.x;//-bounds.height;// left y of the widget + int right = bounds.x + bounds.width;// right y of the widget + if(point.x < left) {//above the widget + int cor = left-point.x; + point.x += cor; + point.y += cor; + } else if ( point.x > right) { + int cor = point.x - right; + point.x -= cor; + point.y += cor; + } + + } + + return new Anchor.Result (point, direction); + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/layout/HierarchicalCompoundLayout.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/layout/HierarchicalCompoundLayout.java new file mode 100644 index 000000000000..c726a2605a71 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/layout/HierarchicalCompoundLayout.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.graph.layout; + +import at.ssw.visualizer.cfg.graph.CfgScene; +import at.ssw.visualizer.cfg.model.CfgEdge; +import at.ssw.visualizer.cfg.model.CfgNode; +import at.ssw.visualizer.cfg.model.LoopInfo; +import java.awt.Point; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import org.eclipse.draw2d.geometry.Insets; +import org.netbeans.api.visual.graph.layout.GraphLayout; +import org.netbeans.api.visual.graph.layout.UniversalGraph; +import org.eclipse.draw2d.graph.CompoundDirectedGraph; +import org.eclipse.draw2d.graph.CompoundDirectedGraphLayout; +import org.eclipse.draw2d.graph.Edge; +import org.eclipse.draw2d.graph.EdgeList; +import org.eclipse.draw2d.graph.Node; +import org.eclipse.draw2d.graph.NodeList; +import org.eclipse.draw2d.graph.Subgraph; +import org.netbeans.api.visual.widget.Widget; + +public class HierarchicalCompoundLayout extends GraphLayout { + + private static final int TOP_BORDER = 20; + private static final int LEFT_BORDER = 20; + private int PADDING = 20; + private static final int INSET = 20; + private CfgScene scene; + + public HierarchicalCompoundLayout(CfgScene scene){ + this.scene = scene; + } + + @Override + protected void performGraphLayout(UniversalGraph ug) { + CompoundDirectedGraph dg = new CompoundDirectedGraph(); + CompoundDirectedGraphLayout layout = new CompoundDirectedGraphLayout(); + NodeList nodeList = dg.nodes; + EdgeList edgeList = dg.edges; + + Map idx2graph = new HashMap(); + Subgraph base = new Subgraph(0); + idx2graph.put(0, base); + base.insets=getInsets(); + for(LoopInfo info : scene.getCfgEnv().getLoopMap().values()){ + Subgraph subg = new Subgraph(info.getLoopIndex()); + subg.insets=getInsets(); + idx2graph.put(info.getLoopIndex(), subg); + } + + for(CfgNode n : scene.getCfgEnv().getNodes() ) { + Widget nodeWidget = scene.findWidget(n); + Node node = new Node(n); + node.width=nodeWidget.getBounds().width; + node.height = nodeWidget.getBounds().height; + node.setPadding(new Insets(PADDING, PADDING, PADDING, PADDING)); + Subgraph subg = idx2graph.get(n.getLoopIndex()); + assert(subg != null); + node.setParent(subg); + subg.addMember(node); + nodeList.add(node); + + } + nodeList.addAll(idx2graph.values()); + for(LoopInfo info : scene.getCfgEnv().getLoopMap().values()){ + Subgraph subg = idx2graph.get(info.getLoopIndex()); + if(info.getParent() != null){ + Subgraph parentsubg = idx2graph.get(info.getParent().getLoopIndex()); + Edge edge = new Edge(parentsubg, subg); + parentsubg.addMember(subg); + subg.setParent(parentsubg); + edgeList.add(edge); + } + } + for(CfgEdge e : scene.getCfgEnv().getEdges() ) { + if(e.isBackEdge()) continue; + Edge edge = new Edge(e, nodeList.getNode(e.getSourceNode().getNodeIndex()), nodeList.getNode(e.getTargetNode().getNodeIndex())); + edgeList.add(edge); + } + layout.visit(dg); + + for(Object obj : dg.nodes){ + Node n = (Node) obj; + if(n.data instanceof CfgNode){ + CfgNode cfgNode = (CfgNode) n.data; + Point pos = new Point(n.x + LEFT_BORDER, n.y + TOP_BORDER); + Point scenepos = scene.convertLocalToScene(pos); + this.setResolvedNodeLocation(ug, cfgNode, scenepos); + } + } + } + + @Override + protected void performNodesLayout(UniversalGraph ug, Collection collection) { + } + + private Insets getInsets(){ + return new Insets(INSET, INSET, INSET, INSET); + } + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/layout/HierarchicalNodeLayout.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/layout/HierarchicalNodeLayout.java new file mode 100644 index 000000000000..0b77ded992ce --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/graph/layout/HierarchicalNodeLayout.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.graph.layout; + +import at.ssw.visualizer.cfg.graph.CfgScene; +import at.ssw.visualizer.cfg.model.CfgEdge; +import at.ssw.visualizer.cfg.model.CfgNode; +import java.awt.Point; +import java.util.Collection; +import org.netbeans.api.visual.graph.layout.GraphLayout; +import org.netbeans.api.visual.graph.layout.UniversalGraph; +import org.eclipse.draw2d.graph.DirectedGraph; +import org.eclipse.draw2d.graph.DirectedGraphLayout; +import org.eclipse.draw2d.graph.Edge; +import org.eclipse.draw2d.graph.EdgeList; +import org.eclipse.draw2d.graph.Node; +import org.eclipse.draw2d.graph.NodeList; +import org.netbeans.api.visual.widget.Widget; + + +public class HierarchicalNodeLayout extends GraphLayout { + + private static final int TOP_BORDER = 20; + private static final int LEFT_BORDER = 40; + + private CfgScene scene; + + public HierarchicalNodeLayout(CfgScene scene){ + this.scene = scene; + } + + @Override + protected void performGraphLayout(UniversalGraph ug) { + DirectedGraph dg = new DirectedGraph(); + DirectedGraphLayout layout = new DirectedGraphLayout(); + + NodeList nodeList = dg.nodes; + EdgeList edgeList = dg.edges; + + for(CfgNode n : scene.getCfgEnv().getNodes() ) { + Widget nodeWidget = scene.findWidget(n); + Node node = new Node(n); + node.width=nodeWidget.getBounds().width; + node.height = nodeWidget.getBounds().height; + nodeList.add(node); + } + + for(CfgEdge e : scene.getCfgEnv().getEdges() ) { + if(e.isBackEdge()) continue; + Edge edge = new Edge(e, nodeList.getNode(e.getSourceNode().getNodeIndex()), + nodeList.getNode(e.getTargetNode().getNodeIndex())); + edgeList.add(edge); + } + + layout.visit(dg); + + for(Object obj : dg.nodes){ + Node n = (Node) obj; + CfgNode cfgNode = (CfgNode) n.data; + Point pos = new Point(n.x + LEFT_BORDER , n.y + TOP_BORDER); + Point scenepos = scene.convertLocalToScene(pos); + setResolvedNodeLocation(ug, cfgNode, scenepos); + } + + } + + @Override + protected void performNodesLayout(UniversalGraph ug, Collection collection) { + } + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/icons/Icons.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/icons/Icons.java new file mode 100644 index 000000000000..1fd1e0c40150 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/icons/Icons.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.icons; + +/** + * + * @author Christian Wimmer + */ +public class Icons { + private static final String PATH = "at/ssw/visualizer/cfg/icons/"; + + public static final String CFG = PATH + "cfg.gif"; + + public static final String ICON_ZOOMIN = PATH + "zoomin.gif"; + public static final String ICON_ZOOMOUT = PATH + "zoomout.gif"; + public static final String ICON_DEFAULT = PATH + "arrangebfs.gif"; + + private Icons() { + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/CfgEdge.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/CfgEdge.java new file mode 100644 index 000000000000..aa05ff5a9e51 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/CfgEdge.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.model; + +public interface CfgEdge { + public CfgNode getSourceNode(); + public CfgNode getTargetNode(); + public boolean isXhandler(); + public boolean isBackEdge(); + public boolean isSymmetric(); + public boolean isReflexive(); +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/CfgEdgeImpl.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/CfgEdgeImpl.java new file mode 100644 index 000000000000..7ddf15b0d6fd --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/CfgEdgeImpl.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.model; + +public class CfgEdgeImpl implements CfgEdge { + private CfgNodeImpl sourceNode; + private CfgNodeImpl targetNode; + private boolean symmetric; + private boolean backedge; + + public CfgEdgeImpl(CfgNodeImpl sourceNode, CfgNodeImpl targetNode) { + this.sourceNode = sourceNode; + this.targetNode = targetNode; + } + + public void setSymmetric(boolean symmetric){ + this.symmetric = symmetric; + } + + public void setBackEdge(boolean isBackedge){ + this.backedge = isBackedge; + } + + public CfgNode getSourceNode() { + return sourceNode; + } + + public CfgNode getTargetNode() { + return targetNode; + } + + public boolean isBackEdge() { + return this.backedge; + } + + public boolean isSymmetric() { + return symmetric; + } + + public boolean isReflexive() { + return sourceNode==targetNode; + } + + public boolean isXhandler() { + return sourceNode.getBasicBlock().getXhandlers().contains(targetNode.getBasicBlock()); + } + + @Override + public String toString(){ + return this.sourceNode.getBasicBlock().getName() + "->" + targetNode.getBasicBlock().getName(); + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/CfgEnv.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/CfgEnv.java new file mode 100644 index 000000000000..73d1da492233 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/CfgEnv.java @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.model; + +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + + +/** + * This is the container class for the data model, + * it prepares creates nodes and edges for the CfgScene + * from a ControlFlowGraph of the Compilation Model + */ +public class CfgEnv { + private ControlFlowGraph cfg; + private Map loopMap;//maps: LoopHeader --> LoopInfo + private CfgNodeImpl[] nodeArr; + private CfgEdgeImpl[] edgeArr; + + public CfgEnv(ControlFlowGraph cfg) { + this.cfg = cfg; + int blockCount = cfg.getBasicBlocks().size(); + CfgNodeImpl[] nodes = new CfgNodeImpl[blockCount]; + Map block2nodeMap = new HashMap(); + Map> inputMap = new HashMap>(); + ArrayList allEdges = new ArrayList(); + List blocks = cfg.getBasicBlocks(); + //create nodes + for(int idx=0 ; idx < blockCount ; idx++) { + BasicBlock b = blocks.get(idx); + + String description = "Name: " + b.getName() + "\n"; + description += "BCI: [" + b.getFromBci() + "," + b.getToBci() + "]\n"; + if (b.getLoopDepth() > 0) { + description += "Loop " + b.getLoopIndex() + " Depth " + b.getLoopDepth() + "\n"; + } + description += "Predecessors: " + getBlockList(b.getPredecessors()) + "\n"; + description += "Successors: " + getBlockList(b.getSuccessors()) + "\n"; + description += "XHandlers: " + getBlockList(b.getXhandlers()); + if (b.getDominator() != null) { + description += "\nDominator: " + b.getDominator().getName(); + } + nodes[idx] = new CfgNodeImpl(b, idx, description); + block2nodeMap.put(b, nodes[idx]); + } + + + //create edges + Set cache = new HashSet();//avoids identical edges with same source and same target + for(int i = 0 ; i < blockCount ; i++) { + BasicBlock b = blocks.get(i); + List outputEdges = new ArrayList(); + + Set successors = new HashSet(); + successors.addAll(b.getSuccessors()); + successors.addAll(b.getXhandlers()); + for(BasicBlock sb : successors) { + CfgNodeImpl succNode = block2nodeMap.get(sb); + CfgEdgeImpl edge = new CfgEdgeImpl(nodes[i], succNode); + if(cache.contains(edge.toString())) + continue; + cache.add(edge.toString()); + //check for symtric edges + if(sb.getXhandlers().contains(b) || sb.getSuccessors().contains(b)) + edge.setSymmetric(true); + outputEdges.add(edge); + } + allEdges.addAll(outputEdges); + nodes[i].setOutputEdges(outputEdges.toArray(new CfgEdgeImpl[outputEdges.size()])); + } + + for(CfgEdgeImpl e: allEdges) { + //CfgNodeImpl src = (CfgNodeImpl) e.getSourceNode(); + CfgNodeImpl tar = (CfgNodeImpl) e.getTargetNode(); + Set set = inputMap.get(tar); + if( set == null) { + set = new HashSet(); + set.add(e); + inputMap.put(tar, set); + } + set.add(e); + } + for(CfgNodeImpl n : nodes){ + Set inputEdges = inputMap.get(n); + if(inputEdges == null) continue; + n.setInputEdges(inputEdges.toArray(new CfgEdgeImpl[inputEdges.size()])); + } + CfgEdgeImpl[] edges = allEdges.toArray(new CfgEdgeImpl[allEdges.size()]); + this.edgeArr=edges; + this.nodeArr=nodes; + CfgNodeImpl rootNode = nodeArr[0]; + setNodeLevels(rootNode); + indexLoops(rootNode); + + } + + + private String getBlockList(List blocks) { + if (blocks.size() == 0) { + return "None"; + } + StringBuilder sb = new StringBuilder(); + String prefix = ""; + for (BasicBlock b : blocks) { + sb.append(prefix).append(b.getName()); + prefix = ", "; + } + return sb.toString(); + } + + + public CfgNode[] getNodes(){ + return this.nodeArr; + } + + public CfgEdge[] getEdges(){ + return this.edgeArr; + } + + public Map getLoopMap() { + return loopMap; + } + + public void setLoopMap(Map loopMap) { + this.loopMap = loopMap; + } + + private void indexLoops(CfgNodeImpl rootNode){ + LoopEnv env = new LoopEnv(Arrays.asList(nodeArr)); + loopDetection(env, rootNode); + calcLoopDepth(env); + + int loopIndex=1; + + for(LoopInfo info : env.loopMap.values()) { + info.setLoopIndex(loopIndex++); + info.setLoopDepth(info.getHeader().getLoopDepth()); + for(CfgNode n : info.getMembers()){ + if(n.getLoopDepth()>info.getLoopDepth()) continue; + CfgNodeImpl ni = (CfgNodeImpl) n; + ni.setLoopDepth(info.getLoopDepth()); + ni.setLoopIndex(info.getLoopIndex()); + } + } + + for(LoopInfo info : env.loopMap.values()) { + HashSet members = new HashSet(info.getMembers()); + members.remove(info.getHeader());//remove own header + for(CfgNode n: members){ + if(n.isLoopHeader()) { + LoopInfo memberInfo = env.loopMap.get(n); + if (info.getLoopDepth() == memberInfo.getLoopDepth()-1) + memberInfo.setParent(info); + } + } + } + this.loopMap = env.loopMap; + } + + + private class LoopEnv { + Set allNodes; + Set activeNodes; + Set visitedNodes; + Map loopMap; + private int loopIndex=0; + + public LoopEnv(Collection nodes){ + allNodes = new HashSet(nodes); + activeNodes = new HashSet(2 * allNodes.size()); + visitedNodes = new HashSet(2 * allNodes.size()); + loopMap = new HashMap(); + } + + public int getLoopIndex(){ + return ++loopIndex; + } + } + + + private void loopDetection(LoopEnv env, CfgNodeImpl root) { + for (CfgNodeImpl n : env.allNodes) { + n.setLoopHeader(false); + for (CfgEdge e : n.getInputEdges()) { + CfgEdgeImpl ei = (CfgEdgeImpl) e; + ei.setBackEdge(false); + } + } + visit(env, root, null); + } + + + + private void visit(LoopEnv env, CfgNodeImpl n, CfgEdgeImpl e) { + if (env.activeNodes.contains(n)) { + // This node is b loop header! + n.setLoopHeader(true); + e.setBackEdge(true); + } else if (!env.visitedNodes.contains(n)) { + env.visitedNodes.add(n); + env.activeNodes.add(n); + + for (CfgEdge edge : n.getOutputEdges()) { + if(!edge.getTargetNode().isOSR()) { + CfgEdgeImpl ei = (CfgEdgeImpl) edge; + CfgNodeImpl ni = (CfgNodeImpl) edge.getTargetNode(); + visit(env, ni, ei); + } + } + env.activeNodes.remove(n); + } + } + + + + private void calcLoopDepth(LoopEnv env) { + for (CfgNodeImpl n : env.allNodes) { + env.visitedNodes.clear(); + + if (n.isLoopHeader()) { + LoopInfo loop = new LoopInfo(); + loop.setHeader(n); + n.setLoopIndex(env.getLoopIndex()); + HashSet members = new HashSet(); + loop.setMembers(members); + members.add(n); + env.loopMap.put(loop.getHeader(), loop); + int loopDepth = n.getLoopDepth() + 1; + loop.setLoopDepth(loopDepth); + n.setLoopDepth(loopDepth); + for (CfgEdge e : n.getInputEdges()) { + if (e.isBackEdge() && !e.getSourceNode().isOSR()) { + CfgNodeImpl src = (CfgNodeImpl) e.getSourceNode(); + backwardIteration(env, n, src, loop); + loop.getBackEdges().add(e); + } + } + } + } + } + + + private void backwardIteration(LoopEnv env, CfgNodeImpl endNode, CfgNodeImpl n, LoopInfo loop) { + if (endNode != n && !env.visitedNodes.contains(n)) { + env.visitedNodes.add(n); + + for (CfgEdge e : n.getInputEdges()) { + if (!e.getSourceNode().isOSR()) { + CfgNodeImpl src = (CfgNodeImpl) e.getSourceNode(); + backwardIteration(env, endNode, src, loop); + } + } + loop.getMembers().add(n); + n.setLoopDepth(n.getLoopDepth() + 1); + } + } + + private void setNodeLevels(CfgNode rootNode){ + Set cache = new HashSet(); + Queue queue = new LinkedList(); + queue.add(rootNode); + cache.add(rootNode); + int level=0; + int[] nodeCount = new int[2]; + nodeCount[0]=1; + while(!queue.isEmpty()){ + CfgNodeImpl curNode = (CfgNodeImpl) queue.poll(); + curNode.setLevel(level); + nodeCount[0]--; + for(CfgEdge outEdge : curNode.getOutputEdges()) { + CfgNode succNode = outEdge.getTargetNode(); + if(cache.contains(succNode)) continue; + cache.add(succNode); + queue.add(succNode); + nodeCount[1]++; + } + if(nodeCount[0]==0){ + nodeCount[0]=nodeCount[1]; + nodeCount[1]=0; + level++; + } + } + } + + public ControlFlowGraph getCfg() { + return cfg; + } + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/CfgNode.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/CfgNode.java new file mode 100644 index 000000000000..0a7b1b93a09f --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/CfgNode.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.model; + +import at.ssw.visualizer.model.cfg.BasicBlock; + + +public interface CfgNode { + //testers + public boolean isOSR(); + public boolean isRoot(); + public boolean isLoopHeader(); + public boolean isLoopMember(); + + //getters + public int getLevel(); + public int getLoopDepth(); + public int getLoopIndex(); + public int getNodeIndex(); + public CfgEdge[] getInputEdges(); + public CfgEdge[] getOutputEdges(); + public BasicBlock getBasicBlock(); + public String getDescription(); + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/CfgNodeImpl.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/CfgNodeImpl.java new file mode 100644 index 000000000000..b55d203aa65d --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/CfgNodeImpl.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.model; + +import at.ssw.visualizer.model.cfg.BasicBlock; +import java.awt.Color; + + + +public class CfgNodeImpl implements CfgNode { + private int nodeIndex; + private BasicBlock basicBlock; + private int level; + private int loopDepth=0; + private int loopIndex=0; + private boolean osr=false; + private CfgNodeImpl dominator=null; + private CfgEdgeImpl[] inputEdges = new CfgEdgeImpl[0]; + private CfgEdgeImpl[] outputEdges = new CfgEdgeImpl[0]; + private String description; + private Color customColor=null; + private boolean loopHeader; + + public CfgNodeImpl(BasicBlock bb, int nodeIndex, String description) { + this.basicBlock = bb; + this.nodeIndex = nodeIndex; + this.description = description; + + if (bb.getPredecessors().size() == 1) { + BasicBlock pred = bb.getPredecessors().get(0); + boolean isStd = pred.getPredecessors().size() == 0; + if (isStd) { + for (String s : bb.getFlags()) { + if (s.equals("osr")) { + osr = true; + break; + } + } + } + } + } + + public int getNodeIndex() { + return nodeIndex; + } + + + public void setDominator(CfgNodeImpl dominator) { + this.dominator = dominator; + } + + public void setLevel(int level) { + this.level = level; + } + + public void setLoopDepth(int loopDepth) { + this.loopDepth = loopDepth; + } + + public void setLoopHeader(boolean loopHeader) { + this.loopHeader = loopHeader; + } + + public void setLoopIndex(int loopIndex) { + this.loopIndex = loopIndex; + } + + public void setNodeIndex(int nodeIndex) { + this.nodeIndex = nodeIndex; + } + + public boolean isRoot() { + return nodeIndex==0; + } + + public boolean isLoopHeader() { + return loopHeader; + } + + public boolean isLoopMember() { + return loopIndex > 0; + } + + public int getLevel() { + return level; + } + + public BasicBlock getBasicBlock() { + return basicBlock; + } + + public int getLoopDepth() { + return loopDepth; + } + + public int getLoopIndex() { + return loopIndex; + } + + public boolean isOSR() { + return osr; + } + + @Override + public String toString(){ + return basicBlock.getName(); + } + + public void setInputEdges(CfgEdgeImpl[] inputEdges){ + this.inputEdges = inputEdges; + } + + + public void setOutputEdges(CfgEdgeImpl[] outputEdges){ + this.outputEdges = outputEdges; + } + + public CfgEdge[] getInputEdges() { + return this.inputEdges; + } + + public CfgEdge[] getOutputEdges() { + return outputEdges; + } + + public String getDescription() { + return description; + } + + public void setColor(Color color) { + this.customColor=color; + } + + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/LoopInfo.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/LoopInfo.java new file mode 100644 index 000000000000..26d036a4fde7 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/model/LoopInfo.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + + +public class LoopInfo { + private CfgNode header;//the target node of the backedge + private int loopIndex; //index of the min cycleSet + private int loopDepth; //nested depth >=1 + private LoopInfo parent=null; + private Set members; + private List backEdges = new ArrayList();//dfs backEdge + + protected void setLoopDepth(int depth) { + this.loopDepth=depth; + } + + protected void setLoopIndex(int loopIndex) { + this.loopIndex = loopIndex; + } + + public int getLoopDepth() { + return loopDepth; + } + + public Set getMembers() { + return members; + } + + protected void setMembers(Set members) { + this.members = members; + } + + public int getLoopIndex() { + return loopIndex; + } + + protected void setParent(LoopInfo parent) { + this.parent = parent; + } + + public LoopInfo getParent(){ + return parent; + } + + public List getBackEdges() { + return backEdges; + } + + public CfgNode getHeader() { + return header; + } + + protected void setHeader(CfgNode header) { + this.header = header; + } + + @Override + public String toString(){ + return "Loop(" + header.toString()+ ")-->" + members.toString(); + } + + + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/CFGOptionsCategory.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/CFGOptionsCategory.java new file mode 100644 index 000000000000..0a8f42fc7e5b --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/CFGOptionsCategory.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.preferences; + +import javax.swing.Icon; +import javax.swing.ImageIcon; +import org.netbeans.spi.options.OptionsCategory; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.ImageUtilities; + +/** + * Descriptor for the settings page displayed in the options dialog. + * + * @author Bernhard Stiftner + */ +public class CFGOptionsCategory extends OptionsCategory { + + public OptionsPanelController create() { + return new CFGOptionsPanelController(); + } + + public String getCategoryName() { + return "Control Flow Graph"; + } + + @Override + public Icon getIcon() { + return new ImageIcon(ImageUtilities.loadImage("at/ssw/visualizer/cfg/icons/cfg32.gif")); + } + + public String getTitle() { + return "CFG Visualizer"; + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/CFGOptionsPanel.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/CFGOptionsPanel.java new file mode 100644 index 000000000000..ea550622451a --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/CFGOptionsPanel.java @@ -0,0 +1,504 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.preferences; + +import java.awt.Color; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; +import javax.swing.AbstractAction; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.SwingConstants; + +/** + * The actual component for changing the CFG visualizer settings. + * + * @author Bernhard Stiftner + * @author Rumpfhuber Stefan + */ +public class CFGOptionsPanel extends JPanel { + + List elements = new ArrayList(); + + /** Creates a new instance of CFGOptionsPanel */ + public CFGOptionsPanel() { + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + JPanel cfgPanel = new JPanel(new GridBagLayout()); + + // color/font settings + addColorChooser(cfgPanel, "Background color: ", new ColorChooser(CfgPreferences.PROP_BACKGROUND_COLOR){ + @Override + public void apply() { + CfgPreferences.getInstance().setBackgroundColor(getColor()); + } + + @Override + public void update() { + this.originalColor = CfgPreferences.getInstance().getBackgroundColor(); + setColor(this.originalColor); + } + + public void reset() { + this.setColor(CfgPreferencesDefaults.DEFAULT_BACKGROUND_COLOR); + } + + + + }); + addColorChooser(cfgPanel, "Back edge color: ", new ColorChooser(CfgPreferences.PROP_BACK_EDGE_COLOR){ + + @Override + public void apply() { + CfgPreferences.getInstance().setBackedgeColor(getColor()); + } + + @Override + public void update() { + this.originalColor = CfgPreferences.getInstance().getBackedgeColor(); + this.setColor(this.originalColor); + + } + + public void reset() { + this.setColor(CfgPreferencesDefaults.DEFAULT_BACKEDGE_COLOR); + } + }); + addColorChooser(cfgPanel, "Edge color: ", new ColorChooser(CfgPreferences.PROP_EDGE_COLOR){ + + @Override + public void apply() { + CfgPreferences.getInstance().setEdgeColor(getColor()); + } + + @Override + public void update() { + this.originalColor = CfgPreferences.getInstance().getEdgeColor(); + setColor(this.originalColor); + } + + public void reset() { + this.setColor(CfgPreferencesDefaults.DEFAULT_EDGE_COLOR); + } + + }); + addColorChooser(cfgPanel, "Exception edge color: ", new ColorChooser(CfgPreferences.PROP_EXCEPTION_EDGE_COLOR){ + + @Override + public void apply() { + CfgPreferences.getInstance().setExceptionEdgeColor(getColor()); + } + + @Override + public void update() { + this.originalColor = CfgPreferences.getInstance().getExceptionEdgeColor(); + setColor(this.originalColor); + + } + + public void reset() { + this.setColor(CfgPreferencesDefaults.DEFAULT_EXCEPTIONEDGE_COLOR); + } + }); + addColorChooser(cfgPanel, "Node color: ", new ColorChooser(CfgPreferences.PROP_NODE_COLOR){ + + @Override + public void apply() { + CfgPreferences.getInstance().setNodeColor(getColor()); + } + + @Override + public void update() { + this.originalColor = CfgPreferences.getInstance().getNodeColor(); + setColor(this.originalColor); + + } + + public void reset() { + this.setColor(CfgPreferencesDefaults.DEFAUT_NODE_COLOR); + } + }); + addColorChooser(cfgPanel, "Text color: ", new ColorChooser(CfgPreferences.PROP_TEXT_COLOR){ + + @Override + public void apply() { + CfgPreferences.getInstance().setTextColor(getColor()); + } + + @Override + public void update() { + this.originalColor = CfgPreferences.getInstance().getTextColor(); + + } + + public void reset() { + this.setColor(CfgPreferencesDefaults.DEFAULT_TEXT_COLOR); + } + }); + addColorChooser(cfgPanel, "Border color: ", new ColorChooser(CfgPreferences.PROP_BORDER_COLOR){ + + @Override + public void apply() { + CfgPreferences.getInstance().setBorderColor(getColor()); + } + + @Override + public void update() { + this.originalColor = CfgPreferences.getInstance().getBorderColor(); + setColor(this.originalColor); + + } + public void reset() { + this.setColor(CfgPreferencesDefaults.DEFAULT_BORDER_COLOR); + } + }); + addColorChooser(cfgPanel, "Selected Nodes color: ", new ColorChooser(CfgPreferences.PROP_SELECTION_COLOR_FG){ + + @Override + public void apply() { + CfgPreferences.getInstance().setSelectionColorForeground(getColor()); + } + + @Override + public void update() { + this.originalColor = CfgPreferences.getInstance().getSelectionColorForeground(); + setColor(this.originalColor); + } + + public void reset() { + this.setColor(CfgPreferencesDefaults.DEFAULT_SELECTION_COLOR_FOREGROUND); + } + }); + addColorChooser(cfgPanel, "Selection Rect color: ", new ColorChooser(CfgPreferences.PROP_SELECTION_COLOR_BG){ + + @Override + public void apply() { + CfgPreferences.getInstance().setSelectionColorBackground(getColor()); + } + + @Override + public void update() { + this.originalColor = CfgPreferences.getInstance().getSelectionColorBackground(); + setColor(this.originalColor); + } + + public void reset() { + this.setColor(CfgPreferencesDefaults.DEFAULT_SELECTION_COLOR_BACKGROUND); + } + }); + addFontChooser(cfgPanel, "Text font: ", new FontChooser(CfgPreferences.PROP_TEXT_FONT){ + + @Override + public void apply() { + CfgPreferences.getInstance().setTextFont(getSelectedFont()); + } + + @Override + public void update() { + this.originalFont = CfgPreferences.getInstance().getTextFont(); + this.setSelectedFont(originalFont); + + } + + public void reset() { + this.setSelectedFont(CfgPreferencesDefaults.DEFAULT_TEXT_FONT); + } + }); + + // flags editor + addFlagsEditor(cfgPanel, "Flags: "); + add(cfgPanel); + + // add update button + Box hBox = new Box(BoxLayout.X_AXIS); + hBox.add(new JButton(new ResetAction())); + hBox.add(Box.createHorizontalGlue()); + add(hBox); + + add(Box.createVerticalGlue()); + } + + public void update() { + for (ConfigurationElement e : elements) { + e.update(); + } + } + + public void cancel() { + for (ConfigurationElement e : elements) { + e.update(); + } + } + + public void applyChanges() { + for (ConfigurationElement e : elements) { + if(e.isChanged()) + e.apply(); + } + } + + public boolean isDataValid() { + return true; + } + + public boolean isChanged() { + for (ConfigurationElement e : elements) { + if (e.isChanged()) { + return true; + } + } + return false; + } + + public void loadDefault() { + for (ConfigurationElement e : elements) { + e.reset(); + } + } + + private void addColorChooser(JComponent c, String displayName, ColorChooser chooser) { + GridBagLayout layout = (GridBagLayout)c.getLayout(); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.insets = new Insets(3, 3, 3, 3); + constraints.weightx = 0.0; + constraints.fill = GridBagConstraints.NONE; + constraints.anchor = GridBagConstraints.EAST; + JLabel label = new JLabel(displayName, JLabel.TRAILING); + layout.setConstraints(label, constraints); + c.add(label); + constraints.anchor = GridBagConstraints.WEST; + constraints.gridwidth = GridBagConstraints.REMAINDER; + layout.setConstraints(chooser, constraints); + c.add(chooser); + elements.add(chooser); + label.setLabelFor(chooser); + } + + + + private void addFontChooser(JComponent c, String displayName, FontChooser chooser) { + GridBagLayout layout = (GridBagLayout)c.getLayout(); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.insets = new Insets(3, 3, 3, 3); + constraints.weightx = 0.0; + constraints.fill = GridBagConstraints.NONE; + constraints.anchor = GridBagConstraints.EAST; + JLabel label = new JLabel(displayName, JLabel.TRAILING); + layout.setConstraints(label, constraints); + c.add(label); + constraints.insets = new Insets(3, 3, 3, 1); + constraints.weightx = 1.0; + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.anchor = GridBagConstraints.CENTER; + layout.setConstraints(chooser.getPreview(), constraints); + c.add(chooser.getPreview()); + constraints.insets = new Insets(3, 1, 3, 3); + constraints.weightx = 0.0; + constraints.fill = GridBagConstraints.NONE; + constraints.anchor = GridBagConstraints.EAST; + constraints.gridwidth = GridBagConstraints.REMAINDER; + layout.setConstraints(chooser.getButton(), constraints); + c.add(chooser.getButton()); + elements.add(chooser); + label.setLabelFor(chooser.getButton()); + } + + private void addFlagsEditor(JComponent c, String displayName) { + GridBagLayout layout = (GridBagLayout)c.getLayout(); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.insets = new Insets(3, 3, 3, 3); + constraints.weightx = 0.0; + constraints.fill = GridBagConstraints.NONE; + constraints.anchor = GridBagConstraints.NORTHEAST; + FlagsEditor flagsEditor = new FlagsEditor(); + JLabel flagsLabel = new JLabel(displayName, JLabel.TRAILING); + flagsLabel.setVerticalAlignment(SwingConstants.TOP); + layout.setConstraints(flagsLabel, constraints); + c.add(flagsLabel); + constraints.weightx = 1.0; + constraints.weighty = 1.0; + constraints.fill = GridBagConstraints.BOTH; + constraints.anchor = GridBagConstraints.CENTER; + constraints.gridwidth = GridBagConstraints.REMAINDER; + layout.setConstraints(flagsEditor, constraints); + c.add(flagsEditor); + elements.add(flagsEditor); + flagsLabel.setLabelFor(flagsEditor); + } + + + + + interface ConfigurationElement { + + public boolean isChanged(); + + //apply changes to preferences + public void apply(); + + //optain current value from preferences + public void update(); + + //reset to default value + public void reset(); + + } + + abstract class ColorChooser extends ColorChooserButton implements ConfigurationElement { + + Color originalColor;//the color before any change + String propertyName; + + public ColorChooser(String propertyName) { + this.propertyName = propertyName; + } + + public boolean isChanged() { + return !originalColor.equals(getColor()); + } + + + public abstract void apply(); + public abstract void update(); + + } + + + abstract class FontChooser implements ConfigurationElement, ActionListener { + + Font originalFont; + Font selectedFont; + String propertyName; + JTextField preview; + JButton button; + + public FontChooser(String propertyName) { + this.propertyName = propertyName; + preview = new JTextField(""); + preview.setEditable(false); + button = new JButton("..."); + button.setMargin(new Insets(0, 0, 0, 0)); + button.addActionListener(this); + } + + public boolean isChanged() { + return !originalFont.equals(selectedFont); + } + + public abstract void apply(); + + public abstract void update(); + + public abstract void reset(); + + public JTextField getPreview() { + return preview; + } + + public JButton getButton() { + return button; + }; + + public Font getSelectedFont() { + return selectedFont; + } + + public void setSelectedFont(Font f) { + selectedFont = f; + preview.setText(fontToString(f)); + preview.revalidate(); + } + + public void actionPerformed(ActionEvent e) { + setSelectedFont(FontChooserDialog.show(selectedFont)); + } + + public String fontToString(Font font) { + StringBuffer sb = new StringBuffer(); + sb.append(font.getName()); + sb.append(" "); + sb.append(font.getSize()); + if (font.isBold()) { + sb.append(" bold"); + } + if (font.isItalic()) { + sb.append(" italic"); + } + return sb.toString(); + } + + } + + class FlagsEditor extends FlagsEditorPanel implements ConfigurationElement { + + FlagsSetting originalFlags; + + public FlagsEditor() { + super(null); + } + + public boolean isChanged() { + return !originalFlags.getFlagString().equals(getFlagString()); + } + + public void apply() { + CfgPreferences.getInstance().setFlagsSetting(new FlagsSetting(getFlagString())); + update(); + } + + public void update() { + originalFlags = CfgPreferences.getInstance().getFlagsSetting(); + setFlagString(originalFlags.getFlagString()); + } + + public void reset() { + FlagsSetting defaultFlags = new FlagsSetting(CfgPreferencesDefaults.DEFAULT_FLAGSTRING); + setFlagString(defaultFlags.getFlagString()); + colorButton.setColor(getParent().getBackground()); + } + + } + + class ResetAction extends AbstractAction { + + public ResetAction() { + super("Reset"); + } + + public void actionPerformed(ActionEvent e) { + CFGOptionsPanel.this.loadDefault(); + } + + } + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/CFGOptionsPanelController.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/CFGOptionsPanelController.java new file mode 100644 index 000000000000..bb5fcc032409 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/CFGOptionsPanelController.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.preferences; + +import java.beans.PropertyChangeListener; +import javax.swing.JComponent; +import org.netbeans.spi.options.OptionsPanelController; +import org.openide.util.HelpCtx; +import org.openide.util.Lookup; + +/** + * Controller for the settings page displayed in the options dialog. + * + * @author Bernhard Stiftner + */ +public class CFGOptionsPanelController extends OptionsPanelController { + + CFGOptionsPanel optionsPanel; + + + public void update() { + getOptionsPanel().update(); + } + + public void applyChanges() { + getOptionsPanel().applyChanges(); + } + + public void cancel() { + getOptionsPanel().cancel(); + } + + public boolean isValid() { + return getOptionsPanel().isDataValid(); + } + + public boolean isChanged() { + return getOptionsPanel().isChanged(); + } + + public JComponent getComponent(Lookup masterLookup) { + return getOptionsPanel(); + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + public void addPropertyChangeListener(PropertyChangeListener l) { + getOptionsPanel().addPropertyChangeListener(l); + } + + //todo: investigate - who removes the changelistener ? + public void removePropertyChangeListener(PropertyChangeListener l) { + getOptionsPanel().removePropertyChangeListener(l); + } + + private CFGOptionsPanel getOptionsPanel() { + if (optionsPanel == null) { + optionsPanel = new CFGOptionsPanel(); + } + return optionsPanel; + } + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/CfgPreferences.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/CfgPreferences.java new file mode 100644 index 000000000000..a44e216b1f33 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/CfgPreferences.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.preferences; + +import at.ssw.visualizer.cfg.editor.CfgEditorTopComponent; +import java.awt.Color; +import java.awt.Font; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.prefs.Preferences; +import javax.swing.event.EventListenerList; +import org.openide.util.NbPreferences; + +/** + * Replacement for old CFGSettings to remove dependency on deprecated SystemOption package + * + * @author Rumpfhuber Stefan + */ +public class CfgPreferences { + public static final String PROP_FLAGS = "flagsPreference"; + public static final String PROP_TEXT_FONT = "textFontPreference"; + public static final String PROP_TEXT_COLOR = "textColorPreference"; + public static final String PROP_NODE_COLOR = "nodeColorPreference"; + public static final String PROP_EDGE_COLOR = "edgeColorPreference"; + public static final String PROP_BORDER_COLOR = "borderColorPreference"; + public static final String PROP_BACK_EDGE_COLOR = "backEdgeColorPreference"; + public static final String PROP_BACKGROUND_COLOR = "backgroundColorPreference"; + public static final String PROP_SELECTION_COLOR_FG = "selectionColorFgPreference"; + public static final String PROP_SELECTION_COLOR_BG = "selectionColorBgPreference"; + public static final String PROP_EXCEPTION_EDGE_COLOR = "exceptionEdgeColorPreference"; + + private static final String PROP_FONTNAME = "_FontFamily"; + private static final String PROP_FONTSIZE = "_FontSize"; + private static final String PROP_FONTSTYLE = "_FontStyle"; + + protected static final String nodeName = "CfgPreferences"; + + private static CfgPreferences instance = new CfgPreferences(); + private EventListenerList listenerList; + + private FlagsSetting flagsSetting; + private Color node_color; + private Color background_color; + private Color backedge_color; + private Color edge_color; + private Color border_color; + private Color exceptionEdgeColor; + private Color text_color; + private Font text_font; + private Color selection_color_fg; + private Color selection_color_bg; + + + private CfgPreferences(){ + listenerList = new EventListenerList(); + init(); + } + + public static CfgPreferences getInstance(){ + return instance; + } + + public void addPropertyChangeListener(CfgEditorTopComponent listener) { + listenerList.add(PropertyChangeListener.class, listener); + } + + public void removePropertyChangeListener(CfgEditorTopComponent listener) { + listenerList.remove(PropertyChangeListener.class, listener); + } + + protected final Preferences getPreferences() { + return NbPreferences.forModule(this.getClass()).node("options").node(nodeName); + } + + + protected void init(){ + Preferences prefs = this.getPreferences(); + String flagString = prefs.get(PROP_FLAGS, CfgPreferencesDefaults.DEFAULT_FLAGSTRING); + flagsSetting = new FlagsSetting(flagString); + node_color = this.getColorProperty(PROP_NODE_COLOR, CfgPreferencesDefaults.DEFAUT_NODE_COLOR); + background_color = this.getColorProperty(PROP_BACKGROUND_COLOR, CfgPreferencesDefaults.DEFAULT_BACKGROUND_COLOR); + backedge_color = this.getColorProperty(PROP_BACK_EDGE_COLOR, CfgPreferencesDefaults.DEFAULT_BACKEDGE_COLOR); + edge_color = this.getColorProperty(PROP_EDGE_COLOR, CfgPreferencesDefaults.DEFAULT_EDGE_COLOR); + selection_color_fg= this.getColorProperty(PROP_SELECTION_COLOR_FG, CfgPreferencesDefaults.DEFAULT_SELECTION_COLOR_FOREGROUND); + border_color = this.getColorProperty(PROP_BORDER_COLOR, CfgPreferencesDefaults.DEFAULT_BORDER_COLOR); + exceptionEdgeColor = this.getColorProperty(PROP_EXCEPTION_EDGE_COLOR, CfgPreferencesDefaults.DEFAULT_EXCEPTIONEDGE_COLOR); + text_color= this.getColorProperty(PROP_TEXT_COLOR, CfgPreferencesDefaults.DEFAULT_TEXT_COLOR); + selection_color_bg = this.getColorProperty(PROP_SELECTION_COLOR_BG, CfgPreferencesDefaults.DEFAULT_SELECTION_COLOR_BACKGROUND); + selection_color_fg = this.getColorProperty(PROP_SELECTION_COLOR_FG, CfgPreferencesDefaults.DEFAULT_SELECTION_COLOR_FOREGROUND); + text_font = this.getFontProperty(PROP_TEXT_FONT, CfgPreferencesDefaults.DEFAULT_TEXT_FONT); + } + + private void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + Object[] listeners = listenerList.getListenerList(); + + PropertyChangeEvent event = new PropertyChangeEvent(this, propertyName, oldValue, newValue); + for (int i = listeners.length - 2; i >= 0; i -= 2) { + if (listeners[i] == PropertyChangeListener.class) { + ((PropertyChangeListener) listeners[i+1]).propertyChange(event); + } + } + } + + private Font getFontProperty(String propName, Font defaultFont){ + Preferences prefs = this.getPreferences(); + String fontName = prefs.get(propName+PROP_FONTNAME, defaultFont.getFamily()); + int fontSize = prefs.getInt(propName+PROP_FONTSIZE, defaultFont.getSize()); + int fontStyle = prefs.getInt(propName+PROP_FONTSTYLE, defaultFont.getStyle()); + return new Font(fontName, fontStyle, fontSize); + } + + private Color getColorProperty(String propName, Color defaultColor){ + Preferences prefs = this.getPreferences(); + int srgb = prefs.getInt(propName, defaultColor.getRGB()); + if(srgb == defaultColor.getRGB()) + return defaultColor; + return new Color(srgb); + } + + public Color getBackedgeColor() { + return backedge_color; + } + + public Color getBackgroundColor() { + return background_color; + } + + public Color getBorderColor() { + return border_color; + } + + public Color getEdgeColor() { + return edge_color; + } + + public Color getExceptionEdgeColor() { + return exceptionEdgeColor; + } + + public Color getNodeColor() { + return node_color; + } + + public Color getSelectionColorForeground() { + return selection_color_fg; + } + + public Color getSelectionColorBackground() { + return selection_color_bg; + } + + public Color getTextColor() { + return text_color; + } + + public Font getTextFont() { + return text_font; + } + + public FlagsSetting getFlagsSetting() { + return flagsSetting; + } + + + public void setFlagsSetting(FlagsSetting flagsSetting) { + FlagsSetting old = this.getFlagsSetting(); + this.flagsSetting = flagsSetting; + Preferences prefs = getPreferences(); + firePropertyChange(PROP_FLAGS, old, flagsSetting); + prefs.put(PROP_FLAGS, flagsSetting.getFlagString()); + } + + + public void setTextFont(Font text_font) { + Font old = this.getTextFont(); + Preferences prefs = getPreferences(); + this.text_font = text_font; + firePropertyChange(PROP_TEXT_FONT, old, text_font); + prefs.put(PROP_TEXT_FONT + PROP_FONTNAME , text_font.getFamily()); + prefs.putInt(PROP_TEXT_FONT + PROP_FONTSIZE, text_font.getSize()); + prefs.putInt(PROP_TEXT_FONT + PROP_FONTSTYLE, text_font.getStyle()); + } + + public void setBackedgeColor(Color backedge_color) { + Color old = this.getBackedgeColor(); + this.backedge_color = backedge_color; + firePropertyChange(PROP_BACK_EDGE_COLOR, old, backedge_color); + getPreferences().putInt(PROP_BACK_EDGE_COLOR, backedge_color.getRGB()); + } + + public void setBackgroundColor(Color bg_color) { + Color old = this.getBackgroundColor(); + background_color = bg_color; + firePropertyChange(PROP_BACKGROUND_COLOR, old, bg_color ); + getPreferences().putInt(PROP_BACKGROUND_COLOR, bg_color.getRGB()); + } + + public void setBorderColor(Color border_color) { + Color old = getBorderColor(); + this.border_color = border_color; + firePropertyChange(PROP_BORDER_COLOR, old, border_color ); + getPreferences().putInt(PROP_BORDER_COLOR, border_color.getRGB()); + } + + public void setEdgeColor(Color edge_color) { + Color old = getEdgeColor(); + this.edge_color = edge_color; + firePropertyChange(PROP_EDGE_COLOR, old, edge_color); + getPreferences().putInt(PROP_EDGE_COLOR, edge_color.getRGB()); + } + + public void setNodeColor(Color node_color) { + Color old = getNodeColor(); + this.node_color = node_color; + firePropertyChange(PROP_NODE_COLOR, old, node_color); + getPreferences().putInt(PROP_NODE_COLOR, node_color.getRGB()); + + } + + public void setSelectionColorForeground(Color selection_color) { + Color old = this.getSelectionColorForeground(); + this.selection_color_fg = selection_color; + firePropertyChange(PROP_SELECTION_COLOR_FG, old, selection_color); + getPreferences().putInt(PROP_SELECTION_COLOR_FG, selection_color.getRGB()); + } + + public void setSelectionColorBackground(Color selection_color) { + Color old = this.getSelectionColorBackground(); + this.selection_color_bg = selection_color; + firePropertyChange(PROP_SELECTION_COLOR_BG, old, selection_color); + getPreferences().putInt(PROP_SELECTION_COLOR_BG, selection_color.getRGB()); + } + + public void setTextColor(Color text_color) { + Color old = this.getTextColor(); + this.text_color = text_color; + firePropertyChange(PROP_TEXT_COLOR, old, text_color); + getPreferences().putInt(PROP_TEXT_COLOR, text_color.getRGB()); + } + + public void setExceptionEdgeColor(Color exceptionEdgeColor) { + Color old = this.getExceptionEdgeColor(); + this.exceptionEdgeColor = exceptionEdgeColor; + firePropertyChange(PROP_EXCEPTION_EDGE_COLOR, old, exceptionEdgeColor); + getPreferences().putInt(PROP_EXCEPTION_EDGE_COLOR, exceptionEdgeColor.getRGB()); + } + + + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/CfgPreferencesDefaults.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/CfgPreferencesDefaults.java new file mode 100644 index 000000000000..65288d5bc1de --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/CfgPreferencesDefaults.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.preferences; + +import java.awt.Color; +import java.awt.Font; + +/** + * Default Configuration for options panel + * + * @author Rumpfhuber Stefan + */ +public final class CfgPreferencesDefaults { + public static final String DEFAULT_FLAGSTRING = "std(224,224,128);osr(224,224,0);ex(128,128,224);sr(128,224,128);llh(224,128,128);lle(224,192,192);plh(128,224,128);bb(160,0,0);ces(192,192,192)"; + public static final Color DEFAUT_NODE_COLOR = new Color(208, 208, 208); + public static final Color DEFAULT_BACKGROUND_COLOR = new Color(255, 255, 255); + public static final Color DEFAULT_BACKEDGE_COLOR = new Color(160, 0, 0); + public static final Color DEFAULT_EDGE_COLOR = new Color(0, 0, 0); + public static final Color DEFAULT_SELECTION_COLOR_FOREGROUND = Color.BLUE; + public static final Color DEFAULT_SELECTION_COLOR_BACKGROUND = Color.BLUE; + public static final Color DEFAULT_BORDER_COLOR = new Color(0, 0, 0); + public static final Color DEFAULT_EXCEPTIONEDGE_COLOR = new Color(0, 0, 160); + public static final Color DEFAULT_TEXT_COLOR = new Color(0, 0, 0); + public static final Font DEFAULT_TEXT_FONT = new Font("Dialog", Font.PLAIN, 18); +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/ColorChooserButton.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/ColorChooserButton.java new file mode 100644 index 000000000000..d16c4ecbc079 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/ColorChooserButton.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.preferences; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JColorChooser; + +/** + * A color selection button. It will preview the currently selected color as + * an icon. Clicking the button will bring up the JColorChooser dialog. + * + * @author Bernhard Stiftner + */ +public class ColorChooserButton extends JButton implements ActionListener { + + public static final Dimension ICON_SIZE = new Dimension(24, 8); + + Color color; + boolean colorChooserEnabled = true; // bring up dialog when clicked? + + + public ColorChooserButton() { + this(Color.black); + } + + public ColorChooserButton(Color defaultColor) { + setIcon(new ColorBoxIcon()); + addActionListener(this); + color = defaultColor; + Dimension size = new Dimension(ICON_SIZE); + size.width += getInsets().left + getInsets().right; + size.height += getInsets().top + getInsets().bottom; + setPreferredSize(size); + } + + public void setColor(Color newColor) { + color = newColor; + repaint(); + } + + public Color getColor() { + return color; + } + + public boolean isColorChooserEnabled() { + return colorChooserEnabled; + } + + public void setColorChooserEnabled(boolean enabled) { + this.colorChooserEnabled = enabled; + } + + public void actionPerformed(ActionEvent e) { + if (!colorChooserEnabled) { + return; + } + + Color c = JColorChooser.showDialog(this, "Choose color", color); + if (c != null) { + setColor(c); + } + } + + class ColorBoxIcon implements Icon { + + public int getIconWidth() { + return ICON_SIZE.width; + } + + public int getIconHeight() { + return ICON_SIZE.height; + } + + public void paintIcon(Component c, Graphics g, int x, int y) { + Color oldColor = g.getColor(); + g.translate(x, y); + + g.setColor(color); + g.fillRect(0, 0, ICON_SIZE.width, ICON_SIZE.height); + + g.setColor(Color.black); + g.drawRect(0, 0, ICON_SIZE.width, ICON_SIZE.height); + + g.translate(-x, -y); + g.setColor(oldColor); + } + } +} + diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/FlagsEditorPanel.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/FlagsEditorPanel.java new file mode 100644 index 000000000000..b20868c0796f --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/FlagsEditorPanel.java @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.preferences; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Enumeration; +import java.util.StringTokenizer; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JColorChooser; +import javax.swing.JComponent; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +/** + * + * @author Bernhard Stiftner + * @author Rumpfhuber Stefan + */ +public class FlagsEditorPanel extends JPanel implements ActionListener, + ListSelectionListener { + + FlagListModel listModel; + JList list; + ColorChooserButton colorButton; + JButton newButton; + JButton removeButton; + JButton upButton; + JButton downButton; + + + /** Creates a new instance of FlagsEditorPanel */ + public FlagsEditorPanel(String flagString) { + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + + listModel = new FlagListModel(flagString); + list = new JList(listModel); + list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + list.addListSelectionListener(this); + add(new JScrollPane(list)); + + add(Box.createHorizontalStrut(3)); + + Box buttonBox = new Box(BoxLayout.Y_AXIS); + buttonBox.add(colorButton = new ColorChooserButton()); + buttonBox.add(newButton = new JButton("New...")); + buttonBox.add(removeButton = new JButton("Remove")); + buttonBox.add(upButton = new JButton("Up")); + buttonBox.add(downButton = new JButton("Down")); + buttonBox.add(Box.createVerticalGlue()); + add(buttonBox); + layoutButtonContainer(buttonBox); + + colorButton.setColorChooserEnabled(false); + colorButton.addActionListener(this); + newButton.addActionListener(this); + removeButton.addActionListener(this); + upButton.addActionListener(this); + downButton.addActionListener(this); + + selectionChanged(-1); // no selection + } + + /** + * Ugly helper to make a nice layout for vertically aligned buttons. + */ + private static void layoutButtonContainer(JComponent buttonContainer) { + int width = 0; + int height = 0; + + for (int i=0; i= listModel.size()-1) { + return; + } + Object o = listModel.getElementAt(index); + listModel.removeElementAt(index); + listModel.insertElementAt(o, index+1); + } + } + + public void valueChanged(ListSelectionEvent e) { + if (e.getValueIsAdjusting()) { + return; // another event will be fired soon + } + selectionChanged(list.getSelectedIndex()); + } + + protected void selectionChanged(int index) { //index is -1 if there is no selection + colorButton.setEnabled(index >= 0); + removeButton.setEnabled(index >= 0); + upButton.setEnabled(index > 0); + downButton.setEnabled(index >= 0 && index < listModel.getSize()-1); + + if (index >= 0) { + FlagListItem item = (FlagListItem)listModel.elementAt(index); + colorButton.setColor(item.getColor()); + list.setSelectedIndex(index); + } else { + colorButton.setColor(getBackground()); + } + } + + protected void changeColor() { + int selectedIndex = list.getSelectedIndex(); + FlagListItem item = (FlagListItem)listModel.elementAt(selectedIndex); + Color c = JColorChooser.showDialog(this, "Choose color", item.getColor()); + + if (c != null) { + item.setColor(c); + colorButton.setColor(c); + } + } + + class FlagListModel extends DefaultListModel { + + public FlagListModel(String flagString) { + if (flagString != null) { + setFlagString(flagString); + } + } + + public String getFlagString() { + StringBuffer sb = new StringBuffer(); + Enumeration e = elements(); + while (e.hasMoreElements()) { + FlagListItem item = (FlagListItem)e.nextElement(); + sb.append(item.getFlagString()); + Color c = item.getColor(); + sb.append("(").append(c.getRed()).append(",").append(c.getGreen()).append(",").append(c.getBlue()).append(")"); + if (e.hasMoreElements()) { + sb.append(";"); + } + } + return sb.toString(); + } + + public void setFlagString(String flagString) { + clear(); + StringTokenizer st = new StringTokenizer(flagString, ";"); + while (st.hasMoreTokens()) { + String s = st.nextToken(); + String flag = s.split("\\(")[0]; + Color color = FlagsSetting.toColor(s); + addElement(new FlagListItem(flag, color)); + } + } + + } + + class FlagListItem { + + Color color; + String flagString; + + public FlagListItem(String flagString, Color color) { + this.flagString = flagString; + this.color = color; + } + + public Color getColor() { + return color; + } + + public String getFlagString() { + return flagString; + } + + public void setColor(Color c) { + color = c; + } + + @Override + public String toString() { + return flagString; + } + } + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/FlagsSetting.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/FlagsSetting.java new file mode 100644 index 000000000000..e2f0cdf204fe --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/FlagsSetting.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.preferences; + +import java.awt.Color; +import java.io.Serializable; +import java.util.Hashtable; +import java.util.List; + +/** + * + * @author Thomas Wuerthinger + */ +public class FlagsSetting implements Serializable { + + private Hashtable flag2color; + private Hashtable priority; + private String flagString; + + + public FlagsSetting(String flagString) { + this.flagString = flagString; + flag2color = new Hashtable(); + priority = new Hashtable(); + String[] flags = flagString.split(";"); + + int z = 0; + for(String s : flags) { + String flag = s.split("\\(")[0]; + Color c = toColor(s); + flag2color.put(flag, c); + priority.put(flag, z); + z++; + } + } + + public Color getColor(List strings) { + int minPriority = Integer.MAX_VALUE; + Color result = null; + + for(String s : strings) { + Color curColor = flag2color.get(s); + if(curColor != null) { + int curPriority = priority.get(s); + if(curPriority < minPriority) { + minPriority = curPriority; + result = curColor; + } + } + } + + return result; + } + + public static Color toColor(String s) { + String sArr[] = s.split("\\("); + String Color = sArr[1].substring(0, sArr[1].length() - 1); + String ColorArr[] = Color.split(","); + int r = Integer.parseInt(ColorArr[0]); + int g = Integer.parseInt(ColorArr[1]); + int b = Integer.parseInt(ColorArr[2]); + return new Color(r, g, b); + } + + public String getFlagString() { + return flagString; + } + + @Override + public boolean equals(Object o) { + if(o==null) + return false; + return this.toString().equals(o.toString()); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 19 * hash + (this.flagString != null ? this.flagString.hashCode() : 0); + return hash; + } + + @Override + public String toString(){ + return "FlagSetting[" + flagString + "]"; + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/FontChooserDialog.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/FontChooserDialog.java new file mode 100644 index 000000000000..51c5f17c76bb --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/preferences/FontChooserDialog.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.preferences; + +import java.awt.Font; +import java.beans.PropertyEditor; +import java.beans.PropertyEditorManager; +import org.openide.DialogDescriptor; +import org.openide.DialogDisplayer; + +/** + * + * @author Bernhard Stiftner + */ +public class FontChooserDialog { + + /* + * Displays a font selection dialog. + * @return The selected font, or the initial font if the user chose + * to bail out + */ + public static Font show(Font initialFont) { + PropertyEditor pe = PropertyEditorManager.findEditor(Font.class); + if (pe == null) { + throw new RuntimeException("Could not find font editor component."); + } + pe.setValue(initialFont); + DialogDescriptor dd = new DialogDescriptor( + pe.getCustomEditor(), + "Choose Font"); + DialogDisplayer.getDefault().createDialog(dd).setVisible(true); + if (dd.getValue() == DialogDescriptor.OK_OPTION) { + Font f = (Font)pe.getValue(); + return f; + } + return initialFont; + } + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/visual/BezierWidget.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/visual/BezierWidget.java new file mode 100644 index 000000000000..1d7d4853cd8d --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/visual/BezierWidget.java @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.visual; + +import org.netbeans.api.visual.widget.ConnectionWidget; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.CubicCurve2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.util.List; +import org.netbeans.api.visual.anchor.AnchorShape; +import org.netbeans.api.visual.graph.GraphScene; +import org.netbeans.api.visual.widget.Scene; +import org.netbeans.api.visual.widget.Widget; + + +/** + * In comparison to the default ConnectionWidget this class is able to connect + * Widgets with a curve instead of a straight line sequence. Between two control + * points a curve is painted as cubic bezier curve the control points are + * calculated automaticaly they depend on the position of the prior- and the + * following control points. + * In conjunction with a suitable router the connection will be a straight line + * or a curve depending on the amount and the position of the controlpoints. + * Controlpoints supplied by the router, are treated as curve intersection points. + * For Reflexive edges the router doesn`t need to supply controlpoints, they + * get painted by this class automatically. If the router supplys more as 2 + * control points for a recursive edge the edge gets painted with the default + * curve approximation algorithm. + */ +public class BezierWidget extends ConnectionWidget { + private static final double BEZIER_SCALE = 0.3; + private static final double ENDPOINT_DEVIATION = 3;//curve endpoint approximation accuracy + + private GraphScene scene=null; + + public BezierWidget(Scene scene) { + super(scene); + } + + public BezierWidget(GraphScene scene) { + super(scene); + this.scene=scene; + } + + + private boolean isReflexive(){ + return getSourceAnchor().getRelatedWidget() == getTargetAnchor().getRelatedWidget(); + } + + + @Override + protected Rectangle calculateClientArea() { + Rectangle bounds = null; + if(this.getControlPoints().size()>0){ + for(Point p : this.getControlPoints()){ + if(bounds==null) + bounds = new Rectangle(p); + else + bounds.add(p); + } + bounds.grow(5,5); + } + if(isReflexive()){ + Widget related = this.getTargetAnchor().getRelatedWidget(); + bounds = related.convertLocalToScene(related.getBounds()); + bounds.grow(10, 10); + } + if(bounds==null) + bounds = super.calculateClientArea(); + + return bounds; + } + + + + //returns prefered location for an edge -1 for left and 1 for right + private int edgeBalance(Widget nodeWidget) { + if(scene == null) + return 1; + + Point nodeLocation = nodeWidget.getLocation(); + int left = 0, right = 0; + + Object node = scene.findObject(nodeWidget); + + for(Object e : scene.findNodeEdges(node, true, true)) {//inputedges + ConnectionWidget cw = (ConnectionWidget) scene.findWidget(e); + + if(cw != this) { + Widget targetNodeWidget = cw.getTargetAnchor().getRelatedWidget(); + + Point location; + if(targetNodeWidget == nodeWidget) { + Widget sourceNodeWidget = cw.getSourceAnchor().getRelatedWidget(); + location = sourceNodeWidget.getLocation(); + } else { + location = targetNodeWidget.getLocation(); + } + + if(location.x < nodeLocation.x) + left++; + else + right++; + } + } + if(left < right) + return -1; + else + return 1; + } + + + + + /** + * if the edge is reflexive its painted as a cyclic edge + * if there are 2 controlpoints the connection is painted as a straight line from the source to the targetanchor + * if there are more as 2 controlpoints the connection path between 2 control points is painted as bezier curve + */ + + @Override + protected void paintWidget () { + + List contrPoints = this.getControlPoints(); + int listSize = contrPoints.size(); + + Graphics2D gr = getGraphics (); + + if (listSize <= 2) { + if(isReflexive()) { //special case for reflexive connection widgets + Widget related = this.getTargetAnchor().getRelatedWidget(); + int position = this.edgeBalance(related); + Rectangle bounds = related.convertLocalToScene(related.getBounds()); + gr.setColor (getLineColor()); + Point first = new Point(); + Point last = new Point(); + double centerX = bounds.getCenterX(); + first.x = (int) (centerX + bounds.width / 4); + first.y = bounds.y + bounds.height; + last.x = first.x; + last.y = bounds.y; + + gr.setStroke(this.getStroke()); + + double cutDistance = this.getTargetAnchorShape().getCutDistance(); + double anchorAngle = Math.PI/-3.0; + double cutX = Math.abs(Math.cos(anchorAngle)*cutDistance); + double cutY = Math.abs(Math.sin(anchorAngle)*cutDistance); + int ydiff=first.y-last.y; + int endy = -ydiff; + double height=bounds.getHeight(); + double cy = height/4.0; + double cx=bounds.getWidth()/5.0; + double dcx = cx*2; + GeneralPath gp = new GeneralPath(); + gp.moveTo(0, 0); + gp.quadTo(0, cy, cx, cy); + gp.quadTo(dcx, cy, dcx, -height/2.0); + gp.quadTo(dcx, endy - cy, cy, -(cy+ydiff)); + gp.quadTo(cutX*1.5, endy - cy, cutX, endy-cutY); + + AffineTransform af = new AffineTransform(); + AnchorShape anchorShape = this.getTargetAnchorShape(); + + if(position < 0) { + first.x = (int) (centerX - bounds.width / 4); + af.translate(first.x, first.y); + af.scale(-1.0, 1.0); + last.x = first.x; + } else { + af.translate(first.x, first.y); + } + Shape s = gp.createTransformedShape(af); + gr.draw(s); + + if (last != null) { + AffineTransform previousTransform = gr.getTransform (); + gr.translate (last.x, last.y); + + if(position < 0) + gr.rotate(Math.PI - anchorAngle); + else + gr.rotate (anchorAngle); + + anchorShape.paint (gr, false); + gr.setTransform (previousTransform); + } + + } else { + super.paintWidget(); + } + return; + } + + //bezier curve... + GeneralPath curvePath = new GeneralPath(); + Point lastControlPoint = null; + double lastControlPointRotation = 0.0; + + Point prev = null; + for (int i = 0; i < listSize - 1; i++) { + Point cur = contrPoints.get(i); + Point next = contrPoints.get(i + 1); + Point nextnext = null; + if (i < listSize - 2) { + nextnext = contrPoints.get(i + 2); + } + + double len = cur.distance(next); + double scale = len * BEZIER_SCALE; + Point bezierFrom = null;//first ControlPoint + Point bezierTo = null;//second ControlPoint + + if (prev == null) { + //first point + curvePath.moveTo(cur.x, cur.y);//startpoint + bezierFrom = cur; + } else { + bezierFrom = new Point(next.x - prev.x, next.y - prev.y); + bezierFrom = scaleVector(bezierFrom, scale); + bezierFrom.translate(cur.x, cur.y); + } + + if (nextnext == null) {//next== last point (curve to) + lastControlPoint=next; + bezierTo = next;//set 2nd intermediate point to endpoint + GeneralPath lastseg = this.subdivide(cur, bezierFrom, bezierTo, next); + if(lastseg != null) + curvePath.append(lastseg, true); + break; + } else { + bezierTo = new Point(cur.x - nextnext.x, cur.y - nextnext.y); + bezierTo = scaleVector(bezierTo, scale); + bezierTo.translate(next.x, next.y); + } + + curvePath.curveTo( + bezierFrom.x, bezierFrom.y,//controlPoint1 + bezierTo.x, bezierTo.y,//controlPoint2 + next.x,next.y + ); + prev = cur; + } + Point2D cur = curvePath.getCurrentPoint(); + Point next = lastControlPoint; + + lastControlPointRotation = //anchor anchorAngle + Math.atan2 (cur.getY() - next.y, cur.getX() - next.x); + + Color previousColor = gr.getColor(); + gr.setColor (getLineColor()); + Stroke s = this.getStroke(); + gr.setStroke(s); + gr.setColor(this.getLineColor()); + gr.draw(curvePath); + + AffineTransform previousTransform = gr.getTransform (); + gr.translate (lastControlPoint.x, lastControlPoint.y); + gr.rotate (lastControlPointRotation); + AnchorShape targetAnchorShape = this.getTargetAnchorShape(); + targetAnchorShape.paint (gr, false); + gr.setTransform (previousTransform); + + //paint ControlPoints if enabled + if (isPaintControlPoints()) { + int last = listSize - 1; + for (int index = 0; index <= last; index ++) { + Point point = contrPoints.get (index); + previousTransform = gr.getTransform (); + gr.translate (point.x, point.y); + if (index == 0 || index == last) + getEndPointShape().paint (gr); + else + getControlPointShape().paint (gr); + gr.setTransform (previousTransform); + } + + } + gr.setColor(previousColor); + } + + + + private GeneralPath subdivide (Point b0, Point b1, Point b2, Point b3) { + double cutDistance = getTargetAnchorShape().getCutDistance(); + double minDistance = cutDistance - ENDPOINT_DEVIATION; + /** + * if the cutDistance is valid the last segment of the curve + * gets reduced by subdivision until the distance of the endpoint(epDistance) + * satisfys the condition (cutDistance > epDistance > (cutDistance - ENDPOINT-DEVIATION) + */ + if(cutDistance > minDistance && minDistance > 0 ) { + GeneralPath path = new GeneralPath(); + + path.moveTo(b0.x, b0.y); + + CubicCurve2D.Double left = new CubicCurve2D.Double( + b0.x, b0.y, + b1.x, b1.y, + b2.x, b2.y, + b3.x, b3.y); + + CubicCurve2D right=new CubicCurve2D.Double(); + left.subdivide(left, right); + double distance = b3.distance(left.getP2()); + //if the distance is bigger as the cutDistance the left segment is added + //and the right segment is divided again + while(distance>cutDistance){ + path.append(left, true); + right.subdivide(left, right); + distance = b3.distance(left.getP2()); + //if the devision removed to much the left segment is divided + while(distance < minDistance) { + //changes the distance to ~ (distance+distance/2) + left.subdivide(left, right); + distance = b3.distance(left.getP2()); + } + } + //append the last segment with (minDistance < distance < cutDistance) + //actually we should check if the a division happend, but this is very unlikly + path.append(left, true); + return path; + } + return null; + } + + + + + + private static Point scaleVector(Point vector, double len) { + double scale = Math.sqrt(vector.x * vector.x + vector.y * vector.y); + if(scale==0.0) return vector; + scale = len / scale; + return new Point( + Math.round(vector.x * (float)scale), + Math.round(vector.y * (float)scale)); + } + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/visual/PolylineRouter.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/visual/PolylineRouter.java new file mode 100644 index 000000000000..5a24494a5116 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/visual/PolylineRouter.java @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.visual; + +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.geom.Line2D; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.netbeans.api.visual.anchor.Anchor; +import org.netbeans.api.visual.router.Router; +import org.netbeans.api.visual.widget.ConnectionWidget; +import org.netbeans.api.visual.widget.Widget; + +/** + * Router Class with Collision Detection + * + * + * The returned path is a straight line there is no node widget between the source and the target node, + * if there are nodewidgets in between it tries to find a path around those abstacles to avoid collisions. + * The algorithm for the search of this path is limited by a fixed number of iterations defined in + * NUMER_OF_ITERATIONS to return a result in reasonable time. If the calculation exceeds this limit the + * path returned contains at least 1 collision with a nodewidget. + */ + +public class PolylineRouter implements Router { + private static final float FACTOR = 0.70f; + + private static final int HEXPAND = 3; + private static final int VEXPAND = 3; + private static final int NUMBER_OF_ITERATIONS = 8; + + WidgetCollisionCollector collector; + + + public PolylineRouter(WidgetCollisionCollector collector){ + this.collector=collector; + } + + + public List routeConnection(final ConnectionWidget widget) { + if(!widget.isVisible()) return Collections.emptyList(); + + Anchor sourceAnchor = widget.getSourceAnchor(); + Anchor targetAnchor = widget.getTargetAnchor(); + + if(sourceAnchor == null || targetAnchor == null) + return null; + + Point start = sourceAnchor.compute(widget.getSourceAnchorEntry()).getAnchorSceneLocation(); + Point end = targetAnchor.compute(widget.getTargetAnchorEntry()).getAnchorSceneLocation(); + + Widget sourceWidget = sourceAnchor.getRelatedWidget(); + Widget targetWidget = targetAnchor.getRelatedWidget(); + + + if(sourceWidget == targetWidget){//reflexive edges doesnt need any path + return Collections.emptyList(); + } + + List nodeWidgets = new ArrayList(); + + if(collector != null){ + collector.collectCollisions(nodeWidgets); + //source and target widget are not treatet as obstacle + nodeWidgets.remove(sourceWidget); + nodeWidgets.remove(targetWidget); + } + + List controlPoints = optimize(nodeWidgets, widget, start, end); + + //size==2 => straight line + //size==3 => ensures a collision between cp0 and cp2 therefore its not possible to simplify it + if(controlPoints.size() > 3) + controlPoints = simplify(nodeWidgets, controlPoints); + + return controlPoints; + } + + + private List simplify(Collection nodeWidgets, List list) { + List result = new ArrayList(); + result.add( list.get(0) );//add startpoint + for (int i = 1; i < list.size(); i++) { + Point prev = list.get(i - 1); + for (int j = i; j < list.size(); j++) { + Point cur = list.get(j); + if (!intersects(nodeWidgets, prev, cur)) { + i = j; + } + } + result.add(list.get(i)); + } + return result; + } + + /** + * Computates the Anchorposition like the Rectangular anchor for a + * given widget as source/target and a controlpoint as opposit anchorposition + */ + + private Point computateAnchorPosition(Widget relatedWidget, Point controlPoint) { + Rectangle bounds = relatedWidget.getBounds(); + Point relatedLocation = relatedWidget.getLocation();//center of the widget + + if (bounds.isEmpty () || relatedLocation.equals (controlPoint)) + return relatedLocation; + + float dx = controlPoint.x - relatedLocation.x; + float dy = controlPoint.y - relatedLocation.y; + + float ddx = Math.abs (dx) / (float) bounds.width; + float ddy = Math.abs (dy) / (float) bounds.height; + + float scale = 0.5f / Math.max (ddx, ddy); + + Point point = new Point (Math.round (relatedLocation.x + scale * dx), + Math.round (relatedLocation.y + scale * dy)); + return point; + } + + + + private List optimize(Collection nodeWidgets, ConnectionWidget connWidget, Point start, Point end) { + + List list = new ArrayList(); + list.add(start); + list.add(end); + + boolean progress = true; + + for (int j = 0; progress && j < NUMBER_OF_ITERATIONS ; j++) { + progress = false; + List newList = new ArrayList(); + for (int i = 0; i < list.size() - 1 ; i++) { + Point cur = list.get(i); + Point next = list.get(i + 1); + newList.add(cur); + List intermediate = optimizeLine(nodeWidgets, cur, next); + if (intermediate != null && intermediate.size() > 0) { + progress = true; + newList.addAll(intermediate);//insert new controlpoints between cur and next + } + } + newList.add(list.get(list.size()-1));//add endpoint of the polyline + list = newList; + + } + + if(list.size() > 2) { + Widget sourceNode = connWidget.getSourceAnchor().getRelatedWidget(); + Widget targetNode = connWidget.getTargetAnchor().getRelatedWidget(); + Rectangle sourceBounds = sourceNode.convertLocalToScene(sourceNode.getBounds()); + Rectangle targetBounds = targetNode.convertLocalToScene(targetNode.getBounds()); + sourceBounds.grow(HEXPAND, VEXPAND); + + /** + * add only points which are not intersecting the source and the target + * widget bounds caused by invalid bad anchor positions. The first + * and the last point is ignored cause the anchor is recalculated + * anyway. + */ + ArrayList tmp = new ArrayList(); + int listSize=list.size(); + tmp.add(list.get(0)); + int i=0; + while(++i < listSize-1) { + Point p = list.get(i); + if(!sourceBounds.contains(p) || !targetBounds.contains(p)) + tmp.add(p); + } + + tmp.add(list.get(i)); + if(tmp.size() < 3) + return tmp; + + list=tmp; + //calculate a proper anchor position using the second/penultimate controlpoint for start/end + start = this.computateAnchorPosition(connWidget.getSourceAnchor().getRelatedWidget(), list.get(1)); + end = this.computateAnchorPosition(connWidget.getTargetAnchor().getRelatedWidget(), list.get(list.size()-2)); + list.set(0,start); + list.set(list.size()-1, end); + } + return list; + } + + + /** + * trys to optimize a line from p1 to p2 to avoid collisions with node widgets + * returns null if the line doesn`t intersect with any nodewidget + * or a list with immediate points between p1 and p2 to avoid collisions + */ + private List optimizeLine(Collection nodeWidgets, Point p1, Point p2) { + Line2D line = new Line2D.Double(p1, p2); + + for(Widget w : nodeWidgets ) { + if( w.isVisible() ) { + Rectangle r = w.convertLocalToScene(w.getBounds()); + r.grow(HEXPAND, VEXPAND); + + if (!line.intersects(r)) continue; + + Point location = w.getLocation(); + int distx = (int) (r.width * FACTOR); + int disty = (int) (r.height * FACTOR); + + int minIntersects = Integer.MAX_VALUE; + int min = Integer.MAX_VALUE; + List minSol = null; + for (int i = -1; i <= 1; i++) { + for (int j = -1; j <= 1; j++) { + if (i != 0 || j != 0) { + Point cur = new Point(location.x + i * distx, location.y + j * disty); + List list1 = new ArrayList(); + list1.add(p1); + list1.add(cur); + list1.add(p2); + int crossProd = Math.abs(crossProduct(p1, cur, p2)); + if (!intersects(w, list1)) { + Line2D line1 = new Line2D.Float(p1, cur); + Line2D line2 = new Line2D.Float(p2, cur); + int curIntersects = this.countNodeIntersections(nodeWidgets, line1, line2); + + if (curIntersects < minIntersects + || (curIntersects == minIntersects && crossProd < min)) { + minIntersects = curIntersects; + min = crossProd; + minSol = new ArrayList(); + minSol.add(cur); + } + } + + if (i == 0 || j == 0) { + Point cur1, cur2; + if (i == 0) { + cur1 = new Point(location.x + distx/2, location.y + j * disty); + cur2 = new Point(location.x - distx/2, location.y + j * disty); + } else { // (j == 0) + cur1 = new Point(location.x + i * distx, location.y + disty/2); + cur2 = new Point(location.x + i * distx, location.y - disty/2); + } + + Point vec1 = new Point(p1.x - cur1.x, p1.y - cur1.y); + int offset1 = vec1.x * vec1.x + vec1.y * vec1.y; + + Point vec2 = new Point(p1.x - cur2.x, p1.y - cur2.y); + int offset2 = vec2.x * vec2.x + vec2.y * vec2.y; + + if (offset2 < offset1) { + Point tmp = cur1; + cur1 = cur2; + cur2 = tmp; + } + + List list2 = new ArrayList(); + list2.add(p1); + list2.add(cur1); + list2.add(cur2); + list2.add(p2); + + int cross1 = crossProduct(p1, cur1, cur2); + int cross2 = crossProduct(cur1, cur2, p2); + + if (cross1 > 0) { + cross1 = 1; + } else if (cross1 < 0) { + cross1 = -1; + } + + if (cross2 > 0) { + cross2 = 1; + } else if (cross2 < 0) { + cross2 = -1; + } + if ((cross1 == cross2 || cross1 == 0 || cross2 == 0) && !intersects(w, list2)) { + Line2D line1 = new Line2D.Float(p1, cur1); + Line2D line2 = new Line2D.Float(cur1, cur2); + Line2D line3 = new Line2D.Float(p2, cur2); + int curIntersects = this.countNodeIntersections(nodeWidgets, line1, line2, line3); + + // This is a bit better + crossProd--; + + if (curIntersects < minIntersects + || (curIntersects == minIntersects && crossProd < min)) { + minIntersects = curIntersects; + min = crossProd; + minSol = new ArrayList(); + minSol.add(cur1); + minSol.add(cur2); + } + } + } + } + } + } + if (minSol != null) { + return minSol; + } + } + } + return null; + } + + + + private int countNodeIntersections(Collection nodeWidgets, Line2D... lines){ + int count=0; + for(Widget nw : nodeWidgets){ + if(nw.isVisible()) { + Rectangle bounds = nw.convertLocalToScene(nw.getBounds()); + for( Line2D line : lines){ + if(line.intersects(bounds)) + count++; + } + } + } + return count; + } + + + private boolean intersects(Collection nodeWidgets, Point start, Point end) { + List pointlist = new ArrayList(); + pointlist.add(start); + pointlist.add(end); + + for(Widget w : nodeWidgets){ + if(w.isVisible() && intersects(w, pointlist)) { + return true; + } + } + return false; + } + + + private boolean intersects(Widget w, List list) { + Rectangle r = w.convertLocalToScene(w.getBounds()); + r.grow(HEXPAND, VEXPAND); + return intersects(list, r); + } + + + private boolean intersects(List list, Rectangle rect){ + for(int i=1; i < list.size(); i++) { + Point cur = list.get(i-1); + Point next = list.get(i); + if(rect.intersectsLine(cur.x, cur.y, next.x, next.y)) + return true; + } + return false; + } + + + private int crossProduct(Point p1, Point p2, Point p3) { + Point off1 = new Point(p1.x - p2.x, p1.y - p2.y); + Point off2 = new Point(p3.x - p2.x, p3.y - p2.y); + return (off1.x * off2.y - off1.y * off2.x); + } + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/visual/PolylineRouterV2.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/visual/PolylineRouterV2.java new file mode 100644 index 000000000000..ce3a509b94c7 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/visual/PolylineRouterV2.java @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.visual; + +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.geom.Line2D; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.netbeans.api.visual.anchor.Anchor; +import org.netbeans.api.visual.router.Router; +import org.netbeans.api.visual.widget.ConnectionWidget; +import org.netbeans.api.visual.widget.Widget; + +/** + * Router Class with Collision Detection + * + * + * The returned path is a straight line there is no node widget between the source and the target node, + * if there are nodewidgets in between it tries to find a path around those abstacles to avoid collisions. + * The algorithm for the search of this path is limited by a fixed number of iterations defined in + * NUMER_OF_ITERATIONS to return a result in reasonable time. If the calculation exceeds this limit the + * path returned contains at least 1 collision with a nodewidget. + * + * This class intend to solve the same problem as the class PolyLineRouter but + * uses slightly different methods. + * + uses another algorithm for solving the collision. + * + the obstacle bounds are calulated in advance to avoid a recalculation. + * - there is no heuristic, the shortest path around the widget is choosen asap. + * + * + * + */ + +public class PolylineRouterV2 implements Router { + private static final int HEXPAND = 3; + private static final int VEXPAND = 3; + private static final int NUMBER_OF_ITERATIONS = 8; + + WidgetCollisionCollector collector; + + + public PolylineRouterV2(WidgetCollisionCollector collector){ + this.collector=collector; + } + + + public List routeConnection(final ConnectionWidget widget) { + if(!widget.isVisible()) return Collections.emptyList(); + + Anchor sourceAnchor = widget.getSourceAnchor(); + Anchor targetAnchor = widget.getTargetAnchor(); + + if(sourceAnchor == null || targetAnchor == null) + return null; + + Point start = sourceAnchor.compute(widget.getSourceAnchorEntry()).getAnchorSceneLocation(); + Point end = targetAnchor.compute(widget.getTargetAnchorEntry()).getAnchorSceneLocation(); + + Widget sourceWidget = sourceAnchor.getRelatedWidget(); + Widget targetWidget = targetAnchor.getRelatedWidget(); + + + if(sourceWidget == targetWidget){//reflexive edges doesnt need any path + return Collections.emptyList(); + } + + + Point srcCenter = this.getSceneLocation(sourceWidget); + Point tarCenter = this.getSceneLocation(targetWidget); + + List widgetObstacles = new ArrayList(); + + if(collector != null){ + collector.collectCollisions(widgetObstacles); + } + + List obstacles = new ArrayList(widgetObstacles.size()); + this.collectObstacles(obstacles, widgetObstacles, widget); + + + List controlPoints = optimize(obstacles, srcCenter, tarCenter); +// size==2 => straight line +// size==3 => ensures a collision between cp0 and cp2 therefore its not possible to simplify it + if(controlPoints.size() > 3){ + Point rstart = this.computateAnchorPosition(sourceWidget, controlPoints.get(1)); + Point rend = this.computateAnchorPosition(targetWidget, controlPoints.get(controlPoints.size()-2)); + controlPoints.set(0, rstart); + controlPoints.set(controlPoints.size()-1, rend); + controlPoints = simplify(obstacles, controlPoints); + } else if (controlPoints.size()>=2){ + //use old points + controlPoints.set(0, start); + controlPoints.set(controlPoints.size()-1, end); + + } + return controlPoints; + } + + + private int collectObstacles(List colrects, List colwidgets , ConnectionWidget cw){ + int count=0; + Anchor sourceAnchor = cw.getSourceAnchor(); + Anchor targetAnchor = cw.getTargetAnchor(); + Widget sourceWidget = sourceAnchor.getRelatedWidget(); + Widget targetWidget = targetAnchor.getRelatedWidget(); + Point start = sourceAnchor.compute(cw.getSourceAnchorEntry()).getAnchorSceneLocation(); + Point end = targetAnchor.compute(cw.getTargetAnchorEntry()).getAnchorSceneLocation(); + + for(Widget w : colwidgets){ + + if(w==sourceWidget || w == targetWidget) continue; + + Rectangle r = w.convertLocalToScene(w.getBounds()); + r.grow(HEXPAND, VEXPAND); + if(r.intersectsLine(start.x,start.y,end.x,end.y)) + count++; + colrects.add(r); + } + return count; + } + + + private Point center (Rectangle bounds) { + return new Point (bounds.x + bounds.width / 2, bounds.y + bounds.height / 2); + } + + /** + * Returns the scene location of a related widget. + * bounds might be null if the widget was not added to the scene + * @return the scene location; null if no related widget is assigned + */ + public Point getSceneLocation (Widget relatedWidget) { + if (relatedWidget != null) { + Rectangle bounds = relatedWidget.getBounds (); + if(bounds != null) + return center(relatedWidget.convertLocalToScene(bounds)); + } + return null; + } + + /** + * Computates the Anchorposition like the Rectangular anchor for a + * given widget as source/target and a controlpoint as opposit anchorposition + */ + + private Point computateAnchorPosition(Widget relatedWidget, Point controlPoint) { + Rectangle bounds = relatedWidget.getBounds(); + + //todo: fix, center of widget must be cacluated trough the bounds + //since there are some wheird widgets where the location is not the center of the widget + Point relatedLocation = relatedWidget.getLocation();//center of the widget + + if (bounds.isEmpty () || relatedLocation.equals (controlPoint)) + return relatedLocation; + + float dx = controlPoint.x - relatedLocation.x; + float dy = controlPoint.y - relatedLocation.y; + + float ddx = Math.abs (dx) / (float) bounds.width; + float ddy = Math.abs (dy) / (float) bounds.height; + + float scale = 0.5f / Math.max (ddx, ddy); + + Point point = new Point (Math.round (relatedLocation.x + scale * dx), + Math.round (relatedLocation.y + scale * dy)); + return point; + } + + private List simplify(List obstacles, List list) { + List result = new ArrayList(list.size()); + result.add( list.get(0) );//add startpoint + for (int i = 1; i < list.size(); i++) { + Point prev = list.get(i - 1); + for (int j = i; j < list.size(); j++) { + Point cur = list.get(j); + if (!intersects(obstacles, prev, cur)) { + i = j; + } + } + result.add(list.get(i)); + } + return result; + } + + private List optimize(List nodeWidgets, Point start, Point end) { + + List list = new ArrayList(); + list.add(start); + list.add(end); + + boolean progress = true; + + for (int j = 0; progress && j < NUMBER_OF_ITERATIONS ; j++) { + progress = false; + List newList = new ArrayList(); + for (int i = 0; i < list.size() - 1 ; i++) { + Point cur = list.get(i); + Point next = list.get(i + 1); + newList.add(cur); + List intermediate = optimizeLine(nodeWidgets, cur, next); + if (intermediate != null && intermediate.size() > 0) { + progress = true; + newList.addAll(intermediate);//insert new controlpoints between cur and next + } + } + newList.add(list.get(list.size()-1));//add endpoint of the polyline + list = newList; + + } + + return list; + } + + + /** + * trys to optimize a line from p1 to p2 to avoid collisions with rectangles + * returns null if the line doesn`t intersect with any nodewidget or + * if the obstacles are overlapping + * ---------------------------------------------------------------------- + * if the collision is solved it returns a list with immediate points + * between p1 and p2. The points are taken from hull points of and grown + * rectangle. + */ + private List optimizeLine(List obstacles, Point p1, Point p2) { + Line2D line = new Line2D.Double(p1, p2); + boolean inbounds=false; + Rectangle ibr=null; + ArrayList sol = new ArrayList(); + boolean leftIntersection; + boolean rightIntersection; + boolean bottomIntersection; + boolean topIntersection; + Point interLeft=new Point(); + Point interRight=new Point(); + Point interBot=new Point(); + Point interTop=new Point(); + + + + for(Rectangle r : obstacles ) { + if (!line.intersects(r)) continue; + + int w=r.width+2; + int h=r.height+2; + Point topLeft = r.getLocation(); + topLeft.x-=1; + topLeft.y-=1; + Point topRight = new Point(topLeft.x+w, topLeft.y); + Point bottomLeft = new Point(topLeft.x, topLeft.y+h); + Point bottomRight = new Point(topRight.x, bottomLeft.y); + leftIntersection = findIntersectionPoint(p1, p2, topLeft, bottomLeft, interLeft); + rightIntersection = findIntersectionPoint(p1, p2, topRight, bottomRight, interRight); + bottomIntersection = findIntersectionPoint(p1, p2, bottomLeft, bottomRight, interBot); + topIntersection = findIntersectionPoint(p1, p2, topLeft, topRight, interTop); + + //Intersection points are not used yet. This could be actually a + //good approach to avoid additional collisions because it would be + //still the same vector. + + if(leftIntersection) { + if(topIntersection) {//left and top + sol.add(topLeft); + } + else if(bottomIntersection){//left and bottom + sol.add(bottomLeft); + } + else if(rightIntersection){//left and right + double disttl = topLeft.distance(p1); + double distbl = bottomLeft.distance(p1); + if(disttl > distbl){ + //pass at the bottom + double distbr = bottomRight.distance(p1); + if(distbl < distbr){ + //from the left to the right + sol.add(bottomLeft); + sol.add(bottomRight); + } else { + //from the right to the left + sol.add(bottomRight); + sol.add(bottomLeft); + } + } else { + //pass at the top + double disttr = topRight.distance(p1); + if(disttl < disttr){ + //from the left to the right + sol.add(topLeft); + sol.add(topRight); + } else { + //from the right to the left + sol.add(topRight); + sol.add(topLeft); + } + } + } else {//only left => inside bounds + inbounds=true; + } + } else if (rightIntersection) { + if(topIntersection) {//right and top + sol.add(topRight); + } + else if(bottomIntersection){//right and bottom + sol.add(bottomRight); + } else { //only right => inside the bounds + inbounds=true; + } + } else if (topIntersection && bottomIntersection) {//top and bottom + double disttop = interTop.distance(p1); + double distbot = interBot.distance(p1); + if(disttop < distbot ){ + //from the top to the bottom + double distleft = interTop.distance(topLeft); + double distright = interTop.distance(topRight); + if(distleft < distright){ + //pass left + sol.add(topLeft); + sol.add(bottomLeft); + } else { + //pass right + sol.add(topRight); + sol.add(bottomRight); + } + } else { + //from the bottom to the top + double distleft = interBot.distance(bottomLeft); + double distright = interBot.distance(bottomRight); + if(distleft < distright){ + //pass left + sol.add(bottomLeft); + sol.add(topLeft); + } else { + //pass right + sol.add(bottomRight); + sol.add(topRight); + } + } + } else {//only bottom or only top + inbounds=true; + } /* ENDIF */ + + //breakpoint <-- collision detected + + if(sol.size()>0) {//solution found + assert(!inbounds); + return sol; + } else { //no solution found=> inbounds + assert(inbounds); + assert(sol.size()==0); + //handle collision or just skip it and search for the next collisionj + ibr=r; + //jump out of the loop to able to interate over the obstacles + break; + } + }/* end foreach obstacle */ + + if(inbounds || ibr != null){ + assert(inbounds); + assert(ibr!=null); + } + return null;//no collison found + }/* end optimizeLine */ + + + + //check intersection between line p0->p1 for a given set of obstacles + private static boolean intersects(List obstacles, Point p0, Point p1) { + for(Rectangle r : obstacles){ + if(r.intersectsLine(p0.x, p0.y, p1.x, p1.y)) + return true; + } + return false; + } + + + private boolean findIntersectionPoint( + Point p0, Point p1, Point p2, Point p3, Point pI) { + float q = (p0.y - p2.y)*(p3.x - p2.x) - (p0.x - p2.x)*(p3.y - p2.y); + float d = (p1.x - p0.x)*(p3.y - p2.y) - (p1.y - p0.y)*(p3.x - p2.x); + + //parallel ? + if(d==0) return false; + + float r = q / d; + q = (p0.y - p2.y)*(p1.x - p0.x) - (p0.x - p2.x)*(p1.y - p0.y); + + float s = q / d; + if(r<0 || r>1 || s<0 || s>1) return false; + + pI.x = p0.x + (int) (0.5f + r * (p1.x - p0.x)); + pI.y = p0.y + (int) (0.5f + r * (p1.y - p0.y)); + return true; + } +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/visual/SplineConnectionWidget.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/visual/SplineConnectionWidget.java new file mode 100644 index 000000000000..e3552cb10b1a --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/visual/SplineConnectionWidget.java @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.visual; + +import org.netbeans.api.visual.widget.ConnectionWidget; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.CubicCurve2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.List; +import org.netbeans.api.visual.anchor.AnchorShape; +import org.netbeans.api.visual.graph.GraphScene; +import org.netbeans.api.visual.widget.Scene; +import org.netbeans.api.visual.widget.Widget; + + +/** + * In comparison to the default ConnectionWidget this class is able to connect + * widgets with a curve instead of a straight line sequence. + * In conjunction with a suitable router the connection will be a straight line + * or a curve depending on the amount and the position of the controlpoints. + * Controlpoints supplied by the router, are treated as curve intersection points. + * For Reflexive edges the router doesn`t necessarily need to supply + * any controlpoints, they get painted by this automatically, this can be + * excepted if the router supplys more as 2 control points for a self edge, + * then the edge gets painted with the default curve interpolation algorithm. + * The method used for drawing curves uses a piecewise cubic interpolation + * algorithm. Between two control points a curve is painted as cubic bezier + * curve the, inner bezier points are calculated automatically with FMILL + * tangents and a chord parametrization, this interpolant is also known as + * cutmull-rom spline. The resulting spline fullfills c^1 continuity. + * The the end points the interpolation algorithm uses the bessel end condition. + */ + +public class SplineConnectionWidget extends ConnectionWidget { + private static final double ENDPOINT_DEVIATION = 3;//curve endpoint approximation accuracy + private static final double HIT_DISTANCE_SQUARE = 4.0;//distance for intersection test + private GraphScene scene=null; + private Point2D [] bezierPoints = null; + + public SplineConnectionWidget(Scene scene) { + super(scene); + if(scene instanceof GraphScene) + this.scene=(GraphScene) scene; + } + + //check for self - edge + private boolean isReflexive(){ + return getSourceAnchor().getRelatedWidget() == getTargetAnchor().getRelatedWidget(); + } + + + @Override + protected Rectangle calculateClientArea() { + + Rectangle bounds = null; + + if(this.getControlPoints().size()>2){ + bezierPoints = createBezierPoints(getControlPoints()); + } + //minmax method - returns the smallest bounding rectangle + //Curves and surfaces for CAGD (3rd ed.), p.54 + //exact calculation of the bounding min rect + if(bezierPoints != null) { + Rectangle2D bounds2D = null; + for (int i = 0; i < bezierPoints.length; i++) { + Point2D point = bezierPoints[i]; + if(bounds2D==null) + bounds2D = new Rectangle2D.Double(point.getX(),point.getY(),0,0); + else + bounds2D.add(point); + } + bounds = bounds2D.getBounds(); + bounds.grow(2, 2); + + } else if (getControlPoints().size()>0){ + for(Point p : this.getControlPoints()){ + if(bounds==null) + bounds = new Rectangle(p); + else + bounds.add(p); + } + bounds.grow(5,5); + + } else if (isReflexive()) { + Widget related = this.getTargetAnchor().getRelatedWidget(); + bounds = related.convertLocalToScene(related.getBounds()); + bounds.grow(10, 10); + } + + if(bounds==null) + bounds = super.calculateClientArea(); + + return bounds; + } + + + /** + * if the edge is reflexive its painted as a cyclic self edge, if there + * are two controlpoints the connection is painted as a straight line from + * the source to the targetanchor, if there are more as 2 controlpoints the + * connection path between two control points is painted as cutmull rom + * spline with bessel end tangents. + */ + @Override + protected void paintWidget () { + List contrPoints = this.getControlPoints(); + int listSize = contrPoints.size(); + + Graphics2D gr = getGraphics (); + + if (listSize <= 2) { + this.bezierPoints=null;//set bezier Points null for calulateClientArea() + if(isReflexive()) { //special case for reflexive connection widgets + this.drawReflexive(gr); + } else { + super.paintWidget(); + } + return; + } + + //bezier curve... listSize > 2 + GeneralPath curvePath = new GeneralPath(); + double lastControlPointRotation = 0.0; + + + Point2D [] bezPoints = this.createBezierPoints(contrPoints); + curvePath.moveTo(bezPoints[0].getX(), bezPoints[0].getY());//b00 + + //last segment is added by subdivision thats why its -5 + for (int i = 1; i < bezPoints.length-5; i+=3) { + curvePath.curveTo( + bezPoints[i].getX(), bezPoints[i].getY(),//b1i + bezPoints[i+1].getX(), bezPoints[i+1].getY(),//b2i + bezPoints[i+2].getX(), bezPoints[i+2].getY());//b3i + + } + + GeneralPath lastseg = subdivide2D( + bezPoints[bezPoints.length-4], + bezPoints[bezPoints.length-3], + bezPoints[bezPoints.length-2], + bezPoints[bezPoints.length-1]); + + + if(lastseg != null) + curvePath.append(lastseg, true); + + Point2D cur = curvePath.getCurrentPoint(); + Point lastControlPoint = contrPoints.get(listSize-1); + + lastControlPointRotation = //anchor anchorAngle + Math.atan2 (cur.getY() - lastControlPoint.y, cur.getX() - lastControlPoint.x); + + gr.setStroke(getStroke()); + gr.setColor(getLineColor()); + gr.draw(curvePath); + + + AffineTransform previousTransform = gr.getTransform (); + gr.translate (lastControlPoint.x, lastControlPoint.y); + gr.rotate (lastControlPointRotation); + AnchorShape targetAnchorShape = this.getTargetAnchorShape(); + targetAnchorShape.paint (gr, false); + gr.setTransform (previousTransform); + + //paint ControlPoints if enabled + if (isPaintControlPoints()) { + int last = listSize - 1; + for (int index = 0; index <= last; index ++) { + Point point = contrPoints.get (index); + previousTransform = gr.getTransform (); + gr.translate (point.x, point.y); + if (index == 0 || index == last) + getEndPointShape().paint (gr); + else + getControlPointShape().paint (gr); + gr.setTransform (previousTransform); + } + + } + } + + private void drawReflexive(Graphics2D gr){ + Widget related = this.getTargetAnchor().getRelatedWidget(); + int position = this.edgeBalance(related); + Rectangle bounds = related.convertLocalToScene(related.getBounds()); + gr.setColor (getLineColor()); + Point first = new Point(); + Point last = new Point(); + double centerX = bounds.getCenterX(); + first.x = (int) (centerX + bounds.width / 4); + first.y = bounds.y + bounds.height; + last.x = first.x; + last.y = bounds.y; + + gr.setStroke(this.getStroke()); + + double cutDistance = this.getTargetAnchorShape().getCutDistance(); + double anchorAngle = Math.PI/-3.0; + double cutX = Math.abs(Math.cos(anchorAngle)*cutDistance); + double cutY = Math.abs(Math.sin(anchorAngle)*cutDistance); + int ydiff=first.y-last.y; + int endy = -ydiff; + double height=bounds.getHeight(); + double cy = height/4.0; + double cx=bounds.getWidth()/5.0; + double dcx = cx*2; + GeneralPath gp = new GeneralPath(); + gp.moveTo(0, 0); + gp.quadTo(0, cy, cx, cy); + gp.quadTo(dcx, cy, dcx, -height/2.0); + gp.quadTo(dcx, endy - cy, cy, -(cy+ydiff)); + gp.quadTo(cutX*1.5, endy - cy, cutX, endy-cutY); + + AffineTransform af = new AffineTransform(); + AnchorShape anchorShape = this.getTargetAnchorShape(); + + if(position < 0) { + first.x = (int) (centerX - bounds.width / 4); + af.translate(first.x, first.y); + af.scale(-1.0, 1.0); + last.x = first.x; + } else { + af.translate(first.x, first.y); + } + Shape s = gp.createTransformedShape(af); + gr.draw(s); + + if (last != null) { + AffineTransform previousTransform = gr.getTransform (); + gr.translate (last.x, last.y); + + if(position < 0) + gr.rotate(Math.PI - anchorAngle); + else + gr.rotate (anchorAngle); + + anchorShape.paint (gr, false); + gr.setTransform (previousTransform); + } + } + + //returns prefered location for an edge -1 for left and 1 for right + private int edgeBalance(Widget nodeWidget) { + if(scene == null) + return 1; + + Point nodeLocation = nodeWidget.getLocation(); + int left = 0, right = 0; + + Object node = scene.findObject(nodeWidget); + + for(Object e : scene.findNodeEdges(node, true, true)) {//inputedges + ConnectionWidget cw = (ConnectionWidget) scene.findWidget(e); + + if(cw != this) { + Widget targetNodeWidget = cw.getTargetAnchor().getRelatedWidget(); + + Point location; + if(targetNodeWidget == nodeWidget) { + Widget sourceNodeWidget = cw.getSourceAnchor().getRelatedWidget(); + location = sourceNodeWidget.getLocation(); + } else { + location = targetNodeWidget.getLocation(); + } + + if(location.x < nodeLocation.x) + left++; + else + right++; + } + } + if(left < right) + return -1; + else + return 1; + } + + + private Point2D[] createBezierPoints(List list){ + if(list.size()<3) return null ; + + + int lastIdx = list.size()-1; + + + //chord length parametrization + double[] uis = new double[list.size()]; + uis[0]=0; + uis[1] = list.get(1).distance(list.get(0)); + for (int i = 1; i < uis.length; i++) { + Point cur = list.get(i); + Point prev = list.get(i-1); + uis[i]=uis[i-1]+ cur.distance(prev); + } + + + for (int i = 1; i < uis.length; i++) { + uis[i] /= uis[lastIdx]; + + } + double[] delta = new double[uis.length-1]; + for (int i = 0; i < delta.length; i++) { + double ui = uis[i]; + double uin = uis[i+1]; + delta[i] = uin-ui; + } + + + //FMILL tangent directions (chord length) + Point2D[] tangents = new Point2D[list.size()]; + + for (int i = 1; i < list.size()-1; i++) { + Point xBefore = list.get(i-1); + Point xAfter = list.get(i+1); + Point2D.Double tangent = new Point2D.Double (xAfter.x - xBefore.x, xAfter.y - xBefore.y); + tangents[i] = tangent; + } + + + Point2D [] bezPoints = new Point2D[(list.size()-1)*2+list.size()]; + //Catmull-Rom + for (int i = 1; i < list.size()-1; i++) { + Point b3i = list.get(i); + Point2D b3ib = b3iBefore(b3i, delta[i-1], delta[i], tangents[i]); + Point2D b3ia = b3iAfter(b3i, delta[i-1], delta[i], tangents[i]); + bezPoints[3*i] = b3i; + bezPoints[3*i-1] = b3ib; + bezPoints[3*i+1] = b3ia; + } + bezPoints[0] = list.get(0); + bezPoints[bezPoints.length-1] = list.get(list.size()-1); + + Point p0 = list.get(0); + Point p1 = list.get(1); + Point p2 = list.get(2); + Point pL_2 = list.get(lastIdx-2); + Point pL_1 = list.get(lastIdx-1); + Point pL = list.get(lastIdx); + + Point2D m1 = besselTangent(delta[0], delta[1], p0, p1, p2); + Point2D m0 = besselEndTangent(p0, p1, delta[0], m1); + + Point2D mLb = besselTangent(delta[delta.length-2], delta[delta.length-1], + pL_2,pL_1, pL); + Point2D mL = besselEndTangent(pL_1, pL, delta[delta.length-1], mLb); + + Point2D scaleM0 = scale(normalize(m0), p0.distance(p1));//increase distx/distxl to make curve rounder at the end + Point2D scaleML = scale(normalize(mL), pL.distance(pL_1)); + //Catmull-Rom for bessel points + Point2D b30a = b3iAfter(p0, delta[0], delta[0],scaleM0); + Point2D b33b = b3iBefore(pL, delta[delta.length-1], delta[delta.length-1],scaleML); + + bezPoints[1] = b30a; + bezPoints[bezPoints.length-2] = b33b; + + return bezPoints; + } + + + + private static Point2D besselTangent(double delta_ib, double delta_i, Point2D p0, Point2D p1 , Point2D p2){ + double alpha_i = delta_ib/(delta_ib+delta_i); + + double x = (1-alpha_i)/delta_ib * (p1.getX() - p0.getX()) + + alpha_i/delta_i * (p2.getX()-p1.getX()); + double y = (1-alpha_i)/delta_ib * (p1.getY() - p0.getY()) + + alpha_i/delta_i * (p2.getY()-p1.getY()); + + return new Point2D.Double(x,y); + } + + private static Point2D besselEndTangent(Point2D p0, Point2D p1, double delta_u, Point2D m){ + double x = 2*((p1.getX()-p0.getX())/delta_u) - m.getX(); + double y = 2*((p1.getY()-p0.getY())/delta_u) - m.getY(); + return new Point2D.Double(x,y); + } + + private static Point2D b3iBefore(Point2D b3i, double delta_ib, double delta_i, Point2D li){ + double x = b3i.getX() - (delta_ib/(3*(delta_ib+delta_i)))*li.getX(); + double y = b3i.getY() - (delta_ib/(3*(delta_ib+delta_i)))*li.getY(); + return new Point.Double(x,y); + } + + private static Point2D b3iAfter(Point2D b3i, double delta_ib,double delta_i,Point2D li){ + double x = b3i.getX() + (delta_i/(3*(delta_ib+delta_i)))*li.getX(); + double y = b3i.getY() + (delta_i/(3*(delta_ib+delta_i)))*li.getY(); + return new Point.Double(x,y); + } + + + + + //returns length of vector v + private static double norm(Point2D v){ + return Math.sqrt(v.getX()*v.getX()+v.getY()*v.getY()); + } + + //returns unity vector of vector v + private static Point2D normalize(Point2D v){ + double norm = norm(v); + if(norm==0) return new Point2D.Double(v.getX(), v.getY()); + return new Point2D.Double(v.getX()/norm , v.getY()/norm); + } + + //scale vector to size of length + private static Point2D scale(Point2D v, double length){ + Point2D tmp = normalize(v); + return new Point2D.Double(tmp.getX()*length, tmp.getY()*length); + } + + + + private GeneralPath subdivide2D (Point2D b0, Point2D b1, Point2D b2, Point2D b3) { + //set 2nd intermediate point to endpoint + //we could actually use another "better" point if we like to have a smoother curve + + double cutDistance = getTargetAnchorShape().getCutDistance(); + double minDistance = cutDistance - ENDPOINT_DEVIATION; + /** + * if the cutDistance is valid the last segment of the curve + * gets reduced by subdivision until the distance of the endpoint(epDistance) + * satisfys the condition (cutDistance > epDistance > (cutDistance - ENDPOINT-DEVIATION) + */ + if(cutDistance > minDistance && minDistance > 0 ) { + GeneralPath path = new GeneralPath(); + + path.moveTo(b0.getX(), b0.getY()); + + CubicCurve2D.Double curve = new CubicCurve2D.Double( + b0.getX(), b0.getY(), + b1.getX(), b1.getY(), + b2.getX(), b2.getY(), + b3.getX(), b3.getY()); + + + + CubicCurve2D right=new CubicCurve2D.Double(); + CubicCurve2D left=new CubicCurve2D.Double(); + curve.subdivide(left, right); + double distance = b3.distance(left.getP2()); + //if the distance is bigger as the cutDistance the left segment is added + //and the right segment is divided again + while(distance>cutDistance){ + path.append(left, true); + right.subdivide(left, right); + distance = b3.distance(left.getP2()); + //if the devision removed to much the left segment is divided + while(distance < minDistance) { + //changes the distance to ~ (distance+distance/2) + left.subdivide(left, right); + distance = b3.distance(left.getP2()); + } + } + //append the last segment with (minDistance < distance < cutDistance) + path.append(left, true); + return path; + } + return null; + } + + + /** + * Returns whether a specified local point pL is a part of the connection + * widget. + * First it make a rough bounds check + * for Line Segments => use Super call (ConnectionWidget.isHitAt(pL)). + * for self-edges => its sufficent to return getBounds.contains(pL). + * for Splines => Interate over all Partitial segments of the curve and make + * a minmax check with the bezier points. If pL is inside the minmax + * rectangle of one segment the curve is constructed and subdivided until + * the distance d between center point pC (of the bounding rectangle) + * and pL is below HIT_DISTANCE_SQUARE, in this case it returns true. + * If no no minmax check was successful or the subdivision lead to an + * rectangle witch doesn`t contain pL return false. + * @param localLocation the local location + * @return true, if the location is a part of the connection widget + */ + @Override + public boolean isHitAt(Point localLocation) { + if(!isVisible() || !getBounds ().contains (localLocation)) + return false; + + List controlPoints = getControlPoints (); + if(controlPoints.size() <=2){ + if(isReflexive()) return true; + return super.isHitAt(localLocation); + } + + if(bezierPoints != null) { + for (int i = 0; i < bezierPoints.length-1; i+=3) { + Point2D b0 = bezierPoints[i]; + Point2D b1 = bezierPoints[i+1]; + Point2D b2 = bezierPoints[i+2]; + Point2D b3 = bezierPoints[i+3]; + + CubicCurve2D left = new CubicCurve2D.Double( + b0.getX(), b0.getY(), + b1.getX(), b1.getY(), + b2.getX(), b2.getY(), + b3.getX(), b3.getY()); + + + Rectangle2D bounds = left.getBounds2D(); + while(bounds.contains(localLocation)) { + //calculate the center and use HIT_DISTANCE_SQUARE for a range check + Point2D test = new Point2D.Double(bounds.getCenterX(),bounds.getCenterY()); + if(test.distance(localLocation) < HIT_DISTANCE_SQUARE){ + return true; + } + + + CubicCurve2D right = new CubicCurve2D.Double(); + left.subdivide(left, right); + Rectangle2D lb2d = left.getBounds2D(); + Rectangle2D rb2d = right.getBounds2D(); + if( lb2d.contains(localLocation)){ + bounds = lb2d; + } else if (rb2d.contains(localLocation)) { + left = right; + bounds = rb2d; + } else { + return false; + } + + }//end while + }//end for + } + return false; + } + + +} + + + + diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/visual/WidgetCollisionCollector.java b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/visual/WidgetCollisionCollector.java new file mode 100644 index 000000000000..860764d38f14 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/java/at/ssw/visualizer/cfg/visual/WidgetCollisionCollector.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.cfg.visual; + +import java.util.List; +import org.netbeans.api.visual.widget.Widget; + + +public interface WidgetCollisionCollector { + + //returns a list of widgets which should be handled as obstacles + void collectCollisions(List collisions); + +} diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/ControlFlowEditor/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..2b8164a66f4b --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/nbm/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.cfg +OpenIDE-Module-Layer: at/ssw/visualizer/cfg/layer.xml +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/cfg/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/Bundle.properties b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/Bundle.properties new file mode 100644 index 000000000000..8a928b7a69ad --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=Control Flow Editor diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/arrangebfs.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/arrangebfs.gif new file mode 100644 index 000000000000..2f093c74e7f0 Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/arrangebfs.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/arrangehier.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/arrangehier.gif new file mode 100644 index 000000000000..cb69bf80f5f0 Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/arrangehier.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/arrangeloop.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/arrangeloop.gif new file mode 100644 index 000000000000..6c7d10410c77 Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/arrangeloop.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/autosize.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/autosize.gif new file mode 100644 index 000000000000..073f47fb367f Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/autosize.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/autosize_selection.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/autosize_selection.gif new file mode 100644 index 000000000000..ae69de6d7816 Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/autosize_selection.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/bezierrouter.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/bezierrouter.gif new file mode 100644 index 000000000000..618d15c2c4fe Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/bezierrouter.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/cfg.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/cfg.gif new file mode 100644 index 000000000000..695e5a5cfa5b Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/cfg.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/cfg32.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/cfg32.gif new file mode 100644 index 000000000000..4459f742d54e Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/cfg32.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/cluster.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/cluster.gif new file mode 100644 index 000000000000..2a9fb5d67075 Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/cluster.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/color.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/color.gif new file mode 100644 index 000000000000..f5beae9a0827 Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/color.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/combine.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/combine.gif new file mode 100644 index 000000000000..87a106ad42c3 Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/combine.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/combine_disabled.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/combine_disabled.gif new file mode 100644 index 000000000000..d23c22d7e146 Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/combine_disabled.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/disk.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/disk.gif new file mode 100644 index 000000000000..14480e28e12b Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/disk.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/fanrouter.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/fanrouter.gif new file mode 100644 index 000000000000..b260a446f418 Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/fanrouter.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/hideedges.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/hideedges.gif new file mode 100644 index 000000000000..b75bc19602e7 Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/hideedges.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/hideedges_disabled.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/hideedges_disabled.gif new file mode 100644 index 000000000000..ce74721ea22a Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/hideedges_disabled.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/manhattanrouter.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/manhattanrouter.gif new file mode 100644 index 000000000000..8f89045d4196 Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/manhattanrouter.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/showedges.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/showedges.gif new file mode 100644 index 000000000000..b2f45306c93a Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/showedges.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/showedges_disabled.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/showedges_disabled.gif new file mode 100644 index 000000000000..bc37b7a4e52c Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/showedges_disabled.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/split.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/split.gif new file mode 100644 index 000000000000..68d1311e777d Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/split.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/split_disabled.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/split_disabled.gif new file mode 100644 index 000000000000..0cb8ac7d59a1 Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/split_disabled.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/zoomin.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/zoomin.gif new file mode 100644 index 000000000000..a2aaaeb008ec Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/zoomin.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/zoomout.gif b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/zoomout.gif new file mode 100644 index 000000000000..6dc4cbfa0955 Binary files /dev/null and b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/icons/zoomout.gif differ diff --git a/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/layer.xml b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/layer.xml new file mode 100644 index 000000000000..99109c49b015 --- /dev/null +++ b/visualizer/C1Visualizer/ControlFlowEditor/src/main/resources/at/ssw/visualizer/cfg/layer.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/DataFlowEditor/pom.xml b/visualizer/C1Visualizer/DataFlowEditor/pom.xml new file mode 100644 index 000000000000..8c33f314547a --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowEditor/pom.xml @@ -0,0 +1,137 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + DataFlowEditor + 1.14-SNAPSHOT + nbm + DataFlowEditor + + UTF-8 + + + + at.ssw.visualizer + GraphLayoutImpl + ${project.version} + + + at.ssw.visualizer + GraphLayoutAPI + ${project.version} + + + at.ssw.visualizer + VisualizerUI + ${project.version} + + + at.ssw.visualizer + DataFlowGraph + ${project.version} + + + at.ssw.visualizer + CompilationModel + ${project.version} + + + org.apache.xmlgraphics + batik-dom + ${batik.version} + + + org.apache.xmlgraphics + batik-svggen + ${batik.version} + + + org.netbeans.api + org-netbeans-api-visual + ${netbeans.version} + + + org.netbeans.api + org-openide-awt + ${netbeans.version} + + + org.netbeans.api + org-openide-util-ui + ${netbeans.version} + + + org.netbeans.api + org-openide-loaders + ${netbeans.version} + + + org.netbeans.api + org-openide-nodes + ${netbeans.version} + + + org.netbeans.api + org-openide-text + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + org.netbeans.api + org-openide-windows + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.DataFlowEditor + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/java/at/ssw/visualizer/dataflow/action/ShowDataFlowEditorAction.java b/visualizer/C1Visualizer/DataFlowEditor/src/main/java/at/ssw/visualizer/dataflow/action/ShowDataFlowEditorAction.java new file mode 100644 index 000000000000..ff7f64b61695 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowEditor/src/main/java/at/ssw/visualizer/dataflow/action/ShowDataFlowEditorAction.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.action; + +import at.ssw.visualizer.core.focus.Focus; +import at.ssw.visualizer.dataflow.editor.DFEditorSupport; +import at.ssw.visualizer.dataflow.editor.DataFlowEditorTopComponent; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import org.openide.nodes.Node; +import org.openide.util.HelpCtx; +import org.openide.util.actions.CookieAction; + +/** + * This is the action that is hooked to an compilation-x-element within the + * filesystem menu. Essentially it extracts the ControlFlowGraph from the + * selected node and passes it to the DFEditorSupport bringing the + * viewer to the front. + * + * @author Stefan Loidl + * @author Christian Wimmer + */ +public final class ShowDataFlowEditorAction extends CookieAction { + private static final String ICON_PATH = "at/ssw/visualizer/dataflow/icons/dfg.gif"; + + /* + * The Action extracts the Control- Flow- Graph- Data- Object from the nodes which + * contain information for data flow too. + */ + protected void performAction(Node[] activatedNodes) { + ControlFlowGraph cfg = activatedNodes[0].getLookup().lookup(ControlFlowGraph.class); + if (!Focus.findEditor(DataFlowEditorTopComponent.class, cfg)) { + DFEditorSupport editor = new DFEditorSupport(cfg); + editor.open(); + } + } + + /* + * Defines when the context menu item triggering this action should be + * enabled (one element has to be chose and this elements has to have an + * ControlFlowGraph Cookie!- moreover HIR Codes has to be present within + * the node) + */ + @Override + protected boolean enable(Node[] activatedNodes) { + if (!super.enable(activatedNodes)) { + return false; + } + ControlFlowGraph cfg = activatedNodes[0].getLookup().lookup(ControlFlowGraph.class); + return cfg.hasHir(); + } + + public String getName() { + return "Open Data Flow Graph"; + } + + @Override + protected String iconResource() { + return ICON_PATH; + } + + protected int mode() { + return CookieAction.MODE_EXACTLY_ONE; + } + + protected Class[] cookieClasses() { + return new Class[]{ControlFlowGraph.class}; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + protected boolean asynchronous() { + return false; + } +} diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/java/at/ssw/visualizer/dataflow/editor/DFEditorSupport.java b/visualizer/C1Visualizer/DataFlowEditor/src/main/java/at/ssw/visualizer/dataflow/editor/DFEditorSupport.java new file mode 100644 index 000000000000..487f76ddddc7 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowEditor/src/main/java/at/ssw/visualizer/dataflow/editor/DFEditorSupport.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.editor; + +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.beans.VetoableChangeListener; +import java.beans.VetoableChangeSupport; +import java.io.IOException; +import org.openide.cookies.OpenCookie; +import org.openide.windows.CloneableOpenSupport; +import org.openide.windows.CloneableTopComponent; + +/** + * This class implements a cookie for opening the Data Flow Editor + * + * @author Stefan Loidl + * @author Christian Wimmer + */ +public class DFEditorSupport extends CloneableOpenSupport implements OpenCookie { + private ControlFlowGraph cfg; + + public DFEditorSupport(ControlFlowGraph cfg) { + super(new Env()); + ((Env) env).editorSupport = this; + this.cfg = cfg; + } + + protected CloneableTopComponent createCloneableTopComponent() { + return new DataFlowEditorTopComponent(cfg); + } + + public String messageOpened() { + return "Opened " + cfg.getCompilation().getMethod() + " - " + cfg.getName(); + } + + public String messageOpening() { + return "Opening " + cfg.getCompilation().getMethod() + " - " + cfg.getName(); + } + + + public static class Env implements CloneableOpenSupport.Env { + private PropertyChangeSupport prop = new PropertyChangeSupport(this); + private VetoableChangeSupport veto = new VetoableChangeSupport(this); + private DFEditorSupport editorSupport; + + public boolean isValid() { + return true; + } + + public boolean isModified() { + return false; + } + + public void markModified() throws IOException { + throw new IOException("Editor is readonly"); + } + + public void unmarkModified() { + // Nothing to do. + } + + public CloneableOpenSupport findCloneableOpenSupport() { + return editorSupport; + } + + public void addPropertyChangeListener(PropertyChangeListener l) { + prop.addPropertyChangeListener(l); + } + + public void removePropertyChangeListener(PropertyChangeListener l) { + prop.removePropertyChangeListener(l); + } + + public void addVetoableChangeListener(VetoableChangeListener l) { + veto.addVetoableChangeListener(l); + } + + public void removeVetoableChangeListener(VetoableChangeListener l) { + veto.removeVetoableChangeListener(l); + } + } +} diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/java/at/ssw/visualizer/dataflow/editor/DataFlowEditorTopComponent.java b/visualizer/C1Visualizer/DataFlowEditor/src/main/java/at/ssw/visualizer/dataflow/editor/DataFlowEditorTopComponent.java new file mode 100644 index 000000000000..a5825632ff87 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowEditor/src/main/java/at/ssw/visualizer/dataflow/editor/DataFlowEditorTopComponent.java @@ -0,0 +1,990 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.editor; + +import at.ssw.graphanalyzer.positioning.HierarchicalLayoutManager; +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.dataflow.attributes.InvisibleNeighbourAttribute; +import at.ssw.visualizer.dataflow.attributes.TBExpandAllAttribute; +import at.ssw.visualizer.dataflow.attributes.TBShowBlockAttribute; +import at.ssw.visualizer.dataflow.graph.InscribeNodeWidget; +import at.ssw.visualizer.dataflow.graph.InstructionNodeWidget; +import at.ssw.dataflow.layout.CompoundForceLayouter; +import at.ssw.dataflow.layout.CompoundHierarchicalNodesLayouter; +import at.ssw.dataflow.layout.ExternalGraphLayoutWrapper; +import at.ssw.dataflow.layout.ForceLayouter; +import at.ssw.dataflow.layout.HierarchicalNodesLayouter; +import at.ssw.visualizer.dataflow.graph.InstructionNodeGraphScene; +import at.ssw.visualizer.dataflow.instructions.Instruction; +import at.ssw.visualizer.dataflow.instructions.InstructionSet; +import at.ssw.visualizer.dataflow.instructions.InstructionSetGenerator; +import at.ssw.dataflow.layout.MagneticSpringForceLayouter; +import at.ssw.dataflow.options.OptionEditor; +import at.ssw.visualizer.core.selection.Selection; +import at.ssw.visualizer.core.selection.SelectionManager; +import at.ssw.visualizer.core.selection.SelectionProvider; +import at.ssw.visualizer.model.cfg.State; +import at.ssw.visualizer.model.cfg.StateEntry; +import at.ssw.visualizer.dataflow.graph.InstructionSceneListener; +import at.ssw.visualizer.model.cfg.IRInstruction; +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JToggleButton; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.filechooser.FileFilter; +import org.openide.awt.Toolbar; +import org.openide.util.ImageUtilities; +import org.openide.windows.CloneableTopComponent; +import org.openide.windows.TopComponent; + +import org.apache.batik.dom.GenericDOMImplementation; +import org.apache.batik.svggen.SVGGeneratorContext; +import org.apache.batik.svggen.SVGGraphics2D; +import org.w3c.dom.DOMImplementation; +import java.io.*; +import java.awt.Graphics2D; + +/** + * Top component which displays the data-flow via the netbeans graph framework. + * + * @author Stefan Loidl + */ +final public class DataFlowEditorTopComponent extends CloneableTopComponent implements SelectionProvider { + //Scene visualizing the data-flow + private InstructionNodeGraphScene scene = null; + + //Datasources of the editor + private ControlFlowGraph cfg; + + // Management of the application global selection + private Selection selection; + private boolean selectionUpdating; + private BasicBlock[] curBlocks; + + //Buttons + private JButton BUTHideConstant; + private JToggleButton BUTShowBlocks; + private JToggleButton BUTExpandAll; + private JButton BUTHideParameter; + private JToggleButton BUTUseHierLayout; + private JToggleButton BUTUseCompoundLayout; + private JToggleButton BUTUseForceLayout; + private JToggleButton BUTUseDirectedForceLayout; + private JToggleButton BUTUseCompoundForceLayout; + private JToggleButton BUTUseHighlightClustering; + private JToggleButton BUTForceAutoLayout; + private JToggleButton BUTClusterLinkGrayed; + private JToggleButton BUTLayoutInvisibleNodes; + private JToggleButton BUTClusterBorderVisible; + private JToggleButton BUTUseNodeAnimation; + private JToggleButton BUTUseCurrentNodePosition; + private JButton BUTDoLayout; + private JButton BUTLayoutOptions; + private JButton BUTShowConstant; + private JButton BUTShowParameter; + private JButton BUTShowAll; + private JButton BUTHideAll; + private JButton BUTShowPhi; + private JButton BUTHidePhi; + private JButton BUTShowOperation; + private JButton BUTHideOperation; + private JButton BUTExportGraph; + private JToggleButton BUTUseAdvancedHierLayout; + + //Button tooltips + private static final String ZOOMIN = "Zoom In"; + private static final String ZOOMOUT = "Zoom Out"; + private static final String HIDECONSTANT = "Hide Constants"; + private static final String SHOWBLOCKS = "Show Blocks"; + private static final String EXPANDALL = "Expand All"; + private static final String HIDEPARAMETER = "Hide Parameter"; + private static final String DOLAYOUT = "Do Layout"; + private static final String USEHIERLAYOUT = "Use Hierachical Layout"; + private static final String USECOMPLAYOUT = "Use Compound Layout"; + private static final String USEFORCELAYOUT = "Use Force Layout"; + private static final String USEDIRECTEDFORCELAYOUT = "Use Directed Force Layout"; + private static final String USECOMPOUNDFORCELAYOUT = "Use Compound Force Layout"; + private static final String USEHIGHLIGHTCLUSTERING = "Use Highlight Clustering"; + private static final String FORCEAUTOLAYOUT = "Force Auto Layout"; + private static final String CLUSTERLINKGRAYED = "Cluster Link Grayed"; + private static final String LAYOUTOPTIONS = "Layout Options..."; + private static final String LAYOUTINVISIBLENODES = "Layout Invisible Nodes"; + private static final String CLUSTERBORDERVISIBLE = "Cluster Border Visible"; + private static final String USENODEANIMATION = "Use Node Animation"; + private static final String USECURRENTNODEPOSITION = "The next layout cycle builds on the current node position"; + private static final String SHOWCONSTANT = "Show Constants"; + private static final String SHOWPARAMETER = "Show Parameter"; + private static final String SHOWALL = "Show All"; + private static final String HIDEALL = "Hide All"; + private static final String SHOWPHI = "Show Phi Functions"; + private static final String HIDEPHI = "Hide Phi Functions"; + private static final String SHOWOPERATION = "Show Operations"; + private static final String HIDEOPERATION = "Hide Operations"; + private static final String EXPORTGRAPH = "Export Graph To..."; + private static final String USEADVANCEDHIERLAYOUT = "Use Advanced Hierachical Layout"; + + //Layouter + private CompoundHierarchicalNodesLayouter compoundLayouter; + private HierarchicalNodesLayouter hierarchicalLayouter; + private ForceLayouter forceLayouter; + private CompoundForceLayouter compoundForceLayouter; + private MagneticSpringForceLayouter directedForceLayouter; + private ExternalGraphLayoutWrapper advancedhierarchicalLayouter; + + /** path to the icon used by the component and its open action */ + private static final String ICON_PATH = "at/ssw/visualizer/dataflow/icons/"; + private static final String ICON_ZOOMIN = "zoomin.gif"; + private static final String ICON_ZOOMOUT = "zoomout.gif"; + private static final String ICON_EDITOR = "dfg.gif"; + private static final String ICON_HIRACHICALLAYOUT = "arrangehier.gif"; + private static final String ICON_HIRACHICALLAYOUTADVANCED = "arrangehieradvanced.gif"; + private static final String ICON_FORCELAYOUT = "arrangeforce.gif"; + private static final String ICON_DIRECTEDFORCELAYOUT = "directedforce.gif"; + private static final String ICON_HIRACHICALLAYOUTCL = "arrangehiercluster.gif"; + private static final String ICON_FORCELAYOUTCL = "arrangeforcecluster.gif"; + private static final String ICON_EXPANDALL = "expall.gif"; + private static final String ICON_EXPANDBLOCKS = "expblocks.gif"; + private static final String ICON_OPTIONS = "options.gif"; + private static final String ICON_LAYOUT = "layout.gif"; + private static final String ICON_AUTOLAYOUT = "autolayout.gif"; + private static final String ICON_LAYOUTINV = "layoutinvisible.gif"; + private static final String ICON_ANIMATE = "animate.gif"; + private static final String ICON_CLUSTERHIGH = "clusterhigh.gif"; + private static final String ICON_LINKGRAY = "linkgrayed.gif"; + private static final String ICON_CLUSTERB = "cluster.gif"; + private static final String ICON_HIDEPARAM = "hideparam.gif"; + private static final String ICON_HIDECONSTANT = "hideconst.gif"; + private static final String ICON_SHOWPARAM = "showparam.gif"; + private static final String ICON_SHOWCONSTANT = "showconst.gif"; + private static final String ICON_HIDEALL = "hideall.gif"; + private static final String ICON_SHOWALL = "showall.gif"; + private static final String ICON_HIDEPHI = "hidephi.gif"; + private static final String ICON_SHOWPHI = "showphi.gif"; + private static final String ICON_HIDEOPERATION = "hideoperation.gif"; + private static final String ICON_SHOWOPERATION = "showoperation.gif"; + private static final String ICON_CURRENTNODE = "currentnode.gif"; + private static final String ICON_EXPORTGRAPH = "disk.gif"; + + //This view is used for the export of vector graphics + private JScrollPane scenePane; + + + /** + * Constructor + */ + public DataFlowEditorTopComponent(ControlFlowGraph cfg) { + this.cfg = cfg; + + setIcon(ImageUtilities.loadImage(ICON_PATH + ICON_EDITOR, true)); + setName(cfg.getCompilation().getShortName()); + setToolTipText(cfg.getCompilation().getMethod() + " - " + cfg.getName()); + + scene = new InstructionNodeGraphScene(); + scene.addInstructionSceneListener(instructionSceneListener); + + selection = new Selection(); + selection.put(cfg); + selection.put(scene); + selection.addChangeListener(selectionListener); + + scenePane = new JScrollPane(scene.createView()); + scenePane.setBorder(BorderFactory.createEmptyBorder()); + scenePane.setViewportBorder(BorderFactory.createEmptyBorder()); + JPanel scenePanel = new JPanel(); + scenePanel.setLayout(new BorderLayout()); + scenePanel.add(scenePane, BorderLayout.CENTER); + + //create the layouters + hierarchicalLayouter = new HierarchicalNodesLayouter(); + compoundLayouter = new CompoundHierarchicalNodesLayouter(); + forceLayouter = new ForceLayouter(); + compoundForceLayouter = new CompoundForceLayouter(); + directedForceLayouter = new MagneticSpringForceLayouter(); + advancedhierarchicalLayouter = new ExternalGraphLayoutWrapper(new HierarchicalLayoutManager(HierarchicalLayoutManager.Combine.NONE), false, false, false); + + scenePanel.add(createToolbar(), BorderLayout.NORTH); + + setLayout(new BorderLayout()); + add(scenePanel, BorderLayout.CENTER); + } + + public Selection getSelection() { + return selection; + } + + private ChangeListener selectionListener = new ChangeListener() { + public void stateChanged(ChangeEvent event) { + if (selectionUpdating) { + return; + } + selectionUpdating = true; + + BasicBlock[] newBlocks = selection.get(BasicBlock[].class); + if (newBlocks != null && newBlocks.length > 0 && !Arrays.equals(curBlocks, newBlocks)) { + HashSet instructions = new HashSet(); + for (BasicBlock b : newBlocks) { + for (IRInstruction i : b.getHirInstructions()) { + InstructionNodeWidget nw = scene.getNodeWidget(i.getValue(IRInstruction.HIR_NAME)); + //Some instructions are within the states of more than one block + if (nw != null) { + instructions.add(nw.getInstruction()); + } + } + for (State s : b.getStates()) { + for (StateEntry se : s.getEntries()) { + InstructionNodeWidget nw = scene.getNodeWidget(se.getName()); + //Some instructions are within the states of more than one block + if (nw != null && b.getName() != null && b.getName().equals(nw.getInstruction().getSourceBlock())) { + instructions.add(nw.getInstruction()); + } + } + } + } + scene.setSelectedObjects(instructions); + scene.refreshAll(); + scene.validate(); + } + curBlocks = newBlocks; + selectionUpdating = false; + } + }; + + + private InstructionSceneListener instructionSceneListener = new InstructionSceneListener() { + public void selectionChanged(Set w) { + if (selectionUpdating) { + return; + } + selectionUpdating = true; + + List newBlocks = new ArrayList(); + for (InstructionNodeWidget widget : w) { + String name = widget.getInstruction().getSourceBlock(); + //Sometimes block values are not filled + if (name == null) { + continue; + } + BasicBlock b = cfg.getBasicBlockByName(name); + if (b != null) { + newBlocks.add(b); + } + } + + curBlocks = newBlocks.toArray(new BasicBlock[newBlocks.size()]); + selection.put(curBlocks); + selectionUpdating = false; + } + + //Not used here + public void doubleClicked(InstructionNodeWidget w) { + } + public void updateNodeData() { + } + }; + + + /** + * This method activates the data source of the viewer. + * BasicBlocks are used as basic granularity for this + * reason. The Scene is also built within this method. + */ + private void activateDataSource() { + scene.validate(); + + InstructionSet iset = InstructionSetGenerator.generateFromBlocks(cfg.getBasicBlocks()); + Instruction[] instructions = iset.getInstructions(); + //No control flow instructions are shown in the graph + instructions = InstructionSet.filterInstructionType(instructions, Instruction.InstructionType.CONTROLFLOW); + + scene.addInstructions(instructions); + + for (Instruction inst : instructions) { + InstructionNodeWidget inw = scene.getNodeWidget(inst.getID()); + + //Hide Constants + if (inst.getInstructionType() == Instruction.InstructionType.CONSTANT) { + //inw.addNodeAttribute(new TBInvisibilityAttribute(BUTHideConstant)); + //Show constants in successor nodes + for (Instruction i : inst.getSuccessors()) { + InstructionNodeWidget widget = scene.getNodeWidget(i.getID()); + if (widget != null) { + InscribeNodeWidget newW = new InscribeNodeWidget(inst, scene); + widget.addNodeAttribute(new InvisibleNeighbourAttribute(newW, inw, false)); + } + } + } + + //Hide Parameters + if (inst.getInstructionType() == Instruction.InstructionType.PARAMETER) { + //inw.addNodeAttribute(new TBInvisibilityAttribute(BUTHideParameter)); + //Show constants in successor nodes + for (Instruction i : inst.getSuccessors()) { + InstructionNodeWidget widget = scene.getNodeWidget(i.getID()); + if (widget != null) { + InscribeNodeWidget newW = new InscribeNodeWidget(inst, scene); + widget.addNodeAttribute(new InvisibleNeighbourAttribute(newW, inw, false)); + } + } + } + + //Show Blocks + inw.addNodeAttribute(new TBShowBlockAttribute(BUTShowBlocks)); + + //Show Instructionstring + inw.addNodeAttribute(new TBExpandAllAttribute(BUTExpandAll)); + } + + //Assign standard layouter + scene.setExternalLayouter(hierarchicalLayouter); + scene.refreshAll(); + scene.layout(); + scene.validate(); + } + + @Override + protected void componentOpened() { + super.componentOpened(); + activateDataSource(); + } + + /* overwritten from parent- called if editor is activated*/ + @Override + protected void componentActivated() { + super.componentActivated(); + SelectionManager.getDefault().setSelection(selection); + } + + /* overwritten from parent- called if editor is closed*/ + @Override + protected void componentClosed() { + super.componentClosed(); + SelectionManager.getDefault().removeSelection(selection); + } + + + @Override + public int getPersistenceType() { + return TopComponent.PERSISTENCE_NEVER; + } + + @Override + protected CloneableTopComponent createClonedObject() { + return new DataFlowEditorTopComponent(cfg); + } + + // + /** Creates the toolbar */ + private Toolbar createToolbar() { + Toolbar toolBar = new Toolbar(); + toolBar.setBorder((Border) UIManager.get("Nb.Editor.Toolbar.border")); + + JButton zoomIn = new JButton(); + zoomIn.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_ZOOMIN, true))); + zoomIn.setToolTipText(ZOOMIN); + zoomIn.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + scene.setZoomFactor(scene.getZoomFactor() * 1.2); + scene.validate(); + } + }); + + JButton zoomOut = new JButton(); + zoomOut.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_ZOOMOUT, true))); + zoomOut.setToolTipText(ZOOMOUT); + zoomOut.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + scene.setZoomFactor(scene.getZoomFactor() / 1.2); + scene.validate(); + } + }); + + BUTShowAll = new JButton(); + BUTShowAll.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_SHOWALL, true))); + BUTShowAll.setToolTipText(SHOWALL); + BUTShowAll.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + scene.handleSetVisibilityNodeType(true, null); + scene.refreshAll(); + scene.validate(); + scene.autoLayout(); + } + }); + + BUTHideAll = new JButton(); + BUTHideAll.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_HIDEALL, true))); + BUTHideAll.setToolTipText(HIDEALL); + BUTHideAll.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + scene.handleSetVisibilityNodeType(false, null); + scene.refreshAll(); + scene.validate(); + scene.autoLayout(); + } + }); + + BUTHidePhi = new JButton(); + BUTHidePhi.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_HIDEPHI, true))); + BUTHidePhi.setToolTipText(HIDEPHI); + BUTHidePhi.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + scene.handleSetVisibilityNodeType(false, Instruction.InstructionType.PHI); + scene.refreshAll(); + scene.validate(); + scene.autoLayout(); + } + }); + + BUTShowPhi = new JButton(); + BUTShowPhi.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_SHOWPHI, true))); + BUTShowPhi.setToolTipText(SHOWPHI); + BUTShowPhi.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + scene.handleSetVisibilityNodeType(true, Instruction.InstructionType.PHI); + scene.refreshAll(); + scene.validate(); + scene.autoLayout(); + } + }); + + BUTShowOperation = new JButton(); + BUTShowOperation.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_SHOWOPERATION, true))); + BUTShowOperation.setToolTipText(SHOWOPERATION); + BUTShowOperation.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + scene.handleSetVisibilityNodeType(true, Instruction.InstructionType.OPERATION); + scene.refreshAll(); + scene.validate(); + scene.autoLayout(); + } + }); + + BUTHideOperation = new JButton(); + BUTHideOperation.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_HIDEOPERATION, true))); + BUTHideOperation.setToolTipText(HIDEOPERATION); + BUTHideOperation.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + scene.handleSetVisibilityNodeType(false, Instruction.InstructionType.OPERATION); + scene.refreshAll(); + scene.validate(); + scene.autoLayout(); + } + }); + + BUTShowConstant = new JButton(); + BUTShowConstant.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_SHOWCONSTANT, true))); + BUTShowConstant.setToolTipText(SHOWCONSTANT); + BUTShowConstant.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + scene.handleSetVisibilityNodeType(true, Instruction.InstructionType.CONSTANT); + scene.refreshAll(); + scene.validate(); + scene.autoLayout(); + } + }); + + BUTShowParameter = new JButton(); + BUTShowParameter.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_SHOWPARAM, true))); + BUTShowParameter.setToolTipText(SHOWPARAMETER); + BUTShowParameter.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + scene.handleSetVisibilityNodeType(true, Instruction.InstructionType.PARAMETER); + scene.refreshAll(); + scene.validate(); + scene.autoLayout(); + } + }); + + BUTHideConstant = new JButton(); + BUTHideConstant.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_HIDECONSTANT, true))); + BUTHideConstant.setToolTipText(HIDECONSTANT); + BUTHideConstant.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + scene.handleSetVisibilityNodeType(false, Instruction.InstructionType.CONSTANT); + scene.refreshAll(); + scene.validate(); + scene.autoLayout(); + } + }); + + BUTShowBlocks = new JToggleButton(); + BUTShowBlocks.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_EXPANDBLOCKS, true))); + BUTShowBlocks.setToolTipText(SHOWBLOCKS); + BUTShowBlocks.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + scene.refreshAll(); + scene.validate(); + scene.autoLayout(); + } + }); + + BUTExpandAll = new JToggleButton(); + BUTExpandAll.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_EXPANDALL, true))); + BUTExpandAll.setToolTipText(EXPANDALL); + BUTExpandAll.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + scene.refreshAll(); + scene.validate(); + scene.autoLayout(); + } + }); + + BUTHideParameter = new JButton(); + BUTHideParameter.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_HIDEPARAM, true))); + BUTHideParameter.setToolTipText(HIDEPARAMETER); + BUTHideParameter.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + scene.handleSetVisibilityNodeType(false, Instruction.InstructionType.PARAMETER); + scene.refreshAll(); + scene.validate(); + scene.autoLayout(); + } + }); + + BUTDoLayout = new JButton(); + BUTDoLayout.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_LAYOUT, true))); + BUTDoLayout.setToolTipText(DOLAYOUT); + BUTDoLayout.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + scene.layout(); + scene.refreshAll(); + scene.validate(); + } + }); + + BUTLayoutOptions = new JButton(); + BUTLayoutOptions.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_OPTIONS, true))); + BUTLayoutOptions.setToolTipText(LAYOUTOPTIONS); + BUTLayoutOptions.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + OptionEditor ed = new OptionEditor(scene.getExternalLayouter()); + ed.setVisible(true); + scene.refreshAll(); + scene.validate(); + scene.layout(); + } + }); + + BUTUseHierLayout = new JToggleButton(); + BUTUseHierLayout.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_HIRACHICALLAYOUT, true))); + BUTUseHierLayout.setToolTipText(USEHIERLAYOUT); + BUTUseHierLayout.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + changeLayouter((JToggleButton) e.getSource()); + } + }); + //Default selected button + BUTUseHierLayout.setSelected(true); + + BUTUseAdvancedHierLayout = new JToggleButton(); + BUTUseAdvancedHierLayout.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_HIRACHICALLAYOUTADVANCED, true))); + BUTUseAdvancedHierLayout.setToolTipText(USEADVANCEDHIERLAYOUT); + BUTUseAdvancedHierLayout.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + changeLayouter((JToggleButton) e.getSource()); + } + }); + + + //BUTTON: Use Compound Layout + BUTUseCompoundLayout = new JToggleButton(); + BUTUseCompoundLayout.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_HIRACHICALLAYOUTCL, true))); + BUTUseCompoundLayout.setToolTipText(USECOMPLAYOUT); + BUTUseCompoundLayout.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + changeLayouter((JToggleButton) e.getSource()); + } + }); + + //BUTTON: Use Force Layout + BUTUseForceLayout = new JToggleButton(); + BUTUseForceLayout.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_FORCELAYOUT, true))); + BUTUseForceLayout.setToolTipText(USEFORCELAYOUT); + BUTUseForceLayout.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + changeLayouter((JToggleButton) e.getSource()); + } + }); + + //BUTTON: Use Directed Force Layout + BUTUseDirectedForceLayout = new JToggleButton(); + BUTUseDirectedForceLayout.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_DIRECTEDFORCELAYOUT, true))); + BUTUseDirectedForceLayout.setToolTipText(USEDIRECTEDFORCELAYOUT); + BUTUseDirectedForceLayout.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + changeLayouter((JToggleButton) e.getSource()); + } + }); + + //BUTTON: Use Compound Force Layout + BUTUseCompoundForceLayout = new JToggleButton(); + BUTUseCompoundForceLayout.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_FORCELAYOUTCL, true))); + BUTUseCompoundForceLayout.setToolTipText(USECOMPOUNDFORCELAYOUT); + BUTUseCompoundForceLayout.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + changeLayouter((JToggleButton) e.getSource()); + } + }); + + //BUTTON: Highlight Clustering + BUTUseHighlightClustering = new JToggleButton(); + BUTUseHighlightClustering.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_CLUSTERHIGH, true))); + BUTUseHighlightClustering.setToolTipText(USEHIGHLIGHTCLUSTERING); + BUTUseHighlightClustering.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + boolean b = BUTUseHighlightClustering.isSelected(); + scene.setHighlightClustering(b); + scene.layout(); + } + }); + BUTUseHighlightClustering.setEnabled(false); + + //BUTTON: Auto Layout + BUTForceAutoLayout = new JToggleButton(); + BUTForceAutoLayout.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_AUTOLAYOUT, true))); + BUTForceAutoLayout.setToolTipText(FORCEAUTOLAYOUT); + BUTForceAutoLayout.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + boolean b = BUTForceAutoLayout.isSelected(); + scene.setAutoLayout(b); + } + }); + BUTForceAutoLayout.setSelected(scene.isAutoLayout()); + + + //BUTTON: Cluster Link Grayed + BUTClusterLinkGrayed = new JToggleButton(); + BUTClusterLinkGrayed.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_LINKGRAY, true))); + BUTClusterLinkGrayed.setToolTipText(CLUSTERLINKGRAYED); + BUTClusterLinkGrayed.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + boolean b = BUTClusterLinkGrayed.isSelected(); + scene.setInterClusterLinkGrayed(b); + scene.validate(); + } + }); + BUTClusterLinkGrayed.setSelected(scene.isInterClusterLinkGrayed()); + BUTClusterLinkGrayed.setEnabled(false); + + //BUTTON: Cluster Border Visible + BUTClusterBorderVisible = new JToggleButton(); + BUTClusterBorderVisible.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_CLUSTERB, true))); + BUTClusterBorderVisible.setToolTipText(CLUSTERBORDERVISIBLE); + BUTClusterBorderVisible.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + boolean b = BUTClusterBorderVisible.isSelected(); + scene.setClusterBordersVisible(b); + scene.validate(); + } + }); + BUTClusterBorderVisible.setSelected(scene.isClusterBordersVisible()); + BUTClusterBorderVisible.setEnabled(false); + + //BUTTON: Layout Invisible Nodes + BUTLayoutInvisibleNodes = new JToggleButton(); + BUTLayoutInvisibleNodes.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_LAYOUTINV, true))); + BUTLayoutInvisibleNodes.setToolTipText(LAYOUTINVISIBLENODES); + BUTLayoutInvisibleNodes.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + boolean b = BUTLayoutInvisibleNodes.isSelected(); + scene.setLayoutInvisibleNodes(b); + scene.layout(); + scene.validate(); + } + }); + BUTLayoutInvisibleNodes.setSelected(scene.isLayoutInvisibleNodes()); + + //BUTTON: Use Node Animation + BUTUseNodeAnimation = new JToggleButton(); + BUTUseNodeAnimation.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_ANIMATE, true))); + BUTUseNodeAnimation.setToolTipText(USENODEANIMATION); + BUTUseNodeAnimation.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + boolean b = BUTUseNodeAnimation.isSelected(); + scene.setNodeAnimation(b); + } + }); + BUTUseNodeAnimation.setSelected(scene.isNodeAnimation()); + + //BUTTON: Use current node position + BUTUseCurrentNodePosition = new JToggleButton(); + BUTUseCurrentNodePosition.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_CURRENTNODE, true))); + BUTUseCurrentNodePosition.setToolTipText(USECURRENTNODEPOSITION); + BUTUseCurrentNodePosition.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + boolean b = BUTUseCurrentNodePosition.isSelected(); + scene.setUseCurrentNodePositions(b); + } + }); + BUTUseCurrentNodePosition.setSelected(scene.isUseCurrentNodePositions()); + + // Button: Export graph + BUTExportGraph = new JButton(); + BUTExportGraph.setIcon(new ImageIcon(ImageUtilities.loadImage(ICON_PATH + ICON_EXPORTGRAPH, true))); + BUTExportGraph.setToolTipText(EXPORTGRAPH); + BUTExportGraph.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + JFileChooser fc = new JFileChooser(); + fc.setAcceptAllFileFilterUsed(false); + fc.setDialogTitle(EXPORTGRAPH); + fc.addChoosableFileFilter(new SimpleFileFilter("Scaleable Vector Format (*.svg)", ".svg")); + fc.addChoosableFileFilter(new SimpleFileFilter("Graph Modelling Language (*.gml)", ".gml")); + fc.setMultiSelectionEnabled(false); + fc.showSaveDialog(scenePane); + //file selected? + if (fc.getSelectedFile() != null) { + String fileName = fc.getSelectedFile().getAbsolutePath().toLowerCase(); + SimpleFileFilter filter = (SimpleFileFilter) fc.getFileFilter(); + if (!fileName.endsWith(filter.getFilter())) { + fileName += filter.getFilter(); + } + + if (fileName.endsWith("svg")) { + File f = new File(fileName); + DOMImplementation dom = GenericDOMImplementation.getDOMImplementation(); + org.w3c.dom.Document document = dom.createDocument("http://www.w3.org/2000/svg", "svg", null); + SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(document); + ctx.setEmbeddedFontsOn(true); + Graphics2D svgGenerator = new SVGGraphics2D(ctx, true); + scenePane.paint(svgGenerator); + FileOutputStream os = null; + try { + os = new FileOutputStream(f); + Writer out = new OutputStreamWriter(os, "UTF-8"); + assert svgGenerator instanceof SVGGraphics2D; + SVGGraphics2D svgGraphics = (SVGGraphics2D)svgGenerator; + svgGraphics.stream(out, true); + } catch (IOException exc) { + // NotifyDescriptor message = new NotifyDescriptor.Message( + // Bundle.EXPORT_BATIK_ErrorExportingSVG(e.getLocalizedMessage()), NotifyDescriptor.ERROR_MESSAGE); + // DialogDisplayer.getDefault().notifyLater(message); + } finally { + if (os != null) { + try { + os.close(); + } catch (IOException exc) { + } + } + } + } + + if (fileName.endsWith("gml")) { + try { + File f = new File(fileName); + scene.exportToGMLFile(f); + } catch (Exception ex) { + } + } + } + } + }); + + toolBar.add(zoomIn); + toolBar.add(zoomOut); + toolBar.addSeparator(); + + // JLabel lab=new JLabel("Hide:"); + // Font font=lab.getFont().deriveFont(Font.BOLD); + // lab.setFont(font); + // toolBar.add(lab); + toolBar.add(BUTHideConstant); + toolBar.add(BUTHideParameter); + toolBar.add(BUTHidePhi); + toolBar.add(BUTHideOperation); + toolBar.add(BUTHideAll); + + toolBar.addSeparator(); + + toolBar.add(BUTShowConstant); + toolBar.add(BUTShowParameter); + toolBar.add(BUTShowPhi); + toolBar.add(BUTShowOperation); + toolBar.add(BUTShowAll); + + toolBar.addSeparator(); + + // lab=new JLabel("Expand:"); + // lab.setFont(font); + // toolBar.add(lab); + toolBar.add(BUTShowBlocks); + toolBar.add(BUTExpandAll); + toolBar.addSeparator(); + + // lab=new JLabel("Layout:"); + // lab.setFont(font); + //toolBar.add(lab); + toolBar.add(BUTUseHierLayout); + toolBar.add(BUTUseCompoundLayout); + toolBar.add(BUTUseForceLayout); + toolBar.add(BUTUseDirectedForceLayout); + toolBar.add(BUTUseCompoundForceLayout); + // [cwi] temporarily removed until new implementation of algorithm is integrated + // toolBar.add(BUTUseAdvancedHierLayout); + toolBar.addSeparator(); + + // lab=new JLabel("Misc:"); + // lab.setFont(font); + //toolBar.add(lab); + toolBar.add(BUTDoLayout); + toolBar.add(BUTForceAutoLayout); + toolBar.add(BUTUseCurrentNodePosition); + toolBar.add(BUTLayoutOptions); + toolBar.add(BUTLayoutInvisibleNodes); + toolBar.add(BUTUseNodeAnimation); + toolBar.addSeparator(); + + // lab=new JLabel("Clustering:"); + // lab.setFont(font); + //toolBar.add(lab); + toolBar.add(BUTUseHighlightClustering); + toolBar.add(BUTClusterLinkGrayed); + toolBar.add(BUTClusterBorderVisible); + toolBar.addSeparator(); + + toolBar.add(BUTExportGraph); + + return toolBar; + } + + /** Used if one of the layouter buttons was activated */ + private void changeLayouter(JToggleButton sender) { + BUTUseHierLayout.setSelected(false); + BUTUseCompoundLayout.setSelected(false); + BUTUseForceLayout.setSelected(false); + BUTUseCompoundForceLayout.setSelected(false); + BUTUseDirectedForceLayout.setSelected(false); + BUTUseAdvancedHierLayout.setSelected(false); + //doesn't trigger a action event + sender.setSelected(true); + + if (sender == BUTUseHierLayout) { + scene.setExternalLayouter(hierarchicalLayouter); + } + + if (sender == BUTUseCompoundLayout) { + scene.setExternalLayouter(compoundLayouter); + } + if (sender == BUTUseForceLayout) { + scene.setExternalLayouter(forceLayouter); + } + if (sender == BUTUseCompoundForceLayout) { + scene.setExternalLayouter(compoundForceLayouter); + } + if (sender == BUTUseAdvancedHierLayout) { + scene.setExternalLayouter(advancedhierarchicalLayouter); + } + if (sender == BUTUseDirectedForceLayout) { + scene.setExternalLayouter(directedForceLayouter); + } + boolean clustering = scene.getExternalLayouter().isClusteringSupported(); + BUTUseHighlightClustering.setEnabled(clustering); + BUTClusterLinkGrayed.setEnabled(clustering); + BUTClusterBorderVisible.setEnabled(clustering); + + scene.refreshAll(); + scene.layout(); + scene.validate(); + } + +/** + * Simple file filter implementation that filters all files that are no + * diretories and do not end with a specified filter string. + * This string can be used to get all files with specified type. + */ + class SimpleFileFilter extends FileFilter { + + private String desc = null; + private String filter = null; + + public SimpleFileFilter(String desc, String filter) { + this.filter = filter.toLowerCase(); + this.desc = desc; + } + + public boolean accept(File f) { + if (f == null) { + return false; + } + if (f.getName().toLowerCase().endsWith(filter)) { + return true; + } + if (f.isDirectory()) { + return true; + } + return false; + } + + public String getDescription() { + return desc; + } + + public String getFilter() { + return filter; + } + } + // +} diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/DataFlowEditor/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..ef9cea5e2300 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowEditor/src/main/nbm/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.dataflow +OpenIDE-Module-Layer: at/ssw/visualizer/dataflow/layer.xml +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/dataflow/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/Bundle.properties b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/Bundle.properties new file mode 100644 index 000000000000..dfbd1e4f271c --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=Data Flow Editor diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/animate.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/animate.gif new file mode 100644 index 000000000000..164ca6926acf Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/animate.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/arrangeforce.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/arrangeforce.gif new file mode 100644 index 000000000000..34165b8de3a8 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/arrangeforce.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/arrangeforcecluster.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/arrangeforcecluster.gif new file mode 100644 index 000000000000..b4a07f426add Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/arrangeforcecluster.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/arrangehier.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/arrangehier.gif new file mode 100644 index 000000000000..54260ed87053 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/arrangehier.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/arrangehieradvanced.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/arrangehieradvanced.gif new file mode 100644 index 000000000000..1c399fd43aeb Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/arrangehieradvanced.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/arrangehiercluster.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/arrangehiercluster.gif new file mode 100644 index 000000000000..bad94377581d Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/arrangehiercluster.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/autolayout.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/autolayout.gif new file mode 100644 index 000000000000..d6cfb0479690 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/autolayout.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/cluster.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/cluster.gif new file mode 100644 index 000000000000..2a9fb5d67075 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/cluster.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/clusterhigh.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/clusterhigh.gif new file mode 100644 index 000000000000..7c6d2b7567f7 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/clusterhigh.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/currentnode.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/currentnode.gif new file mode 100644 index 000000000000..4a6908740f6b Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/currentnode.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/dfg.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/dfg.gif new file mode 100644 index 000000000000..00a8ca6e4516 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/dfg.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/directedforce.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/directedforce.gif new file mode 100644 index 000000000000..bff65f6e83ec Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/directedforce.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/disk.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/disk.gif new file mode 100644 index 000000000000..14480e28e12b Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/disk.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/expall.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/expall.gif new file mode 100644 index 000000000000..d0c872d9265e Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/expall.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/expandblock.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/expandblock.gif new file mode 100644 index 000000000000..52e1f3cfa89b Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/expandblock.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/expblocks.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/expblocks.gif new file mode 100644 index 000000000000..4e14cfab61bf Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/expblocks.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/fanrouter.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/fanrouter.gif new file mode 100644 index 000000000000..b260a446f418 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/fanrouter.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/hideall.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/hideall.gif new file mode 100644 index 000000000000..74c64f093ff0 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/hideall.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/hideconst.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/hideconst.gif new file mode 100644 index 000000000000..23f1bf70ebe9 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/hideconst.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/hideoperation.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/hideoperation.gif new file mode 100644 index 000000000000..6eeb96fd45d8 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/hideoperation.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/hideparam.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/hideparam.gif new file mode 100644 index 000000000000..a56ee70235e1 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/hideparam.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/hidephi.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/hidephi.gif new file mode 100644 index 000000000000..2bda720fe9a4 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/hidephi.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/layout.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/layout.gif new file mode 100644 index 000000000000..bc1187c60551 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/layout.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/layoutinvisible.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/layoutinvisible.gif new file mode 100644 index 000000000000..f08f10dac9d6 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/layoutinvisible.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/linkgrayed.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/linkgrayed.gif new file mode 100644 index 000000000000..f8a03d913c00 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/linkgrayed.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/options.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/options.gif new file mode 100644 index 000000000000..f17636997503 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/options.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/showall.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/showall.gif new file mode 100644 index 000000000000..0205b29176d4 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/showall.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/showconst.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/showconst.gif new file mode 100644 index 000000000000..e909ab6c1cf2 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/showconst.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/showoperation.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/showoperation.gif new file mode 100644 index 000000000000..ce829e37b5cb Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/showoperation.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/showparam.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/showparam.gif new file mode 100644 index 000000000000..bb6de210779e Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/showparam.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/showphi.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/showphi.gif new file mode 100644 index 000000000000..6b68c5e0cc86 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/showphi.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/zoomin.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/zoomin.gif new file mode 100644 index 000000000000..a2aaaeb008ec Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/zoomin.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/zoomout.gif b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/zoomout.gif new file mode 100644 index 000000000000..6dc4cbfa0955 Binary files /dev/null and b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/icons/zoomout.gif differ diff --git a/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/layer.xml b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/layer.xml new file mode 100644 index 000000000000..f3ef00571f01 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowEditor/src/main/resources/at/ssw/visualizer/dataflow/layer.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/DataFlowGraph/pom.xml b/visualizer/C1Visualizer/DataFlowGraph/pom.xml new file mode 100644 index 000000000000..13aebf85b058 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/pom.xml @@ -0,0 +1,84 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + DataFlowGraph + 1.14-SNAPSHOT + nbm + DataFlowGraph + + UTF-8 + + + + at.ssw.visualizer + GraphLayoutImpl + ${project.version} + + + at.ssw.visualizer + GraphLayoutAPI + ${project.version} + + + at.ssw.visualizer + GraphHelper + ${project.version} + + + at.ssw.visualizer + CompilationModel + ${project.version} + + + org.netbeans.api + org-netbeans-api-visual + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.dataflow.attributes + at.ssw.visualizer.dataflow.graph + at.ssw.visualizer.dataflow.instructions + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/positionmanager/impl/GraphCluster.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/positionmanager/impl/GraphCluster.java new file mode 100644 index 000000000000..059b84bdf9ff --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/positionmanager/impl/GraphCluster.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.positionmanager.impl; + +import at.ssw.positionmanager.Cluster; +import java.util.Set; +import java.util.TreeSet; + +/** + * Implements a cluster in the open layouter graph model of SSW. + * + * @author Stefan Loidl + */ +public class GraphCluster implements Cluster, Comparable{ + + Set successors; + Set predecessors; + + private int index; + + /** Creates a new instance of GraphCluster */ + public GraphCluster(int index, Set successors, Set predecessors) { + this.index=index; + if(successors!=null){ + this.successors=successors; + } + else this.successors=new TreeSet(); + + if(predecessors!=null){ + this.predecessors=predecessors; + } + else this.predecessors=new TreeSet(); + } + + //Never used here! + public Cluster getOuter() { + return null; + } + + public Set getSuccessors() { + return successors; + } + + public Set getPredecessors() { + return predecessors; + } + + + public int compareTo(Cluster o) { + if(o instanceof GraphCluster){ + return index-((GraphCluster)o).index; + } + return this.hashCode()-o.hashCode(); + } +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/positionmanager/impl/GraphLink.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/positionmanager/impl/GraphLink.java new file mode 100644 index 000000000000..07ac01edea00 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/positionmanager/impl/GraphLink.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.positionmanager.impl; + +import at.ssw.positionmanager.Link; +import at.ssw.positionmanager.Port; +import java.awt.Point; +import java.util.LinkedList; +import java.util.List; + +/** + * Implements a Link in the open Layouter specification of SSW. + * + * @author Stefan Loidl + */ +public class GraphLink implements Link, Comparable{ + + private Port from, to; + List controlPoints; + protected int index; + + /** Creates a new instance of GraphLink */ + public GraphLink(int index ,Port from, Port to) { + this.index=index; + this.from=from; + this.to=to; + controlPoints=new LinkedList(); + } + + public Port getFrom() { + return from; + } + + public Port getTo() { + return to; + } + + public List getControlPoints() { + return controlPoints; + } + + public void setControlPoints(List list) { + controlPoints=list; + } + + + public int compareTo(Link o) { + if(o instanceof GraphLink){ + return index-((GraphLink)o).index; + } + return this.hashCode()-o.hashCode(); + } +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/positionmanager/impl/GraphPort.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/positionmanager/impl/GraphPort.java new file mode 100644 index 000000000000..e54b28936bff --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/positionmanager/impl/GraphPort.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.positionmanager.impl; + +import at.ssw.positionmanager.LayoutGraph; +import at.ssw.positionmanager.Port; +import at.ssw.positionmanager.Vertex; +import java.awt.Dimension; +import java.awt.Point; +import java.util.Set; + +/** + * Implements a Port in the open layouter model of SSW. + * + * @author Stefan Loidl + */ +public class GraphPort implements Port{ + + private Vertex vertex; + private LayoutGraph graph=null; + + /** Creates a new instance of GraphPort */ + public GraphPort(Vertex v) { + this.vertex=v; + } + + public Vertex getVertex() { + return vertex; + } + + public void setLayoutGraph(LayoutGraph graph){ + this.graph=graph; + } + + public Point getRelativePosition() { + Dimension d=vertex.getSize(); + int y=0, x=0; + + if(graph==null) return new Point(d.width/2,d.height/2); + + Set ports=graph.getInputPorts(vertex); + if(!ports.contains(this)){ + ports=graph.getOutputPorts(vertex); + y=d.height; + } + + int offset= d.width/(ports.size()+1); + boolean found=false; + for(Port p:ports){ + x+=offset; + if(p==this) { + found=true; + break; + } + } + if(found) return new Point(x,y); + else return new Point(d.width/2,d.height/2); + } +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/positionmanager/impl/GraphVertex.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/positionmanager/impl/GraphVertex.java new file mode 100644 index 000000000000..affa443eaf7a --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/positionmanager/impl/GraphVertex.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.positionmanager.impl; + +import at.ssw.positionmanager.Cluster; +import at.ssw.positionmanager.Vertex; +import at.ssw.visualizer.dataflow.graph.InstructionNodeWidget; +import java.awt.Dimension; +import java.awt.Point; + +/** + * Implements a Vertex which is used as node representation in the open layouter definition of + * SSW. + * + * @author Stefan Loidl + */ +public class GraphVertex implements Vertex { + + private Cluster cluster=null; + private Point position=null; + private boolean dirty=false; + private boolean fixed=false; + private boolean marked=false; + + private InstructionNodeWidget widget; + protected int index; + + /** Creates a new instance of GraphVertex */ + public GraphVertex(int index, Cluster cluster, Point position, InstructionNodeWidget widget) { + assert widget !=null; + this.index=index; + this.cluster=cluster; + this.position=position; + this.widget=widget; + } + + public Cluster getCluster() { + return cluster; + } + + public Dimension getSize() { + return widget.getBounds().getSize(); + } + + public Point getPosition() { + Point p=(Point)position.clone(); + p.translate(-InstructionNodeWidget.BORDERINSET,-InstructionNodeWidget.BORDERINSET); + return p; + } + + public void setPosition(Point p) { + position=p; + } + + public boolean isDirty() { + return dirty; + } + + public void setDirty(boolean d){ + dirty=d; + } + + public boolean isRoot() { + return false; + } + + public int compareTo(Vertex o) { + if(o instanceof GraphVertex){ + return index-((GraphVertex)o).index; + } + return this.hashCode()-o.hashCode(); + } + + public boolean isExpanded() { + return widget.isExpanded(); + } + + public boolean isFixed() { + return fixed; + } + + public boolean isMarked() { + return marked; + } + + public void setFixed(boolean b){ + fixed=b; + } + + public void setMarked(boolean b){ + marked=b; + } + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/ExpandNodeSwitchAttribute.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/ExpandNodeSwitchAttribute.java new file mode 100644 index 000000000000..062da3a8b82d --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/ExpandNodeSwitchAttribute.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.attributes; + +import at.ssw.visualizer.dataflow.graph.InstructionNodeGraphScene; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JMenuItem; + +/** + * + * @author Stefan Loidl + */ +public class ExpandNodeSwitchAttribute implements ISwitchAttribute, IPopupContributorAttribute, IExpandNodeAttribute, IPathHighlightAttribute{ + + private boolean value, remove; + private String description; + private JMenuItem mitem; + private InstructionNodeGraphScene scene; + + /** Creates a new instance of ExpandInfluenceSwitchAttribute */ + public ExpandNodeSwitchAttribute(boolean initialValue, String desc, boolean remove, InstructionNodeGraphScene scene) { + value=initialValue; + description=desc; + this.remove=remove; + mitem=new JMenuItem(description); + mitem.addActionListener(this); + this.scene=scene; + } + + public void setSwitch(boolean s) { + value=s; + } + + public boolean getSwitch() { + return value; + } + + public String getSwitchString() { + return description; + } + + public boolean validate() { + return value; + } + + public boolean removeable() { + return remove; + } + + public JMenuItem getMenuItem() { + return mitem; + } + + public void actionPerformed(ActionEvent e) { + value=!value; + scene.refreshAll(); + scene.validate(); + scene.autoLayout(); + + } + + public boolean showBlock() { + return false; + } + + public boolean showInstruction() { + return true; + } +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/ExpandStructureAttribute.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/ExpandStructureAttribute.java new file mode 100644 index 000000000000..3f8323ebe241 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/ExpandStructureAttribute.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.attributes; + +import at.ssw.visualizer.dataflow.graph.InstructionNodeGraphScene; +import java.awt.event.ActionEvent; +import javax.swing.JMenuItem; + +/** + * An ExpandStructureAttribute is an IExpandNodeAttribute that is depending on + * a ISwitchAttribute and is contribuing a Menuitem to a Popup Menu implementing + * the IPopupContributer interface. + * + * @author Stefan Loidl + */ +public class ExpandStructureAttribute implements IExpandNodeAttribute, IPopupContributorAttribute, IPathHighlightAttribute{ + + private ISwitchAttribute parentAttribute; + private JMenuItem mitem; + private InstructionNodeGraphScene scene; + + /** Creates a new instance of ExpandStructureAttribute */ + public ExpandStructureAttribute(ISwitchAttribute parent, InstructionNodeGraphScene scene) { + parentAttribute=parent; + mitem=new JMenuItem(parent.getSwitchString()); + mitem.addActionListener(this); + this.scene=scene; + } + + public boolean showBlock() { + return false; + } + + public boolean showInstruction() { + return true; + } + + public boolean validate() { + return parentAttribute.getSwitch(); + } + + public boolean removeable() { + return true; + } + + public JMenuItem getMenuItem() { + return mitem; + } + + public void actionPerformed(ActionEvent e) { + parentAttribute.setSwitch(false); + scene.refreshAll(); + scene.validate(); + scene.autoLayout(); + } + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/IAdditionalWidgetAttribute.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/IAdditionalWidgetAttribute.java new file mode 100644 index 000000000000..95c84e66ca9d --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/IAdditionalWidgetAttribute.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.attributes; + +import org.netbeans.api.visual.widget.Widget; + +/** + * + * @author Stefan Loidl + */ +public interface IAdditionalWidgetAttribute extends INodeAttribute{ + + public Widget getWidget(); +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/IExpandNodeAttribute.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/IExpandNodeAttribute.java new file mode 100644 index 000000000000..9fff166f6289 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/IExpandNodeAttribute.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.attributes; + +/** + * + * @author Stefan Loidl + */ +public interface IExpandNodeAttribute extends INodeAttribute { + public boolean showBlock(); + public boolean showInstruction(); +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/IInvisibilityAttribute.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/IInvisibilityAttribute.java new file mode 100644 index 000000000000..77ea8a12a235 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/IInvisibilityAttribute.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.attributes; + +/** + * This interface is for reasons of grouping only. + * + * @author Stefan Loidl + */ +public interface IInvisibilityAttribute extends INodeAttribute{} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/INodeAttribute.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/INodeAttribute.java new file mode 100644 index 000000000000..975a52fe808b --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/INodeAttribute.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.attributes; + +/** + * This is the interface for attributes that can be applied + * to a node. Such attributes could be: visibility or + * full information... + * + * @author Stefan Loidl + */ +public interface INodeAttribute { + + /** + * This methode is meant to return if the attribute + * is active (from its own point of view). If this + * method returns false and removable return true the + * attribute can savely be removed from the attribute list. + */ + public boolean validate(); + + /** + * Returns if the attribute can be removed from the attributelist + * after validate returns true. + * Some attributes are chained to nodes for lifetime. In this + * case removable always returns false. + */ + public boolean removeable(); +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/IPathHighlightAttribute.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/IPathHighlightAttribute.java new file mode 100644 index 000000000000..b3dcffcd9908 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/IPathHighlightAttribute.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.attributes; + +/** + * Nodes implementing this interface are members of a highlighted + * path. + * + * @author Stefan Loidl + */ +public interface IPathHighlightAttribute extends INodeAttribute{ + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/IPopupContributorAttribute.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/IPopupContributorAttribute.java new file mode 100644 index 000000000000..7d3b6fed9198 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/IPopupContributorAttribute.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.attributes; + +import java.awt.event.ActionListener; +import javax.swing.JMenuItem; + +/** + * + * @author Stefan Loidl + */ +public interface IPopupContributorAttribute extends INodeAttribute, ActionListener{ + public JMenuItem getMenuItem(); + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/ISwitchAttribute.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/ISwitchAttribute.java new file mode 100644 index 000000000000..ed78b729379d --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/ISwitchAttribute.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.attributes; + +/** + * + * @author Stefan Loidl + */ +public interface ISwitchAttribute extends INodeAttribute{ + + public void setSwitch(boolean s); + + public boolean getSwitch(); + + /** + * Returns a string as description for the switch. + */ + public String getSwitchString(); +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/InvisibleAttribute.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/InvisibleAttribute.java new file mode 100644 index 000000000000..0ee8e5d89bfa --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/InvisibleAttribute.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.attributes; + +/** + * + * @author Stefan Loidl + */ +public class InvisibleAttribute implements IInvisibilityAttribute{ + + public boolean validate() { + return true; + } + + public boolean removeable() { + return true; + } +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/InvisibleNeighbourAttribute.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/InvisibleNeighbourAttribute.java new file mode 100644 index 000000000000..ab7ed756b48c --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/InvisibleNeighbourAttribute.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.attributes; + +import at.ssw.visualizer.dataflow.graph.InstructionNodeWidget; +import javax.swing.JToggleButton; +import org.netbeans.api.visual.widget.Widget; + +/** + * + * @author Stefan Loidl + */ +public class InvisibleNeighbourAttribute implements IAdditionalWidgetAttribute{ + + private Widget widget; + private InstructionNodeWidget neighbour; + private boolean removeable; + + /** Creates a new instance of TBAdditionalWidgetAttribute */ + public InvisibleNeighbourAttribute(Widget w, InstructionNodeWidget n, boolean removeable) { + widget=w; + neighbour=n; + this.removeable=removeable; + } + + public Widget getWidget() { + return widget; + } + + public boolean validate() { + return !neighbour.isWidgetVisible(); + } + + public boolean removeable() { + return removeable; + } + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/SelfSwitchingExpandAttribute.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/SelfSwitchingExpandAttribute.java new file mode 100644 index 000000000000..6a42d0f1e26f --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/SelfSwitchingExpandAttribute.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.attributes; + +import at.ssw.visualizer.dataflow.graph.InstructionNodeGraphScene; +import java.awt.event.ActionEvent; +import javax.swing.JMenuItem; + +/** + * Expand Attribute that contributes a menuitem to the popupmenu which enables the user to + * deactivate the attribute itself. + * + * @author Stefan Loidl + */ +public class SelfSwitchingExpandAttribute implements IPopupContributorAttribute, IExpandNodeAttribute, IPathHighlightAttribute{ + + private boolean state; + private JMenuItem mitem; + private InstructionNodeGraphScene scene; + + /** Creates a new instance of SelfSwitchingExpandAttribute */ + public SelfSwitchingExpandAttribute(String description, InstructionNodeGraphScene scene) { + state=true; + mitem=new JMenuItem(description); + mitem.addActionListener(this); + this.scene=scene; + } + + public JMenuItem getMenuItem() { + return mitem; + } + + public boolean validate() { + return state; + } + + public boolean removeable() { + return true; + } + + public void actionPerformed(ActionEvent e) { + state=false; + scene.refreshAll(); + scene.validate(); + scene.autoLayout(); + } + + public boolean showBlock() { + return false; + } + + public boolean showInstruction() { + return true; + } + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/TBExpandAllAttribute.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/TBExpandAllAttribute.java new file mode 100644 index 000000000000..a7dab0547b4d --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/TBExpandAllAttribute.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.attributes; + +import javax.swing.JToggleButton; + +/** + * + * @author Stefan Loidl + */ +public class TBExpandAllAttribute implements IExpandNodeAttribute{ + + private JToggleButton button; + + /** Creates a new instance of TBExpandAllAttribute */ + public TBExpandAllAttribute(JToggleButton tb) { + button=tb; + } + + public boolean showBlock() { + return false; + } + + public boolean showInstruction() { + return true; + } + + public boolean validate() { + return button.isSelected(); + } + + public boolean removeable() { + return false; + } + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/TBInvisibilityAttribute.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/TBInvisibilityAttribute.java new file mode 100644 index 000000000000..6c3a9acf8e57 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/TBInvisibilityAttribute.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.attributes; + +import javax.swing.JToggleButton; + +/** + * + * @author Stefan Loidl + */ +public class TBInvisibilityAttribute implements IInvisibilityAttribute{ + + private JToggleButton button; + + /** Creates a new instance of TBVisibilityAttribute */ + public TBInvisibilityAttribute(JToggleButton tb) { + button=tb; + } + + public boolean validate() { + return button.isSelected(); + } + + public boolean removeable() { + return false; + } + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/TBShowBlockAttribute.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/TBShowBlockAttribute.java new file mode 100644 index 000000000000..4148a5c2e87a --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/attributes/TBShowBlockAttribute.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.attributes; + +import javax.swing.JToggleButton; + +/** + * + * @author Stefan Loidl + */ +public class TBShowBlockAttribute implements IExpandNodeAttribute{ + + private JToggleButton button; + + /** Creates a new instance of TBShowBlockAttribute */ + public TBShowBlockAttribute(JToggleButton tb) { + button=tb; + } + + public boolean showBlock() { + return true; + } + + public boolean showInstruction() { + return false; + } + + public boolean validate() { + return button.isSelected(); + } + + public boolean removeable() { + return false; + } + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/ClusterWidget.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/ClusterWidget.java new file mode 100644 index 000000000000..c2b9e934c344 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/ClusterWidget.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.graph; + +import java.awt.Color; +import java.awt.Point; +import java.awt.Rectangle; +import java.util.Collection; +import org.netbeans.api.visual.action.ActionFactory; +import org.netbeans.api.visual.action.EditProvider; +import org.netbeans.api.visual.action.WidgetAction; +import org.netbeans.api.visual.border.Border; +import org.netbeans.api.visual.border.BorderFactory; +import org.netbeans.api.visual.widget.LabelWidget; +import org.netbeans.api.visual.widget.Scene; +import org.netbeans.api.visual.widget.Widget; + +/** + * + * @author Stefan Loidl + */ +public class ClusterWidget extends Widget{ + + private Collection widgets; + private String id; + + private Border VISIBLEBORDER; + private static Border INVISIBLEBORDER=BorderFactory.createEmptyBorder(); + + private static final int DASHSIZE=10; + + + private static final int PADDING=10; + //This value has to be applied to the + //position because of an error in border framework + private static final int CORRECT=5; + + private boolean visible=true; + + private LabelWidget label; + + + /** Creates a new instance of ClusterWidget */ + public ClusterWidget(String id, Collection widgets, Scene scene, Color col) { + super(scene); + this.widgets=widgets; + this.id=id; + VISIBLEBORDER=BorderFactory.createDashedBorder(col,DASHSIZE,DASHSIZE/2,true); + + label=new LabelWidget(scene,id); + label.setForeground(col); + addChild(label); + + //If edit suppored by scene + if(scene instanceof EditProvider){ + WidgetAction.Chain actions = getActions (); + actions.addAction(ActionFactory.createEditAction((EditProvider)scene)); + } + } + + /** Returns the id of the cluster */ + public String getId(){ + return id; + } + + /** Returns the clustered widgets */ + public Collection getWidgets(){ + return widgets; + } + + /** Sets the border of the widget to modify its visibility*/ + private void setWidgetVisible(boolean v){ + removeChildren(); + if(v) { + this.setBorder(VISIBLEBORDER); + addChild(label); + } + else this.setBorder(INVISIBLEBORDER); + } + + /** Defines if border is shown*/ + public void setVisibility(boolean b){ + visible=b; + } + + /** Refreshes the Compoments- size and position*/ + public void refresh(){ + if(widgets!=null){ + int minX=Integer.MAX_VALUE; + int minY=Integer.MAX_VALUE; + int maxX=Integer.MIN_VALUE; + int maxY=Integer.MIN_VALUE; + boolean changed=false; + + for(Widget w:widgets){ + //discard invisible widgets + if(w instanceof InstructionNodeWidget){ + if(!((InstructionNodeWidget)w).isWidgetVisible()) continue; + } + + Point p=w.getPreferredLocation(); + Rectangle r=w.getBounds(); + + if(w!=null && r!=null){ + if(minX > p.x) minX=p.x; + if(minY > p.y) minY=p.y; + if(maxX < p.x+r.width) maxX=p.x+r.width; + if(maxY < p.y+r.height) maxY=p.y+r.height; + changed=true; + } + } + + if(changed && visible){ + setPreferredBounds(new Rectangle(maxX-minX+2*PADDING,maxY-minY+2*PADDING)); + setPreferredLocation(new Point(minX-PADDING-CORRECT,minY-PADDING-CORRECT)); + setWidgetVisible(true); + revalidate(); + } + else setWidgetVisible(false); + } + } + + + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/DirectLineRouter.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/DirectLineRouter.java new file mode 100644 index 000000000000..aa717d4c3e9c --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/DirectLineRouter.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.graph; + +import at.ssw.positionmanager.LayoutGraph; +import at.ssw.positionmanager.Link; +import at.ssw.positionmanager.Port; +import at.ssw.positionmanager.Vertex; +import at.ssw.positionmanager.impl.GraphVertex; +import java.awt.Point; +import java.util.Hashtable; +import java.util.LinkedList; +import java.util.List; +import org.netbeans.api.visual.router.Router; +import org.netbeans.api.visual.widget.ConnectionWidget; +import org.netbeans.api.visual.widget.Widget; + +/** + * + * @author Stefan Loidl + */ +public class DirectLineRouter implements Router{ + + private InstructionNodeGraphScene scene; + + /** Creates a new instance of DirectLineRouter */ + public DirectLineRouter(InstructionNodeGraphScene scene) { + this.scene=scene; + } + + public List routeConnection(ConnectionWidget widget) { + List cp=new LinkedList(); + + LayoutGraph graph=scene.getLayoutGraph(); + Hashtable WidgetToVertex=scene.getWidgetToVertex(); + if(graph==null || WidgetToVertex==null) return cp; + + Vertex v1=WidgetToVertex.get(widget.getSourceAnchor().getRelatedWidget()); + Vertex v2=WidgetToVertex.get(widget.getTargetAnchor().getRelatedWidget()); + + if(v1==null || v2==null) return cp; + + //do the routing + scene.getExternalLayouter().doRouting(graph); + + //Reset dirty state + for(Vertex v: graph.getVertices()){ + if(v instanceof GraphVertex) ((GraphVertex)v).setDirty(false); + } + + for(Port p: graph.getOutputPorts(v1)){ + for(Link l: graph.getPortLinks(p)){ + if(l.getTo().getVertex()==v2){ + cp=l.getControlPoints(); + } + } + } + + + return cp; + } + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/HiddenNodesWidget.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/HiddenNodesWidget.java new file mode 100644 index 000000000000..483fd0c94e2f --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/HiddenNodesWidget.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.graph; + +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.Shape; +import javax.swing.JPopupMenu; +import org.netbeans.api.visual.action.ActionFactory; +import org.netbeans.api.visual.action.PopupMenuProvider; +import org.netbeans.api.visual.widget.Scene; +import org.netbeans.api.visual.widget.Widget; + +/** + * + * @author Stefan Loidl + */ +public class HiddenNodesWidget extends Widget{ + + //true if arrow points upward + private boolean upward; + private final int ARROWHIGHT=4; + + /** + * Creates a hidden-nodes widget. + * @param up is true if arrow shall point upwards. + */ + public HiddenNodesWidget (Scene scene, boolean up) { + super (scene); + upward=up; + } + + /** + * Calculates a client area of the widget. (The width is 0, as + * it should be defined by parent widget width) + * @return the calculated client area + */ + protected Rectangle calculateClientArea () { + return new Rectangle (0, 0, 0, ARROWHIGHT); + } + + /** + * Paints the separator widget. + */ + protected void paintWidget() { + Graphics2D gr = getGraphics(); + gr.setColor (getForeground()); + Rectangle bounds = getBounds (); + Insets insets = getBorder ().getInsets (); + int x[]=new int[4]; + int y[]=new int[4]; + + x[0]=0; + x[1]=bounds.width -insets.left -insets.right; + x[2]=(bounds.width - insets.left - insets.right)/2; + x[3]=0; + + if(upward){ + y[0]=ARROWHIGHT; + y[1]=ARROWHIGHT; + y[2]=0; + y[3]=ARROWHIGHT; + } + else{ + y[0]=0; + y[1]=0; + y[2]=ARROWHIGHT; + y[3]=0; + } + + Polygon p=new Polygon(x,y,4); + + gr.drawPolygon(p); + gr.fill(p); + } + + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/InscribeNodeWidget.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/InscribeNodeWidget.java new file mode 100644 index 000000000000..4d3b8541e962 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/InscribeNodeWidget.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.graph; + +import at.ssw.visualizer.dataflow.instructions.Instruction; +import java.awt.Color; +import java.awt.Font; +import org.netbeans.api.visual.border.BorderFactory; +import org.netbeans.api.visual.widget.LabelWidget; + +/** + * + * @author Stefan Loidl + */ +public class InscribeNodeWidget extends LabelWidget{ + + private final static int LEFTINSET=2; + private final static int TOPINSET=1; + + + /** Creates a new instance of InscribeNodeWidget */ + public InscribeNodeWidget(Instruction i, InstructionNodeGraphScene s) { + super(s); + + if(i.getInstructionType()==Instruction.InstructionType.PARAMETER) + setLabel(i.getID()); + else{ + setLabel(InstructionNodeWidget.modifiyStringLength(i.getID()+": "+i.getInstructionString(), + InstructionNodeWidget.MAXSTRINGLENGTH,InstructionNodeWidget.MAXSTRINGLENGTH, + InstructionNodeWidget.ABBREV)); + } + + setFont(Font.decode(InstructionNodeWidget.DEFAULTFONT).deriveFont(Font.BOLD)); + setForeground(InstructionNodeWidget.getLineColor()); + setOpaque(false); + InstructionNodeWidget nw=s.getNodeWidget(i.getID()); + if(nw!=null){ + setToolTipText(nw.createToolTip()); + } + + Color c=InstructionNodeWidget.getColorByType(i.getInstructionType(),false); + Color dc=InstructionNodeWidget.getLineColor(); + setBorder(BorderFactory.createRoundedBorder(0,0,LEFTINSET,TOPINSET,c,dc)); + + } + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/InstructionConnectionWidget.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/InstructionConnectionWidget.java new file mode 100644 index 000000000000..c69440139ee7 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/InstructionConnectionWidget.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.graph; + +import java.awt.BasicStroke; +import java.awt.Color; +import org.netbeans.api.visual.widget.ConnectionWidget; +import org.netbeans.api.visual.widget.Scene; +import org.netbeans.api.visual.widget.Widget; + +/** + * + * @author Stefan Loidl + */ +public class InstructionConnectionWidget extends ConnectionWidget{ + + private static final BasicStroke EXPANDEDSTROKE=new BasicStroke(2.0f); + private static final BasicStroke UNEXPANDEDSTROKE=new BasicStroke(1.0f); + private static final BasicStroke SELECTEDSTROKE=new BasicStroke(3.0f); + + + private static final Color EXPANDEDCOLOR=new Color(200,0,0); + private static final Color UNEXPANDEDCOLOR=Color.BLACK; + private static final Color INTERCLUSTERCOLOR=Color.GRAY; + + //If one if the endpoints is selected + private static final Color SELECTEDCOLOR=Color.BLACK; + private static final Color SELECTEDBACKWARDCOLOR=Color.BLUE; + private static final Color INTERSELECTIONCOLOR=new Color(0,100,0); + + + + + + /** Creates a new instance of InstructionConnectionWidget */ + public InstructionConnectionWidget(Scene s) { + super(s); + } + + + protected void paintWidget(){ + + boolean expanded=false; + String cluster=null; + //is the link crossing cluster borders? + boolean interClusterLink=false; + ClusterWidget cw1=null,cw2=null; + boolean sourceSelected=false; + boolean targetSelected=false; + + + Scene scene=this.getScene(); + if(scene instanceof InstructionNodeGraphScene) + interClusterLink=((InstructionNodeGraphScene)scene).isInterClusterLinkGrayed(); + + + + Widget w=getSourceAnchor().getRelatedWidget(); + if(w instanceof InstructionNodeWidget){ + if(!((InstructionNodeWidget)w).isWidgetVisible()) return; + expanded=((InstructionNodeWidget)w).isPathHighlighted(); + cw1=((InstructionNodeWidget)w).getClusterWidget(); + } + sourceSelected=w.getState().isSelected(); + + + w=getTargetAnchor().getRelatedWidget(); + if(w instanceof InstructionNodeWidget){ + if(!((InstructionNodeWidget)w).isWidgetVisible()) return; + expanded = expanded && ((InstructionNodeWidget)w).isPathHighlighted(); + cw2=((InstructionNodeWidget)w).getClusterWidget(); + interClusterLink= interClusterLink && (cw1!=cw2); + } + targetSelected=w.getState().isSelected(); + + if(expanded) { + this.setStroke(EXPANDEDSTROKE); + this.setForeground(EXPANDEDCOLOR); + } + else if(sourceSelected && targetSelected){ + this.setStroke(SELECTEDSTROKE); + this.setForeground(INTERSELECTIONCOLOR); + } + else if(sourceSelected){ + this.setStroke(SELECTEDSTROKE); + this.setForeground(SELECTEDCOLOR); + } + else if(targetSelected){ + this.setStroke(SELECTEDSTROKE); + this.setForeground(SELECTEDBACKWARDCOLOR); + } + else { + this.setStroke(UNEXPANDEDSTROKE); + if(interClusterLink) this.setForeground(INTERCLUSTERCOLOR); + else this.setForeground(UNEXPANDEDCOLOR); + } + + super.paintWidget(); + } + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/InstructionNodeGraphScene.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/InstructionNodeGraphScene.java new file mode 100644 index 000000000000..5ecb4217fe88 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/InstructionNodeGraphScene.java @@ -0,0 +1,1332 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.graph; + +import at.ssw.positionmanager.Cluster; +import at.ssw.positionmanager.LayoutGraph; +import at.ssw.positionmanager.Link; +import at.ssw.positionmanager.Port; +import at.ssw.positionmanager.Vertex; +import at.ssw.positionmanager.export.GMLFileExport; +import at.ssw.positionmanager.impl.GraphCluster; +import at.ssw.positionmanager.impl.GraphLink; +import at.ssw.positionmanager.impl.GraphPort; +import at.ssw.positionmanager.impl.GraphVertex; +import at.ssw.visualizer.dataflow.attributes.ExpandNodeSwitchAttribute; +import at.ssw.visualizer.dataflow.attributes.ExpandStructureAttribute; +import at.ssw.visualizer.dataflow.attributes.ISwitchAttribute; +import at.ssw.visualizer.dataflow.attributes.InvisibleAttribute; +import at.ssw.visualizer.dataflow.attributes.SelfSwitchingExpandAttribute; +import at.ssw.visualizer.dataflow.instructions.Instruction; +import at.ssw.dataflow.layout.ExternalGraphLayouter; +import at.ssw.visualizer.graphhelper.DiGraph; +import at.ssw.visualizer.graphhelper.Edge; +import at.ssw.visualizer.graphhelper.Node; +import java.awt.Color; +import java.awt.Font; +import java.awt.Point; +import java.awt.Rectangle; +import java.io.File; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import javax.swing.JPopupMenu; +import javax.swing.JViewport; +import org.netbeans.api.visual.action.ActionFactory; +import org.netbeans.api.visual.action.EditProvider; +import org.netbeans.api.visual.action.MoveProvider; +import org.netbeans.api.visual.action.MoveStrategy; +import org.netbeans.api.visual.action.PopupMenuProvider; +import org.netbeans.api.visual.action.RectangularSelectDecorator; +import org.netbeans.api.visual.action.RectangularSelectProvider; +import org.netbeans.api.visual.action.SelectProvider; +import org.netbeans.api.visual.action.WidgetAction; +import org.netbeans.api.visual.anchor.Anchor; +import org.netbeans.api.visual.anchor.AnchorFactory; +import org.netbeans.api.visual.anchor.AnchorShape; +import org.netbeans.api.visual.border.BorderFactory; +import org.netbeans.api.visual.graph.GraphScene; +import org.netbeans.api.visual.graph.layout.TreeGraphLayout; +import org.netbeans.api.visual.layout.LayoutFactory; +import org.netbeans.api.visual.router.Router; +import org.netbeans.api.visual.router.RouterFactory; +import org.netbeans.api.visual.widget.ConnectionWidget; +import org.netbeans.api.visual.widget.LabelWidget; +import org.netbeans.api.visual.widget.LayerWidget; +import org.netbeans.api.visual.widget.Scene; +import org.netbeans.api.visual.widget.Widget; + +/** + * + * @author Stefan Loidl + */ +public class InstructionNodeGraphScene extends GraphScene implements RectangularSelectDecorator, + RectangularSelectProvider, + MoveStrategy, + EditProvider, SelectProvider{ + + //scene layers + private Widget mainLayer; + private Widget connectionLayer; + private Widget selectLayer; + private Widget clusterLayer; + + //Nodeclusters- not always used- layouter dependent + private Collection clusters=null; + //nodes- saperated to achive efficiency + private Hashtable nodewidgets; + + //Menu item strings + private String RELEASEINFTREE="Collapse Influence Tree"; + private String RELEASEPARENTINF="Collapse Parent Influence"; + private String RELEASECYCLES="Collapse Cycles"; + private String RELEASESINGLECYCLE="Collapse Single Cycle"; + private String RELEASENODE="Collapse Node"; + + private WidgetAction moveAction = ActionFactory.createMoveAction (this, new MultiWidgetMovementProvider()); + private WidgetAction selectAction= ActionFactory.createSelectAction(this); + + private ExternalGraphLayouter layouter=null; + + private LinkedList listeners; + + //Defines if links between clusters are grayed + private boolean interCluserLinkGray=true; + + //Used to determine planarity, cycles... + private DiGraph calculationModel; + + //Is layouting done automaticly? + private boolean autoLayout=true; + + //Are invisible Nodes layouted? + private boolean layoutInvisibleNodes=false; + + //Is highlight clustering used? + private boolean highlightClustering=false; + + //Are Clusterboarders visible + private boolean clusterBordersVisible=true; + + //Are Location animations used + private boolean locationAnimation=true; + + + //Default Block/Cluster names + private final static String HIGHLIGHTBLOCK="HIGHLIGHT"; + private final static String DEFAULTBLOCK="NULL"; + + //Are current node positions used by layouters to build on? + private boolean USECURRENTNODEPOSITIONS=false; + + private Router linkRouter= new at.ssw.visualizer.dataflow.graph.DirectLineRouter(this); + + + /** + * Constructor + */ + public InstructionNodeGraphScene () { + + clusterLayer=new LayerWidget(this); + addChild(clusterLayer); + mainLayer = new LayerWidget (this); + addChild (mainLayer); + connectionLayer = new Widget (this); + addChild (connectionLayer); + selectLayer=new LayerWidget(this); + addChild(selectLayer); + + getInputBindings().setZoomActionModifiers(0); + getActions().addAction(ActionFactory.createMouseCenteredZoomAction(1.2)); + getActions().addAction(ActionFactory.createPanAction()); + getActions().addAction(ActionFactory.createRectangularSelectAction(this, (LayerWidget) selectLayer,this)); + + nodewidgets=new Hashtable(); + listeners=new LinkedList(); + } + + /** + * Adds an array of instructions to the Scene. This is the only way nodes should + * be added to this type of scene. + */ + public void addInstructions(Instruction[] instructions){ + calculationModel=new DiGraph(); + + //Add nodes to scene + for(Instruction inst : instructions){ + addNode(inst); + + calculationModel.addNode(new Node(inst.getID())); + } + + //Add edges to scene + for(Instruction inst : instructions){ + for(Instruction x: inst.getSuccessors()){ + //in some cases it may happen that a phi expression + //uses the same instruction more than once + //edges would be duplicated. This if prevents this + if(!getEdges().contains(inst.getID()+x.getID())){ + addEdge(inst.getID()+x.getID()); + setEdgeSource(inst.getID()+x.getID(),inst); + setEdgeTarget(inst.getID()+x.getID(),x); + + + calculationModel.addEdge(new Edge(calculationModel.getNode(inst.getID()), + calculationModel.getNode(x.getID()))); + } + } + } + + //Add cycle information to each node + for(InstructionNodeWidget nw: getNodeWidgets()){ + Node n=calculationModel.getNode(nw.getID()); + if(n!=null){ + Collection cycles=calculationModel.getCircularDependency(n); + LinkedList intc=new LinkedList(); + InstructionNodeWidget[] wid; + + for(Node[] nl:cycles){ + wid=new InstructionNodeWidget[nl.length]; + for(int i=0;i < nl.length;i++){ + wid[i]=getNodeWidget(nl[i].ID); + } + intc.add(wid); + } + + nw.setCycles(intc); + } + } + } + + + /** + * Refreshes all node & cluster widget data + */ + public void refreshAll(){ + Enumeration enu=nodewidgets.elements(); + + while(enu.hasMoreElements()) { + InstructionNodeWidget w=enu.nextElement(); + w.refresh(); + } + for(Widget w: connectionLayer.getChildren()){ + w.revalidate(true); + } + + refreshClusterWidgets(); + refreshLayoutVertex(); + } + + + /** + * Updates the data of the layoutgraph verticles. + */ + protected void refreshLayoutVertex(){ + if(VertexToWidget!=null){ + for(Map.Entry e:VertexToWidget.entrySet()){ + if(e.getKey() instanceof GraphVertex){ + GraphVertex v=(GraphVertex)e.getKey(); + v.setDirty(true); + } + } + } + + } + + /** + * Refreshes the size calculation of the cluster widgets. + */ + protected void refreshClusterWidgets(){ + if(clusters!=null){ + for(ClusterWidget c:clusters){ + c.refresh(); + } + } + } + + /** + * Centers the view at point p. + */ + public void centerOn(Point p){ + Object o=getView().getParent(); + + if(o instanceof JViewport){ + JViewport v=(JViewport)o; + Point upperLeft=convertViewToScene(v.getViewPosition()); + Point lowerRight=v.getViewPosition(); + lowerRight.translate(v.getExtentSize().width,v.getExtentSize().height); + lowerRight=convertViewToScene(lowerRight); + + int x=p.x-(lowerRight.x-upperLeft.x)/2; + int y=p.y-(lowerRight.y-upperLeft.y)/2; + + Point n=convertSceneToView(new Point(x,y)); + + int maxX=v.getViewSize().width-v.getExtentSize().width; + int maxY=v.getViewSize().height-v.getExtentSize().height; + + if(n.x < 0) n.x=0; + if(n.x > maxX) n.x=maxX; + if(n.y < 0) n.y=0; + if(n.y > maxY) n.y=maxY; + + + v.setViewPosition(n); + } + } + + + /** + * Edit provider action- This method is called if a node is + * double clicked. + */ + public void edit(Widget widget) { + if(widget instanceof InstructionNodeWidget){ + fireDoubleClicked((InstructionNodeWidget)widget); + } + if(widget instanceof ClusterWidget){ + try{ + int x=widget.getPreferredLocation().x+widget.getPreferredBounds().width/2; + int y=widget.getPreferredLocation().y+widget.getPreferredBounds().height/2; + centerOn(new Point(x,y)); + + HashSet set=new HashSet(); + for(Widget w:((ClusterWidget)widget).getWidgets()){ + if(w instanceof InstructionNodeWidget){ + set.add(((InstructionNodeWidget)w).getInstruction()); + } + } + + setSelectedObjects(set); + fireSelectionChanged(); + refreshAll(); + validate(); + }catch(Exception e){ + } + } + } + + /** + * Exports the current layout graph into the GML file format. + */ + public boolean exportToGMLFile(File f){ + Hashtable names=new Hashtable(); + Hashtable colors=new Hashtable(); + + for(Vertex v:layoutGraph.getVertices()){ + InstructionNodeWidget w=VertexToWidget.get(v); + if(w!=null){ + names.put(v,VertexToWidget.get(v).getID()); + colors.put(v,InstructionNodeWidget.getColorByType(w.getInstruction().getInstructionType(),false)); + } + } + GMLFileExport exp=new GMLFileExport(f,names,colors); + return exp.export(layoutGraph); + } + + + // + /** + * This method expands the subtree with w as the root. Expansion is done + * via the internal attribute system. If forward is true the successors + * are used... if not the tree is traversed backward via Predecessors. + */ + protected void handleExpandInfluenceTree(Instruction i, boolean forward){ + String desc="("+i.getID()+")"; + + if(forward) desc=RELEASEINFTREE+desc; + else desc=RELEASEPARENTINF+desc; + + + ExpandNodeSwitchAttribute en= new ExpandNodeSwitchAttribute(true,desc,true, this); + + LinkedList list=new LinkedList(); + + getNodeWidget(i.getID()).addNodeAttribute(en); + + list.add(i); + + if(forward){ + for(Instruction inst: i.getSuccessors()){ + rek_ExpandInfluenceTree(inst,en,list, forward); + } + } else{ + for(Instruction inst: i.getPredecessors()){ + rek_ExpandInfluenceTree(inst,en,list, forward); + } + } + + refreshAll(); + validate(); + autoLayout(); + } + + private void rek_ExpandInfluenceTree(Instruction i, ISwitchAttribute a,LinkedList list, boolean forward){ + if(list.contains(i)) return; + list.add(i); + + ExpandStructureAttribute es=new ExpandStructureAttribute(a,this); + + getNodeWidget(i.getID()).addNodeAttribute(es); + + + if(forward){ + for(Instruction inst: i.getSuccessors()){ + rek_ExpandInfluenceTree(inst,a,list, forward); + } + } else{ + for(Instruction inst: i.getPredecessors()){ + rek_ExpandInfluenceTree(inst,a,list, forward); + } + } + } + + /** + * This Method expands a single Node + */ + protected void handleExpandNode(Instruction instruction) { + Set selected=(Set)getSelectedObjects(); + + //Prepare for multiselected usage + if(selected == null || selected.size()==0 || !selected.contains(instruction)){ + selected=new HashSet(); + selected.add(instruction); + } + + for(Instruction i: selected){ + InstructionNodeWidget n=getNodeWidget(i.getID()); + SelfSwitchingExpandAttribute en=new SelfSwitchingExpandAttribute(RELEASENODE+"("+i.getID()+")", this); + n.addNodeAttribute(en); + } + + refreshAll(); + validate(); + autoLayout(); + } + + + /** + * Add Expand attributes to the nodes making up cycles the the + * Node with the given id + */ + void handleExpandCycles(String id) { + InstructionNodeWidget nw=nodewidgets.get(id); + + if(nw!=null){ + String desc=RELEASECYCLES+" ("+id+")"; + ExpandNodeSwitchAttribute en= new ExpandNodeSwitchAttribute(true,desc,true, this); + nw.addNodeAttribute(en); + + + Collection c=nw.getCycleWidgets(); + LinkedList handled=new LinkedList(); + handled.add(nw); + + for(InstructionNodeWidget[] a:c){ + for(InstructionNodeWidget w: a){ + if(!handled.contains(w)){ + handled.add(w); + ExpandStructureAttribute es=new ExpandStructureAttribute(en,this); + w.addNodeAttribute(es); + } + } + } + } + + refreshAll(); + validate(); + autoLayout(); + } + + /** + * This Method expands the Cycle with given index of the node. + */ + public void handleExpandCycle(String node, int cycleIndex){ + InstructionNodeWidget nw=nodewidgets.get(node); + + if(nw!=null){ + String desc=RELEASESINGLECYCLE+" ("+node+"/"+cycleIndex+")"; + ExpandNodeSwitchAttribute en= new ExpandNodeSwitchAttribute(true,desc,true, this); + nw.addNodeAttribute(en); + + LinkedList handled=new LinkedList(); + handled.add(nw); + + int i=0; + for(InstructionNodeWidget[] a:nw.getCycleWidgets()){ + if(i==cycleIndex){ + for(InstructionNodeWidget w: a){ + if(!handled.contains(w)){ + handled.add(w); + ExpandStructureAttribute es=new ExpandStructureAttribute(en,this); + w.addNodeAttribute(es); + } + } + break; + } + i++; + } + } + refreshAll(); + validate(); + autoLayout(); + } + + /** + * Hides all nodes but the current one (or all selected if so) + */ + public void handleHideAllBut(Instruction instruction) { + Set selected=(Set)getSelectedObjects(); + boolean deselect=false; + + //Prepare for multiselected usage + if(selected.size()==0){ + selected=new HashSet(); + selected.add(instruction); + } else if(!selected.contains(instruction)){ + selected=new HashSet(); + selected.add(instruction); + }else{ + deselect=true; + } + + for(InstructionNodeWidget w:getNodeWidgets()){ + if(!(selected!=null && selected.contains(w.getInstruction()))){ + if(w.isWidgetVisible()) w.addNodeAttribute(new InvisibleAttribute()); + } + } + + if(deselect){ + //deselect all! + setSelectedObjects(new HashSet()); + fireSelectionChanged(); + } + + refreshAll(); + validate(); + autoLayout(); + } + + /** + * Handles the toggle visibility event. If includeSelection==ture selected + * widgets are included if they contain inst. + */ + public void handleToggleVisibility(Instruction inst, boolean includeSelection){ + Set selected=null; + if(includeSelection){ + selected=(Set)getSelectedObjects(); + + //Prepare for multiselected usage + if(selected.size()==0){ + selected=new HashSet(); + selected.add(inst); + } else if(!selected.contains(inst)){ + selected=new HashSet(); + selected.add(inst); + } + }else{ + selected=new HashSet(); + selected.add(inst); + } + + //If nodes get visible- selection has to be canceled + boolean unselect= false; + for(Instruction i:selected){ + InstructionNodeWidget nw=getNodeWidget(i.getID()); + if(nw.isWidgetVisible()) { + nw.addNodeAttribute(new InvisibleAttribute()); + unselect=true; + } + else { + nw.makeVisible(); + createCurrentLayoutGraph(); + } + } + + if(unselect){ + selected=new HashSet(); + setSelectedObjects(selected); + fireSelectionChanged(); + } + + refreshAll(); + validate(); + autoLayout(); + } + + + /** + * Makes the influence tree (forward==true) or the parent influence tree (==false) + * visible. + */ + public void handleMakeTreeVisible(Instruction inst, boolean forward){ + Set selected=(Set)getSelectedObjects(); + LinkedList handled= new LinkedList(); + + //Prepare for multiselected usage + if(selected.size()==0){ + selected=new HashSet(); + selected.add(inst); + } + else if(!selected.contains(inst)){ + selected=new HashSet(); + selected.add(inst); + } + + for(Instruction i: selected){ + InstructionNodeWidget nw=getNodeWidget(i.getID()); + nw.makeVisible(); + handled.add(i); + if(forward) for(Instruction x: i.getSuccessors()) recHandleMakeTreeVisible(x,forward,handled); + else for(Instruction x: i.getPredecessors()) recHandleMakeTreeVisible(x,forward,handled); + } + createCurrentLayoutGraph(); + refreshAll(); + validate(); + autoLayout(); + } + + private void recHandleMakeTreeVisible(Instruction inst, boolean forward, LinkedList handled){ + if(handled.contains(inst)) return; + handled.add(inst); + InstructionNodeWidget nw=getNodeWidget(inst.getID()); + nw.makeVisible(); + //set vertex dirty + if(WidgetToVertex!=null) { + Vertex v=WidgetToVertex.get(nw); + if(v!=null && v instanceof GraphVertex) ((GraphVertex)v).setDirty(true); + } + if(forward) for(Instruction x: inst.getSuccessors()) recHandleMakeTreeVisible(x,forward, handled); + else for(Instruction x: inst.getPredecessors()) recHandleMakeTreeVisible(x,forward, handled); + } + + /** + * Makes the Cycle Widgets visible- if cycleIndex==-1 then all cycles of the node + * are expanded. + */ + public void handleMakeCycleVisible(String node, int cycleIndex){ + InstructionNodeWidget nw=nodewidgets.get(node); + + if(nw!=null){ + nw.makeVisible(); + + int i=0; + for(InstructionNodeWidget[] a:nw.getCycleWidgets()){ + if(i==cycleIndex || cycleIndex==-1){ + for(InstructionNodeWidget w: a) { + w.makeVisible(); + } + } + i++; + } + } + createCurrentLayoutGraph(); + refreshAll(); + validate(); + autoLayout(); + } + + /** + * Hides all nodes of a specific type. + */ + public void handleSetVisibilityNodeType(boolean visible, Instruction.InstructionType type){ + + for(InstructionNodeWidget w: nodewidgets.values()){ + if(type==null || w.getInstruction().getInstructionType()==type){ + if(visible){ + w.makeVisible(); + } else{ + if(w.isWidgetVisible()) w.addNodeAttribute(new InvisibleAttribute()); + } + } + } + + createCurrentLayoutGraph(); + fireUpdateNodeData(); + } + + + /** + * Makes all nodes within the list visible + */ + void handleShowNodes(Instruction[] instruction) { + for(Instruction i: instruction){ + InstructionNodeWidget w=getNodeWidget(i.getID()); + if(w!=null){ + w.makeVisible(); + } + } + + createCurrentLayoutGraph(); + refreshAll(); + validate(); + autoLayout(); + } + // + + + // + + /** + * Performs a layout id autoLayout==true + */ + public void autoLayout() { + if(autoLayout){ + layout(); + } + } + + private LayoutGraph layoutGraph=null; + private Hashtable VertexToWidget=null; + private Hashtable WidgetToVertex=null; + private Hashtable clusterToId=null; + + /** + * Returns the current layoutgraph. + */ + public LayoutGraph getLayoutGraph(){ + return layoutGraph; + } + + /** + * This method returns the binding list of Widgets an Verticles of the layoutgraph. + */ + public Hashtable getWidgetToVertex(){ + return WidgetToVertex; + } + + /** + * This method fills the layoutGraph, clusterToId, WidgetToVertex and vertexToWidget Datastructures + */ + private void createCurrentLayoutGraph(){ + Hashtable idToVertex=new Hashtable(); + TreeSet links=new TreeSet(); + TreeSet verticles=new TreeSet(); + + clusterToId=new Hashtable(); + VertexToWidget=new Hashtable(); + WidgetToVertex=new Hashtable(); + + DiGraph model=calculationModel.clone(); + + //Remove all invisible verticles from the model + if(!layoutInvisibleNodes){ + for(InstructionNodeWidget nw:getNodeWidgets()){ + if(!nw.isWidgetVisible()) model.removeNode(model.getNode(nw.getID())); + } + } + + int i=0, j=0, ci=0; + //collect the link set + for(DiGraph dg:model.getConnectedComponents()){ + Hashtable idToCluster=new Hashtable(); + for(Edge e:dg.getEdges()){ + String nID1=e.source.ID; + String nID2=e.destination.ID; + Vertex v1,v2; + InstructionNodeWidget nw1=getNodeWidget(nID1); + InstructionNodeWidget nw2=getNodeWidget(nID2); + + if(idToVertex.containsKey(nID1)) v1=idToVertex.get(nID1); + else{ + Cluster c; + String block=nw1.getInstruction().getSourceBlock(); + if(block==null) block=DEFAULTBLOCK; + if(highlightClustering&&nw1.isPathHighlighted()) block=HIGHLIGHTBLOCK; + + if(idToCluster.containsKey(block)){ + c=idToCluster.get(block); + } else{ + c=new GraphCluster(ci++,null,null); + idToCluster.put(block,c); + clusterToId.put(c,block); + } + v1=new GraphVertex(i++,c,nw1.getLocation(),nw1); + idToVertex.put(nID1,v1); + VertexToWidget.put(v1,nw1); + WidgetToVertex.put(nw1,v1); + } + + if(idToVertex.containsKey(nID2)) v2=idToVertex.get(nID2); + else{ + Cluster c; + String block=nw2.getInstruction().getSourceBlock(); + if(block==null) block=DEFAULTBLOCK; + if(highlightClustering&&nw2.isPathHighlighted()) block=HIGHLIGHTBLOCK; + + if(idToCluster.containsKey(block)){ + c=idToCluster.get(block); + } else{ + c=new GraphCluster(ci++,null,null); + idToCluster.put(block,c); + clusterToId.put(c,block); + } + v2=new GraphVertex(i++,c,nw2.getLocation(),nw2); + idToVertex.put(nID2,v2); + VertexToWidget.put(v2,nw2); + WidgetToVertex.put(nw2,v2); + } + + Port p1=new GraphPort(v1); + Port p2=new GraphPort(v2); + + + //Add dataflow clusterlinks + Cluster source=v1.getCluster(); + Cluster target=v2.getCluster(); + ((Set)source.getSuccessors()).add(target); + ((Set)target.getPredecessors()).add(source); + + links.add(new GraphLink(j++,p1,p2)); + } + } + + //collect additional Verticles + for(Node n:model.getNodes()){ + if(n.edges.size()==0){ + InstructionNodeWidget nw=getNodeWidget(n.ID); + Cluster c=new GraphCluster(ci++,null,null); + Vertex v=new GraphVertex(i++,c,nw.getLocation(),nw); + verticles.add(v); + VertexToWidget.put(v,nw); + WidgetToVertex.put(nw,v); + idToVertex.put(n.ID,v); + String block=nw.getInstruction().getSourceBlock(); + if(block==null) block=DEFAULTBLOCK; + if(highlightClustering&&nw.isPathHighlighted()) block=HIGHLIGHTBLOCK; + clusterToId.put(c,block); + } + } + + layoutGraph=new LayoutGraph(links,verticles); + for(Link l: layoutGraph.getLinks()){ + ((GraphPort)l.getFrom()).setLayoutGraph(layoutGraph); + ((GraphPort)l.getTo()).setLayoutGraph(layoutGraph); + } + + } + + /** Performs the layout according to the External Layouter */ + public void layout(){ + //fill datastructures: layoutgraph, VertexToWidget and clusterToId + createCurrentLayoutGraph(); + //Do layouting step! + removeClusterWidgets(); + + layouter.setUseCurrentNodePositions(USECURRENTNODEPOSITIONS); + layouter.doLayout(layoutGraph); + + + //Add cluster widgets + if(layouter.isClusteringSupported()){ + Hashtable> clusters=new Hashtable>(); + for(Vertex v:layoutGraph.getVertices()){ + if(!clusters.containsKey(v.getCluster())){ + clusters.put(v.getCluster(),new LinkedList()); + } + clusters.get(v.getCluster()).add(VertexToWidget.get(v)); + } + + LinkedList widgets=new LinkedList(); + for(Cluster c :clusters.keySet()){ + LinkedList l=clusters.get(c); + String id=clusterToId.get(c); + Color col=null; + if(HIGHLIGHTBLOCK.equals(id)) col=Color.RED; + else col=Color.BLUE; + widgets.add(new ClusterWidget(id,l,this,col)); + } + + setClustersWidgets(widgets); + } + + + + + //Set the real positions of the graph + if(locationAnimation && layouter.isAnimationSupported()){ + SetLocationAnimator animator=new SetLocationAnimator(this,VertexToWidget); + animator.animate(); + } + else{ + for(Vertex v: layoutGraph.getVertices()){ + InstructionNodeWidget nw=VertexToWidget.get(v); + //Instruction widget inset correction: Neccessary because layouter do not support insets! + Point pos=v.getPosition(); + pos.translate(InstructionNodeWidget.BORDERINSET,InstructionNodeWidget.BORDERINSET); + nw.setPreferredLocation(pos); + if(v instanceof GraphVertex) ((GraphVertex)v).setDirty(true); + } + refreshClusterWidgets(); + } + + validate(); + + } + + /** + * Sets the cluster widgets, this method is meant to be + * used within the external layouter. + */ + public void setClustersWidgets(Collection clusters){ + removeClusterWidgets(); + if(clusters!=null){ + this.clusters=clusters; + + for(ClusterWidget w:clusters) { + clusterLayer.addChild(w); + w.setVisibility(clusterBordersVisible); + w.refresh(); + for(Widget iw:w.getWidgets()){ + if(iw instanceof InstructionNodeWidget) + ((InstructionNodeWidget)iw).setClusterWidget(w); + } + + } + } + fireUpdateNodeData(); + } + + + /** + * Removes all clusters. This method is called + * before layouting is done. + */ + public void removeClusterWidgets(){ + if(clusters!=null){ + for(ClusterWidget w:clusters){ + clusterLayer.removeChild(w); + } + for(InstructionNodeWidget w:nodewidgets.values()){ + w.setClusterWidget(null); + } + } + clusters=null; + fireUpdateNodeData(); + } + // + + + // + /** + * Implemented to achive a clear cut between data model and visualisation. + * Otherwise the Instruction class would have to save position data. + */ + public InstructionNodeWidget getNodeWidget(String i){ + return nodewidgets.get(i); + } + + /** + * Returns all Nodewidgets + */ + public InstructionNodeWidget[] getNodeWidgets(){ + return nodewidgets.values().toArray(new InstructionNodeWidget[nodewidgets.size()]); + } + + /** Sets the layouter */ + public void setExternalLayouter(ExternalGraphLayouter l){ + layouter=l; + } + + /** Return the current layouter */ + public ExternalGraphLayouter getExternalLayouter(){ + return layouter; + } + + /** + * Defines if links between different clusters are grayed. + */ + public void setInterClusterLinkGrayed(boolean b){ + interCluserLinkGray=b; + } + + /** + * Returns if links between different clusters are grayed + */ + public boolean isInterClusterLinkGrayed(){ + return interCluserLinkGray; + } + + /** + * Defines if scene performs autolayout. + */ + public void setAutoLayout(boolean b){ + autoLayout=b; + autoLayout(); + + } + + /** + * Is Auto Layout active?. + */ + public boolean isAutoLayout(){ + return autoLayout; + } + + + /** + * Defines if invisible nodes are layouted. + */ + public void setLayoutInvisibleNodes(boolean b){ + layoutInvisibleNodes=b; + } + + /** + * Returns if invisible Nodes are layouted or not. + */ + public boolean isLayoutInvisibleNodes(){ + return layoutInvisibleNodes; + } + + /** + * Defines if node animation is used. + */ + public void setNodeAnimation(boolean b){ + locationAnimation=b; + } + + /** + * Returns if invisible Nodes are layouted or not. + */ + public boolean isNodeAnimation(){ + return locationAnimation; + } + + /** + * Returns a clone of the calculation model of the scene + */ + public DiGraph getCaluculationModel(){ + return calculationModel.clone(); + } + + /** + * Defines if highlightclustering is used. + */ + public void setHighlightClustering(boolean b){ + highlightClustering=b; + } + + /** + * Returns if highlightclustering is active + */ + public boolean isHighlightClustering(){ + return highlightClustering; + } + + /** + * Returns if cluster borders are visible + */ + public boolean isClusterBordersVisible(){ + return clusterBordersVisible; + } + + /** + * Defines if the layout algorithms should build on the current node positions. + * This does only applys to layout-algorithms optimizing from a predefined positioning + * (like force layouts) + */ + public void setUseCurrentNodePositions(boolean b){ + USECURRENTNODEPOSITIONS=b; + } + + /** + * Returns if the current node position is used to build on by the external layouter. + * This does only applys to layout-algorithms optimizing from a predefined positioning + * (like force layouts) + */ + public boolean isUseCurrentNodePositions(){ + return USECURRENTNODEPOSITIONS; + } + + + /** + * Sets is clusters borders are shown or not. + */ + public void setClusterBordersVisible(boolean b){ + clusterBordersVisible=b; + if(clusters!=null){ + for(ClusterWidget w: clusters){ + w.setVisibility(b); + } + } + refreshAll(); + validate(); + } + // + + + // + /** + * Registers an scene listener + */ + public void addInstructionSceneListener(InstructionSceneListener l){ + if(!listeners.contains(l)){ + listeners.add(l); + } + } + + /** + * Removes a scene listener + */ + public void removeInstructionSceneListener(InstructionSceneListener l){ + listeners.remove(l); + } + + /** + * Fires the double clicked event + */ + protected void fireDoubleClicked(InstructionNodeWidget w){ + for(InstructionSceneListener l: listeners){ + l.doubleClicked(w); + } + } + + /** + * Fires the update node data event + */ + protected void fireUpdateNodeData(){ + for(InstructionSceneListener l: listeners){ + l.updateNodeData(); + } + } + + /** + * Fires the selection changed event + */ + protected void fireSelectionChanged(){ + for(InstructionSceneListener l: listeners){ + HashSet w=new HashSet(); + for(Object o:getSelectedObjects()){ + if(o instanceof Instruction){ + InstructionNodeWidget wi=getNodeWidget(((Instruction)o).getID()); + if(wi != null) w.add(wi); + } + } + l.selectionChanged(w); + } + } + + // + + + // + /** + * RectangularSelect Method returning a widget to be use as + * selection boarder + */ + public Widget createSelectionWidget() { + Widget ret=new Widget(this); + ret.setBorder(BorderFactory.createDashedBorder(Color.BLACK,10,5,true)); + return ret; + } + + /** + * RectangularSelect execution + */ + public void performSelection(Rectangle rectangle) { + HashSet set=new HashSet(); + + //change to a rectangle with (x,y) at the top left + if(rectangle.width<0){ + rectangle.x=rectangle.x+rectangle.width; + rectangle.width*=-1; + } + if(rectangle.height<0){ + rectangle.y=rectangle.y+rectangle.height; + rectangle.height*=-1; + } + + for(InstructionNodeWidget n:nodewidgets.values()){ + if(n.isWidgetVisible() && rectangle.contains(n.getPreferredLocation())) + set.add(n.getInstruction()); + } + + setSelectedObjects(set); + refreshAll(); + validate(); + fireSelectionChanged(); + } + + /** MoveStrategy- free move, no modifications*/ + public Point locationSuggested(Widget widget, Point original, Point suggested) { + return suggested; + } + + /** + * Selectes exactly one widget defined by the id and centers the view + * on it if centeron==true. + */ + public void setSingleSelectedWidget(String id, boolean centeron) { + InstructionNodeWidget node=getNodeWidget(id); + + if(node!=null && node.isWidgetVisible()) { + setSelectedObjects(Collections.singleton(node.getInstruction())); + refreshAll(); + validate(); + fireSelectionChanged(); + if(centeron) centerOn(node.getPreferredLocation()); + } + } + + /* Single click selection method- Copy from ObjectScene Selectprovider*/ + public boolean isAimingAllowed(Widget widget, Point point, boolean b) { + return false; + } + + /* Single click selection method- Copy from ObjectScene Selectprovider*/ + public boolean isSelectionAllowed(Widget widget, Point point, boolean b) { + Object object = findObject (widget); + return object != null && (b || ! getSelectedObjects ().contains (object)); + } + + /* Single click selection method- Copy from ObjectScene Selectprovider*/ + public void select(Widget widget, Point point, boolean b) { + Object object = findObject (widget); + if (object != null) { + if (getSelectedObjects ().contains (object)) return; + userSelectionSuggested (Collections.singleton (object), b); + } else + userSelectionSuggested (Collections.emptySet (), b); + fireSelectionChanged(); + } + + + + /** + * This class implements a MoveProvider to achieve the funktionality of + * a multi widget movement. + */ + private class MultiWidgetMovementProvider implements MoveProvider{ + + private MoveProvider mp=ActionFactory.createDefaultMoveProvider(); + + public void movementStarted(Widget widget) { + mp.movementStarted(widget); + } + + public void movementFinished(Widget widget) { + mp.movementFinished(widget); + } + + public Point getOriginalLocation(Widget widget) { + return widget.getPreferredLocation();//mp.getOriginalLocation(widget); + } + + public void setNewLocation(Widget widget, Point point) { + //test for movement support + Scene s=widget.getScene(); + if(s instanceof InstructionNodeGraphScene){ + boolean r=((InstructionNodeGraphScene)s).getExternalLayouter().isMovementSupported(); + if(!r) return; + } + //perform movement + Point original=getOriginalLocation(widget); + int dx=point.x-original.x; + int dy=point.y-original.y; + for(Object o: getSelectedObjects()){ + if(o instanceof Instruction){ + InstructionNodeWidget w=getNodeWidget(((Instruction)o).getID()); + if(w!=widget){ + Point p=(Point)w.getPreferredLocation().clone(); + p.translate(dx,dy); + + //Update layoutgraph Positions + Vertex v=WidgetToVertex.get(w); + if(v!=null) v.setPosition(p); + ((GraphVertex)v).setDirty(true); + + w.setPreferredLocation(p); + w.refresh(); + ClusterWidget cw=w.getClusterWidget(); + if(cw!=null) cw.refresh(); + } + } + + } + mp.setNewLocation(widget,point); + + //Update layoutgraph Positions + Vertex v=WidgetToVertex.get(widget); + if(v!=null) v.setPosition(point); + ((GraphVertex)v).setDirty(true); + + //Refresh of cluster for single node movement + if(widget instanceof InstructionNodeWidget){ + ClusterWidget cw=((InstructionNodeWidget)widget).getClusterWidget(); + if(cw!=null) cw.refresh(); + } + } + } + + // + + + protected Widget attachNodeWidget (Instruction node) { + + InstructionNodeWidget widget = new InstructionNodeWidget (node,this); + + WidgetAction.Chain actions = widget.getActions (); + actions.addAction (createObjectHoverAction ()); + + actions.addAction (ActionFactory.createSelectAction(this)); + //actions.addAction(createSelectAction()); + actions.addAction (moveAction); + + actions.addAction(ActionFactory.createEditAction(this)); + + nodewidgets.put(node.getID(),widget); + mainLayer.addChild (widget); + return widget; + } + + + protected void attachEdgeSourceAnchor (String edge, Instruction oldSourceNode, Instruction sourceNode) { + ConnectionWidget edgeWidget = (ConnectionWidget) findWidget (edge); + Widget sourceNodeWidget = findWidget (sourceNode); + Anchor sourceAnchor = AnchorFactory.createRectangularAnchor (sourceNodeWidget); + edgeWidget.setSourceAnchor (sourceAnchor); + } + + protected void attachEdgeTargetAnchor (String edge, Instruction oldTargetNode, Instruction targetNode) { + ConnectionWidget edgeWidget = (ConnectionWidget) findWidget (edge); + Widget targetNodeWidget = findWidget (targetNode); + Anchor targetAnchor = AnchorFactory.createRectangularAnchor (targetNodeWidget); + edgeWidget.setTargetAnchor (targetAnchor); + } + + protected Widget attachEdgeWidget(String edge) { + ConnectionWidget widget = new InstructionConnectionWidget (this); + widget.setTargetAnchorShape (AnchorShape.TRIANGLE_FILLED); + connectionLayer.addChild (widget); + widget.setRouter(linkRouter); + return widget; + } + + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/InstructionNodeWidget.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/InstructionNodeWidget.java new file mode 100644 index 000000000000..9c2c9a75dafa --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/InstructionNodeWidget.java @@ -0,0 +1,879 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.graph; + +import at.ssw.visualizer.dataflow.attributes.IAdditionalWidgetAttribute; +import at.ssw.visualizer.dataflow.attributes.IExpandNodeAttribute; +import at.ssw.visualizer.dataflow.attributes.INodeAttribute; +import at.ssw.visualizer.dataflow.attributes.IInvisibilityAttribute; +import at.ssw.visualizer.dataflow.attributes.IPathHighlightAttribute; +import at.ssw.visualizer.dataflow.attributes.IPopupContributorAttribute; +import at.ssw.visualizer.dataflow.attributes.InvisibleAttribute; +import at.ssw.visualizer.dataflow.instructions.Instruction; +import java.awt.Color; +import java.awt.Font; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.event.MenuKeyEvent; +import javax.swing.event.MenuKeyListener; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; +import org.netbeans.api.visual.action.ActionFactory; +import org.netbeans.api.visual.action.PopupMenuProvider; +import org.netbeans.api.visual.border.Border; +import org.netbeans.api.visual.border.BorderFactory; +import org.netbeans.api.visual.layout.LayoutFactory; +import org.netbeans.api.visual.widget.LabelWidget; +import org.netbeans.api.visual.widget.Scene; +import org.netbeans.api.visual.widget.SeparatorWidget; +import org.netbeans.api.visual.widget.Widget; + +/** + * + * @author Stefan Loidl + */ +public class InstructionNodeWidget extends Widget{ + + /*Font of the nodes*/ + protected final static String DEFAULTFONT = "Arial-12"; + /*This string is used if text in the node is abbriviated*/ + protected final static String ABBREV="..."; + //Length bounds of text within nodes + protected final static int MAXSTRINGLENGTH=15; + protected final static int MINSTRINGLENGTH=7; + + + //Colors + private static Color colorParameter=new Color(255,230,200); + private static Color colorParameterSelect=new Color(240,180,0); + private static Color colorConstant=new Color(255,200,200); + private static Color colorConstantSelect=new Color(240,0,0); + private static Color colorPhi=new Color(200,200,255); + private static Color colorPhiSelect=new Color(0,0,240); + private static Color colorOperation=new Color(200,255,200); + private static Color colorOperationSelect=new Color(0,240,0); + private static Color colorControlFlow=new Color(210,255,255); + private static Color colorControlFlowSelect=new Color(0,240,200); + private static Color colorDefault=new Color(255,255,255); + private static Color colorDefaultSelect=new Color(245,245,245); + + private static Color colorLine=Color.BLACK; + private static Color colorHLLine=Color.GRAY; + + + //Because Borders can only be created by Factorys, but colors cannot be + //changed afterwards- to reduce GC Load Boarders are created only once! + private Border selectedBorder; + private Border selectedHLBorder; + private Border border; + private Border HLBorder; + private Border emptyBorder; + + public static final int BORDERARC=10; + public static final int BORDERINSET=5; + + //popup of the node widget + private JPopupMenu popup; + + //default menu item names + private static final String EXPANDINFLUENCE="Expand Influenced Tree"; + private static final String EXPANDNODE="Expand Node"; + private static final String EXPANDPARENTINFLUENCE="Expand Parent Influence"; + private static final String EXPANDCYCLES="Expand Cycles"; + + private static final String ALL="(All)"; + + private static final String TOGGLEVISIBILITY="Toggle Visibility"; + private static final String HIDEALLBUT="Hide All But Current"; + private static final String SHOWINFLUENCE="Show Influence Tree"; + private static final String SHOWPARENTINFLUENCE="Show Parent Influence"; + private static final String SHOWCYCLES="Show Cycles"; + private static final String SHOWPREDECESSORS="Show Predecessors"; + private static final String SHOWSUCCESSORS="Show Successors"; + + + private static final String CYCLEITEMSEPERATOR="-"; + private static final char CYCLECMDSEPERATOR='-'; + + + //Sub Menu within the popup + private static final String GOTOSUCCESSORS="Go to Successor"; + private static final String GOTOPREDECESSOR="Go to Predecessor"; + private static final String VISIBILITY="Visibility"; + + + + private LabelWidget lwID; + private LabelWidget lwInstruction; + private LabelWidget lwBlock; + + //indicate if predecessors or sucessors exist. + private Widget pred, suc; + + //Data model for the node. + private Instruction instruction; + + //Attributelist + private LinkedList attributeList; + + //Widgets that are appended to the standard view + private LinkedList additionalWidgets; + + //true if the node contains an IPathHighlightAttribute + private boolean pathHighlighted=false; + + //Inforation of cycles the node is within. + private Collection cycles=null; + + //Cluster the node is within + private ClusterWidget cluster; + + //This value is set during setWidgetData method + private boolean expanded=false; + + + /** Creates a new instance of InstructionNodeWidget */ + public InstructionNodeWidget(Instruction i, Scene s) { + super(s); + instruction=i; + + additionalWidgets=new LinkedList(); + attributeList=new LinkedList(); + + //initialize popup menu + popup=new JPopupMenu(); + getActions().addAction(ActionFactory.createPopupMenuAction(new PopupMenuProvider() { + public JPopupMenu getPopupMenu(Widget widget, Point point) { + return getPopup(); + } + })); + + this.setLayout(LayoutFactory.createVerticalFlowLayout()); + + lwID= new LabelWidget(s,i.getID()); + lwID.setFont(Font.decode(DEFAULTFONT).deriveFont(Font.BOLD)); + this.addChild (lwID); + lwID.resolveBounds(lwID.getLocation(),lwID.getPreferredBounds()); + + lwInstruction=new LabelWidget(s,modifiyStringLength(i.getInstructionString(),MINSTRINGLENGTH,MAXSTRINGLENGTH,ABBREV)); + lwInstruction.setFont(Font.decode(DEFAULTFONT)); + lwInstruction.setAlignment(LabelWidget.Alignment.CENTER); + this.addChild(lwInstruction); + lwInstruction.resolveBounds(lwInstruction.getLocation(),lwInstruction.getPreferredBounds()); + + lwBlock=new LabelWidget(s,i.getSourceBlock()); + lwBlock.setFont(Font.decode(DEFAULTFONT)); + this.addChild(lwBlock); + lwBlock.resolveBounds(lwBlock.getLocation(),lwBlock.getPreferredBounds()); + + pred=new HiddenNodesWidget(s,true); + suc=new HiddenNodesWidget(s,false); + + this.setToolTipText(createToolTip()); + + initWidgetBorders(); + setWidgetData(); + } + + + // + /** + * Return the color that is used to paint a node of specific type. + */ + protected static Color getColorByType(Instruction.InstructionType type, boolean selected){ + switch(type){ + case PARAMETER: + if(selected) return colorParameterSelect; + else return colorParameter; + case CONSTANT: + if(selected) return colorConstantSelect; + else return colorConstant; + case PHI: + if(selected) return colorPhiSelect; + else return colorPhi; + case CONTROLFLOW: + if(selected) return colorControlFlowSelect; + else return colorControlFlow; + case OPERATION: + if(selected) return colorOperationSelect; + else return colorOperation; + default: + if(selected) return colorDefaultSelect; + else return colorDefault; + } + } + + /** + * Returns the line color to be used in the widget. + */ + protected static Color getLineColor(){ + return colorLine; + } + + /** + * This function sets the borders (highlighted and selected combinations); + */ + private void initWidgetBorders(){ + Color c=getColorByType(instruction.getInstructionType(),false); + Color cs=getColorByType(instruction.getInstructionType(),true); + + border= BorderFactory.createRoundedBorder(BORDERARC,BORDERARC, + BORDERINSET,BORDERINSET,c,colorLine); + selectedBorder= BorderFactory.createRoundedBorder(BORDERARC,BORDERARC, + BORDERINSET,BORDERINSET,cs,colorLine); + HLBorder= BorderFactory.createRoundedBorder(BORDERARC,BORDERARC, + BORDERINSET,BORDERINSET,c,colorHLLine); + selectedHLBorder= BorderFactory.createRoundedBorder(BORDERARC,BORDERARC, + BORDERINSET,BORDERINSET,cs,colorHLLine); + + emptyBorder=BorderFactory.createEmptyBorder(); + } + + + + /** + * This method handles the the Data that is shown within the widget. This + * data depends on the Attibutes of the Widget. + */ + private void setWidgetData(){ + boolean visible=true, showBlock=false, showInstr=false; + additionalWidgets.clear(); + pathHighlighted=false; + + //determine node attibutes + Iterator iter=attributeList.iterator(); + while(iter.hasNext()){ + INodeAttribute a=iter.next(); + + if(!a.validate()) { + if(a.removeable()) { + iter.remove(); + } + + continue; + } + + if(a instanceof IInvisibilityAttribute) visible=false; + if(a instanceof IExpandNodeAttribute){ + if(((IExpandNodeAttribute)a).showBlock()) showBlock=true; + if(((IExpandNodeAttribute)a).showInstruction()) showInstr=true; + } + + if(a instanceof IPathHighlightAttribute) pathHighlighted=true; + + if(a instanceof IAdditionalWidgetAttribute){ + additionalWidgets.add(((IAdditionalWidgetAttribute)a).getWidget()); + } + } + + removeChildren(); + + if(visible){ + //Add invisible predecessor indicator + InstructionNodeGraphScene s=(InstructionNodeGraphScene) getScene(); + for(Instruction i: instruction.getPredecessors()){ + InstructionNodeWidget w=s.getNodeWidget(i.getID()); + if(!(w==null) && !w.isWidgetVisible()){ + addChild(pred); + break; + } + } + + expanded=false; + addChild(lwID); + if(showInstr) { + addChild(lwInstruction); + expanded=true; + } + if(showBlock) { + addChild(lwBlock); + expanded=true; + } + //Embedded widgets are only shown if expanded + if(showInstr){ + Iterator iterator =additionalWidgets.iterator(); + while(iterator.hasNext()){ + addChild(iterator.next()); + } + } + + //Add invisible sucessor indicator + for(Instruction i: instruction.getSuccessors()){ + InstructionNodeWidget w=s.getNodeWidget(i.getID()); + if(!(w==null) && !w.isWidgetVisible()){ + addChild(suc); + break; + } + } + } + + setWidgetBorder(); + resolveBounds(getLocation(),calculatePreferredBounds()); + } + + /** + * NOTE: This method is a only slightly changed copy an paste from Widget!!! + * Instead of getBounds- getPreferredBounds is used. + */ + private Rectangle calculatePreferredBounds () { + Insets insets = border.getInsets (); + Rectangle clientArea = calculateClientArea (); + + + + for (Widget child : getChildren()) { + Point location = child.getLocation (); + Rectangle bounds = child.getPreferredBounds (); + bounds.translate (location.x, location.y); + clientArea.add (bounds); + } + clientArea.x -= insets.left; + clientArea.y -= insets.top; + clientArea.width += insets.left + insets.right; + clientArea.height += insets.top + insets.bottom; + return clientArea; + } + + + /** + * This Method handels the Widgets border and coloring depending on hovering + * and selection state. + */ + private void setWidgetBorder(){ + if(isWidgetVisible()){ + if(this.getState().isHovered()){ + + if(this.getState().isSelected()) + this.setBorder(selectedHLBorder); + else + this.setBorder(HLBorder); + + lwID.setForeground(colorHLLine); + lwInstruction.setForeground(colorHLLine); + lwBlock.setForeground(colorHLLine); + } else{ + + if(this.getState().isSelected()) + this.setBorder(selectedBorder); + else + this.setBorder(border); + + lwID.setForeground(colorLine); + lwInstruction.setForeground(colorLine); + lwBlock.setForeground(colorLine); + } + } + else this.setBorder(emptyBorder); + } + + // + + + /** + * This Method is used to unifiy the length of Stings. Therefore a String + * is modified according to the following rules: + * 1. If in==null -> Return a String with min blanks + * 2. If length of in is grater max then in is cut and abbrev is concatinated + * to indicate that the String was shortend + * 3. If Length is smaller than min blanks are added + */ + protected static String modifiyStringLength(String in, int min, int max, String abbrev){ + StringBuffer mod; + if(in == null) + mod=new StringBuffer(""); + else + mod=new StringBuffer(in); + + if(mod.length() max){ + mod=new StringBuffer(mod.substring(0,max-abbrev.length())+abbrev); + } + + return mod.toString(); + } + + /** + * Returns the instruction which this nodewidget is a representation for. + */ + public Instruction getInstruction(){ + return instruction; + } + + /** + * Overwritten from parent class- Painting the widget + */ + protected void paintWidget(){ + setWidgetBorder(); + super.paintWidget(); + } + + + /** + * Is the node visible? + */ + public boolean isWidgetVisible(){ + Iterator iter=attributeList.iterator(); + while(iter.hasNext()){ + INodeAttribute a=iter.next(); + if(a instanceof IInvisibilityAttribute && a.validate()) return false; + } + return true; + } + + /** + * Removes all invisible attributes from the widget. + */ + public void makeVisible(){ + for(INodeAttribute att: attributeList.toArray(new INodeAttribute[attributeList.size()])){ + if(att instanceof IInvisibilityAttribute && att.removeable()) attributeList.remove(att); + } + } + + /** + * Returns if the instruction-string or basic-block is shown. + */ + public boolean isExpanded(){ + return expanded; + } + + /** + * Adds a new attriute to the node. + */ + public void addNodeAttribute(INodeAttribute a){ + if(a!=null) attributeList.add(a); + } + + /** + * Refreshes the visual appearence of the node and schedules it for + * revalidation. + */ + protected void refresh() { + setWidgetData(); + revalidate(); + } + + /** + * This method fills the popup menu. + */ + private void rebuildPopupMenu(){ + popup.removeAll(); + JMenuItem item; + JMenu menu; + + + //MENU Go to Predecessor + menu=new JMenu(GOTOPREDECESSOR); + if(instruction.getPredecessors().length==0){ + menu.setEnabled(false); + } else{ + for(Instruction i: instruction.getPredecessors()){ + item=new JMenuItem(i.getID()); + item.setActionCommand(i.getID()); + item.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + InstructionNodeGraphScene s=(InstructionNodeGraphScene)getScene(); + s.setSingleSelectedWidget(e.getActionCommand(), true); + //Make the new node edited -> selected in the list + s.edit(s.getNodeWidget(e.getActionCommand())); + } + }); + menu.add(item); + } + } + popup.add(menu); + + //MENU Go to Successor + menu=new JMenu(GOTOSUCCESSORS); + if(instruction.getSuccessors().length==0){ + menu.setEnabled(false); + } else{ + for(Instruction i: instruction.getSuccessors()){ + item=new JMenuItem(i.getID()); + item.setActionCommand(i.getID()); + item.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + InstructionNodeGraphScene s=(InstructionNodeGraphScene)getScene(); + s.setSingleSelectedWidget(e.getActionCommand(), true); + //Make the new node edited -> selected in the list + s.edit(s.getNodeWidget(e.getActionCommand())); + } + }); + menu.add(item); + } + } + popup.add(menu); + popup.addSeparator(); + + + //MENUITEM: Expand Node + item =new JMenuItem(EXPANDNODE); + item.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + Scene s=getScene(); + if(s instanceof InstructionNodeGraphScene){ + ((InstructionNodeGraphScene)s).handleExpandNode(instruction); + } + } + }); + popup.add(item); + + //MENUITEM: Expand Influence Tree + item =new JMenuItem(EXPANDINFLUENCE); + item.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + Scene s=getScene(); + if(s instanceof InstructionNodeGraphScene){ + ((InstructionNodeGraphScene)s).handleExpandInfluenceTree(instruction, true); + } + } + }); + popup.add(item); + item.setEnabled(instruction.getSuccessors().length>0); + + + //MENUITEM: Expand Parent Influence + item =new JMenuItem(EXPANDPARENTINFLUENCE); + item.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + Scene s=getScene(); + if(s instanceof InstructionNodeGraphScene){ + ((InstructionNodeGraphScene)s).handleExpandInfluenceTree(instruction,false); + } + } + }); + popup.add(item); + item.setEnabled(instruction.getPredecessors().length>0); + + + + //MENU Expand Cycles + menu=new JMenu(EXPANDCYCLES); + if(cycles==null || cycles.size()==0){ + menu.setEnabled(false); + } else{ + + //Expand all Cycles + item =new JMenuItem(ALL); + item.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + Scene s=getScene(); + if(s instanceof InstructionNodeGraphScene){ + ((InstructionNodeGraphScene)s).handleExpandCycles(getID()); + } + } + }); + menu.add(item); + + int i=0; + for(InstructionNodeWidget[] nw: cycles){ + StringBuffer st=new StringBuffer(); + for(InstructionNodeWidget n:nw){ + st.append(n.getID()); + st.append(CYCLEITEMSEPERATOR); + } + st.setLength(st.length()-CYCLEITEMSEPERATOR.length()); + + item=new JMenuItem(st.toString()); + item.setActionCommand(getID()+CYCLECMDSEPERATOR+String.valueOf(i)); + item.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + InstructionNodeGraphScene s=(InstructionNodeGraphScene)getScene(); + String com=e.getActionCommand(); + int index=com.indexOf(CYCLECMDSEPERATOR); + if(index!=-1){ + try{ + s.handleExpandCycle(com.substring(0,index), + Integer.parseInt(com.substring(index+1))); + }catch(Exception ex){} + } + } + }); + menu.add(item); + i++; + } + } + popup.add(menu); + popup.addSeparator(); + + //ITEM Toggle Visibility + item =new JMenuItem(TOGGLEVISIBILITY); + item.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + InstructionNodeGraphScene s=(InstructionNodeGraphScene)getScene(); + s.handleToggleVisibility(instruction, true); + + } + }); + popup.add(item); + + //ITEM Hide All BuT + item =new JMenuItem(HIDEALLBUT); + item.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + InstructionNodeGraphScene s=(InstructionNodeGraphScene)getScene(); + s.handleHideAllBut(instruction); + } + }); + popup.add(item); + + //ITEM: Make Influence Tree Visible + item =new JMenuItem(SHOWINFLUENCE); + item.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + InstructionNodeGraphScene s=(InstructionNodeGraphScene)getScene(); + s.handleMakeTreeVisible(instruction,true); + } + }); + popup.add(item); + item.setEnabled(instruction.getSuccessors().length>0); + + //ITEM: Make Parent Influence Visible + item =new JMenuItem(SHOWPARENTINFLUENCE); + item.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + InstructionNodeGraphScene s=(InstructionNodeGraphScene)getScene(); + s.handleMakeTreeVisible(instruction,false); + } + }); + popup.add(item); + item.setEnabled(instruction.getPredecessors().length>0); + + + //MENU Make Cycles Visible + menu=new JMenu(SHOWCYCLES); + if(cycles==null || cycles.size()==0){ + menu.setEnabled(false); + } else{ + + //Expand all Cycles + item =new JMenuItem(ALL); + item.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + Scene s=getScene(); + if(s instanceof InstructionNodeGraphScene){ + ((InstructionNodeGraphScene)s).handleMakeCycleVisible(getID(),-1); + } + } + }); + menu.add(item); + + int i=0; + for(InstructionNodeWidget[] nw: cycles){ + StringBuffer st=new StringBuffer(); + for(InstructionNodeWidget n:nw){ + st.append(n.getID()); + st.append(CYCLEITEMSEPERATOR); + } + st.setLength(st.length()-CYCLEITEMSEPERATOR.length()); + + item=new JMenuItem(st.toString()); + item.setActionCommand(getID()+CYCLECMDSEPERATOR+String.valueOf(i)); + item.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + InstructionNodeGraphScene s=(InstructionNodeGraphScene)getScene(); + String com=e.getActionCommand(); + int index=com.indexOf(CYCLECMDSEPERATOR); + if(index!=-1){ + try{ + s.handleMakeCycleVisible(com.substring(0,index), + Integer.parseInt(com.substring(index+1))); + }catch(Exception ex){} + } + } + }); + menu.add(item); + i++; + } + } + popup.add(menu); + + //MENU Show Predecessors + menu=new JMenu(SHOWPREDECESSORS); + LinkedList list=new LinkedList(); + InstructionNodeGraphScene s=(InstructionNodeGraphScene)getScene(); + for(Instruction i:instruction.getPredecessors()){ + InstructionNodeWidget w=s.getNodeWidget(i.getID()); + if(w!=null && !w.isWidgetVisible()) list.add(w); + } + + if(list.size()>0){ + //Show all + item =new JMenuItem(ALL); + item.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + InstructionNodeGraphScene scene=(InstructionNodeGraphScene)getScene(); + scene.handleShowNodes(getInstruction().getPredecessors()); + } + }); + menu.add(item); + + for(InstructionNodeWidget w: list){ + item =new JMenuItem(w.getID()); + item.setActionCommand(w.getID()); + item.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + InstructionNodeGraphScene scene=(InstructionNodeGraphScene)getScene(); + InstructionNodeWidget nw=scene.getNodeWidget(e.getActionCommand()); + if(nw!=null){ + Instruction[] inst=new Instruction[1]; + inst[0]=nw.getInstruction(); + scene.handleShowNodes(inst); + } + } + }); + menu.add(item); + } + } + else menu.setEnabled(false); + popup.add(menu); + + + //MENU Show Successors + menu=new JMenu(SHOWSUCCESSORS); + list=new LinkedList(); + + for(Instruction i:instruction.getSuccessors()){ + InstructionNodeWidget w=s.getNodeWidget(i.getID()); + if(w!=null && !w.isWidgetVisible()) list.add(w); + } + + if(list.size()>0){ + //Show all + item =new JMenuItem(ALL); + item.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + InstructionNodeGraphScene scene=(InstructionNodeGraphScene)getScene(); + scene.handleShowNodes(getInstruction().getSuccessors()); + } + }); + menu.add(item); + + for(InstructionNodeWidget w: list){ + item =new JMenuItem(w.getID()); + item.setActionCommand(w.getID()); + item.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + InstructionNodeGraphScene scene=(InstructionNodeGraphScene)getScene(); + InstructionNodeWidget nw=scene.getNodeWidget(e.getActionCommand()); + if(nw!=null){ + Instruction[] inst=new Instruction[1]; + inst[0]=nw.getInstruction(); + scene.handleShowNodes(inst); + } + } + }); + menu.add(item); + } + } + else menu.setEnabled(false); + popup.add(menu); + + boolean first=true; + //MENUITEMS added by contibutor attibutes + Iterator iter=attributeList.iterator(); + while(iter.hasNext()){ + INodeAttribute a=iter.next(); + if(a instanceof IPopupContributorAttribute && a.validate()) { + item=((IPopupContributorAttribute)a).getMenuItem(); + //seperator before the first element + if(first){ + popup.addSeparator(); + first=false; + } + popup.add(item); + } + } + } + + /** + * Returns if the node is expanded. + */ + public boolean isPathHighlighted(){ + return pathHighlighted; + } + + /** Returns the unique id of the node*/ + public String getID() { + return instruction.getID(); + } + + /** Sets the cycles the node is within*/ + public void setCycles(Collection cycles) { + this.cycles=cycles; + setToolTipText(createToolTip()); + } + + /** Returns a collection of all cycles the node is within */ + public Collection getCycleWidgets(){ + return cycles; + } + + /** Sets the cluster the node is within */ + public void setClusterWidget(ClusterWidget w){ + cluster=w; + } + + /** Returns the cluster */ + public ClusterWidget getClusterWidget(){ + return cluster; + } + + /** Creates a html tooltip for the instruction */ + protected String createToolTip(){ + StringBuffer ret=new StringBuffer(); + ret.append("Name: "+instruction.getID()+"

"); + ret.append("Instruction: "+instruction.getInstructionString()+"

"); + if(cycles!=null && cycles.size() >0){ + ret.append("Cycles:"); + for(InstructionNodeWidget[] a:cycles){ + ret.append("

* "); + for(InstructionNodeWidget w:a){ + ret.append(w.getID()); + ret.append(" "); + } + } + } + ret.append(""); + return ret.toString(); + } + + /** + * Returns the popup menu of the node + */ + public JPopupMenu getPopup(){ + rebuildPopupMenu(); + return popup; + } + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/InstructionSceneListener.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/InstructionSceneListener.java new file mode 100644 index 000000000000..ada8ea61f98e --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/InstructionSceneListener.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.graph; + +import java.util.Set; + +/** + * Listener interface for the Node Graph Scene. Listeners are notified on changes + * within the Scene. + * + * @author Stefan Loidl + */ +public interface InstructionSceneListener { + public void doubleClicked(InstructionNodeWidget w); + public void updateNodeData(); + public void selectionChanged(Set w); +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/SetLocationAnimator.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/SetLocationAnimator.java new file mode 100644 index 000000000000..76017c076403 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/graph/SetLocationAnimator.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.graph; + +import at.ssw.positionmanager.Vertex; +import at.ssw.positionmanager.impl.GraphVertex; +import java.awt.Point; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import org.netbeans.api.visual.animator.Animator; + +/** + * This Animator works fairly like the SetPreferredLocation Animator built within + * the visual library. The special difference is, that it handles more than one element + * per iteration. + * The reason why it is implemented are the cluster nodes which are calculated from the + * node positions have to be updated after each step. + * + * @author Stefan Loidl + */ +public class SetLocationAnimator extends Animator{ + + private LinkedList widgets; + private LinkedList startpoint; + private LinkedList targetpoint; + private InstructionNodeGraphScene scene; + + + /** + * VertexToWidget contains the Vertex with the target position, and the widget with the + * start position. + * It's essential that scene parameter is assigned- otherwise a nullpointer exception will occur! + */ + public SetLocationAnimator(InstructionNodeGraphScene scene, Hashtable VertexToWidget) { + super(scene.getSceneAnimator()); + + widgets= new LinkedList(); + startpoint= new LinkedList(); + targetpoint=new LinkedList(); + + if(VertexToWidget==null) return; + this.scene=scene; + + Point p1; + Point p2; + + + for(Map.Entry e: VertexToWidget.entrySet()){ + p1=e.getKey().getPosition(); + + if(p1==null) p1=new Point(); + //instruction widget inset correction: needed because used layouter do not support insets! + else p1.translate(InstructionNodeWidget.BORDERINSET,InstructionNodeWidget.BORDERINSET); + + p2=e.getValue().getPreferredLocation(); + if(p2==null) p2=new Point(); + + //Set vertex position to start position to avoid some line flickering + e.getKey().setPosition(p2); + + if(p1.x!=p2.x || p1.y!= p2.y){ + startpoint.add(p2); + targetpoint.add(p1); + widgets.add(e.getValue()); + } + //No Animation in this case- but dirty flags have to be set nevertheless! + else{ + Vertex v=e.getKey(); + if(v instanceof GraphVertex) ((GraphVertex)v).setDirty(true); + } + } + + } + + /** + * Starts the animation. + */ + public void animate(){ + start(); + } + + protected void tick(double d) { + Iterator w=widgets.iterator(); + Iterator start=startpoint.iterator(); + Iterator target=targetpoint.iterator(); + while(w.hasNext()){ + InstructionNodeWidget widget=w.next(); + Point p1=start.next(); + Point p2=target.next(); + Point p3; + + if(d>=1.0) p3=p2; + else p3=new Point ((int) (p1.x + d * (p2.x - p1.x)), + (int) (p1.y + d * (p2.y - p1.y))); + + widget.setPreferredLocation(p3); + //Update layoutGraph positions + Vertex v=scene.getWidgetToVertex().get(widget); + if(v!=null) v.setPosition(p3); + if(v instanceof GraphVertex) ((GraphVertex)v).setDirty(true); + } + + scene.refreshClusterWidgets(); + } + + + + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/instructions/Instruction.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/instructions/Instruction.java new file mode 100644 index 000000000000..0850d047668d --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/instructions/Instruction.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.instructions; + +import java.util.ArrayList; + + +/** + * An Instruction represents a HIR operation. It has predecessors which are + * the parameters of the function, and it has successors which are the + * usages of the result later on. The instructionString component is the + * string representation of the instruction behaviour. Each function is + * unique and therefore identified with an id. + * This class is not intended to be constructed outside of the package. + * + * @author Stefan Loidl + */ +public class Instruction { + + //unique identifier of the instruction + private String id; + + //unique identifier of the source block + private String sourceBlock; + + private String instructionString; + private ArrayList predecessors; + private ArrayList successors; + + private InstructionType type; + + + /** + * Internally used Constructor + */ + protected Instruction(String id){ + this.id=id; + predecessors=new ArrayList(); + successors=new ArrayList(); + instructionString=null; + sourceBlock=null; + type=InstructionType.UNKNOWN; + } + + /** + * Constructor + */ + protected Instruction(String id, String instruction, String source,Instruction[] pre, Instruction[] succ, InstructionType t) { + this.id=id; + this.instructionString=instruction; + + predecessors=new ArrayList(); + successors=new ArrayList(); + sourceBlock=source; + + if(pre!=null) + for(Instruction i : pre) predecessors.add(i); + if(succ!=null) + for(Instruction i :succ) successors.add(i); + type=t; + } + + /** + * As we are in single static assignment form two instructions are + * equal if their id is eqaul. + */ + public boolean equals(Object o){ + if(o instanceof Instruction) + return (((Instruction)o).getID().equals(id)); + return false; + } + + + // + /** + * Returns the id of the Instruction. + */ + public String getID(){ + return id; + } + + /** + * Sets the instruction string defining the behaviour + * of the instruction. + */ + protected void setInstructionString(String is){ + instructionString=is; + } + + /** + * Returns the string defining the instruction + */ + public String getInstructionString(){ + return instructionString; + } + + /** + * Returns the string identifying the source block + */ + public String getSourceBlock(){ + return sourceBlock; + } + + /** + * Sets the source Block + */ + protected void setSourceBlock(String s){ + sourceBlock=s; + } + + /** + * Sets the type of the Instruction + */ + protected void setInstructionType(InstructionType t){ + type=t; + } + + /** + * Returns the type of the Instruction + */ + public InstructionType getInstructionType(){ + return type; + } + // + + // + /** + * Returns an array with all predecessors + */ + public Instruction[] getPredecessors(){ + Instruction[] ret=new Instruction[predecessors.size()]; + ret=predecessors.toArray(ret); + return ret; + } + + /** + * Adds a Predecessor to the Instruction + */ + protected void addPredecessor(Instruction i){ + predecessors.add(i); + } + + /** + * Removes a Predecessor from the Instruction + */ + protected void removePredecessor(Instruction i){ + predecessors.remove(i); + } + + /** + * Returns an array with all successors + */ + public Instruction[] getSuccessors(){ + Instruction[] ret=new Instruction[successors.size()]; + ret=successors.toArray(ret); + return ret; + } + + /** + * Adds a successors to the Instruction + */ + protected void addSuccessors(Instruction i){ + successors.add(i); + } + + /** + * Removes a successors from the Instruction + */ + protected void removeSuccessors(Instruction i){ + successors.remove(i); + } + // + + public enum InstructionType{ + UNKNOWN, + CONSTANT, + PHI, + PARAMETER, + OPERATION, + CONTROLFLOW + } + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/instructions/InstructionSet.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/instructions/InstructionSet.java new file mode 100644 index 000000000000..87c256eb94d3 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/instructions/InstructionSet.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.instructions; + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.LinkedList; + +/** + * This class can be seen as an infinite set of instructions. It contains + * a number of instructions, but if an instruction is queried that is not + * within the set, an instruction is added to the set and is returned. + * Using this behaviour a simple out of order construction of the instruction + * graph is possible. + * + * @author Stefan Loidl + */ +public class InstructionSet { + + private Hashtable instructions; + + /** + * Default Constructor + */ + public InstructionSet() { + instructions=new Hashtable(); + } + + /** + * Returns the instruction with id if its within the set, or a new + * instruction if not (the new instruction is then added to the set too) + */ + public Instruction getInstruction(String id){ + if(instructions.containsKey(id)) + return instructions.get(id); + else{ + Instruction i=new Instruction(id); + instructions.put(id,i); + return i; + } + } + + /** + * Simply adds a link between two instructions by adding + * prede- and successor to the corresponding elements. + */ + public boolean addLink(Instruction from,Instruction to){ + if(from!=null && to!= null){ + from.addSuccessors(to); + to.addPredecessor(from); + return true; + } + else return false; + } + + /** + * Returns an array of Instructions contained in the set, + * no assumptions about the ordering should be maid. + */ + public Instruction[] getInstructions(){ + Instruction[] inst=new Instruction[instructions.size()]; + inst=instructions.values().toArray(inst); + return inst; + } + + /** + * Removes all nodes of the specified type from the set. + * Currently no Control Flow Instrucitons are shown within the + * Graph. For this purpose this method is used. + */ + public static Instruction[] filterInstructionType(Instruction[] iset,Instruction.InstructionType type){ + LinkedList list=new LinkedList(); + + for(Instruction i : iset){ + if(i.getInstructionType()!=type) list.add(i); + } + + Instruction[] ret=new Instruction[list.size()]; + + return list.toArray(ret); + } + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/instructions/InstructionSetGenerator.java b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/instructions/InstructionSetGenerator.java new file mode 100644 index 000000000000..15e5c64c26cf --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/java/at/ssw/visualizer/dataflow/instructions/InstructionSetGenerator.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.instructions; + +import at.ssw.visualizer.model.cfg.*; +import java.util.ArrayList; +import java.util.List; + +/** + * This class is a static helper class to create a InstructionSet out of + * the BasicBlocks extraced from the ControlFlowGraph component. It is + * light weight to be easy to adopt to changes in the structure of the + * datasource (BasicBlock[]). + * + * @author Stefan Loidl + */ +public class InstructionSetGenerator { + + //characters that can occur as prefix for an operation + //source for this information: see at.ssw.visualizer.ir.model.IRScanner.isHir() + private static String idStart="ailfdv"; + + private static char idBlock='B'; + + /** + * Generates a InstructionSet from an array of BasicBlocks + */ + public static InstructionSet generateFromBlocks(List blocks){ + if(blocks==null) return null; + InstructionSet iset=new InstructionSet(); + Instruction instr1, instr2; + + for(BasicBlock b : blocks){ + if(!b.hasHir()) continue; + //Handle HIR Instructions for standard operations + for(IRInstruction hi:b.getHirInstructions()){ + + instr1= iset.getInstruction(hi.getValue(IRInstruction.HIR_NAME)); + instr1.setInstructionString(hi.getValue(IRInstruction.HIR_TEXT)); + instr1.setSourceBlock(b.getName()); + + if(containsBlockIdentifier(hi.getValue(IRInstruction.HIR_TEXT))) + instr1.setInstructionType(Instruction.InstructionType.CONTROLFLOW); + else{ + if(startsLikeConstant(hi.getValue(IRInstruction.HIR_TEXT))) instr1.setInstructionType(Instruction.InstructionType.CONSTANT); + else instr1.setInstructionType(Instruction.InstructionType.OPERATION); + } + + //Extract instructions from InstructionString + for(String s : parseInstructionString(hi.getValue(IRInstruction.HIR_TEXT))){ + instr2= iset.getInstruction(s); + if(instr1!=instr2) iset.addLink(instr2,instr1); + //If an instruction contains predecessors it's an operation + instr1.setInstructionType(Instruction.InstructionType.OPERATION); + } + } + + //Handle local state of the BasicBlock b + for(State state: b.getStates()){ + for(StateEntry stentry: state.getEntries()){ + + instr1= iset.getInstruction(stentry.getName()); + + //First occurance of local state var without Phi is decided to be + //a method param + if(instr1.getInstructionString()==null && !stentry.hasPhiOperands()){ + instr1.setInstructionString(""); + instr1.setInstructionType(Instruction.InstructionType.PARAMETER); + instr1.setSourceBlock(b.getName()); + } + + //Add Phi operands if some exist + if(stentry.hasPhiOperands()){ + instr1.setSourceBlock(b.getName()); + instr1.setInstructionType(Instruction.InstructionType.PHI); + String instrString="Phi["; + for(String s : stentry.getPhiOperands()){ + instr2=iset.getInstruction(s); + if(instr1!=instr2) iset.addLink(instr2,instr1); + instrString+=s+","; + } + instr1.setInstructionString(instrString.substring(0,instrString.length()-1)+"]"); + } + } + } + } + return iset; + } + + + /** + * Returns if an Blockidentifier is contained within the string. (Eg. B4, B56...) + */ + private static boolean containsBlockIdentifier(String iString){ + int pos=iString.indexOf(idBlock); + if(pos!=-1 && iString.length()>pos+1 && isNumber(iString.charAt(pos+1))) return true; + else return false; + } + + + /** + * This method extracts the identifiers from an instuction string. + * These are returned as a collection of strings. + */ + private static ArrayList parseInstructionString(String iString){ + //In most cases the Instruction string will hold two identifiers + ArrayList list=new ArrayList(2); + + if(iString!=null){ + int from=0, pos=0; + + while(pos='0' && c<='9'); + } + + /** + * Returns if char c is alphanumeric + */ + private static boolean isAlphaNumeric(char c){ + if(isNumber(c)) return true; + return (c>='a' && c<='z') || (c>='A' && c<='Z'); + } + + /** + * Returns if s starts like a constant: + * - "1){ + return isNumber(s.charAt(1)); + } + return false; + } + +} diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/DataFlowGraph/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..9208a5f1d4f1 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/nbm/manifest.mf @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.dataflow.graph +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/dataflow/graph/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/DataFlowGraph/src/main/resources/at/ssw/visualizer/dataflow/graph/Bundle.properties b/visualizer/C1Visualizer/DataFlowGraph/src/main/resources/at/ssw/visualizer/dataflow/graph/Bundle.properties new file mode 100644 index 000000000000..df360d41cddc --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowGraph/src/main/resources/at/ssw/visualizer/dataflow/graph/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=Data Flow Graph diff --git a/visualizer/C1Visualizer/DataFlowView/pom.xml b/visualizer/C1Visualizer/DataFlowView/pom.xml new file mode 100644 index 000000000000..ac238904597d --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowView/pom.xml @@ -0,0 +1,97 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + DataFlowView + 1.14-SNAPSHOT + nbm + DataFlowView + + UTF-8 + + + + at.ssw.visualizer + VisualizerUI + ${project.version} + + + at.ssw.visualizer + DataFlowGraph + ${project.version} + + + at.ssw.visualizer + CompilationModel + ${project.version} + + + org.netbeans.api + org-netbeans-api-visual + ${netbeans.version} + + + org.netbeans.api + org-openide-nodes + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + org.netbeans.api + org-openide-windows + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.DataFlowView + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/DataFlowView/src/main/java/at/ssw/visualizer/dataflow/view/DataflowTableModel.java b/visualizer/C1Visualizer/DataFlowView/src/main/java/at/ssw/visualizer/dataflow/view/DataflowTableModel.java new file mode 100644 index 000000000000..965d2bd93810 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowView/src/main/java/at/ssw/visualizer/dataflow/view/DataflowTableModel.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.view; + +import at.ssw.visualizer.dataflow.graph.ClusterWidget; +import at.ssw.visualizer.dataflow.graph.InstructionNodeGraphScene; +import at.ssw.visualizer.dataflow.graph.InstructionNodeWidget; +import at.ssw.visualizer.dataflow.instructions.Instruction; +import java.util.Arrays; +import java.util.Comparator; +import javax.swing.table.AbstractTableModel; + +/** + * + * @author Stefan Loidl + * @author Christian Wimmer + */ +public class DataflowTableModel extends AbstractTableModel { + public static final int COLUMN_VISIBLE = 0; + public static final int COLUMN_NAME = 1; + public static final int COLUMN_BLOCK = 2; + public static final int COLUMN_TYPE = 3; + public static final int COLUMN_STATEMENT = 4; + public static final int COLUMN_SUCC = 5; + public static final int COLUMN_PRED = 6; + public static final int COLUMN_CLUSTER = 7; + + public static final String[] COLUMN_NAMES = {"Visible", "Name", "Block", "Type", "Statement", "Successors", "Predecessors", "Cluster"}; + public static final int[] COLUMN_WIDTHS = {60, 60, 60, 60, 200, 120, 120, 60}; + + private InstructionNodeWidget[] widgets = null; + + /** Sets the data source for the tablemodel*/ + public void setDataSource(InstructionNodeGraphScene scene) { + if (scene == null) { + widgets = null; + } else { + widgets = scene.getNodeWidgets(); + } + fireTableDataChanged(); + } + + + /** Sorts the data source, according to the value within specified column */ + public void sort(final int column) { + if (widgets == null) { + return; + } + Arrays.sort(widgets, new Comparator() { + public int compare(InstructionNodeWidget w1, InstructionNodeWidget w2) { + String s1 = getValue(w1, column).toString(); + String s2 = getValue(w2, column).toString(); + if (column == COLUMN_NAME || column == COLUMN_BLOCK || column == COLUMN_CLUSTER) { + if (s1.length() > 1 && s2.length() > 1) { + s1 = s1.substring(1); + s2 = s2.substring(1); + try { + int i1 = Integer.valueOf(s1); + int i2 = Integer.valueOf(s2); + return i1 - i2; + } catch (Exception e) { + } + } + } + return s1.compareTo(s2); + } + }); + fireTableDataChanged(); + } + + + public int getRowCount() { + return widgets == null ? 0 : widgets.length; + } + + public int getColumnCount() { + return COLUMN_NAMES.length; + } + + @Override + public String getColumnName(int column) { + return COLUMN_NAMES[column]; + } + + public Object getValueAt(int row, int column) { + return getValue(widgets[row], column); + } + + protected Object getValue(InstructionNodeWidget nw, int column) { + Instruction inst = nw.getInstruction(); + ClusterWidget cw = nw.getClusterWidget(); + if (inst == null) { + return ""; + } + + switch (column) { + case COLUMN_VISIBLE: + return nw.isWidgetVisible(); + case COLUMN_NAME: + return nw.getID(); + case COLUMN_BLOCK: + return inst.getSourceBlock(); + case COLUMN_TYPE: + return getTypeString(inst.getInstructionType()); + case COLUMN_STATEMENT: + return inst.getInstructionString(); + case COLUMN_SUCC: + return getIDString(inst.getSuccessors()); + case COLUMN_PRED: + return getIDString(inst.getPredecessors()); + case COLUMN_CLUSTER: + return cw == null ? "" : cw.getId(); + default: + throw new Error("invalid column"); + } + } + + private String getTypeString(Instruction.InstructionType type) { + switch (type) { + case CONSTANT: + return "constant"; + case CONTROLFLOW: + return "control flow"; + case OPERATION: + return "operation"; + case PARAMETER: + return "parameter"; + case PHI: + return "phi"; + default: + return "undefined"; + } + } + + private String getIDString(Instruction[] instructions) { + if (instructions == null) { + return ""; + } + + StringBuilder sb = new StringBuilder(); + for (Instruction instruction : instructions) { + if (sb.length() > 0) { + sb.append(", "); + } + sb.append(instruction.getID()); + } + return sb.toString(); + } + + /** Returns a Widget for the given row */ + public InstructionNodeWidget getWidgetAtRow(int row) { + if (widgets != null && row < widgets.length) { + return widgets[row]; + } else { + return null; + } + } + + /** Sets the value of a cell- only visible state can be changed */ + @Override + public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + if (columnIndex == COLUMN_VISIBLE && widgets != null && rowIndex < widgets.length) { + InstructionNodeGraphScene s = (InstructionNodeGraphScene) widgets[rowIndex].getScene(); + s.handleToggleVisibility(widgets[rowIndex].getInstruction(), false); + } + } + + @Override + public Class getColumnClass(int column) { + if (column == COLUMN_VISIBLE) { + return Boolean.class; + } + return String.class; + } + + /** Only visible state is editable */ + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + return columnIndex == COLUMN_VISIBLE; + } +} diff --git a/visualizer/C1Visualizer/DataFlowView/src/main/java/at/ssw/visualizer/dataflow/view/DataflowViewTopComponent.java b/visualizer/C1Visualizer/DataFlowView/src/main/java/at/ssw/visualizer/dataflow/view/DataflowViewTopComponent.java new file mode 100644 index 000000000000..87a7b41376f3 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowView/src/main/java/at/ssw/visualizer/dataflow/view/DataflowViewTopComponent.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.view; + +import at.ssw.visualizer.core.selection.Selection; +import at.ssw.visualizer.core.selection.SelectionManager; +import at.ssw.visualizer.dataflow.graph.InstructionNodeGraphScene; +import at.ssw.visualizer.dataflow.graph.InstructionNodeWidget; +import at.ssw.visualizer.dataflow.graph.InstructionSceneListener; +import java.awt.BorderLayout; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.io.Serializable; +import java.util.Set; +import javax.swing.BorderFactory; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import org.openide.windows.TopComponent; +import org.openide.windows.WindowManager; + +/** + * Top component which displays something. + * + * @author Stefan Loidl + * @author Christian Wimmer + */ +public final class DataflowViewTopComponent extends TopComponent { + private InstructionNodeGraphScene curScene; + + private JTable nodeTable; + private DataflowTableModel tableModel; + + private DataflowViewTopComponent() { + setName("Data Flow"); + setToolTipText("Data Flow"); + + tableModel = new DataflowTableModel(); + nodeTable = new javax.swing.JTable(tableModel); + nodeTable.setRowMargin(0); + nodeTable.getColumnModel().setColumnMargin(0); + nodeTable.setShowGrid(false); + nodeTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + for (int i = 0; i < DataflowTableModel.COLUMN_WIDTHS.length; i++) { + nodeTable.getColumnModel().getColumn(i).setPreferredWidth(DataflowTableModel.COLUMN_WIDTHS[i]); + } + nodeTable.addMouseListener(tableMouseListener); + nodeTable.getTableHeader().addMouseListener(headerMouseListener); + + JScrollPane scrollPane = new JScrollPane(nodeTable); + scrollPane.setViewportBorder(BorderFactory.createEmptyBorder()); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + setLayout(new BorderLayout()); + add(scrollPane); + } + + @Override + protected void componentShowing() { + super.componentShowing(); + SelectionManager.getDefault().addChangeListener(selectionChangeListener); + updateContent(); + } + + @Override + protected void componentHidden() { + super.componentHidden(); + SelectionManager.getDefault().removeChangeListener(selectionChangeListener); + if (curScene != null) { + curScene.removeInstructionSceneListener(instructionSceneListener); + } + curScene = null; + tableModel.setDataSource(null); + } + + + private MouseListener tableMouseListener = new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent event) { + if (event.getButton() == MouseEvent.BUTTON1 && event.getClickCount() == 2) { + // Selects the node in the scene on doubleclick of an item + String s = nodeTable.getValueAt(nodeTable.getSelectedRow(), DataflowTableModel.COLUMN_NAME).toString(); + if (curScene != null) { + curScene.setSingleSelectedWidget(s, true); + } + } else if (event.getButton() == MouseEvent.BUTTON3 && event.getClickCount() == 1) { + // Show popup menu of the node in table + InstructionNodeWidget w = tableModel.getWidgetAtRow(nodeTable.rowAtPoint(event.getPoint())); + if (w != null) { + JPopupMenu pop = w.getPopup(); + if (pop != null) { + pop.show(nodeTable, event.getX(), event.getY()); + } + } + } + } + }; + + private MouseListener headerMouseListener = new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent event) { + tableModel.sort(nodeTable.columnAtPoint(event.getPoint())); + } + }; + + + private ChangeListener selectionChangeListener = new ChangeListener() { + public void stateChanged(ChangeEvent event) { + updateContent(); + } + }; + + private void updateContent() { + Selection selection = SelectionManager.getDefault().getCurSelection(); + InstructionNodeGraphScene newScene = selection.get(InstructionNodeGraphScene.class); + + if (newScene != curScene) { + if (curScene != null) { + curScene.removeInstructionSceneListener(instructionSceneListener); + } + tableModel.setDataSource(newScene); + if (newScene != null) { + newScene.addInstructionSceneListener(instructionSceneListener); + } + curScene = newScene; + } + } + + private InstructionSceneListener instructionSceneListener = new InstructionSceneListener() { + public void doubleClicked(InstructionNodeWidget w) { + if (w == null) { + return; + } + String id = w.getID(); + + for (int i = 0; i < nodeTable.getRowCount(); i++) { + if (nodeTable.getValueAt(i, DataflowTableModel.COLUMN_NAME).equals(id)) { + nodeTable.changeSelection(i, 0, false, false); + break; + } + } + } + + /** Data in InstructionNodeScene changed */ + public void updateNodeData() { + nodeTable.repaint(); + } + + /** Selection in editor has changes */ + public void selectionChanged(Set w) { + } + }; + + // + private static final String PREFERRED_ID = "DataflowViewTopComponent"; + private static DataflowViewTopComponent instance; + + public static synchronized DataflowViewTopComponent getDefault() { + if (instance == null) { + instance = new DataflowViewTopComponent(); + } + return instance; + } + + public static synchronized DataflowViewTopComponent findInstance() { + return (DataflowViewTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID); + } + + @Override + public int getPersistenceType() { + return TopComponent.PERSISTENCE_ALWAYS; + } + + @Override + protected String preferredID() { + return PREFERRED_ID; + } + + @Override + public Object writeReplace() { + return new ResolvableHelper(); + } + + static final class ResolvableHelper implements Serializable { + private static final long serialVersionUID = 1L; + public Object readResolve() { + return DataflowViewTopComponent.getDefault(); + } + } + // +} diff --git a/visualizer/C1Visualizer/DataFlowView/src/main/java/at/ssw/visualizer/dataflow/view/ShowDataflowViewAction.java b/visualizer/C1Visualizer/DataFlowView/src/main/java/at/ssw/visualizer/dataflow/view/ShowDataflowViewAction.java new file mode 100644 index 000000000000..8020be67a110 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowView/src/main/java/at/ssw/visualizer/dataflow/view/ShowDataflowViewAction.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.dataflow.view; + +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; +import org.openide.windows.TopComponent; + +/** + * Action which shows DataflowView component. + * + * @author Stefan Loidl + */ +public class ShowDataflowViewAction extends AbstractAction { + public ShowDataflowViewAction() { + super("Data Flow View"); + } + + public void actionPerformed(ActionEvent event) { + TopComponent win = DataflowViewTopComponent.findInstance(); + win.open(); + win.requestActive(); + } +} diff --git a/visualizer/C1Visualizer/DataFlowView/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/DataFlowView/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..4aac83e37c2c --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowView/src/main/nbm/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.dataflow.view +OpenIDE-Module-Layer: at/ssw/visualizer/dataflow/view/layer.xml +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/dataflow/view/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/DataFlowView/src/main/resources/at/ssw/visualizer/dataflow/view/Bundle.properties b/visualizer/C1Visualizer/DataFlowView/src/main/resources/at/ssw/visualizer/dataflow/view/Bundle.properties new file mode 100644 index 000000000000..986c876559d1 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowView/src/main/resources/at/ssw/visualizer/dataflow/view/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=Data Flow View diff --git a/visualizer/C1Visualizer/DataFlowView/src/main/resources/at/ssw/visualizer/dataflow/view/DataflowViewTopComponentSettings.xml b/visualizer/C1Visualizer/DataFlowView/src/main/resources/at/ssw/visualizer/dataflow/view/DataflowViewTopComponentSettings.xml new file mode 100644 index 000000000000..0482c9eda778 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowView/src/main/resources/at/ssw/visualizer/dataflow/view/DataflowViewTopComponentSettings.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/visualizer/C1Visualizer/DataFlowView/src/main/resources/at/ssw/visualizer/dataflow/view/DataflowViewTopComponentWstcref.xml b/visualizer/C1Visualizer/DataFlowView/src/main/resources/at/ssw/visualizer/dataflow/view/DataflowViewTopComponentWstcref.xml new file mode 100644 index 000000000000..f1e1d33748fa --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowView/src/main/resources/at/ssw/visualizer/dataflow/view/DataflowViewTopComponentWstcref.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/visualizer/C1Visualizer/DataFlowView/src/main/resources/at/ssw/visualizer/dataflow/view/layer.xml b/visualizer/C1Visualizer/DataFlowView/src/main/resources/at/ssw/visualizer/dataflow/view/layer.xml new file mode 100644 index 000000000000..8bec860c49a7 --- /dev/null +++ b/visualizer/C1Visualizer/DataFlowView/src/main/resources/at/ssw/visualizer/dataflow/view/layer.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/GraphHelper/pom.xml b/visualizer/C1Visualizer/GraphHelper/pom.xml new file mode 100644 index 000000000000..665d5076716c --- /dev/null +++ b/visualizer/C1Visualizer/GraphHelper/pom.xml @@ -0,0 +1,57 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + GraphHelper + 1.14-SNAPSHOT + nbm + GraphHelper + + UTF-8 + + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.graphhelper + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/GraphHelper/src/main/java/at/ssw/visualizer/graphhelper/Block.java b/visualizer/C1Visualizer/GraphHelper/src/main/java/at/ssw/visualizer/graphhelper/Block.java new file mode 100644 index 000000000000..1fdb761177a6 --- /dev/null +++ b/visualizer/C1Visualizer/GraphHelper/src/main/java/at/ssw/visualizer/graphhelper/Block.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.graphhelper; + +import java.util.LinkedList; + +/** + * A Block is a connected component. This class is used during the planarity check of a + * graph. + * + * @author Stefan Loidl + */ +public class Block { + + LinkedList Latt, Ratt; + LinkedList Lseg, Rseg; + + /** Creates a new instance of Block */ + public Block(Edge e, LinkedList A) { + Latt=new LinkedList(); + Ratt=new LinkedList(); + Lseg=new LinkedList(); + Rseg=new LinkedList(); + + Lseg.add(e); + Latt= new LinkedList(A); + A.clear(); + } + + /** Interchanges the two sides of the block*/ + public void flip(){ + LinkedList ha; + LinkedList he; + + ha=Ratt; Ratt=Latt; Latt=ha; + he=Rseg; Rseg=Lseg; Lseg=he; + } + + public Integer headOfLatt() { + return Latt.getFirst(); + } + + public boolean emptyLatt(){ + return Latt.isEmpty(); + } + + public Integer headOfRatt() { + return Ratt.getFirst(); + } + + public boolean emptyRatt(){ + return Ratt.isEmpty(); + } + + /** check for interlacing with the left side of the topmost block of S*/ + public boolean leftInterlace(LinkedList S){ + assert !Latt.isEmpty(); + + if(!S.isEmpty() && !S.getFirst().emptyLatt() && Latt.getLast().intValue() < S.getFirst().headOfLatt().intValue()) + return true; + else return false; + } + + /** check for interlacing with the right side of the topmost block of S*/ + public boolean rightInterlace(LinkedList S){ + assert !Latt.isEmpty(); + + if(!S.isEmpty() && !S.getFirst().emptyRatt() && Latt.getLast().intValue() < S.getFirst().headOfRatt().intValue()) + return true; + else return false; + } + + /** Add block Bprime to the rear of this block*/ + public void combine(Block Bprime){ + Latt.addAll(Bprime.Latt); + Ratt.addAll(Bprime.Ratt); + Lseg.addAll(Bprime.Lseg); + Rseg.addAll(Bprime.Rseg); + } + + /** Remove all attachments to w; there may be several */ + public boolean clean(int dfsNumW){ + while(!Latt.isEmpty() && Latt.getFirst().intValue()==dfsNumW) Latt.removeFirst(); + while(!Ratt.isEmpty() && Ratt.getFirst().intValue()==dfsNumW) Ratt.removeFirst(); + + if(!Latt.isEmpty() || !Ratt.isEmpty()) return false; + + // If Latt and Ratt are empty we reorder the placement of the subsegments in alpha + for(Edge e:Lseg) ((DiGraph.PlanarityEdgePayload)e.data).alpha=DiGraph.LEFT; + for(Edge e:Rseg) ((DiGraph.PlanarityEdgePayload)e.data).alpha=DiGraph.RIGHT; + + return true; + } + + /** Add a Block to the rear of Att. Flip if necessary*/ + public void addToAtt(LinkedList Att, int dfsnumW0){ + if(!Ratt.isEmpty() && headOfRatt().intValue() > dfsnumW0) flip(); + Att.addAll(Latt); + Latt.clear(); + Att.addAll(Ratt); + Ratt.clear(); + + //Ratt is either empty or {w0}. Also if Ratt is non-empty then all subsequent sets are + //contained in {w0}. So we indeed compute an ordered set of attachments. + for(Edge e:Lseg) ((DiGraph.PlanarityEdgePayload)e.data).alpha=DiGraph.LEFT; + for(Edge e:Rseg) ((DiGraph.PlanarityEdgePayload)e.data).alpha=DiGraph.RIGHT; + } + + + +} diff --git a/visualizer/C1Visualizer/GraphHelper/src/main/java/at/ssw/visualizer/graphhelper/DiGraph.java b/visualizer/C1Visualizer/GraphHelper/src/main/java/at/ssw/visualizer/graphhelper/DiGraph.java new file mode 100644 index 000000000000..5965b7c7b700 --- /dev/null +++ b/visualizer/C1Visualizer/GraphHelper/src/main/java/at/ssw/visualizer/graphhelper/DiGraph.java @@ -0,0 +1,803 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.graphhelper; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Hashtable; +import java.util.LinkedList; + +/** + * Implements a Directed Graph. Some graph theoretic features like filtering + * connected components and making a graph biconnected are supported. + * Furthermore a planarity testing algorithm is implemented as described in: + * + * Kurt Mehlhorn, Petra Mutzel, Stefan Naeher: An Implementation of the Hopcroft + * and Trajan Planarity Test and Embedding Algorithm. Technical Report, 1993. + * MPI-I-93-151 + * + * @author Stefan Loidl + */ +public class DiGraph { + + protected Hashtable nodes; + protected LinkedList edges; + + //This field is used within makeBiConnected Algorithm. + private int dfsCount=0; + + //Used during planarity testing + final static int LEFT=1; + final static int RIGHT=2; + + //during the panarity-test step this variable + //is assigned to be the biconnected component + //which is used within the embedding step. + private DiGraph BiConnected=null; + + //Used in embedding algorithm + private int cur_nr=0; + + + /** Creates a new instance of DiGraph */ + public DiGraph() { + nodes=new Hashtable(); + edges=new LinkedList(); + } + + /** Adds a new Node to the DiGraph*/ + public void addNode(Node n){ + nodes.put(n.ID,n); + } + + /** Removes a Node from the DiGraph*/ + public void removeNode(Node n){ + for(Edge e : n.edges.toArray(new Edge[n.edges.size()])) + removeEdge(e); + + nodes.remove(n.ID); + } + + /** Returns a Node object with specified id*/ + public Node getNode(String id){ + return nodes.get(id); + } + + /** Returns if a node with specified id is contained*/ + public boolean contains(String id){ + return nodes.containsKey(id); + } + + /** Adds a new edge to the DiGraph*/ + public void addEdge(Edge e){ + e.source.succ.add(e.destination); + e.destination.pred.add(e.source); + edges.add(e); + e.source.edges.add(e); + e.destination.edges.add(e); + } + + /** Removes an edge for the DiGraph*/ + public void removeEdge(Edge e) { + if(e==null) return; + edges.remove(e); + e.source.succ.remove(e.destination); + e.destination.pred.remove(e.source); + e.source.edges.remove(e); + e.destination.edges.remove(e); + } + + /** Returns a Collection of all Nodes*/ + public Collection getNodes(){ + return nodes.values(); + } + + /** Returns a Collection of all Edges*/ + public Collection getEdges(){ + return edges; + } + + /** Returns the Edge object with source and destination given*/ + protected Edge getEdge(Node source, Node dest) { + for(Edge e: edges){ + if(e.source==source && e.destination==dest) return e; + } + return null; + } + + /** Resets the flags of all Nodes*/ + public void resetNodeFlags(){ + for(Node n:nodes.values()){ + n.visited=false; + } + } + + /** + * Returns a clone of the current DiGraph + */ + public DiGraph clone(){ + DiGraph c=new DiGraph(); + + for(Node n: nodes.values()) c.addNode(new Node(n.ID)); + + for(Edge e: edges) + c.addEdge(new Edge(c.getNode(e.source.ID),c.getNode(e.destination.ID))); + + return c; + } + + /** + * Makes the Graph bidirected by adding Edges + */ + public void makeBiDirected(){ + for(Node n: nodes.values()){ + for(Node s:n.succ){ + if(!n.pred.contains(s)) addEdge(new Edge(s,n)); + } + for(Node p:n.pred){ + if(!n.succ.contains(p)) addEdge(new Edge(n,p)); + } + } + } + + /** + * Breaks all bidirected Edges by deleting one of + * them. + */ + public void breakBiDirection(){ + LinkedList del=new LinkedList(); + for(Node n: nodes.values()){ + for(Node p:n.pred){ + if(n.succ.contains(p)){ + Edge ed=getEdge(n,p); + if(!del.contains(ed))del.add(getEdge(p,n)); + } + } + } + for(Edge e:del){ + removeEdge(e); + } + } + + /** + * Returns a list of DiGraphs containing the connected components of + * the current DiGraph- Nodes are cloned! + */ + public Collection getConnectedComponents(){ + LinkedList list=new LinkedList(); + resetNodeFlags(); + for(Node n:nodes.values()){ + if(!n.visited){ + list.add(findConnctedComponent(n)); + } + } + + return list; + } + + /** + * Extracts the connected component from Node n. + */ + private DiGraph findConnctedComponent(Node n) { + DiGraph dg=new DiGraph(); + findConnctedComponent_rek(n,dg); + + for(Edge e: getEdges()){ + if(dg.contains(e.source.ID)){ + dg.addEdge(new Edge(dg.getNode(e.source.ID),dg.getNode(e.destination.ID))); + } + } + + return dg; + } + + private void findConnctedComponent_rek(Node n, DiGraph dg) { + if(n.visited) return; + n.visited=true; + + dg.addNode(new Node(n.ID)); + + for(Node node:n.succ){ + findConnctedComponent_rek(node,dg); + } + for(Node node:n.pred){ + findConnctedComponent_rek(node,dg); + } + } + + /** + * Returns a collecation of arrays of circualar Nodes + */ + public Collection getCircularDependency(Node n){ + LinkedList ret=new LinkedList(); + LinkedList current=new LinkedList(); + + resetNodeFlags(); + + n.visited=true; + current.add(n); + for(Node s:n.succ){ + getCircularDependency_rec(n ,s, current, ret); + } + + + return ret; + } + + /** + * Recusive helper- searching cyclic structures. + */ + private void getCircularDependency_rec(Node source ,Node node, LinkedList current, LinkedList result){ + if(node.visited) return; + node.visited=true; + + current.addLast(node); + + for(Node n:node.succ){ + //Cycle found! + if(n==source){ + result.add(current.toArray(new Node[current.size()])); + } + //Another unvisited node + else if(!n.visited){ + getCircularDependency_rec(source,n,current,result); + } + } + + node.visited=false; + current.removeLast(); + } + + + /** + * This method makes the current graph biconnected by adding + * edges. It is assumed that the graph is connected & bidirected! + * The data field of all nodes is destroyed! + */ + public void makeBiConnected(){ + //distribute payload to all nodes + for(Node n: nodes.values()) n.data=new BiConPayload(); + + dfsCount=0; + + if(nodes.size()>0) + dfsInMakeBiConnected(nodes.values().iterator().next()); + + } + + /** + * This method searches for nodes connecting two subgraphs. + * To satisfy BiConnection another Edge is added to bridge that + * single node. This doesn't influence planarity of the graph. + */ + private void dfsInMakeBiConnected(Node n) { + Node u; + BiConPayload pl=(BiConPayload)n.data; + + pl.dfsNum=dfsCount++; + pl.lowPt=pl.dfsNum; + pl.reached=true; + + if(n.succ.size()==0) return; + + //get first child + u=n.succ.getFirst(); + + //Within the loop changes may happen -> successors. + //these do not have to be traversed. + Node[] na=n.succ.toArray(new Node[n.succ.size()]); + + //traverse all children + for(Node w: na){ + BiConPayload plw=(BiConPayload)w.data; + + //Edge (n->w) is a tree edge + if(!plw.reached){ + plw.parent=n; + dfsInMakeBiConnected(w); + + //Node was found -> a bridging edge has to + //be added. + if(plw.lowPt==pl.dfsNum){ + //if w is the first child and n has a parent + //we simply add a edge between them (bidirected!) + //This may not be done between other than the first + //Node because in the case planerity would be lost! (no face!) + if(w==u && pl.parent!=null){ + Edge e=new Edge(w,pl.parent); + addEdge(e); + addEdge(e.getReverseEdge()); + } + //else we simply add a bidirected Edge between the first child and the + //current child + if(u!=w){ + Edge e=new Edge(w,u); + addEdge(e); + addEdge(e.getReverseEdge()); + } + } + + //lowPt of our node n is minimum of lowPts + if(pl.lowPt > plw.lowPt) pl.lowPt=plw.lowPt; + } + //Edge (n->w) is not a tree edge + else{ + if(pl.lowPt > plw.dfsNum) pl.lowPt=plw.dfsNum; + } + } //for + } + + + + /** Overrides the toString for debug reasons only.*/ + public String toString(){ + String ret="Nodes: "; + for(Node n:nodes.values()){ + ret+=n.ID; + if(n.data!=null) ret+="("+n.data+")"; + ret+=", "; + } + + ret+="\nEdges: "; + for(Edge e: edges) + ret+="("+e.source.ID+"->"+e.destination.ID+") "; + + return ret; + } + + /** Tests if the current graph is planar*/ + public boolean isPlanar(){ + boolean ret=true; + for(DiGraph dg : getConnectedComponents()){ + ret= ret && dg.planar(); + } + return ret; + } + + /** + * Tests for the planarity of the graph destroying its structure! + * The graph is assumed to be connected. + */ + protected boolean planar(){ + int num=nodes.size(); + if(num <= 3) return true; + if(edges.size() > 6*num-12) return false; + + makeBiDirected(); + makeBiConnected(); + BiConnected=clone(); + + //Planarity Test + for(Node n: nodes.values()) n.data=new PlanarityNodePayload(); + for(Edge e: edges) e.data=new PlanarityEdgePayload(); + + reorder(); + LinkedList Att=new LinkedList(); + + if(nodes.size()==0) return true; + Node first=nodes.values().iterator().next(); + + assert first.succ.size()>0; + Edge firstEdge=getEdge(first,first.succ.getFirst()); + + ((PlanarityEdgePayload)firstEdge.data).alpha=LEFT; + + if(!stronglyPlanar(firstEdge,Att)) return false; + + return true; + } + + + private boolean stronglyPlanar(Edge e0,LinkedList Att){ + //determine the cycle C(e0) + Node x=e0.source; + Node y=e0.destination; + //get first adiacent edge to y + Edge e= getEdge(y,y.succ.getFirst()); + + Node wk=y; + + //while is a tree edge + while(((PlanarityNodePayload)e.destination.data).dfsNum > ((PlanarityNodePayload)wk.data).dfsNum){ + wk=e.destination; + e=getEdge(wk,wk.succ.getFirst()); + } + + Node w0=e.destination; + + //Process all edges leaving the spine of S(e0) + Node w=wk; + LinkedList S=new LinkedList(); + + while(w!=x){ + int count=0; + for(Node r:w.succ){ + e=getEdge(w,r); + count++; + if(count!=1){ + //Test Recursivly + LinkedList A=new LinkedList(); + //if: tree edge + if(((PlanarityNodePayload)w.data).dfsNum < ((PlanarityNodePayload)e.destination.data).dfsNum){ + if(!stronglyPlanar(e,A)) return false; + } + //else: back edge + else A.add(new Integer(((PlanarityNodePayload)e.destination.data).dfsNum)); + + //update stack S + Block B=new Block(e,A); + while(true){ + if(B.leftInterlace(S)) S.getFirst().flip(); + if(B.leftInterlace(S)) return false; + if(B.rightInterlace(S)) B.combine(S.poll()); + else break; + } + S.addFirst(B); + } + } + + //Prepare for next iteration + while(!S.isEmpty() && S.getFirst().clean(((PlanarityNodePayload)((PlanarityNodePayload)w.data).parent.data).dfsNum)) + S.removeFirst(); + + w=((PlanarityNodePayload)w.data).parent; + } + + + //test strong planerity and compute Att + Att.clear(); + while(!S.isEmpty()){ + Block B=S.poll(); + if(!B.emptyLatt() && !B.emptyRatt() && B.headOfLatt().intValue() > ((PlanarityNodePayload)w0.data).dfsNum + && B.headOfRatt().intValue() > ((PlanarityNodePayload)w0.data).dfsNum){ + return false; + } + + B.addToAtt(Att,((PlanarityNodePayload)w0.data).dfsNum); + } + + //w0 is an attachment of S(e0) except if w0 = x + if(w0 != x) Att.add(new Integer(((PlanarityNodePayload)w0.data).dfsNum)); + + return true; + } + + + /** + * This is a substep in planarity test algorithm- All data fields of + * nodes and edges are lost during this step! + */ + private void reorder(){ + if(nodes.size()==0) return; + + LinkedList deledges=new LinkedList(); + dfsCount=0; + + dfsInReorder(nodes.values().iterator().next(),deledges); + + for(Edge e: deledges) removeEdge(e); + + for(Edge e: edges){ + PlanarityNodePayload source=(PlanarityNodePayload)e.source.data; + PlanarityNodePayload dest=(PlanarityNodePayload)e.destination.data; + + PlanarityEdgePayload epl=(PlanarityEdgePayload)e.data; + + if(dest.dfsNum < source.dfsNum){ + epl.cost=2*dest.dfsNum; + } else{ + if(dest.lowpt2 >= source.dfsNum) epl.cost=2*dest.lowpt1; + else epl.cost=2*dest.lowpt1+1; + } + } + + sortEdges(); + } + + private void dfsInReorder(Node v, LinkedList deledges) { + PlanarityNodePayload vpl=(PlanarityNodePayload)v.data; + + vpl.dfsNum=dfsCount++; + vpl.lowpt1=vpl.lowpt2=vpl.dfsNum; + vpl.reached=true; + + //First pass + for(Node w: v.succ){ + PlanarityNodePayload wpl=(PlanarityNodePayload)w.data; + + //The edge (v -> w) is a tree edge + if(!wpl.reached){ + wpl.parent=v; + dfsInReorder(w,deledges); + vpl.lowpt1=Min(wpl.lowpt1,vpl.lowpt1); + } + else{ + vpl.lowpt1=Min(wpl.dfsNum,vpl.lowpt1); + + //the edge (v -> w) is a forward edge or the reversal of a tree edge. + if((wpl.dfsNum >= vpl.dfsNum) || w==vpl.parent) + deledges.add(getEdge(v,w)); + } + } + + + //Second pass + for(Node w: v.succ){ + PlanarityNodePayload wpl=(PlanarityNodePayload)w.data; + + //tree edge (assigned during first pass) + if(wpl.parent==v){ + if(wpl.lowpt1!=vpl.lowpt1) vpl.lowpt2=Min(wpl.lowpt1,vpl.lowpt2); + vpl.lowpt2=Min(vpl.lowpt2, wpl.lowpt2); + } + else{ + if(vpl.lowpt1 != wpl.dfsNum) vpl.lowpt2=Min(vpl.lowpt2, wpl.dfsNum); + } + } + + } + + /** Simple helper function returning the minimum*/ + private int Min(int a, int b) { + if(a>b) return b; + else return a; + } + + /** + * Used in reordering process of the planarity test. Edges are sorted + * according to their cost value. + */ + private void sortEdges() { + Edge[] e=edges.toArray(new Edge[edges.size()]); + + Arrays.sort(e,new Comparator(){ + public int compare(Edge e1, Edge e2) { + if(e1==null || e2==null || + e1.data==null || e2.data==null|| + !(e1.data instanceof PlanarityEdgePayload) || + !(e2.data instanceof PlanarityEdgePayload) ) return 0; + + return ((PlanarityEdgePayload)e1.data).cost- ((PlanarityEdgePayload)e2.data).cost; + } + }); + + //Delete all edges + for(Edge edge:e){ + removeEdge(edge); + } + + //Insert all edges is correct order + for(Edge edge:e){ + addEdge(edge); + } + } + + /** + * Assuming that the Graph is connected the method + * tests the planarity of the graph and constructs an + * embedding if it is planar. + */ + public Embedding createEmbedding(){ + + DiGraph G=clone(); + //Planar- algorithm makes shure the correct + //payload types are in the nodes. + if(G.planar()){ + if(G.nodes.size()<4) return new Embedding(clone()); + + DiGraph H=G.BiConnected; + for(Edge e :H.edges) e.data=new EmbeddingEdgePayload(); + //Lists of Edges of H + LinkedList T=new LinkedList(); + LinkedList A=new LinkedList(); + + cur_nr=0; + + Node first=G.nodes.values().iterator().next(); + assert first.succ.size()>0; + Edge firstEdge=G.getEdge(first,first.succ.getFirst()); + + + + G.resetNodeFlags(); + + embedding(firstEdge,G,H,LEFT,T,A); + + //conc R and A + T.addAll(A); + + for(Edge e:T) ((EmbeddingEdgePayload)e.data).sortNum=cur_nr++; + + //PlanarityPayload is used because cost can be used for + //sorting purposes. + for(Edge e:edges) { + PlanarityEdgePayload pl=new PlanarityEdgePayload(); + pl.cost=((EmbeddingEdgePayload)H.getEdge(H.getNode(e.source.ID),H.getNode(e.destination.ID)).data).sortNum; + e.data=pl; + } + + sortEdges(); + + return new Embedding(clone()); + } + return null; + } + + private void embedding(Edge e0, DiGraph G,DiGraph H, int t, LinkedList T, LinkedList A) { + //Determine Cycle C(e0) + Node x=e0.source; + Node y=e0.destination; + + ((PlanarityNodePayload)y.data).treeEdgeInto=e0; + Edge e1; + //first adjacent edge + e1=G.getEdge(y,y.succ.getFirst()); + Node wk=y; + + //e is a tree edge? + while(((PlanarityNodePayload)e1.destination.data).dfsNum > ((PlanarityNodePayload)wk.data).dfsNum){ + wk=e1.destination; + + ((PlanarityNodePayload)wk.data).treeEdgeInto=e1; + e1=G.getEdge(wk,wk.succ.getFirst()); + } + + Node w0=e1.destination; + Edge back_edge_into_w0=e1; + + //Process the subsegments + Node w=wk; + LinkedList Al=new LinkedList(); + LinkedList Ar=new LinkedList(); + LinkedList Tprime=new LinkedList(); + LinkedList Aprime=new LinkedList(); + + T.clear(); + T.add(H.getEdge(H.getNode(e1.source.ID),H.getNode(e1.destination.ID))); + while(w!=x){ + int count=0; + for(Node n:w.succ){ + + Edge e=G.getEdge(w,n); + count++; + if(count!=1){ + //Embed recursivly + //tree edge + if(((PlanarityNodePayload)w.data).dfsNum < ((PlanarityNodePayload)e.destination.data).dfsNum){ + int tprime=(t==((PlanarityEdgePayload)e.data).alpha) ? LEFT : RIGHT; + embedding(e,G, H,tprime,Tprime,Aprime); + } + else{ + Tprime.add(H.getEdge(H.getNode(e.source.ID),H.getNode(e.destination.ID))); + Aprime.add(H.getEdge(H.getNode(e.destination.ID),H.getNode(e.source.ID))); + } + //update lists T, Al and Ar + if(t==((PlanarityEdgePayload)e.data).alpha){ + Tprime.addAll(T); + T.clear(); + T.addAll(Tprime); //T= Tprime conc T + Tprime.clear(); + Al.addAll(Aprime); //Al= Al conc Aprime + Aprime.clear(); + } + else{ + T.addAll(Tprime); //T= T conc Tprime + Tprime.clear(); + Aprime.addAll(Ar); + Ar.clear(); + Ar.addAll(Aprime); //Ar= Aprime conc Ar + Aprime.clear(); + } + } + } //for + //Compute w's adjacency list and prepare for next iteration + Edge ne=((PlanarityNodePayload)w.data).treeEdgeInto; + T.add(H.getEdge(H.getNode(ne.destination.ID),H.getNode(ne.source.ID))); + + for(Edge e: T) ((EmbeddingEdgePayload)e.data).sortNum=cur_nr++; + T.clear(); + while(!Al.isEmpty() && Al.getLast().source ==H.getNode(((PlanarityNodePayload)w.data).parent.ID)){ + T.addFirst(Al.removeLast()); + } + ne=((PlanarityNodePayload)w.data).treeEdgeInto; + T.add(H.getEdge(H.getNode(ne.source.ID),H.getNode(ne.destination.ID))); + while(!Ar.isEmpty() && Ar.getFirst().source == H.getNode(((PlanarityNodePayload)w.data).parent.ID)){ + T.add(Ar.removeFirst()); + } + + w=((PlanarityNodePayload)w.data).parent; + }//while + + //Prepare the output + A.clear(); + A.addAll(Ar); + Ar.clear(); + A.add(H.getEdge(H.getNode(back_edge_into_w0.destination.ID),H.getNode(back_edge_into_w0.source.ID))); + A.addAll(Al); + Al.clear(); + } + + /** + * Reverses the edge e + */ + public boolean reverseEdge(Edge e){ + if(edges.contains(e)){ + Node source=e.source; + Node dest=e.destination; + source.succ.remove(dest); + dest.pred.remove(source); + source.pred.add(dest); + dest.succ.add(source); + e.reverseEdge(); + return true; + } + return false; + } + + + /** + * This class encapsulates the payload a node + * has to carry turing the planarization step. + */ + public class PlanarityNodePayload{ + public int dfsNum=0; + public Node parent=null; + public boolean reached=false; + public int lowpt1=0; + public int lowpt2=0; + //Embedding vars + public Edge treeEdgeInto=null; + } + + /** + * This class encapsulates the payload a edge + * has to carry turing the planarization step. + */ + public class PlanarityEdgePayload{ + public int alpha=0; + public int cost=0; + } + + /** + * This class encapsulates the payload a edge + * has to carry turing the planarisation step. + */ + public class EmbeddingEdgePayload{ + public int sortNum=0; + } + + + /** + * This class encapsulates the payloade a node + * has to carry within the algotithm for making it + * biconnectional + */ + public class BiConPayload{ + public int dfsNum=0; + public int lowPt=0; + public boolean reached=false; + public Node parent=null; + } + +} diff --git a/visualizer/C1Visualizer/GraphHelper/src/main/java/at/ssw/visualizer/graphhelper/Edge.java b/visualizer/C1Visualizer/GraphHelper/src/main/java/at/ssw/visualizer/graphhelper/Edge.java new file mode 100644 index 000000000000..b530240ce125 --- /dev/null +++ b/visualizer/C1Visualizer/GraphHelper/src/main/java/at/ssw/visualizer/graphhelper/Edge.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.graphhelper; + +/** + * Implements the edge of an directed graph. + * + * @author Stefan Loidl + */ +public class Edge { + + public Node source, destination; + + /** data field for the use with user algorithms*/ + public Object data=null; + + public boolean visited=false; + + /** + * Creates a new edge form source to destination Node + */ + public Edge(Node source, Node destination) { + this.source=source; + this.destination=destination; + } + + /** + * Returns a new Edge from destination to source. + */ + public Edge getReverseEdge(){ + return new Edge(destination,source); + } + + /** + * Reverses this Edge. + */ + public void reverseEdge(){ + Node n=source; + source=destination; + destination=n; + } +} diff --git a/visualizer/C1Visualizer/GraphHelper/src/main/java/at/ssw/visualizer/graphhelper/Embedding.java b/visualizer/C1Visualizer/GraphHelper/src/main/java/at/ssw/visualizer/graphhelper/Embedding.java new file mode 100644 index 000000000000..3a68ffe083d0 --- /dev/null +++ b/visualizer/C1Visualizer/GraphHelper/src/main/java/at/ssw/visualizer/graphhelper/Embedding.java @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.graphhelper; + +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; + +/** + * This class is a helper that creates an embedding of a DiGraph + * by traversing the nodes (and their edges) according to the clockwise + * and counter clockwise ordering of the edges within the node. + * Before creating an instance the DiGraph must be: + * - planarized + * - embedded + * The embedding is represented by a finite list of faces. + * + * @author Stefan Loidl + */ +public class Embedding { + + private DiGraph graph; + private LinkedList faces=new LinkedList(); + private Face infinityFace=null; + + + /** + * Creates an embedding from a planar DiGraph with + * Edges sorted clockwise. Bidirected Edges are not allowed and + * therefore broken within this class. + */ + public Embedding(DiGraph dg) { + graph=dg; + graph.breakBiDirection(); + + Collection edges=dg.getEdges(); + for(Edge e:edges) e.data=new EdgePayload(); + for(Edge e:edges){ + EdgePayload ep=(EdgePayload)e.data; + if(ep.left==null){ + Face p=new Face(); + createFace(e,e,p,true, true); + for(Edge ed:p.edges){ + EdgePayload payload=(EdgePayload)ed.data; + if(payload.left==null) payload.left=p; + else payload.right=p; + } + faces.add(p); + } + if(ep.right==null){ + Face p=new Face(); + createFace(e,e,p,true, false); + if(ep.left.equals(p)){ + p=new Face(); + createFace(e,e,p,true, true); + } + for(Edge ed:p.edges){ + EdgePayload payload=(EdgePayload)ed.data; + if(payload.left==null) payload.left=p; + else payload.right=p; + } + faces.add(p); + } + } + + //Determine the infinity face as the plane + //with the most surrounding edges + int max=0; + for(Face p:faces){ + if(p.edges.size()>max){ + max=p.edges.size(); + infinityFace=p; + } + } + } + + /** + * Creates a new face by traversing the graph according to the + * clockwise ordering of the edges. + */ + private void createFace(Edge first, Edge e, Face p, boolean forward, boolean left){ + Edge next; + int index; + p.edges.add(e); + //calculate next Edge + Node n; + if(forward) n=e.destination; + else n=e.source; + + if(left){ + index=n.edges.indexOf(e); + index++; + if(index >= n.edges.size()) index=0; + next=n.edges.get(index); + } + else{ + index=n.edges.indexOf(e); + index--; + if(index < 0) index=n.edges.size()-1; + next=n.edges.get(index); + } + + forward=(next.source==n); + if(next==first) { + Node no; + if(forward) no=next.destination; + else no=next.source; + if(no.edges.size()==1) p.edges.add(next); + return; + } + + createFace(first,next,p,forward,left); + } + + /** + * Returns the graph. + */ + public DiGraph getGraph(){ + return graph; + } + + /** + * Returns the faces of the embedding. + */ + public LinkedList getFaces(){ + return faces; + } + + /** + * Returns the infinity face. + */ + public Face getInfinityFace(){ + return infinityFace; + } + + /** + * Class representing a face + */ + public class Face{ + + //surrounding edges + public LinkedList edges=new LinkedList(); + + /** + * Overriden equals + */ + public boolean equals(Object o){ + if(!(o instanceof Face)) return false; + Face p=(Face)o; + + if(edges.size()!=p.edges.size()) return false; + + for(Edge e:p.edges) if(!edges.contains(e)) return false; + return true; + } + + /** + * Returns the nodes within the face. + */ + public Collection getNodes(){ + LinkedList n=new LinkedList(); + for(Edge e:edges){ + n.add(e.source); + n.add(e.destination); + } + return n; + } + + /** + * For debug reasons mainly + */ + public String toString(){ + StringBuffer buf=new StringBuffer(); + for(Edge e: edges){ + buf.append("("+e.source.ID+","+e.destination.ID+"), "); + } + return buf.substring(0,buf.length()-2); + } + } + + + + /** + * Adds left and right face to edge via payload data. + */ + public class EdgePayload{ + public Face left=null; + public Face right=null; + } + + + /** + * For debug reasons mainly + */ + public String toString(){ + StringBuffer buf=new StringBuffer(); + buf.append("\n"+graph.toString()+"\n"); + + int i=0; + for(Face p:faces){ + buf.append(i+": "+p.toString()+"\n"); + i++; + } + buf.append("\n"); + for(Node n:graph.getNodes()){ + buf.append(n.ID+" "); + for(Edge e:n.edges){ + buf.append("("+e.source.ID+","+e.destination.ID+") "); + } + buf.append("\n"); + } + return buf.toString(); + } + + /** + * Adds an edge, splitting the face between the two nodes. + */ + void addFaceEdge(Face face, Node from, Node to) { + if(face==null || from==null || to==null) return; + assert faces.contains(face); + assert face.getNodes().contains(from); + assert face.getNodes().contains(to); + + Edge lFrom=null, rFrom=null; + //find the two edges containing from + for(Edge e:face.edges){ + if(e.source==from || e.destination==from){ + if(lFrom==null) lFrom=e; + else{ + rFrom=e; + break; + } + } + } + + //make shure lFrom has smaller index + if(face.edges.indexOf(lFrom) > face.edges.indexOf(rFrom)){ + Edge t=rFrom; + rFrom=lFrom; + lFrom=t; + } + + //find the right half of the face from "from" to "to" + LinkedList rightPart=new LinkedList(); + int index=face.edges.indexOf(rFrom); + while(true){ + Edge e=face.edges.get(index); + rightPart.add(e); + if(e.destination==to || e.source==to) break; + index=(index+1)%face.edges.size(); + } + + //find the right half of the face from "from" to "to" + int prevIndex=index; + LinkedList leftPart=new LinkedList(); + index=face.edges.indexOf(lFrom); + while(index!=prevIndex){ + Edge e=face.edges.get(index); + leftPart.add(e); + index--; + if(index<0) index=face.edges.size()-1; + } + + //Attention! Clockwise ordering is destroyed here + Edge newedge=new Edge(from,to); + graph.addEdge(newedge); + + leftPart.add(newedge); + rightPart.add(newedge); + + //Split faces. + face.edges=leftPart; + Face newface=new Face(); + newface.edges=rightPart; + faces.add(newface); + } +} diff --git a/visualizer/C1Visualizer/GraphHelper/src/main/java/at/ssw/visualizer/graphhelper/Node.java b/visualizer/C1Visualizer/GraphHelper/src/main/java/at/ssw/visualizer/graphhelper/Node.java new file mode 100644 index 000000000000..6e018ca4ccc7 --- /dev/null +++ b/visualizer/C1Visualizer/GraphHelper/src/main/java/at/ssw/visualizer/graphhelper/Node.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.graphhelper; + +import java.util.LinkedList; + +/** + * Implements a node of the directed graph. + * + * @author Stefan Loidl + */ +public class Node { + + /**unique identifier*/ + public String ID; + + /** flag used within algorithms*/ + public boolean visited; + + /** all edges adjacent to the node*/ + public LinkedList edges; + + /** + * List of successors and predecessors of the node + * these lists are not meant to be changed by the user. + * they are modified when inserting edges + */ + public LinkedList succ,pred; + + /** data field for the use with algorithms*/ + public Object data=null; + + /** Creates a new instance of Node- ID should be unique within the graph*/ + public Node(String ID) { + this.ID=ID; + visited=false; + succ=new LinkedList(); + pred=new LinkedList(); + edges=new LinkedList(); + } + + +} diff --git a/visualizer/C1Visualizer/GraphHelper/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/GraphHelper/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..55ebdc16403d --- /dev/null +++ b/visualizer/C1Visualizer/GraphHelper/src/main/nbm/manifest.mf @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.graphhelper +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/graphhelper/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/GraphHelper/src/main/resources/at/ssw/visualizer/graphhelper/Bundle.properties b/visualizer/C1Visualizer/GraphHelper/src/main/resources/at/ssw/visualizer/graphhelper/Bundle.properties new file mode 100644 index 000000000000..4185a17976b2 --- /dev/null +++ b/visualizer/C1Visualizer/GraphHelper/src/main/resources/at/ssw/visualizer/graphhelper/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=Graph Helper diff --git a/visualizer/C1Visualizer/GraphLayoutAPI/pom.xml b/visualizer/C1Visualizer/GraphLayoutAPI/pom.xml new file mode 100644 index 000000000000..b7a77dcdc2f2 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutAPI/pom.xml @@ -0,0 +1,58 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + GraphLayoutAPI + 1.14-SNAPSHOT + nbm + GraphLayoutAPI + + UTF-8 + + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.positionmanager + at.ssw.positionmanager.export + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/Cluster.java b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/Cluster.java new file mode 100644 index 000000000000..2766a2266c43 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/Cluster.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.positionmanager; + +import java.util.Set; + +/** + * + * @author Thomas Wuerthinger + */ +public interface Cluster { + public Cluster getOuter(); + public Set getSuccessors(); + public Set getPredecessors(); +} diff --git a/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/LayoutGraph.java b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/LayoutGraph.java new file mode 100644 index 000000000000..5b117f101141 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/LayoutGraph.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.positionmanager; + +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * + * @author Thomas Wuerthinger + */ +public class LayoutGraph { + + + private Set links; + private SortedSet vertices; + private Hashtable> inputPorts; + private Hashtable> outputPorts; + private Hashtable> portLinks; + + + public LayoutGraph(Set links) { + this(links, new HashSet()); + } + + /** Creates a new instance of LayoutGraph */ + public LayoutGraph(Set links, Set additionalVertices) { + this.links = links; + assert verify(); + + vertices = new TreeSet(); + portLinks = new Hashtable>(); + inputPorts = new Hashtable>(); + outputPorts = new Hashtable>(); + + for(Link l : links) { + Port p = l.getFrom(); + Port p2 = l.getTo(); + Vertex v1 = p.getVertex(); + Vertex v2 = p2.getVertex(); + + if(!vertices.contains(v1)) { + + outputPorts.put(v1, new HashSet()); + inputPorts.put(v1, new HashSet()); + vertices.add(v1); + } + + if(!vertices.contains(v2)) { + vertices.add(v2); + outputPorts.put(v2, new HashSet()); + inputPorts.put(v2, new HashSet()); + } + + if(!portLinks.containsKey(p)) { + HashSet hashSet = new HashSet(); + portLinks.put(p, hashSet); + } + + if(!portLinks.containsKey(p2)) { + portLinks.put(p2, new HashSet()); + } + + outputPorts.get(v1).add(p); + inputPorts.get(v2).add(p2); + + portLinks.get(p).add(l); + portLinks.get(p2).add(l); + } + + for(Vertex v : additionalVertices) { + if(!vertices.contains(v)) { + outputPorts.put(v, new HashSet()); + inputPorts.put(v, new HashSet()); + vertices.add(v); + } + } + } + + public Set getInputPorts(Vertex v) { + return this.inputPorts.get(v); + } + + public Set getOutputPorts(Vertex v) { + return this.outputPorts.get(v); + } + + public Set getPortLinks(Port p) { + return portLinks.get(p); + } + + public Set getLinks() { + return links; + } + + public boolean verify() { + return true; + } + + public SortedSet getVertices() { + return vertices; + } + + private void markNotRoot(Set notRootSet, Set visited, Vertex v, Vertex startingVertex) { + + if(visited.contains(v)) return; + if(v != startingVertex) { + notRootSet.add(v); + } + visited.add(v); + Set outPorts = getOutputPorts(v); + for(Port p : outPorts) { + Set links = getPortLinks(p); + for(Link l : links) { + Port other = l.getTo(); + Vertex otherVertex = other.getVertex(); + markNotRoot(notRootSet, visited, otherVertex, startingVertex); + } + } + } + + // Returns a set of vertices with the following properties: + // - All Vertices in the set startingRoots are elements of the set. + // - When starting a DFS at every vertex in the set, every vertex of the + // whole graph is visited. + public Set findRootVertices(Set startingRoots) { + + Set notRootSet = new HashSet(); + for(Vertex v : startingRoots) { + if(!notRootSet.contains(v)) { + Set visited = new HashSet(); + markNotRoot(notRootSet, visited, v, v); + } + } + + Set vertices = getVertices(); + for(Vertex v : vertices) { + if(!notRootSet.contains(v)) { + Set visited = new HashSet(); + markNotRoot(notRootSet, visited, v, v); + } + } + + Set result = new HashSet(); + for(Vertex v : vertices) { + if(!notRootSet.contains(v)) { + result.add(v); + } + } + + return result; + } + + + public Set findRootVertices() { + return findRootVertices(new HashSet()); + } + + public Set getClusters() { + + Set clusters = new HashSet(); + for(Vertex v : getVertices()) { + if(v.getCluster() != null) { + clusters.add(v.getCluster()); + } + } + + return clusters; + } + +} diff --git a/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/LayoutManager.java b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/LayoutManager.java new file mode 100644 index 000000000000..f4050a2acfe0 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/LayoutManager.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.positionmanager; + +/** + * + * @author Thomas Wuerthinger + */ +public interface LayoutManager { + + public void doLayout(LayoutGraph graph); + public void doRouting(LayoutGraph graph); + +} diff --git a/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/Link.java b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/Link.java new file mode 100644 index 000000000000..288df1010087 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/Link.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.positionmanager; + +import java.awt.Point; +import java.util.List; + +/** + * + * @author Thomas Wuerthinger + */ +public interface Link { + public Port getFrom(); + public Port getTo(); + public List getControlPoints(); + public void setControlPoints(List list); +} diff --git a/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/Port.java b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/Port.java new file mode 100644 index 000000000000..aa5b93167e80 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/Port.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.positionmanager; + +import java.awt.Point; + +/** + * + * @author Thomas Wuerthinger + */ +public interface Port { + + public Vertex getVertex(); + public Point getRelativePosition(); + +} diff --git a/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/Vertex.java b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/Vertex.java new file mode 100644 index 000000000000..e28b284fa325 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/Vertex.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.positionmanager; + +import java.awt.Dimension; +import java.awt.Point; + +/** + * + * @author Thomas Wuerthinger + */ +public interface Vertex extends Comparable{ + + public Cluster getCluster(); + public Dimension getSize(); + public Point getPosition(); + public void setPosition(Point p); + public boolean isDirty(); + public boolean isRoot(); + + public boolean isExpanded(); + public boolean isFixed(); + public boolean isMarked(); +} diff --git a/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/export/GMLFileExport.java b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/export/GMLFileExport.java new file mode 100644 index 000000000000..f89a747466a7 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/export/GMLFileExport.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.positionmanager.export; + +import at.ssw.positionmanager.LayoutGraph; +import at.ssw.positionmanager.Link; +import at.ssw.positionmanager.Vertex; +import java.awt.Color; +import java.awt.Point; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Hashtable; +import java.util.Map; + +/** + * + * @author Stefan Loidl + */ +public class GMLFileExport implements LayoutGraphExporter{ + + private File exportFile; + private Map names; + Map colors; + + /** Creates a new instance of GMLFileExport */ + public GMLFileExport(File f) { + exportFile=f; + } + + public GMLFileExport(File f, Map nameList, Map colors){ + exportFile=f; + names=nameList; + this.colors=colors; + } + + public boolean export(LayoutGraph lg) { + if(lg==null || exportFile==null) return false; + Hashtable index=new Hashtable(); + try{ + FileWriter fw=new FileWriter(exportFile); + fw.write("Creator \"Graph Visualizer SSW\"\nVersion 1.0"); + fw.write("\ngraph [\n\tdirected 1\n"); + + int i=0; + //export nodes + for(Vertex v: lg.getVertices()){ + index.put(v,new Integer(i)); + exportVertex(v,i,fw); + i++; + } + //export edges + for(Link l: lg.getLinks()){ + int from=index.get(l.getFrom().getVertex()).intValue(); + int to=index.get(l.getTo().getVertex()).intValue(); + exportLink(l,from,to,fw); + } + + fw.write("]"); + fw.close(); + } catch(Exception e){ + return false; + } + return true; + } + + + + private void exportVertex(Vertex v, int id, FileWriter fw) throws IOException { + StringBuffer buf=new StringBuffer(); + buf.append("\tnode [\n\t\tid "); + buf.append(id); + buf.append("\n\t\tlabel \""); + if(names!=null){ + buf.append(names.get(v)); + }else buf.append(id); + buf.append("\"\n\t\tlabelAnchor \"c\"\n\t\tgraphics [\n\t\t\tx "); + buf.append((double)v.getPosition().x); + buf.append("\n\t\t\ty "); + buf.append((double)v.getPosition().y); + buf.append("\n\t\t\tw "); + buf.append((double)v.getSize().width); + buf.append("\n\t\t\th "); + buf.append((double)v.getSize().height); + buf.append("\n\t\t\ttype \"rectangle\"\n\t\t\tfill \"#"); + if(colors!=null){ + Color c=colors.get(v); + if(c==null) c=Color.WHITE; + buf.append(Integer.toHexString(c.getRed())); + buf.append(Integer.toHexString(c.getGreen())); + buf.append(Integer.toHexString(c.getBlue())); + } + else buf.append("FFFFFF"); + buf.append("\""); + buf.append("\n\t\t\toutline \"#000000\"\n\t\t]"); + buf.append("\n\t\tLabelGraphics [\n\t\t\ttype \"text\"\n\t\t\tfill \"#000000\""); + buf.append("\n\t\t\tanchor \"c\"\n\t\t]\n\t]\n"); + fw.write(buf.toString()); + } + + private void exportLink(Link l, int from, int to, FileWriter fw) throws IOException { + StringBuffer buf=new StringBuffer(); + buf.append("\tedge [\n\t\tsource "); + buf.append(from); + buf.append("\n\t\ttarget "); + buf.append(to); + buf.append("\n\t\tgraphics [\n\t\t\ttype \"line\""); + buf.append("\n\t\t\tarrow \"last\""); + if(l.getControlPoints()!=null && l.getControlPoints().size()>0){ + buf.append("\n\t\t\tLine ["); + for(Point p:l.getControlPoints()){ + buf.append("\n\t\t\t\tpoint [ x "); + buf.append((double)p.x); + buf.append(" y "); + buf.append((double)p.y); + buf.append(" ]"); + } + buf.append("\n\t\t\t]"); + } + buf.append("\n\t\t]\n\t]\n"); + fw.write(buf.toString()); + } + +} diff --git a/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/export/LayoutGraphExporter.java b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/export/LayoutGraphExporter.java new file mode 100644 index 000000000000..bb81ae7f2552 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/java/at/ssw/positionmanager/export/LayoutGraphExporter.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.positionmanager.export; + +import at.ssw.positionmanager.LayoutGraph; + +/** + * Instances of this interface can be used to export the LayoutGraph + * to different formats. + * + * @author Stefan Loidl + */ +public interface LayoutGraphExporter { + /** Returns if the export was successful*/ + public boolean export(LayoutGraph lg); +} diff --git a/visualizer/C1Visualizer/GraphLayoutAPI/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..ec3b480bc596 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/nbm/manifest.mf @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.positionmanager +OpenIDE-Module-Localizing-Bundle: at/ssw/positionmanager/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/GraphLayoutAPI/src/main/resources/at/ssw/positionmanager/Bundle.properties b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/resources/at/ssw/positionmanager/Bundle.properties new file mode 100644 index 000000000000..a6faff40caa4 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutAPI/src/main/resources/at/ssw/positionmanager/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=Graph Layout API diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/pom.xml b/visualizer/C1Visualizer/GraphLayoutImpl/pom.xml new file mode 100644 index 000000000000..8066b2b5e752 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/pom.xml @@ -0,0 +1,76 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + GraphLayoutImpl + 1.14-SNAPSHOT + nbm + GraphLayoutImpl + + UTF-8 + + + + at.ssw.visualizer + GraphLayoutAPI + + ${project.version} + + + at.ssw.visualizer + GraphHelper + + ${project.version} + + + org.eclipse + draw2d + 3.2.100-v20070529 + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.dataflow.layout + at.ssw.dataflow.options + at.ssw.graphanalyzer.positioning + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/CompoundForceLayouter.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/CompoundForceLayouter.java new file mode 100644 index 000000000000..29d1f227b423 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/CompoundForceLayouter.java @@ -0,0 +1,511 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.dataflow.layout; + +import at.ssw.dataflow.options.BooleanStringValidator; +import at.ssw.dataflow.options.DoubleStringValidator; +import at.ssw.dataflow.options.IntStringValidator; +import at.ssw.dataflow.options.Validator; +import at.ssw.positionmanager.Cluster; +import at.ssw.positionmanager.LayoutGraph; +import at.ssw.positionmanager.Link; +import at.ssw.positionmanager.Vertex; +import at.ssw.visualizer.graphhelper.DiGraph; +import at.ssw.visualizer.graphhelper.Edge; +import at.ssw.visualizer.graphhelper.Node; +import java.awt.Point; +import java.awt.geom.Point2D; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Random; + + +/** + * This class implements a force directed layout algorithm. It uses the well + * known spring model that relys on springs and electrical forces. To implement + * clustering additional virtual nodes are inserted to the original graph. + * For each cluster one such node is inserted that is connected to all of the + * nodes within the cluster. Moreover it is connected to each cluster that has + * links common with the current cluster. + * + * @author Stefan Loidl + */ +public class CompoundForceLayouter implements ExternalGraphLayouter{ + + //Parameters for the force model + private double SPRINGLEN = 50; + private double STIFFNESS = 15; + private double REPULSION = 100; + + //forces between expanded nodes in the same cluster + private double SPRINGLENEXP = 50; + private double STIFFNESSEXP = 15; + private double REPULSIONEXP = 300; + + //forces between nodes within different clusters + private double OVERCLUSTERSPRINGLEN = 70; + private double OVERCLUSTERSTIFFNESS = 15; + private double OVERCLUSTERREPULSION = 200; + + //Forces between real and virtual nodes + private double CLUSTERSPRINGLEN = 50; + private double CLUSTERSTIFFNESS = 15; + private double CLUSTERREPULSION = 100; + + //Forces between virtual nodes + private double INTERCLUSTERSPRINGLEN = 300; + private double INTERCLUSTERSTIFFNESS = 30; + private double INTERCLUSTERREPULSION = 800; + + //During one cycle one node may only move this distance + private double MAXIMUMMOVEMENT=500; + + //Space between the connected components + private int PADDING=30; + + //Iterations the algorithms uses. + private int ITERATIONS=100; + + //Seed for the random node positioning + private long SEED=133; + + //Use logarithmic spring model. The alternative is hookes law. + private boolean USELOGSPRINGS=false; + + //Reuse current node positions for further optimizations + //the alternative is to use random positions. + private boolean USECURRENTNODEPOSITIONS=false; + + //Current positions of the nodes + private Hashtable posList; + + + //Performs the layout task. + public void doLayout(LayoutGraph graph) { + Hashtable idtoverticles=new Hashtable(); + Hashtable verticlestoid=new Hashtable(); + + DiGraph dg=new DiGraph(); + Iterator iter=graph.getVertices().iterator(); + + //Add Nodes to Graphhelper + int i=0; + while(iter.hasNext()){ + String id=String.valueOf(i); + dg.addNode(new Node(id)); + Vertex v=iter.next(); + + idtoverticles.put(id,v); + verticlestoid.put(v,id); + i++; + } + + //Add Edges to Graphhelper + for(Link l: graph.getLinks()){ + String from=verticlestoid.get(l.getFrom().getVertex()); + String to=verticlestoid.get(l.getTo().getVertex()); + + dg.addEdge(new Edge(dg.getNode(from),dg.getNode(to))); + } + + //Add virtual cluster Nodes to Graphhelper + iter=graph.getVertices().iterator(); + Hashtable virtualNodes=new Hashtable(); + Node defaultNode=new Node(String.valueOf(i++)); + dg.addNode(defaultNode); + boolean defNodeUsed=false; + + while(iter.hasNext()){ + Vertex v=iter.next(); + Cluster c=v.getCluster(); + Node vn; + + if(c==null) { + vn=defaultNode; + defNodeUsed=true; + } + else{ + if(virtualNodes.containsKey(c)) vn=virtualNodes.get(c); + else{ + String id=String.valueOf(i++); + vn=new Node(id); + virtualNodes.put(c,vn); + dg.addNode(vn); + } + } + + dg.addEdge(new Edge(vn,dg.getNode(verticlestoid.get(v)))); + } + + //Create structure to identify virtual nodes later-on + HashSet vNodes=new HashSet(); + for(Node n: virtualNodes.values()) vNodes.add(n.ID); + + //nodes with no cluster are added to defaultnode. If none exists + //then delete the node. + if(!defNodeUsed) dg.removeNode(defaultNode); + else vNodes.add(defaultNode.ID); + + //Add cluster links to Graphhelper + for(Cluster c: virtualNodes.keySet()){ + if(c.getSuccessors()!=null){ + for(Cluster c1: c.getSuccessors()){ + dg.addEdge(new Edge(virtualNodes.get(c),virtualNodes.get(c1))); + } + } + } + + //Perform layout + layout(graph,dg,idtoverticles,verticlestoid, vNodes); + } + + + /* Performs the layout of the spring algorithm via an iterative relaxation approach*/ + private void layout(LayoutGraph lg, DiGraph digraph, Hashtable idtoverticles, Hashtable verticlestoid, HashSet vNodes) { + int lastmax=0; + int max=0; + for(DiGraph dg:digraph.getConnectedComponents()){ + + //initialize positions with random numbers + posList=new Hashtable(); + Random rand=new Random(SEED); + for(Node w: dg.getNodes()){ + if(USECURRENTNODEPOSITIONS){ + Vertex v=idtoverticles.get(w.ID); + double x=0.0,y=0.0; + if(v!=null){ + x=v.getPosition().x; + y=v.getPosition().y; + } + posList.put(w.ID,new doublePoint(x,y)); + } + else posList.put(w.ID,new doublePoint(rand.nextDouble()*300d,rand.nextDouble()*300d)); + } + + //perform several relaxation iterations + for(int i=0; ip.getX()) smallestX=p.getX(); + if(smallestY>p.getY()) smallestY=p.getY(); + } + + max=0; + for(Node w: dg.getNodes()){ + Vertex v=idtoverticles.get(w.ID); + if(v==null) continue; + + Point2D p=posList.get(w.ID); + int x=((int)(p.getX()-smallestX))+lastmax+PADDING; + int y=((int)(p.getY()-smallestY))+PADDING; + + if(x+v.getSize().width >max) max=x+v.getSize().width; + v.setPosition(new Point(x, y)); + } + lastmax=max; + } + } + + + /* + * Calculates the forces on node n and moves it a small distance to lessen + * it. Forces for virtual nodes are different from others but do the same + * job. + */ + private void relaxation(Node n, DiGraph dg, HashSet vNodes, Hashtable idtoverticles){ + + double X = posList.get(n.ID).getX(); + double Y = posList.get(n.ID).getY(); + + LinkedList adjacentVertices=new LinkedList(); + + Vertex v=idtoverticles.get(n.ID); + + // Get all adjacent Nodes + for(Node pre: n.pred){ + if(!n.ID.equals(pre.ID)) + adjacentVertices.add(pre); + } + for(Node succ: n.succ){ + if(!n.ID.equals(succ.ID)) + adjacentVertices.add(succ); + } + + //Calcualte Spring len between all adjacent Nodes + double SpringX = 0, SpringY = 0; + for(Node adjacent:adjacentVertices) { + double stiffness, springlen; + + if(vNodes.contains(n.ID) || vNodes.contains(adjacent.ID)){ + if(vNodes.contains(n.ID) && vNodes.contains(adjacent.ID)){ + stiffness=INTERCLUSTERSTIFFNESS; + springlen=INTERCLUSTERSPRINGLEN; + } + else{ + stiffness=CLUSTERSTIFFNESS; + springlen=CLUSTERSPRINGLEN; + } + } + else{ + Vertex v1=idtoverticles.get(adjacent.ID); + if((v1!=null && v!=null)&& v1.getCluster()!=v.getCluster()){ + stiffness=OVERCLUSTERSTIFFNESS; + springlen=OVERCLUSTERSPRINGLEN; + } + else{ + if((v1!=null && v!=null)&&(v1.isExpanded()||v.isExpanded())){ + stiffness=STIFFNESSEXP; + springlen=SPRINGLENEXP; + }else{ + stiffness=STIFFNESS; + springlen=SPRINGLEN; + } + } + } + + double adjX = posList.get(adjacent.ID).getX(); + double adjY = posList.get(adjacent.ID).getY(); + + double distance = Point2D.distance( adjX, adjY, X, Y ); + //Minimum distance between nodes! + if(distance == 0) distance = 0.01d; + + if(USELOGSPRINGS){ + //Logarithmic Springs + SpringX +=stiffness*Math.log(distance/springlen)*((X-adjX)/distance); + SpringY +=stiffness*Math.log(distance/springlen)*((Y-adjY)/distance); + } + else{ + //Hookes Law with relativ springkonstant + SpringX +=stiffness*((distance-springlen)/(2*springlen)) *((X-adjX)/distance); + SpringY +=stiffness*((distance-springlen)/(2*springlen)) *((Y-adjY)/distance); + } + } + + //Calcualte Repulsion + double RepulsionX = 0, RepulsionY = 0; + for(Node w: dg.getNodes()) { + if(w == n) continue; + + double repulsion; + if(vNodes.contains(n.ID) || vNodes.contains(w.ID)) { + if(vNodes.contains(n.ID) && vNodes.contains(w.ID)){ + repulsion=INTERCLUSTERREPULSION; + } + else{ + repulsion=CLUSTERREPULSION; + } + } + else { + Vertex v1=idtoverticles.get(w.ID); + if((v1!=null && v!=null)&& v1.getCluster()!=v.getCluster()){ + repulsion=OVERCLUSTERREPULSION; + } + else { + if((v1!=null && v!=null)&&(v1.isExpanded()||v.isExpanded())) + repulsion=REPULSIONEXP; + else repulsion=REPULSION; + } + } + + double nX = posList.get(w.ID).getX(); + double nY = posList.get(w.ID).getY(); + + double distance = Point2D.distance( nX, nY, X, Y ); + if(distance == 0) distance = 0.01d; + + //If Spring energy is positiv- this one is negativ + RepulsionX -= (repulsion/distance)*((X-nX)/distance); + RepulsionY -= (repulsion/distance)*((Y-nY)/distance); + } + + // Move Node in direction of the force + double dx = -(SpringX + RepulsionX); + double dy = -(SpringY + RepulsionY); + //Make shure the node moves not too far off the others + if(dx>MAXIMUMMOVEMENT) dx=MAXIMUMMOVEMENT; + if(dx*-1>MAXIMUMMOVEMENT) dx=-MAXIMUMMOVEMENT; + if(dy>MAXIMUMMOVEMENT) dy=MAXIMUMMOVEMENT; + if(dy*-1>MAXIMUMMOVEMENT) dy=-MAXIMUMMOVEMENT; + + Point2D p=posList.get(n.ID); + p.setLocation(p.getX()+dx,p.getY()+dy); + } + + /* Performs the routing via a simple direct line router */ + public void doRouting(LayoutGraph graph) { + RoutingHelper.doRouting(graph); + } + + public boolean isClusteringSupported() { + return true; + } + + public boolean isAnimationSupported() { + return true; + } + + public boolean isMovementSupported() { + return true; + } + + public void setUseCurrentNodePositions(boolean b) { + USECURRENTNODEPOSITIONS=b; + } + + // + private static String[] options={"Componentpadding", "Springlength","Over Cl. Sp.Length", "Virtual Springlength", "Stiffness","Over Cl. Stiffness", "Virtual Stiffness", "Iterations", "Repulsion","Over Cl. Repulsion","Virtual Repulsion", "Seed", "Log. Springs"}; + private static String[] descriptions={"Minimum space between connected components", + "Length of the spring between nodes in the same cluster","Length of the spring between nodes in different clusters","Length of the spring between virtual nodes", + "Stiffness of the spring between the nodes in the same cluster","Stiffness of the spring between the nodes in different clusters", "Stiffness of the spring between the virtual nodes", + "Relaxation iterations the algorithm performs", + "Repulsion between nodes in the same cluster","Repulsion between nodes in different clusters", "Repulsion between vitual nodes", + "Seed for the random prepositioning", "Logarithmic spring simulation is used?"}; + private static Class[] optionclass={String.class,String.class, String.class,String.class,String.class,String.class,String.class}; + private static Validator[] validators={ + new IntStringValidator(0,1000), //padding + new DoubleStringValidator(0.0d,1000.0d), //Springlen + new DoubleStringValidator(0.0d,1000.0d), //Over cluster Springlen + new DoubleStringValidator(0.0d,1000.0d), //Virtual Springlen + new DoubleStringValidator(0.0d,1000.0d), //Stiffness + new DoubleStringValidator(0.0d,1000.0d), //over cluster Stiffness + new DoubleStringValidator(0.0d,1000.0d), //virtual Stiffness + new IntStringValidator(0,5000), //Iterations + new DoubleStringValidator(0.0d,10000.0d), //Repulsion + new DoubleStringValidator(0.0d,10000.0d), //Over cluster Repulsion + new DoubleStringValidator(0.0d,10000.0d), //Virtual Repulsion + new IntStringValidator(0,Integer.MAX_VALUE), //Seed + new BooleanStringValidator() //Log Spring + }; + + + public String[] getOptionKeys() { + return options; + } + + public boolean setOption(String key, Object value) { + if(key==null) return false; + + int i=getIndexForKey(key); + if(i==-1) return false; + + if(validators[i].validate(value)){ + switch(i){ + case 0: PADDING=Integer.parseInt((String)value); break; + case 1: SPRINGLEN=Double.parseDouble((String)value);break; + case 2: OVERCLUSTERSPRINGLEN=Double.parseDouble((String)value);break; + case 3: INTERCLUSTERSPRINGLEN=Double.parseDouble((String)value);break; + case 4: STIFFNESS=Double.parseDouble((String)value);break; + case 5: OVERCLUSTERSTIFFNESS=Double.parseDouble((String)value);break; + case 6: INTERCLUSTERSTIFFNESS=Double.parseDouble((String)value);break; + case 7: ITERATIONS=Integer.parseInt((String)value);break; + case 8: REPULSION=Double.parseDouble((String)value);break; + case 9: OVERCLUSTERREPULSION=Double.parseDouble((String)value);break; + case 10: INTERCLUSTERREPULSION=Double.parseDouble((String)value);break; + case 11: SEED=Integer.parseInt((String)value);break; + case 12: USELOGSPRINGS=Boolean.parseBoolean((String)value);break; + default: return false; + } + return true; + } + return false; + } + + + public Object getOption(String key) { + if(key==null) return null; + + int i=getIndexForKey(key); + + switch(i){ + case 0: return String.valueOf(PADDING); + case 1: return String.valueOf(SPRINGLEN); + case 2: return String.valueOf(OVERCLUSTERSPRINGLEN); + case 3: return String.valueOf(INTERCLUSTERSPRINGLEN); + case 4: return String.valueOf(STIFFNESS); + case 5: return String.valueOf(OVERCLUSTERSTIFFNESS); + case 6: return String.valueOf(INTERCLUSTERSTIFFNESS); + case 7: return String.valueOf(ITERATIONS); + case 8: return String.valueOf(REPULSION); + case 9: return String.valueOf(OVERCLUSTERREPULSION); + case 10: return String.valueOf(INTERCLUSTERREPULSION); + case 11: return String.valueOf(SEED); + case 12: return String.valueOf(USELOGSPRINGS); + default: return null; + } + } + + public int getIndexForKey(String key){ + for(int i=0;i + + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/CompoundHierarchicalNodesLayouter.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/CompoundHierarchicalNodesLayouter.java new file mode 100644 index 000000000000..5ff043b7e90a --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/CompoundHierarchicalNodesLayouter.java @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.dataflow.layout; + +import at.ssw.positionmanager.Cluster; +import at.ssw.positionmanager.LayoutGraph; +import at.ssw.positionmanager.Link; +import at.ssw.positionmanager.Vertex; +import at.ssw.visualizer.graphhelper.DiGraph; +import at.ssw.dataflow.options.IntStringValidator; +import at.ssw.dataflow.options.Validator; +import java.awt.Point; +import java.util.Hashtable; +import java.util.Iterator; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.graph.CompoundDirectedGraph; +import org.eclipse.draw2d.graph.CompoundDirectedGraphLayout; +import org.eclipse.draw2d.graph.Edge; +import org.eclipse.draw2d.graph.Node; +import org.eclipse.draw2d.graph.Subgraph; + + +/** + * Positioning using Compound-Hirachical Layout from GEF draw2d library. + * This class is a wrapper that transforms the input graph models. + * + * @author Stefan Loidl + */ +public class CompoundHierarchicalNodesLayouter implements ExternalGraphLayouter{ + + //Borders of the graph within the drawing + private static final int TOP_BORDER = 20; + private static final int LEFT_BORDER = 20; + + //Padding of the nodes + private int PADDING = 50; + //Inset within the clusters + private int SUBGRAPHINSET=10; + + //This Identifier is used as name for the default cluster + private static final String DEFAULTCLUSTER="DEFAULT"; + //Top cluster holding all other clusters + private static final String TOPCLUSTER="TOP"; + + + /* Performs the layout task */ + public void doLayout(LayoutGraph graph) { + Hashtable idtoverticles=new Hashtable(); + Hashtable verticlestoid=new Hashtable(); + + DiGraph dg=new DiGraph(); + Iterator iter=graph.getVertices().iterator(); + + //Add Nodes to Graphhelper + int i=0; + while(iter.hasNext()){ + String id=String.valueOf(i); + dg.addNode(new at.ssw.visualizer.graphhelper.Node(id)); + Vertex v=iter.next(); + + idtoverticles.put(id,v); + verticlestoid.put(v,id); + i++; + } + + //Add Edges to Graphhelper + for(Link l: graph.getLinks()){ + String from=verticlestoid.get(l.getFrom().getVertex()); + String to=verticlestoid.get(l.getTo().getVertex()); + + dg.addEdge(new at.ssw.visualizer.graphhelper.Edge(dg.getNode(from),dg.getNode(to))); + } + + layout(graph,dg,idtoverticles,verticlestoid); + } + + + private void layout(LayoutGraph lg, DiGraph digraph, Hashtable idtoverticles, Hashtable verticlestoid) { + int lastmax=0; + int max=0; + for(DiGraph dg:digraph.getConnectedComponents()){ + //build draw2d compound Graph + CompoundDirectedGraph cdg=new CompoundDirectedGraph(); + + Subgraph top=new Subgraph(TOPCLUSTER); + Subgraph defaultG=new Subgraph(DEFAULTCLUSTER,top); + defaultG.innerPadding=new Insets(SUBGRAPHINSET,SUBGRAPHINSET,SUBGRAPHINSET,SUBGRAPHINSET); + cdg.nodes.add(defaultG); + cdg.nodes.add(top); + + Hashtable subgraphs=new Hashtable(); + Hashtable nodelist=new Hashtable(); + + //Add Nodes and clusters to the cdg + for(at.ssw.visualizer.graphhelper.Node n: dg.getNodes()){ + Vertex v=idtoverticles.get(n.ID); + Cluster c=v.getCluster(); + Subgraph sg; + //find or create subgraph for cluster c + if(c==null) sg=defaultG; + else{ + if(subgraphs.containsKey(c)) sg=subgraphs.get(c); + else{ + sg=new Subgraph(c.toString(),top); + sg.innerPadding=new Insets(SUBGRAPHINSET,SUBGRAPHINSET,SUBGRAPHINSET,SUBGRAPHINSET); + subgraphs.put(c,sg); + cdg.nodes.add(sg); + } + } + + Node node=new Node(v,sg); + + node.width = v.getSize().width; + node.height = v.getSize().height; + node.setPadding(new Insets(PADDING, PADDING, PADDING, PADDING)); + + cdg.nodes.add(node); + nodelist.put(n.ID,node); + } + + //Add all edges to the cdg + for(at.ssw.visualizer.graphhelper.Edge e: dg.getEdges()){ + Node n1=nodelist.get(e.source.ID); + Node n2=nodelist.get(e.destination.ID); + + //Subgraphs are chained by node relation too + if(n1.getParent()!=n2.getParent()) cdg.edges.add(new Edge(n1.getParent(),n2.getParent())); + + cdg.edges.add(new Edge(n1,n2)); + } + + + //do Layouting step + CompoundDirectedGraphLayout layout = new CompoundDirectedGraphLayout(); + layout.visit(cdg); + + //Assign positions + lastmax+=max; + max=0; + for (int i = 0; i < cdg.nodes.size(); i++) { + Node n = cdg.nodes.getNode(i); + assert n.data != null; + + //Continue with subgraphs... + if(!(n.data instanceof Vertex)) continue; + Vertex v=(Vertex)n.data; + + Point p = new Point(lastmax+ n.x + LEFT_BORDER, n.y + TOP_BORDER); + + int width=v.getSize().width; + if(n.x+width > max) max=n.x+width; + + v.setPosition(p); + } + } + } + + /* Performs the routing task via a simple direct-line router */ + public void doRouting(LayoutGraph graph) { + RoutingHelper.doRouting(graph); + } + + public boolean isClusteringSupported() { + return true; + } + + public boolean isAnimationSupported() { + return true; + } + + public boolean isMovementSupported() { + return true; + } + + public void setUseCurrentNodePositions(boolean b) {} + + + // + private static String[] options={"Padding"}; + private static String[] descriptions={"Minimum space between nodes"}; + private static Class[] optionclass={String.class}; + private static Validator paddingValidator=new IntStringValidator(0,1000); + + public String[] getOptionKeys() { + return options; + } + + public boolean setOption(String key, Object value) { + //key equals "padding" + if(key!=null && key.equals(options[0])){ + if(paddingValidator.validate(value)){ + PADDING=Integer.parseInt((String)value); + return true; + } + } + return false; + } + + public Validator getOptionValidator(String key) { + //key equals "padding" + if(key!=null && key.equals(options[0])){ + return paddingValidator; + } + return null; + } + + public String getOptionDescription(String key) { + if (key==null) return null; + for(int i=0; (i < descriptions.length) && (i < options.length); i++){ + if(key.equals(options[i])){ + return descriptions[i]; + } + } + return null; + } + + public Object getOption(String key) { + //key equals "padding" + if(key!=null && key.equals(options[0])){ + return String.valueOf(PADDING); + } + return null; + } + + public Class getOptionClass(String key) { + if (key==null) return null; + for(int i=0; (i < optionclass.length) && (i < options.length); i++){ + if(key.equals(options[i])){ + return optionclass[i]; + } + } + return null; + } + // + +} + diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/ExternalGraphLayoutWrapper.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/ExternalGraphLayoutWrapper.java new file mode 100644 index 000000000000..a7da269ff856 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/ExternalGraphLayoutWrapper.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.dataflow.layout; + +import at.ssw.positionmanager.LayoutGraph; +import at.ssw.positionmanager.LayoutManager; +import at.ssw.dataflow.options.Validator; + +/** + * Wrapper class for LayoutManager implementations that do not support the + * ExternalGraphLayouter Interface used within the project. + * This class can be used to test implementations of the LayoutManager interface + * on data-flow graphs. In advanved cases it is better to implement the + * ExternalGraphLayouter interface because then the full features of the + * optionprovider are released. + * + * @author Stefan Loidl + */ +public class ExternalGraphLayoutWrapper implements ExternalGraphLayouter{ + + private boolean clustering=false; + private LayoutManager layout=null; + private boolean movement=false; + private boolean animation=false; + + /** + * Creates a new instance of ExternalGraphLayoutWrapper + * layout: the LayoutManager + * clustering: is clustering supported? + * movement: is node movement supported? + * animation: is node animation supported? + * + * Note: Movement is supported if the routing can be done indepenent from + * the layout step. + * Note: Node animation is supported if the routing consumes low time. + */ + public ExternalGraphLayoutWrapper(LayoutManager layout, boolean clustering, boolean movement, boolean animation) { + this.clustering=clustering; + this.layout=layout; + this.animation=animation; + this.movement=movement; + } + + public boolean isClusteringSupported() { + return clustering; + } + + public void doLayout(LayoutGraph graph) { + if(layout!=null) layout.doLayout(graph); + } + + public void doRouting(LayoutGraph graph) { + if(layout!=null) layout.doRouting(graph); + } + + + public String[] getOptionKeys() { + return new String[0]; + } + + public boolean setOption(String key, Object value) { + return false; + } + + public Object getOption(String key) { + return null; + } + + public Class getOptionClass(String key) { + return null; + } + + public Validator getOptionValidator(String key) { + return null; + } + + public String getOptionDescription(String key) { + return null; + } + + public boolean isAnimationSupported() { + return animation; + } + + public boolean isMovementSupported() { + return movement; + } + + public void setUseCurrentNodePositions(boolean b) {} + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/ExternalGraphLayouter.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/ExternalGraphLayouter.java new file mode 100644 index 000000000000..a7cf5984a5c3 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/ExternalGraphLayouter.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.dataflow.layout; + +import at.ssw.positionmanager.LayoutManager; +import at.ssw.dataflow.options.OptionProvider; + +/** + * Extended Interface for LayoutManager supporting options and advanced + * features like clustering and movemement. + * + * @author Stefan Loidl + */ +public interface ExternalGraphLayouter extends OptionProvider, LayoutManager{ + + /** Does the algorithm support clustering? */ + public boolean isClusteringSupported(); + /** Is the routing algorithms fast enough to handle an animation task? */ + public boolean isAnimationSupported(); + /** Is routing possible independet from the layout task? */ + public boolean isMovementSupported(); + + /**Defines if the layouter should build on the current node positions*/ + public void setUseCurrentNodePositions(boolean b); + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/ForceLayouter.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/ForceLayouter.java new file mode 100644 index 000000000000..10b448e13834 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/ForceLayouter.java @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.dataflow.layout; + +import at.ssw.positionmanager.LayoutGraph; +import at.ssw.positionmanager.Link; +import at.ssw.positionmanager.Vertex; +import at.ssw.visualizer.graphhelper.DiGraph; +import at.ssw.visualizer.graphhelper.Node; +import at.ssw.dataflow.options.BooleanStringValidator; +import at.ssw.dataflow.options.DoubleStringValidator; +import at.ssw.dataflow.options.IntStringValidator; +import at.ssw.dataflow.options.Validator; +import java.awt.Point; +import java.awt.geom.Point2D; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Random; + +/** + * This class implements a force-directed layout algorithm. It uses the well + * known spring model that relys on springs and electrical forces. + * + * @author Stefan Loidl + */ +public class ForceLayouter implements ExternalGraphLayouter{ + + //Parameters for the foce model + private double SPRINGLEN = 50; + private double STIFFNESS = 15; + private double REPULSION = 100; + //Parameters if one of the nodes is expanded + private double SPRINGLENEXP = 50; + private double STIFFNESSEXP = 15; + private double REPULSIONEXP = 300; + + //During one cycle one node may only move this distance + private double MAXIMUMMOVEMENT=500; + + //Space between the connected components + private int PADDING=30; + + //Iterations the algorithms uses. + private int ITERATIONS=150; + + //Seed for the random node positioning + private long SEED=133; + + //Use logarithmic spring model. The alternative is hookes law. + private boolean USELOGSPRINGS=false; + + //Reuse current node positions for further optimizations + //the alternative is to use random positions. + private boolean USECURRENTNODEPOSITIONS=false; + + //Current positions of the nodes + private Hashtable posList; + + /* Performs the layout cycles */ + public void doLayout(LayoutGraph graph) { + Hashtable idtoverticles=new Hashtable(); + Hashtable verticlestoid=new Hashtable(); + + DiGraph dg=new DiGraph(); + Iterator iter=graph.getVertices().iterator(); + + //Add Nodes to Graphhelper + int i=0; + while(iter.hasNext()){ + String id=String.valueOf(i); + dg.addNode(new at.ssw.visualizer.graphhelper.Node(id)); + Vertex v=iter.next(); + + idtoverticles.put(id,v); + verticlestoid.put(v,id); + i++; + } + + //Add Edges to Graphhelper + for(Link l: graph.getLinks()){ + String from=verticlestoid.get(l.getFrom().getVertex()); + String to=verticlestoid.get(l.getTo().getVertex()); + + dg.addEdge(new at.ssw.visualizer.graphhelper.Edge(dg.getNode(from),dg.getNode(to))); + } + + //Perform the layout + layout(graph,dg,idtoverticles,verticlestoid); + } + + /* Peforms the layout per connected component */ + private void layout(LayoutGraph lg, DiGraph digraph, Hashtable idtoverticles, Hashtable verticlestoid) { + int lastmax=0; + int max=0; + for(DiGraph dg:digraph.getConnectedComponents()){ + + //initialize positions with random numbers + posList=new Hashtable(); + Random rand=new Random(SEED); + for(Node w: dg.getNodes()){ + if(USECURRENTNODEPOSITIONS){ + Vertex v=idtoverticles.get(w.ID); + double x=0.0,y=0.0; + if(v!=null){ + x=v.getPosition().x; + y=v.getPosition().y; + } + posList.put(w.ID,new doublePoint(x,y)); + } + else posList.put(w.ID,new doublePoint(rand.nextDouble()*300d,rand.nextDouble()*300d)); + } + + //perform several relaxation iterations + for(int i=0; ip.getX()) smallestX=p.getX(); + if(smallestY>p.getY()) smallestY=p.getY(); + } + + max=0; + for(Node w: dg.getNodes()){ + Vertex v=idtoverticles.get(w.ID); + + Point2D p=posList.get(w.ID); + int x=((int)(p.getX()-smallestX))+lastmax+PADDING; + int y=((int)(p.getY()-smallestY))+PADDING; + + if(x+v.getSize().width >max) max=x+v.getSize().width; + v.setPosition(new Point(x, y)); + } + lastmax=max; + } + } + + + /* + * Calculates the forces on node n and moves it a small distance to lessen + * it. + */ + private void relaxation(Node n, DiGraph dg, Hashtable idtoverticles){ + double stiffness, repulsion, springlen; + + double X = posList.get(n.ID).getX(); + double Y = posList.get(n.ID).getY(); + boolean nExpanded=false; + Vertex v=idtoverticles.get(n.ID); + if(v!=null) nExpanded=v.isExpanded(); + + LinkedList adjacentVertices=new LinkedList(); + + // Get all adjacent Nodes + for(Node pre: n.pred){ + if(!n.ID.equals(pre.ID)) + adjacentVertices.add(pre); + } + for(Node succ: n.succ){ + if(!n.ID.equals(succ.ID)) + adjacentVertices.add(succ); + } + + //Calcualte Spring len between all adjacent Nodes + double SpringX = 0, SpringY = 0; + for(Node adjacent:adjacentVertices) { + //determine if one of the nodes is expanded + boolean expanded=nExpanded; + v=idtoverticles.get(adjacent.ID); + if(v!=null) expanded|=v.isExpanded(); + + if(expanded){ + stiffness=STIFFNESSEXP; + springlen=SPRINGLENEXP; + } + else{ + stiffness=STIFFNESS; + springlen=SPRINGLEN; + } + + double adjX = posList.get(adjacent.ID).getX(); + double adjY = posList.get(adjacent.ID).getY(); + + double distance = Point2D.distance( adjX, adjY, X, Y ); + //Minimum distance between nodes! + if(distance == 0) distance = 0.01d; + + if(USELOGSPRINGS){ + //Logarithmic Springs + SpringX +=stiffness*Math.log(distance/springlen)*((X-adjX)/distance); + SpringY +=stiffness*Math.log(distance/springlen)*((Y-adjY)/distance); + } + else{ + //Hookes Law with relativ springkonstant + SpringX +=stiffness*((distance-springlen)/(2*springlen)) *((X-adjX)/distance); + SpringY +=stiffness*((distance-springlen)/(2*springlen)) *((Y-adjY)/distance); + } + } + + //Calcualte Repulsion + double RepulsionX = 0, RepulsionY = 0; + for(Node w: dg.getNodes()) { + if(w == n) continue; + + //determine if one of the nodes is expanded + boolean expanded=nExpanded; + v=idtoverticles.get(w.ID); + if(v!=null) expanded|=v.isExpanded(); + + if(expanded) repulsion=REPULSIONEXP; + else repulsion=REPULSION; + + double nX = posList.get(w.ID).getX(); + double nY = posList.get(w.ID).getY(); + + double distance = Point2D.distance( nX, nY, X, Y ); + if(distance == 0) distance = 0.01d; + + //If Spring energy is positiv- this one is negativ + RepulsionX -= (repulsion/distance)*((X-nX)/distance); + RepulsionY -= (repulsion/distance)*((Y-nY)/distance); + + } + + // Move Node in direction of the force + double dx = -(SpringX + RepulsionX); + double dy = -(SpringY + RepulsionY); + + //Make shure the node moves not too far off the others + if(dx>MAXIMUMMOVEMENT) dx=MAXIMUMMOVEMENT; + if(dx*-1>MAXIMUMMOVEMENT) dx=-MAXIMUMMOVEMENT; + if(dy>MAXIMUMMOVEMENT) dy=MAXIMUMMOVEMENT; + if(dy*-1>MAXIMUMMOVEMENT) dy=-MAXIMUMMOVEMENT; + + Point2D p=posList.get(n.ID); + p.setLocation(p.getX()+dx,p.getY()+dy); + } + + + /* Performs the routing using a simple direct-line router */ + public void doRouting(LayoutGraph graph) { + RoutingHelper.doRouting(graph); + } + + + public boolean isClusteringSupported() { + return false; + } + + + public boolean isAnimationSupported() { + return true; + } + + + public boolean isMovementSupported() { + return true; + } + + + public void setUseCurrentNodePositions(boolean b) { + USECURRENTNODEPOSITIONS=b; + } + + + // + private static String[] options={"Componentpadding", "Springlength","Expanded Springlength", "Stiffness","Expanded Stiffness", "Iterations", "Repulsion","Expanded Repulsion", "Seed", "Log. Springs", "Maximum Displacement"}; + private static String[] descriptions={"Minimum space between connected components", + "Length of the spring between nodes","Length of the spring between expanded nodes", "Stiffness of the spring between the nodes", + "Stiffness of the spring between expanded nodes","Relaxation iterations the algorithm performs", "Standard repulsion of a unexpanded Node", "Standard repulsion of a expanded Node", + "Seed for the random prepositioning", "Logarithmic spring simulation is used?", "Maximum displacement in x and y direction during relaxation."}; + private static Class[] optionclass={String.class,String.class,String.class, String.class,String.class,String.class,String.class,String.class}; + private static Validator[] validators={ + new IntStringValidator(0,1000), //padding + new DoubleStringValidator(0.0d,1000.0d), //Springlen + new DoubleStringValidator(0.0d,1000.0d), //expanded Springlen + new DoubleStringValidator(0.0d,1000.0d), //Stiffness + new DoubleStringValidator(0.0d,1000.0d), //expanded Stiffness + new IntStringValidator(0,5000), //Iterations + new DoubleStringValidator(0.0d,10000.0d), //Repulsion + new DoubleStringValidator(0.0d,10000.0d), //expanded Repulsion + new IntStringValidator(0,Integer.MAX_VALUE), //Seed + new BooleanStringValidator(), //Log Spring + new DoubleStringValidator(0.0d,5000.0d) //max. displacement + }; + + + public String[] getOptionKeys() { + return options; + } + + public boolean setOption(String key, Object value) { + if(key==null) return false; + //key equals "Componentpadding" + if(key.equals(options[0])){ + if(validators[0].validate(value)){ + PADDING=Integer.parseInt((String)value); + return true; + } + } + else if(key.equals(options[1])){ + if(validators[1].validate(value)){ + SPRINGLEN=Double.parseDouble((String)value); + return true; + } + } + else if(key.equals(options[2])){ + if(validators[2].validate(value)){ + SPRINGLENEXP=Double.parseDouble((String)value); + return true; + } + } + else if(key.equals(options[3])){ + if(validators[3].validate(value)){ + STIFFNESS=Double.parseDouble((String)value); + return true; + } + } + else if(key.equals(options[4])){ + if(validators[4].validate(value)){ + STIFFNESSEXP=Double.parseDouble((String)value); + return true; + } + } + else if(key.equals(options[5])){ + if(validators[5].validate(value)){ + ITERATIONS=Integer.parseInt((String)value); + return true; + } + } + else if(key.equals(options[6])){ + if(validators[6].validate(value)){ + REPULSION=Double.parseDouble((String)value); + return true; + } + } + else if(key.equals(options[7])){ + if(validators[7].validate(value)){ + REPULSIONEXP=Double.parseDouble((String)value); + return true; + } + } + else if(key.equals(options[8])){ + if(validators[8].validate(value)){ + SEED=Integer.parseInt((String)value); + return true; + } + } + else if(key.equals(options[9])){ + if(validators[9].validate(value)){ + USELOGSPRINGS=Boolean.parseBoolean((String)value); + return true; + } + } + else if(key.equals(options[10])){ + if(validators[10].validate(value)){ + MAXIMUMMOVEMENT=Double.parseDouble((String)value); + return true; + } + } + return false; + } + + + public Object getOption(String key) { + if(key==null) return null; + //key equals "Componentpadding" + if(key.equals(options[0])){ + return String.valueOf(PADDING); + } + //key equals "Springlength" + else if(key.equals(options[1])){ + return String.valueOf(SPRINGLEN); + } + //key equals "Expanded Springlength" + else if(key.equals(options[2])){ + return String.valueOf(SPRINGLENEXP); + } + //key equals "Stiffness" + else if(key.equals(options[3])){ + return String.valueOf(STIFFNESS); + } + //key equals "Expanded Stiffness" + else if(key.equals(options[4])){ + return String.valueOf(STIFFNESSEXP); + } + //key equals "Iterations" + else if(key.equals(options[5])){ + return String.valueOf(ITERATIONS); + } + //key equals "Repulsion" + else if(key.equals(options[6])){ + return String.valueOf(REPULSION); + } + //key equals "Repulsion" + else if(key.equals(options[7])){ + return String.valueOf(REPULSIONEXP); + } + //key equals "SEED" + else if(key.equals(options[8])){ + return String.valueOf(SEED); + } + //key equals "Log Springs" + else if(key.equals(options[9])){ + return String.valueOf(USELOGSPRINGS); + } + //key equals "Maximum Displacement" + else if(key.equals(options[10])){ + return String.valueOf(MAXIMUMMOVEMENT); + } + + return null; + } + + + public String getOptionDescription(String key) { + if (key==null) return null; + for(int i=0; (i < descriptions.length) && (i < options.length); i++){ + if(key.equals(options[i])){ + return descriptions[i]; + } + } + return null; + } + + + public Validator getOptionValidator(String key) { + if (key==null) return null; + for(int i=0; (i < validators.length) && (i < options.length); i++){ + if(key.equals(options[i])){ + return validators[i]; + } + } + return null; + } + + + + public Class getOptionClass(String key) { + if (key==null) return null; + for(int i=0; (i < optionclass.length) && (i < options.length); i++){ + if(key.equals(options[i])){ + return optionclass[i]; + } + } + return null; + } + // + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/HierarchicalNodesLayouter.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/HierarchicalNodesLayouter.java new file mode 100644 index 000000000000..df8c6a11fb21 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/HierarchicalNodesLayouter.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.dataflow.layout; + +import at.ssw.positionmanager.LayoutGraph; +import at.ssw.positionmanager.Link; +import at.ssw.positionmanager.Vertex; +import at.ssw.visualizer.graphhelper.DiGraph; +import at.ssw.dataflow.options.IntStringValidator; +import at.ssw.dataflow.options.Validator; +import java.awt.Point; +import java.util.Hashtable; +import java.util.Iterator; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.graph.DirectedGraph; +import org.eclipse.draw2d.graph.DirectedGraphLayout; +import org.eclipse.draw2d.graph.Edge; +import org.eclipse.draw2d.graph.Node; + +/** + * Node Layout using Hirachical Layout from GEF draw2d library. + * This class is a wrapper that transforms the input graph models. + * + * @author Stefan Loidl + */ +public class HierarchicalNodesLayouter implements ExternalGraphLayouter{ + + //Inset of the graph in the overall painting area + private static final int TOP_BORDER = 20; + private static final int LEFT_BORDER = 20; + + //Padding of the nodes and the connected components + private int PADDING = 30; + + /* + * Performs the layout task via transformation of the LayoutGraph-model + * to that of GEF. Afterwards the algorithm is simply applied. + */ + public void doLayout(LayoutGraph graph) { + Hashtable idtoverticles=new Hashtable(); + Hashtable verticlestoid=new Hashtable(); + + Hashtable d2dNodes=new Hashtable(); + + + DiGraph dg=new DiGraph(); + Iterator iter=graph.getVertices().iterator(); + + //Add Nodes to Graphhelper + int i=0; + while(iter.hasNext()){ + String id=String.valueOf(i); + dg.addNode(new at.ssw.visualizer.graphhelper.Node(id)); + Vertex v=iter.next(); + + idtoverticles.put(id,v); + verticlestoid.put(v,id); + i++; + } + + //Add Edges to Graphhelper + for(Link l: graph.getLinks()){ + String from=verticlestoid.get(l.getFrom().getVertex()); + String to=verticlestoid.get(l.getTo().getVertex()); + + dg.addEdge(new at.ssw.visualizer.graphhelper.Edge(dg.getNode(from),dg.getNode(to))); + } + + int max=0, lastmax=0; + //Layouting for each connected component + for(DiGraph g:dg.getConnectedComponents()){ + DirectedGraph d2dGraph=new DirectedGraph(); + //Add Nodes to Draw2D DirectedGraph + for(at.ssw.visualizer.graphhelper.Node n:g.getNodes()){ + Vertex v=idtoverticles.get(n.ID); + Node node=new Node(); + node.data=v; + node.width = v.getSize().width; + node.height = v.getSize().height; + node.setPadding(new Insets(PADDING, PADDING, PADDING, PADDING)); + d2dGraph.nodes.add(node); + d2dNodes.put(n.ID,node); + } + + //Add Edges to Draw2D DirectedGraph + for(at.ssw.visualizer.graphhelper.Edge e:g.getEdges()){ + Edge edge=new Edge(d2dNodes.get(e.source.ID),d2dNodes.get(e.destination.ID)); + d2dGraph.edges.add(edge); + } + + //Perform Layouting step + DirectedGraphLayout layout = new DirectedGraphLayout(); + layout.visit(d2dGraph); + + //Calculate actual positions + lastmax+=max+PADDING; + max=0; + for (int j = 0; j < d2dGraph.nodes.size(); j++) { + Node n = d2dGraph.nodes.getNode(j); + assert n.data != null; + Vertex v = (Vertex)n.data; + + Point p = new Point(lastmax+ n.x + LEFT_BORDER, n.y + TOP_BORDER); + + int width=v.getSize().width; + if(n.x+width > max) max=n.x+width; + + v.setPosition(p); + } + } + } + + /* Performs the routing using a simple direct-line router */ + public void doRouting(LayoutGraph graph) { + RoutingHelper.doRouting(graph); + } + + public boolean isClusteringSupported() { + return false; + } + + public boolean isAnimationSupported() { + return true; + } + + public boolean isMovementSupported() { + return true; + } + + public void setUseCurrentNodePositions(boolean b) { + } + + // + private static String[] options={"Padding"}; + private static String[] descriptions={"Minimum space between nodes"}; + private static Class[] optionclass={String.class}; + private static Validator paddingValidator=new IntStringValidator(0,1000); + + public String[] getOptionKeys() { + return options; + } + + public boolean setOption(String key, Object value) { + //key equals "padding" + if(key!=null && key.equals(options[0])){ + if(paddingValidator.validate(value)){ + PADDING=Integer.parseInt((String)value); + return true; + } + } + return false; + } + + public Validator getOptionValidator(String key) { + //key equals "padding" + if(key!=null && key.equals(options[0])){ + return paddingValidator; + } + return null; + } + + public String getOptionDescription(String key) { + if (key==null) return null; + for(int i=0; (i < descriptions.length) && (i < options.length); i++){ + if(key.equals(options[i])){ + return descriptions[i]; + } + } + return null; + } + + public Object getOption(String key) { + //key equals "padding" + if(key!=null && key.equals(options[0])){ + return String.valueOf(PADDING); + } + return null; + } + + public Class getOptionClass(String key) { + if (key==null) return null; + for(int i=0; (i < optionclass.length) && (i < options.length); i++){ + if(key.equals(options[i])){ + return optionclass[i]; + } + } + return null; + } + // +} + diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/MagneticSpringForceLayouter.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/MagneticSpringForceLayouter.java new file mode 100644 index 000000000000..35eb5e39155b --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/MagneticSpringForceLayouter.java @@ -0,0 +1,477 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.dataflow.layout; + +import at.ssw.positionmanager.LayoutGraph; +import at.ssw.positionmanager.Link; +import at.ssw.positionmanager.Vertex; +import at.ssw.visualizer.graphhelper.DiGraph; +import at.ssw.visualizer.graphhelper.Node; +import at.ssw.dataflow.options.BooleanStringValidator; +import at.ssw.dataflow.options.DoubleStringValidator; +import at.ssw.dataflow.options.IntStringValidator; +import at.ssw.dataflow.options.Validator; +import java.awt.Point; +import java.awt.geom.Point2D; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Random; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * This class implements a force-directed layout building on the well known + * spring-model. To introduce direction to the graph-layout the magnetic + * spring model descibed in the following work was used: + * + * Kozo Sugiyama, Kazuo Misue: Graph Drawing by Magnetic-Spring Model; + * Technical Report, 1994. ISIS-RR-94-14E. + * + * @author Stefan Loidl + */ +public class MagneticSpringForceLayouter implements ExternalGraphLayouter{ + + //Parameters for the foce model + private double SPRINGLEN = 50; + private double STIFFNESS = 15; + private double REPULSION = 100; + + //Parameters if one of the nodes is expanded + private double SPRINGLENEXP = 50; + private double STIFFNESSEXP = 15; + private double REPULSIONEXP = 250; + + //Strength of the parallel vertical field + private double FIELDSTRENGTH=0.05; + + //During one cycle one node may only move this distance + private double MAXIMUMMOVEMENT=500; + + //Space between the connected components + private int PADDING=30; + + //Iterations the algorithms uses. + private int ITERATIONS=300; + + //Seed for the random node positioning + private long SEED=133; + + //Use logarithmic spring model. The alternative is hookes law. + private boolean USELOGSPRINGS=false; + + //Reuse current node positions for further optimizations + //the alternative is to use random positions. + private boolean USECURRENTNODEPOSITIONS=false; + + //length of the area the initial layout is done on + private double INITIALLAYOUTSIDE=3000d; + private double LAYERHEIGHT=100d; + + //Current positions of the nodes + private Hashtable posList; + + + /* Performs the layout algorithm */ + public void doLayout(LayoutGraph graph) { + Hashtable idtoverticles=new Hashtable(); + Hashtable verticlestoid=new Hashtable(); + + DiGraph dg=new DiGraph(); + Iterator iter=graph.getVertices().iterator(); + + //Add Nodes to Graphhelper + int i=0; + while(iter.hasNext()){ + String id=String.valueOf(i); + dg.addNode(new at.ssw.visualizer.graphhelper.Node(id)); + Vertex v=iter.next(); + + idtoverticles.put(id,v); + verticlestoid.put(v,id); + i++; + } + + //Add Edges to Graphhelper + for(Link l: graph.getLinks()){ + String from=verticlestoid.get(l.getFrom().getVertex()); + String to=verticlestoid.get(l.getTo().getVertex()); + + dg.addEdge(new at.ssw.visualizer.graphhelper.Edge(dg.getNode(from),dg.getNode(to))); + } + + layout(graph,dg,idtoverticles,verticlestoid); + } + + + /* + * Performs an initial layout step based on the breadth first search graph + * traversial. + */ + private void BFSInitialAssignment(LinkedList fringe, HashSet assigned, double layer){ + if(fringe==null || fringe.size()==0) return; + assigned.addAll(fringe); + + LinkedList newFringe=new LinkedList(); + double x=0.0, deltaX=INITIALLAYOUTSIDE/(fringe.size()+1); + + for(Node n: fringe){ + x+=deltaX; + posList.put(n.ID,new doublePoint(x,layer)); + for(Node s:n.succ) if(!assigned.contains(s)) newFringe.add(s); + } + BFSInitialAssignment(newFringe,assigned,layer+LAYERHEIGHT); + } + + /* + * Performs the layout task. Note that three differnent approaches for + * the initial positioning of nodes are implemented. One possibility is + * to use the currnet node positions without change. The second is to use + * a random initial layout. And the third one is to use the BFS positioning + * that introduces a pre-layering step. + */ + private void layout(LayoutGraph lg, DiGraph digraph, Hashtable idtoverticles, Hashtable verticlestoid) { + int lastmax=0; + int max=0; + for(DiGraph dg:digraph.getConnectedComponents()){ + + //initialize positions with random numbers + posList=new Hashtable(); + Random rand=new Random(133); + + //reuse current position + if(USECURRENTNODEPOSITIONS){ + for(Node w: dg.getNodes()){ + Vertex v=idtoverticles.get(w.ID); + double x=0.0,y=0.0; + if(v!=null){ + x=v.getPosition().x; + y=v.getPosition().y; + } + posList.put(w.ID,new doublePoint(x,y)); + } + + } + //Breadth first search layer positioning + else{ + HashSet assigned=new HashSet(); + LinkedList fringe=new LinkedList(); + + for(Vertex v: new TreeSet(lg.findRootVertices())){ + Node n=dg.getNode(verticlestoid.get(v)); + if(n!=null) fringe.add(n); + } + BFSInitialAssignment(fringe,assigned,LAYERHEIGHT); + for(Node n:dg.getNodes()){ + if(!assigned.contains(n)) + posList.put(n.ID,new doublePoint(rand.nextDouble()*INITIALLAYOUTSIDE,rand.nextDouble()*INITIALLAYOUTSIDE)); + } + } + + //perform several relaxation iterations + for(int i=0; ip.getX()) smallestX=p.getX(); + if(smallestY>p.getY()) smallestY=p.getY(); + } + + max=0; + for(Node w: dg.getNodes()){ + Vertex v=idtoverticles.get(w.ID); + + Point2D p=posList.get(w.ID); + int x=((int)(p.getX()-smallestX))+lastmax+PADDING; + int y=((int)(p.getY()-smallestY))+PADDING; + + if(x+v.getSize().width >max) max=x+v.getSize().width; + v.setPosition(new Point(x, y)); + } + lastmax=max; + } + } + + + /* + * Calculates the forces on node n and moves it a small distance to lessen + * it. The magnetical field introduces a radial force on the edges. + * This influence is simplified and modeled as vertical force pointing + * downwards. + */ + public void relaxation(Node n, DiGraph dg, Hashtable idtoverticles){ + double stiffness, repulsion, springlen; + double X = posList.get(n.ID).getX(); + double Y = posList.get(n.ID).getY(); + boolean nExpanded=false; + Vertex v=idtoverticles.get(n.ID); + + if(v!=null) nExpanded=v.isExpanded(); + + LinkedList adjacentVertices=new LinkedList(); + + // Get all adjacent Nodes + for(Node pre: n.pred){ + if(!n.ID.equals(pre.ID)) + adjacentVertices.add(pre); + } + for(Node succ: n.succ){ + if(!n.ID.equals(succ.ID)) + adjacentVertices.add(succ); + } + + //Calcualte Spring len between all adjacent Nodes + double SpringX = 0, SpringY = 0; + for(Node adjacent:adjacentVertices) { + //determine if one of the nodes is expanded + boolean expanded=nExpanded; + v=idtoverticles.get(adjacent.ID); + if(v!=null) expanded|=v.isExpanded(); + + if(expanded){ + stiffness=STIFFNESSEXP; + springlen=SPRINGLENEXP; + } + else{ + stiffness=STIFFNESS; + springlen=SPRINGLEN; + } + + double adjX = posList.get(adjacent.ID).getX(); + double adjY = posList.get(adjacent.ID).getY(); + + double distance = Point2D.distance( adjX, adjY, X, Y ); + //Minimum distance between nodes! + if(distance == 0) distance = 0.01d; + + //Calculate the angle beween the directed spring and the vertical magnetic field + //via the angle between the vector resulting from the positions (vX,vY) and + //the vector (0,1). angle=arcCos( (0*vX+1*vY)/(len(vX,vY)*len(0,1)) ) + //This only applies if the current node is the endpoint of the spring + if(n.pred.contains(adjacent) && !n.succ.contains(adjacent)){ + double vX=X-adjX; + double vY=Y-adjY; + double angle=Math.acos((vY)/Point2D.distance(0,0,vX,vY)); + SpringY-=Math.abs(angle)*distance*FIELDSTRENGTH; + } + + + if(USELOGSPRINGS){ + //Logarithmic Springs + SpringX +=stiffness*Math.log(distance/springlen)*((X-adjX)/distance); + SpringY +=stiffness*Math.log(distance/springlen)*((Y-adjY)/distance); + } + else{ + //Hookes Law with relativ springkonstant + SpringX +=stiffness*((distance-springlen)/(2*springlen)) *((X-adjX)/distance); + SpringY +=stiffness*((distance-springlen)/(2*springlen)) *((Y-adjY)/distance); + } + } + + //Calcualte Repulsion + double RepulsionX = 0, RepulsionY = 0; + for(Node w: dg.getNodes()) { + if(w == n) continue; + + //determine if one of the nodes is expanded + boolean expanded=nExpanded; + v=idtoverticles.get(w.ID); + if(v!=null) expanded|=v.isExpanded(); + + if(expanded) repulsion=REPULSIONEXP; + else repulsion=REPULSION; + + double nX = posList.get(w.ID).getX(); + double nY = posList.get(w.ID).getY(); + + double distance = Point2D.distance( nX, nY, X, Y ); + if(distance == 0) distance = 0.01d; + + //If Spring energy is positiv- this one is negativ + RepulsionX -= (repulsion/distance)*((X-nX)/distance); + RepulsionY -= (repulsion/distance)*((Y-nY)/distance); + } + + // Move Node in direction of the force + double dx = -(SpringX + RepulsionX); + double dy = -(SpringY + RepulsionY); + + //Make shure the node moves not too far off the others + if(dx>MAXIMUMMOVEMENT) dx=MAXIMUMMOVEMENT; + if(dx*-1>MAXIMUMMOVEMENT) dx=-MAXIMUMMOVEMENT; + if(dy>MAXIMUMMOVEMENT) dy=MAXIMUMMOVEMENT; + if(dy*-1>MAXIMUMMOVEMENT) dy=-MAXIMUMMOVEMENT; + + Point2D p=posList.get(n.ID); + p.setLocation(p.getX()+dx,p.getY()+dy); + } + + + + /* Performs the routing using a simple direct-line router */ + public void doRouting(LayoutGraph graph) { + RoutingHelper.doRouting(graph); + } + + public boolean isClusteringSupported() { + return false; + } + + public boolean isAnimationSupported() { + return true; + } + + public boolean isMovementSupported() { + return true; + } + + public void setUseCurrentNodePositions(boolean b) { + USECURRENTNODEPOSITIONS=b; + } + + // + private static String[] options={"Componentpadding", "Springlength","Expanded Springlength", "Stiffness","Expanded Stiffness","Field Strength", "Iterations", "Repulsion","Expanded Repulsion", "Log. Springs", "Maximum Displacement"}; + private static String[] descriptions={"Minimum space between connected components", + "Length of the spring between nodes","Length of the spring between expanded nodes", "Stiffness of the spring between the nodes", + "Stiffness of the spring between expanded nodes","Strength of the vertical parallel field", "Relaxation iterations the algorithm performs", "Standard repulsion of a unexpanded Node", "Standard repulsion of a expanded Node", + "Logarithmic spring simulation is used?", "Maximum displacement in x and y direction during relaxation."}; + private static Class[] optionclass={String.class,String.class,String.class, String.class,String.class,String.class,String.class,String.class}; + private static Validator[] validators={ + new IntStringValidator(0,1000), //padding + new DoubleStringValidator(0.0d,1000.0d), //Springlen + new DoubleStringValidator(0.0d,1000.0d), //expanded Springlen + new DoubleStringValidator(0.0d,1000.0d), //Stiffness + new DoubleStringValidator(0.0d,1000.0d), //expanded Stiffness + new DoubleStringValidator(0.0d,10.0d), //field strength + new IntStringValidator(0,5000), //Iterations + new DoubleStringValidator(0.0d,10000.0d), //Repulsion + new DoubleStringValidator(0.0d,10000.0d), //expanded Repulsion + new BooleanStringValidator(), //Log Spring + new DoubleStringValidator(0.0d,5000.0d) //max. displacement + }; + + + public String[] getOptionKeys() { + return options; + } + + public int getIndexForKey(String key){ + for(int i=0;i + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/RoutingHelper.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/RoutingHelper.java new file mode 100644 index 000000000000..038309a2350c --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/RoutingHelper.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.dataflow.layout; + +import at.ssw.positionmanager.LayoutGraph; +import at.ssw.positionmanager.Link; +import at.ssw.positionmanager.Port; +import at.ssw.positionmanager.Vertex; +import java.awt.Point; +import java.util.LinkedList; + +/** + * This class is a standard implementation of a direct routing algorithm, making it + * possible to seperate two lines between the same two nodes. + * + * @author Stefan Loidl + */ +public class RoutingHelper { + + //This value is needed because of the definition of inset values- outranging the + //position values of the original widget- if no inset is used- value is 0. + private static final int BORDERINSETCORRECT=0; + //Space between two twins lines + private static final int SEPERATETWINLINES=5; + + /** + * Does the routing for the graph. + */ + public static void doRouting(LayoutGraph graph){ + if(graph==null) return; + for(Link l: graph.getLinks()){ + boolean twin=false; + Vertex from=l.getFrom().getVertex(); + Vertex to=l.getTo().getVertex(); + + if(!(from.isDirty() || to.isDirty())) continue; + + //Search for twin link (in diffent direction!) + for(Port p:graph.getInputPorts(from)){ + for(Link l2: graph.getPortLinks(p)){ + if(l2.getFrom().getVertex()==l.getTo().getVertex()) twin=true; + } + } + + routeLink(l,twin); + } + } + + /** + * Does the routing for the link l. The boolean defines if a second link exists connecting + * the two verticles. + */ + private static void routeLink(Link l, boolean hasTwin){ + Vertex v1=l.getFrom().getVertex(); + Vertex v2=l.getTo().getVertex(); + Point p1=(Point)v1.getPosition().clone(); + Point p2=(Point)v2.getPosition().clone(); + + //Translate to the center + p1.translate(-BORDERINSETCORRECT+ v1.getSize().width/2,-BORDERINSETCORRECT+ v1.getSize().height/2); + p2.translate(-BORDERINSETCORRECT+ v2.getSize().width/2, -BORDERINSETCORRECT+ v2.getSize().height/2); + + + //Handle the six cases possible: + //1+2. x- Values are identical + if(p1.x==p2.x){ + int shift=0; + if(p1.y>p2.y){ + if(hasTwin) shift=SEPERATETWINLINES; + p1.translate(shift,-v1.getSize().height/2); + p2.translate(shift,v2.getSize().height/2); + } + else{ + if(hasTwin) shift=-SEPERATETWINLINES; + p1.translate(shift,v1.getSize().height/2); + p2.translate(shift,-v2.getSize().height/2); + } + } + else{ + //gradient of the line + double k=((double)(p1.y-p2.y))/(p1.x-p2.x); + double gk= Math.atan(k); + double twdx=0, twdy=0; + + //If x value of p1 <= p2 then the two are + //simply swapped. + boolean swap=false; + Point p3; + Vertex v3; + + if(hasTwin){ + twdx=Math.abs(Math.sin(gk)*SEPERATETWINLINES); + twdy=Math.abs(Math.cos(gk)*SEPERATETWINLINES); + } + + if(p1.x <= p2.x){ + swap=true; + p3=p1; p1=p2; p2=p3; + v3=v1; v1=v2; v2=v3; + twdx*=-1; twdy*=-1; + } + + if(p1.y>p2.y){ + //lower quarter with respect to p1 + p1.translate((int)twdx,-(int)twdy); + p2.translate((int)twdx,-(int)twdy); + + double x=((double)v2.getSize().height/2.0)/Math.tan(gk); + if(Math.abs(x)<=v2.getSize().width/2){ + p2.translate((int)x,v2.getSize().height/2); + } else{ + x=(Math.tan(gk)*v2.getSize().width)/2.0; + p2.translate(v2.getSize().width/2,(int)x); + } + + x=(Math.tan(Math.PI/2-gk)*v1.getSize().height)/2.0; + if(Math.abs(x)<=v1.getSize().width/2){ + p1.translate(-(int)x,-v1.getSize().height/2); + } else{ + x=((double)(v1.getSize().width)/2.0)/Math.tan(Math.PI/2-gk); + p1.translate(-v1.getSize().width/2,-(int)x); + } + + }else{ + //upper quarter with respect to p1 + p1.translate((int)-twdx,(int)-twdy); + p2.translate((int)-twdx,(int)-twdy); + + double x=((double)v1.getSize().height/2.0)/Math.tan(gk); + if(Math.abs(x)<=v1.getSize().width/2){ + p1.translate((int)x,v1.getSize().height/2); + } else{ + x=(Math.tan(gk)*v1.getSize().width)/2; + p1.translate(-v1.getSize().width/2,-(int)x); + } + + x=(Math.tan(Math.PI/2-gk)*v2.getSize().height)/2.0; + if(Math.abs(x)<=v2.getSize().width/2){ + p2.translate(-(int)x,-v2.getSize().height/2); + } else{ + x=((double)(v2.getSize().width)/2.0)/Math.tan(Math.PI/2-gk); + p2.translate(v2.getSize().width/2,(int)x); + } + } + if(swap){ + p3=p1; p1=p2; p2=p3; + v3=v1; v1=v2; v2=v3; + } + + + } + + LinkedList cp=new LinkedList(); + cp.add(p1); + cp.add(p2); + l.setControlPoints(cp); + } + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/doublePoint.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/doublePoint.java new file mode 100644 index 000000000000..3e772741d6ce --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/layout/doublePoint.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.dataflow.layout; + +import java.awt.geom.Point2D; + +/** + * Simple helper class for the force-directed layouts. + * + * @author Stefan Loidl + */ + +class doublePoint extends Point2D{ + private double x,y; + + public doublePoint(double x, double y){this.x=x; this.y=y;} + + public double getX() {return x;} + + public double getY() {return y;} + + public void setLocation(double x, double y) {this.x=x; this.y=y;} +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/BooleanStringValidator.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/BooleanStringValidator.java new file mode 100644 index 000000000000..0beed4e17000 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/BooleanStringValidator.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.dataflow.options; + +/** + * Validates if the option is parsable as Boolean + * + * @author Stefan Loidl + */ +public class BooleanStringValidator implements Validator{ + + private String error=null; + + public boolean validate(Object option) { + try{ + Boolean.parseBoolean((String)option); + return true; + }catch(Exception e){ + error="Option not parseable as Boolean"; + return false; + } + } + + public String getLastErrorMessage() { + return error; + } + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/DoubleStringValidator.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/DoubleStringValidator.java new file mode 100644 index 000000000000..511ee55d1970 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/DoubleStringValidator.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.dataflow.options; + +/** + * Validates if the string option is parsable as double. + * Optinally min and max value can be given. + * + * @author Stefan Loidl + */ +public class DoubleStringValidator implements Validator{ + + private double min, max; + String error=null; + + /** + * Creates a new instance of IntValidator with min and max bound + * for the option. + */ + public DoubleStringValidator(double min, double max) { + this.min=min; + this.max=max; + } + + /** + * Creates a new instance of the validator without bounds. + */ + public DoubleStringValidator(){ + this(Double.MIN_VALUE,Double.MAX_VALUE); + } + + public boolean validate(Object option) { + try{ + if(!(option instanceof String)){ + error="Option is not a string"; + return false; + } + double x=Double.parseDouble((String)option); + if(x>=min && x <=max) { + error=null; + return true; + } + error="Value not within intervall: ["+min+","+max+"]"; + return false; + }catch(Exception e){ + error="No double value."; + return false; + } + } + + + public String getLastErrorMessage() { + return error; + } + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/IntStringValidator.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/IntStringValidator.java new file mode 100644 index 000000000000..532dc3d3122e --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/IntStringValidator.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.dataflow.options; + +/** + * Validates if the option given as string is parsable as integer. + * Optinally min and max value can be given. + * + * @author Stefan Loidl + */ +public class IntStringValidator implements Validator{ + + private int min, max; + String error=null; + + /** + * Creates a new instance of IntValidator with min and max bound + * for the option. + */ + public IntStringValidator(int min, int max) { + this.min=min; + this.max=max; + } + + /** + * Creates a new instance of the validator without bounds. + */ + public IntStringValidator(){ + this(Integer.MIN_VALUE,Integer.MAX_VALUE); + } + + public boolean validate(Object option) { + try{ + if(!(option instanceof String)){ + error="Option is not a string"; + return false; + } + int x=Integer.parseInt((String)option); + if(x>=min && x <=max) { + error=null; + return true; + } + error="Value not within intervall: ["+min+","+max+"]"; + return false; + }catch(Exception e){ + error="No integer value."; + return false; + } + } + + public String getLastErrorMessage() { + return error; + } + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/OptionEditor.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/OptionEditor.java new file mode 100644 index 000000000000..57d82eb6130a --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/OptionEditor.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.dataflow.options; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableModel; + +/** + * This class implements a dialog for the modification of options provided + * by an optionprovider. + * + * @author Stefan Loidl + */ +public class OptionEditor extends JDialog { + private OptionProvider provider; + private static final String DEFAULTTITEL="Options"; + private static final String BUTCLOSE="Close"; + + /** Creates a new instance of OptionEditor */ + public OptionEditor(OptionProvider provider) { + getContentPane().setLayout(new BorderLayout()); + this.provider=provider; + setSize(300,300); + + //Center on screen + setLocationRelativeTo(null); + + setTitle(DEFAULTTITEL); + setModal(true); + JTable tab=new JTable(); + JLabel errorLabel=new JLabel(); + errorLabel.setHorizontalAlignment(JLabel.CENTER); + errorLabel.setForeground(Color.RED); + + + tab.setModel(new Model(provider,errorLabel)); + + + tab.setColumnSelectionAllowed(true); + + JScrollPane sp=new JScrollPane(tab); + getContentPane().add(sp,BorderLayout.CENTER); + + JPanel p=new JPanel(); + p.setLayout(new GridLayout(2,1)); + p.add(errorLabel); + + JButton b=new JButton(BUTCLOSE); + b.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + OnClose(); + } + }); + p.add(b); + + getContentPane().add(p,BorderLayout.SOUTH); + } + + public void OnClose(){ + setVisible(false); + } + + + + protected class Model extends AbstractTableModel{ + private OptionProvider provider; + private final String[] TableHeader={"Option","Value"}; + private JLabel error; + + public Model(OptionProvider provider, JLabel error){ + this.provider=provider; + this.error=error; + } + + public int getRowCount() { + return provider.getOptionKeys().length; + } + + public int getColumnCount() { + return 2; + } + + public String getColumnName(int col){ + return TableHeader[col]; + } + + public Object getValueAt(int rowIndex, int columnIndex) { + String key=provider.getOptionKeys()[rowIndex]; + if(columnIndex==0){ + return key; + }else if(columnIndex==1){ + return provider.getOption(key); + } + return null; + } + + public void setValueAt(Object o, int row, int col){ + if(col==0) return; + String key=provider.getOptionKeys()[row]; + Validator v=provider.getOptionValidator(key); + if(v.validate(o)){ + error.setText(""); + provider.setOption(key,o); + } else error.setText(v.getLastErrorMessage()); + } + + public boolean isCellEditable(int row, int col){ + return col!=0; + } + + } + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/OptionProvider.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/OptionProvider.java new file mode 100644 index 000000000000..a45888bf4b66 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/OptionProvider.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.dataflow.options; + +/** + * Interface for classes that want to publish options. + * + * @author Stefan Loidl + */ +public interface OptionProvider { + + /** + * Returns all keys for existing options. + */ + public String[] getOptionKeys(); + + /** + * Sets the option defined by key. + */ + public boolean setOption(String key, Object value); + + /** + * Gets the option defined by key. + */ + public Object getOption(String key); + + /** + * Returns the class the option should passed as. + */ + public Class getOptionClass(String key); + + /** + * Returns a validator for a option. + */ + public Validator getOptionValidator(String key); + + /** + * Return the description for the option. + */ + public String getOptionDescription(String key); +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/Validator.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/Validator.java new file mode 100644 index 000000000000..eaec293e6446 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/dataflow/options/Validator.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.dataflow.options; + +/** + * A Validator is used to verifiy if an option has the correct format. + * + * @author Stefan Loidl + */ +public interface Validator { + + /** + * Validates if the option has correct format. + */ + public boolean validate(Object option); + + /** + * Returns the Error Message of the last validation or null + * if it was okay. + */ + public String getLastErrorMessage(); +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/BasicLineGenerator.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/BasicLineGenerator.java new file mode 100644 index 000000000000..d46656047208 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/BasicLineGenerator.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.graphanalyzer.positioning; + +import java.awt.Point; +import java.util.List; + +/** + * + * @author Thomas Wuerthinger + */ +public class BasicLineGenerator implements LineGenerator { + public List createLine(List line, Point startRefPoint, Point endRefPoint) { + return line; + } + + public String iconResource() { + return "at/ssw/graphanalyzer/coordinator/images/polygon_lines.gif"; + } + + public String getName() { + return "Polygon lines"; + } +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/BezierLineGenerator.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/BezierLineGenerator.java new file mode 100644 index 000000000000..c6b40ad435d2 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/BezierLineGenerator.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.graphanalyzer.positioning; + +import java.awt.Point; +import java.util.List; + +/** + * + * @author Thomas Wuerthinger + */ +public class BezierLineGenerator implements LineGenerator{ + public List createLine(List lines, Point startRefPoint, Point endRefPoint) { + return Curves.convertToBezier(lines, startRefPoint, endRefPoint, 0.2, 100); + } + + public String iconResource() { + return "at/ssw/graphanalyzer/coordinator/images/bezier_lines.gif"; + } + + public String getName() { + return "Bezier lines"; + } +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterEdge.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterEdge.java new file mode 100644 index 000000000000..a426f961bbed --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterEdge.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.graphanalyzer.positioning; + +import at.ssw.positionmanager.Link; +import at.ssw.positionmanager.Port; +import java.awt.Point; +import java.util.List; + +/** + * + * @author Thomas Wuerthinger + */ +public class ClusterEdge implements Link{ + + private ClusterNode from; + private ClusterNode to; + private List points; + + public ClusterEdge(ClusterNode from, ClusterNode to) { + assert from != null; + assert to != null; + this.from = from; + this.to = to; + } + + public Port getTo() { + return to.getInputSlot(); + } + + public Port getFrom() { + return from.getInputSlot(); + } + + public void setControlPoints(List p) { + this.points = p; + } + + public List getControlPoints() { + return points; + } + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterIngoingConnection.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterIngoingConnection.java new file mode 100644 index 000000000000..60743d9660a9 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterIngoingConnection.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.graphanalyzer.positioning; + +import at.ssw.positionmanager.Link; +import at.ssw.positionmanager.Port; +import java.awt.Point; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Thomas Wuerthinger + */ +public class ClusterIngoingConnection implements Link{ + + private List controlPoints; + private ClusterInputSlotNode inputSlotNode; + private Link connection; + private Port inputSlot; + private Port outputSlot; + + public ClusterIngoingConnection(ClusterInputSlotNode inputSlotNode, Link c) { + this.inputSlotNode = inputSlotNode; + this.connection = c; + this.controlPoints = new ArrayList(); + + inputSlot = c.getTo(); + outputSlot = inputSlotNode.getOutputSlot(); + } + + public Link getConnection() { + return connection; + } + + public ClusterInputSlotNode getInputSlotNode() { + return inputSlotNode; + } + + public Port getTo() { + return inputSlot; + } + + public Port getFrom() { + return outputSlot; + } + + public void setControlPoints(List p) { + this.controlPoints = p; + } + + public List getControlPoints() { + return controlPoints; + } + +} \ No newline at end of file diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterInputSlotNode.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterInputSlotNode.java new file mode 100644 index 000000000000..dd23710c8843 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterInputSlotNode.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.graphanalyzer.positioning; + +import at.ssw.positionmanager.Cluster; +import at.ssw.positionmanager.Port; +import at.ssw.positionmanager.Vertex; +import java.awt.Dimension; +import java.awt.Point; + +/** + * + * @author Thomas Wuerthinger + */ +public class ClusterInputSlotNode implements Vertex { + + private final int SIZE = 0; + private Point position; + private Port inputSlot; + private Port outputSlot; + private ClusterNode blockNode; + private InterClusterConnection interBlockConnection; + private Cluster cluster; + private boolean dirty; + private ClusterIngoingConnection conn; + + public void setIngoingConnection(ClusterIngoingConnection c) { + conn = c; + } + + public ClusterIngoingConnection getIngoingConnection() { + return conn; + } + + private String id; + + public String toString() { + return id; + } + + public ClusterInputSlotNode(ClusterNode n, String id) { + this.blockNode = n; + this.id = id; + + n.addSubNode(this); + + final Vertex thisNode = this; + final ClusterNode thisBlockNode = blockNode; + + outputSlot = new Port() { + + public Point getRelativePosition() { + return new Point(0, 0); + } + + public Vertex getVertex() { + return thisNode; + } + + public String toString() { + return "OutPort of " + thisNode.toString(); + } + }; + + inputSlot = new Port() { + + public Point getRelativePosition() { + Point p = new Point(thisNode.getPosition()); + Point blockPos = thisBlockNode.getPosition(); + p.x -= blockPos.x; + p.y -= blockPos.y; + return p; + } + + public Vertex getVertex() { + return thisBlockNode; + } + public String toString() { + return "InPort of " + thisNode.toString(); + } + }; + } + + public Port getInputSlot() { + return inputSlot; + } + + public InterClusterConnection getInterBlockConnection() { + return interBlockConnection; + } + + public Port getOutputSlot() { + return outputSlot; + } + + + public Dimension getSize() { + return new Dimension(SIZE, SIZE); + } + + public void setPosition(Point p) { + this.position = p; + } + + public Point getPosition() { + return position; + } + + public void setInterBlockConnection(InterClusterConnection interBlockConnection) { + this.interBlockConnection = interBlockConnection; + } + + public Cluster getCluster() { + return cluster; + } + + public boolean isDirty() { + return dirty; + } + + public boolean isRoot() { + return true; + } + + public int compareTo(Vertex o) { + return toString().compareTo(o.toString()); + } + + public boolean isExpanded() { + return false; + } + + public boolean isFixed() { + return false; + } + + public boolean isMarked() { + return false; + } +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterNode.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterNode.java new file mode 100644 index 000000000000..6eecd5cdd76d --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterNode.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.graphanalyzer.positioning; + +import at.ssw.positionmanager.Cluster; +import at.ssw.positionmanager.Link; +import at.ssw.positionmanager.Port; +import at.ssw.positionmanager.Vertex; +import java.awt.Dimension; +import java.awt.Point; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * + * @author Thomas Wuerthinger + */ +public class ClusterNode implements Vertex { + + private Cluster cluster; + private Port inputSlot; + private Port outputSlot; + private Set subNodes; + private Dimension size; + private Point position; + private Set subEdges; + private boolean dirty; + private boolean root; + private String name; + public static final int BORDER = 10; + + public ClusterNode(Cluster cluster, String name) { + this.subNodes = new HashSet(); + this.subEdges = new HashSet(); + this.cluster = cluster; + position = new Point(0, 0); + this.name = name; + } + + public void addSubNode(Vertex v) { + subNodes.add(v); + } + + public void addSubEdge(Link l) { + subEdges.add(l); + } + + public Set getSubEdges() { + return Collections.unmodifiableSet(subEdges); + } + + public void updateSize() { + + + calculateSize(); + + final ClusterNode widget = this; + inputSlot = new Port() { + + public Point getRelativePosition() { + return new Point(size.width/2, 0); + } + + public Vertex getVertex() { + return widget; + } + }; + + outputSlot = new Port() { + + public Point getRelativePosition() { + return new Point(size.width/2, size.height); + } + + public Vertex getVertex() { + return widget; + } + }; + } + + private void calculateSize() { + + if(subNodes.size() == 0) { + size = new Dimension(0, 0); + } + + int minX = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE; + int minY = Integer.MAX_VALUE; + int maxY = Integer.MIN_VALUE; + + + for(Vertex n : subNodes) { + Point p = n.getPosition(); + minX = Math.min(minX, p.x); + minY = Math.min(minY, p.y); + maxX = Math.max(maxX, p.x + n.getSize().width); + maxY = Math.max(maxY, p.y + n.getSize().height); + } + + size = new Dimension(maxX - minX, maxY - minY); + size.width += 2 * BORDER; + size.height += 2 * BORDER; + + } + + public Port getInputSlot() { + return inputSlot; + + } + + public Port getOutputSlot() { + return outputSlot; + } + + public Dimension getSize() { + return size; + } + + public Point getPosition() { + return position; + } + + + public void setPosition(Point pos) { + this.position = pos; + for(Vertex n : subNodes) { + Point cur = new Point(n.getPosition()); + cur.translate(pos.x, pos.y); + n.setPosition(cur); + } + + for(Link e : subEdges) { + List arr = e.getControlPoints(); + ArrayList newArr = new ArrayList(); + for(Point p : arr) { + Point p2 = new Point(p); + p2.translate(pos.x, pos.y); + newArr.add(p2); + } + + e.setControlPoints(newArr); + } + } + + public Cluster getCluster() { + return cluster; + } + + public void setCluster(Cluster c) { + cluster = c; + } + + public boolean isDirty() { + return dirty; + } + + public void setDirty(boolean b) { + dirty = b; + } + + public void setRoot(boolean b) { + root = b; + } + + public boolean isRoot() { + return root; + } + + public int compareTo(Vertex o) { + return toString().compareTo(o.toString()); + } + + public String toString() { + return name; + } + + public Set getSubNodes() { + return subNodes; + } + + public boolean isExpanded() { + return false; + } + + public boolean isFixed() { + return false; + } + + public boolean isMarked() { + return false; + } + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterOutgoingConnection.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterOutgoingConnection.java new file mode 100644 index 000000000000..8b675bf75e18 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterOutgoingConnection.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.graphanalyzer.positioning; + +import at.ssw.positionmanager.Link; +import at.ssw.positionmanager.Port; +import java.awt.Point; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Thomas Wuerthinger + */ +public class ClusterOutgoingConnection implements Link{ + + private List intermediatePoints; + private ClusterOutputSlotNode outputSlotNode; + private Link connection; + private Port inputSlot; + private Port outputSlot; + + public ClusterOutgoingConnection(ClusterOutputSlotNode outputSlotNode, Link c) { + this.outputSlotNode = outputSlotNode; + this.connection = c; + this.intermediatePoints = new ArrayList(); + + outputSlot = c.getFrom(); + inputSlot = outputSlotNode.getInputSlot(); + } + + public Port getTo() { + return inputSlot; + } + + public Port getFrom() { + return outputSlot; + } + + public void setControlPoints(List p) { + this.intermediatePoints = p; + } + + public List getControlPoints() { + return intermediatePoints; + } + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterOutputSlotNode.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterOutputSlotNode.java new file mode 100644 index 000000000000..166a46a0a0a9 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/ClusterOutputSlotNode.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.graphanalyzer.positioning; + +import at.ssw.positionmanager.Cluster; +import at.ssw.positionmanager.Port; +import at.ssw.positionmanager.Vertex; +import java.awt.Dimension; +import java.awt.Point; + +/** + * + * @author Thomas Wuerthinger + */ +public class ClusterOutputSlotNode implements Vertex{ + + private final int SIZE = 0; + private Point position; + private Port inputSlot; + private Port outputSlot; + private ClusterNode blockNode; + private ClusterOutgoingConnection outgoingConnection; + private boolean dirty; + private boolean root; + private Cluster cluster; + private ClusterOutgoingConnection conn; + private String id; + + public void setOutgoingConnection(ClusterOutgoingConnection c) { + this.conn = c; + } + + public ClusterOutgoingConnection getOutgoingConnection() { + return conn; + } + + public String toString() { + return id; + } + + public ClusterOutputSlotNode(ClusterNode n, String id) { + this.blockNode = n; + this.id = id; + + n.addSubNode(this); + + final Vertex thisNode = this; + final ClusterNode thisBlockNode = blockNode; + + inputSlot = new Port() { + + public Point getRelativePosition() { + return new Point(0, 0); + } + + public Vertex getVertex() { + return thisNode; + } + + public String toString() { + return "InPort of " + thisNode.toString(); + } + + }; + + outputSlot = new Port() { + + public Point getRelativePosition() { + Point p = new Point(thisNode.getPosition()); + Point blockPos = thisBlockNode.getPosition(); + p.x -= blockPos.x; + p.y -= blockPos.y; + return p; + } + + public Vertex getVertex() { + return thisBlockNode; + } + + public String toString() { + return "OutPort of " + thisNode.toString(); + } + + }; + } + + + public Dimension getSize() { + return new Dimension(SIZE, SIZE); + } + + public void setPosition(Point p) { + this.position = p; + } + + public Point getPosition() { + return position; + } + + public Port getInputSlot() { + return inputSlot; + } + + public Port getOutputSlot() { + return outputSlot; + } + + public void setCluster(Cluster c) { + cluster = c; + } + + public void setDirty(boolean b) { + dirty = b; + } + + public void setRoot(boolean b) { + root = b; + } + + public Cluster getCluster() { + return cluster; + } + + public boolean isDirty() { + return dirty; + } + + public boolean isRoot() { + return root; + } + + public int compareTo(Vertex o) { + return toString().compareTo(o.toString()); + } + + public boolean isExpanded() { + return false; + } + + public boolean isFixed() { + return false; + } + + public boolean isMarked() { + return false; + } +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/Curves.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/Curves.java new file mode 100644 index 000000000000..bcf790b5a256 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/Curves.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.graphanalyzer.positioning; + +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.RenderingHints; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Thomas Wuerthinger + */ +public class Curves { + + + // Bsplines taken and modified from http://www.cse.unsw.edu.au/~lambert/splines/Bspline.java + // the basis function for a cubic B spline + private static float b(int i, float t) { + switch (i) { + case -2: + return (((-t+3)*t-3)*t+1)/6; + case -1: + return (((3*t-6)*t)*t+4)/6; + case 0: + return (((-3*t+3)*t+3)*t+1)/6; + case 1: + return (t*t*t)/6; + } + return 0; //we only get here if an invalid i is specified + } + + // evaluate a point on the B spline + private static Point p(int i, float t, List points) { + float px=0; + float py=0; + for (int j = -2; j<=1; j++){ + Point point = points.get(i + j); + px += b(j,t)*point.x; + py += b(j,t)*point.y; + } + return new Point(Math.round(px),Math.round(py)); + } + + + public static List bsplines(List inputPoints, Point startRef, Point endRef, int steps) { + if(inputPoints.size() == 2) return inputPoints; + List points = new ArrayList(inputPoints); + Point firstPoint = inputPoints.get(0); + Point lastPoint = inputPoints.get(inputPoints.size() - 1); + + List result = new ArrayList(); + Point q = p(2, 0, points); + for(int i= 2; i < points.size() - 1; i++) { + for(int j=1; j<=steps; j++) { + q = p(i, j/(float)steps, points); + result.add(q); + } + } + result.add(0, firstPoint); + result.add(lastPoint); + return result; + } + + public static Point scaleVector(Point p, double len) { + double scale = Math.sqrt(p.x * p.x + p.y * p.y); + scale = len / scale; + Point result = new Point(p); + result.x = (int)Math.round((double)result.x * scale); + result.y = (int)Math.round((double)result.y * scale); + return result; + } + + public static int quadraticOffset(Point p1, Point p2) { + return (p1.x - p2.x) *(p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y); + } + + public static List convertToBezier(List list, Point startRefPoint, + Point endRefPoint, double bezierScale, int numberOfIntermediatePoints) { + + // Straight line, no bezier needed + if (list.size() == 2) { + return list; + } + + List result = new ArrayList(); + + Point prev = null; + for (int i = 0; i < list.size() - 1; i++) { + Point cur = list.get(i); + Point next = list.get(i + 1); + Point nextnext = null; + if (i < list.size() - 2) { + nextnext = list.get(i + 2); + } + + Point bezierFrom = null; + Point bezierTo = null; + + if (prev == null) { + bezierFrom = new Point(cur.x - startRefPoint.x, cur.y + - startRefPoint.y); + } else { + bezierFrom = new Point(next.x - prev.x, next.y - prev.y); + } + + if (nextnext == null) { + bezierTo = new Point(next.x - endRefPoint.x, next.y + - endRefPoint.y); + } else { + bezierTo = new Point(cur.x - nextnext.x, cur.y - nextnext.y); + } + + Point vec = new Point(cur.x - next.x, cur.y - next.y); + double len = Math.sqrt(vec.x * vec.x + vec.y * vec.y); + double scale = len * bezierScale; + + bezierFrom = scaleVector(bezierFrom, scale); + bezierFrom.translate(cur.x, cur.y); + bezierTo = scaleVector(bezierTo, scale); + bezierTo.translate(next.x, next.y); + + List curList = bezier(cur, next, bezierFrom, bezierTo, + 1 / (double) numberOfIntermediatePoints); + for(Point p : curList) { + if(result.size() > 0) { + Point other = result.get(result.size() - 1); + if(quadraticOffset(p, other) > 80) { + result.add(p); + } + } else { + result.add(p); + } + } + prev = cur; + } + + result.add(list.get(list.size() - 1)); + return result; + } + + private static List bezier(Point from, Point to, Point bezierFrom, + Point bezierTo, double offset) { + double t; + List list = new ArrayList(); + + // from = P0 + // to = P3 + // bezierFrom = P1 + // bezierTo = P2 + + Point lastPoint = new Point(-1, -1); + + for (t = 0.0; t <= 1.0; t += offset) { + double tInv = 1.0 - t; + double tInv2 = tInv * tInv; + double tInv3 = tInv2 * tInv; + double t2 = t * t; + double t3 = t2 * t; + double x = tInv3 * from.x + 3 * t * tInv2 * bezierFrom.x + 3 * t2 + * tInv * bezierTo.x + t3 * to.x; + double y = tInv3 * from.y + 3 * t * tInv2 * bezierFrom.y + 3 * t2 + * tInv * bezierTo.y + t3 * to.y; + Point p = new Point((int)Math.round(x), (int)Math.round(y)); + if (lastPoint.x != p.x || lastPoint.y != p.y) { + int offx = p.x - lastPoint.x; + int offy = p.y - lastPoint.y; + int off = offx * offx + offy * offy; + if (list.size() == 1 || off > 2) { + list.add(p); + lastPoint = p; + } + } + } + + list.remove(list.size() - 1); + list.add(to); + + return list; + } +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/Edge.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/Edge.java new file mode 100644 index 000000000000..c1291c03310c --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/Edge.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.graphanalyzer.positioning; + +/** + * + * @author Thomas Wuerthinger + */ +public class Edge { + + private E data; + private Node source; + private Node dest; + private Graph graph; + + protected Edge(Graph graph, Node source, Node dest, E data) { + setData(data); + this.graph = graph; + this.source = source; + this.dest = dest; + } + + public Node getSource() { + return source; + } + + public Node getDest() { + return dest; + } + + public E getData() { + return data; + } + + public void setData(E e){ + data = e; + } + + public void remove() { + graph.removeEdge(this, null); + } + + public boolean isSelfLoop() { + return source == dest; + } + + public void reverse() { + + // Remove from current source / dest + source.removeOutEdge(this); + dest.removeInEdge(this); + + Node tmp = source; + source = dest; + dest = tmp; + + // Add to new source / dest + source.addOutEdge(this); + dest.addInEdge(this); + } + + public String toString() { + return "Edge (" + source + " -- " + dest + "): " + data; + } + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/Graph.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/Graph.java new file mode 100644 index 000000000000..cb4c2309af16 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/Graph.java @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.graphanalyzer.positioning; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * + * @author Thomas Wuerthinger + */ +public class Graph { + + private Hashtable> nodes; + private Hashtable> edges; + private List> nodeList; + private List> edgeList; + + + /** Creates a new instance of Graph */ + public Graph() { + nodes = new Hashtable>(); + edges = new Hashtable>(); + nodeList = new ArrayList>(); + edgeList = new ArrayList>(); + } + + + public Node createNode(N data, Object key) { + Node n = new Node(this, data); + assert key == null || !nodes.containsKey(key); + if(key != null) nodes.put(key, n); + nodeList.add(n); + //assert nodes.containsValue(n); + return n; + } + + public Edge createEdge(Node source, Node dest, E data, Object key) { + //assert nodes.containsValue(source); + //assert nodes.containsValue(dest); + Edge e = new Edge(this, source, dest, data); + source.addOutEdge(e); + dest.addInEdge(e); + if(key != null) edges.put(key, e); + edgeList.add(e); + return e; + } + + public Node getNode(Object key) { + return nodes.get(key); + } + + public Edge getEdge(Object key) { + return edges.get(key); + } + + public Collection> getEdges() { + return Collections.unmodifiableCollection(edges.values()); + } + + public Collection> getNodes() { + return Collections.unmodifiableList(nodeList); + } + + public void removeEdge(Edge e, Object key) { + assert key == null || edges.containsKey(key); + if(key != null) edges.remove(key); + edgeList.remove(e); + e.getSource().removeOutEdge(e); + e.getDest().removeInEdge(e); + } + + public class DFSTraversalVisitor { + public void visitNode(Node n) {} + public boolean visitEdge(Edge e, boolean backEdge){return true;} + } + + public class BFSTraversalVisitor { + public void visitNode(Node n, int depth){} + } + + public List> getNodesWithInDegree(int x) { + return getNodesWithInDegree(x, true); + } + public List> getNodesWithInDegree(int x, boolean countSelfLoops) { + + List> result = new ArrayList>(); + for(Node n : getNodes()) { + if(n.getInDegree(countSelfLoops) == x) { + result.add(n); + } + } + + return result; + + } + + private void markReachable(Node startingNode) { + ArrayList> arr = new ArrayList>(); + arr.add(startingNode); + for(Node n : getNodes()) { + n.setReachable(false); + } + traverseDFS(arr, new DFSTraversalVisitor() { + public void visitNode(Node n) { + n.setReachable(true); + } + + }); + } + + public void traverseBFS(Node startingNode, BFSTraversalVisitor tv, boolean longestPath) { + + //if(longestPath) { + // assert !this.hasCycles(); + //} + + + if(longestPath) { + markReachable(startingNode); + } + + for(Node n : getNodes()) { + n.setVisited(false); + n.setActive(false); + } + + Queue> queue = new LinkedList>(); + queue.add(startingNode); + startingNode.setVisited(true); + int layer = 0; + Node lastOfLayer = startingNode; + Node lastAdded = null; + + while(!queue.isEmpty()) { + + Node current = queue.poll(); + tv.visitNode(current, layer); + current.setActive(false); + + + for(Edge e : current.getOutEdges()) { + if(!e.getDest().isVisited()) { + + boolean allow = true; + if(longestPath) { + for(Node pred : e.getDest().getPredecessors()) { + if((!pred.isVisited() || pred.isActive()) && pred.isReachable()) { + allow = false; + break; + } + } + } + + if(allow) { + queue.offer(e.getDest()); + lastAdded = e.getDest(); + e.getDest().setVisited(true); + e.getDest().setActive(true); + } + } + } + + if(current == lastOfLayer && !queue.isEmpty()) { + lastOfLayer = lastAdded; + layer++; + } + } + } + + public void traverseDFS(DFSTraversalVisitor tv) { + traverseDFS(getNodes(), tv); + } + + public void traverseDFS(Collection> startingNodes, DFSTraversalVisitor tv) { + + for(Node n : getNodes()) { + n.setVisited(false); + n.setActive(false); + } + + boolean result = false; + for(Node n : startingNodes) { + traverse(tv, n); + } + } + + private void traverse(DFSTraversalVisitor tv, Node n) { + + if(!n.isVisited()) { + n.setVisited(true); + n.setActive(true); + tv.visitNode(n); + + for(Edge e : n.getOutEdges()) { + + Node next = e.getDest(); + if(next.isActive()) { + tv.visitEdge(e, true); + } else { + if(tv.visitEdge(e, false)) { + traverse(tv, next); + } + } + } + + n.setActive(false); + } + + } + + public boolean hasCycles() { + + for(Node n : getNodes()) { + n.setVisited(false); + n.setActive(false); + } + + boolean result = false; + for(Node n : getNodes()) { + result |= checkCycles(n); + if(result) break; + } + return result; + } + + private boolean checkCycles(Node n) { + + if(n.isActive()) { + return true; + } + + if(!n.isVisited()) { + + n.setVisited(true); + n.setActive(true); + + for(Node succ : n.getSuccessors()) { + if(checkCycles(succ)) { + return true; + } + } + + n.setActive(false); + + } + + return false; + + } + + public String toString() { + + StringBuilder s = new StringBuilder(); + s.append("Nodes: "); + for(Node n : getNodes()) { + s.append(n.toString()); + s.append("\n"); + } + + + s.append("Edges: "); + + for(Edge e : getEdges()) { + s.append(e.toString()); + s.append("\n"); + } + + return s.toString(); + } + + + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/HierarchicalClusterLayoutManager.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/HierarchicalClusterLayoutManager.java new file mode 100644 index 000000000000..b0394a76f8f4 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/HierarchicalClusterLayoutManager.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.graphanalyzer.positioning; + +import at.ssw.graphanalyzer.positioning.HierarchicalLayoutManager.Combine; +import at.ssw.positionmanager.Cluster; +import at.ssw.positionmanager.LayoutGraph; +import at.ssw.positionmanager.LayoutManager; +import at.ssw.positionmanager.Link; +import at.ssw.positionmanager.Port; +import at.ssw.positionmanager.Vertex; +import java.awt.Point; +import java.util.Hashtable; +import java.util.List; +import java.util.Set; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.TreeSet; +import at.ssw.graphanalyzer.positioning.HierarchicalLayoutManager; + +/** + * + * @author Thomas Wuerthinger + */ +public class HierarchicalClusterLayoutManager implements LayoutManager{ + + private HierarchicalLayoutManager.Combine combine; + + /** Creates a new instance of HierarchicalClusterLayoutManager */ + public HierarchicalClusterLayoutManager(Combine combine) { + this.combine = combine; + } + + public void doLayout(LayoutGraph graph) { + + assert graph.verify(); + + HierarchicalLayoutManager subManager = new HierarchicalLayoutManager(combine); + HierarchicalLayoutManager manager = new HierarchicalLayoutManager(combine, 150); + Hashtable> lists = new Hashtable>(); + Hashtable> listsConnection = new Hashtable>(); + Set vertices = graph.getVertices(); + Hashtable> clusterInputSlotHash = new Hashtable>(); + Hashtable> clusterOutputSlotHash = new Hashtable>(); + + Hashtable clusterNodes = new Hashtable(); + Hashtable> clusterInputSlotSet = new Hashtable>(); + Hashtable> clusterOutputSlotSet = new Hashtable>(); + Set clusterEdges = new HashSet(); + Set interClusterEdges = new HashSet(); + Hashtable linkClusterOutgoingConnection = new Hashtable(); + Hashtable linkInterClusterConnection = new Hashtable(); + Hashtable linkClusterIngoingConnection = new Hashtable(); + + Set cluster = graph.getClusters(); + int z = 0; + for(Cluster c : cluster) { + lists.put(c, new ArrayList()); + + listsConnection.put(c, new ArrayList()); + clusterInputSlotHash.put(c, new Hashtable()); + clusterOutputSlotHash.put(c, new Hashtable()); + clusterOutputSlotSet.put(c, new TreeSet()); + clusterInputSlotSet.put(c, new TreeSet()); + clusterNodes.put(c, new ClusterNode(c, "" + z)); + z++; + + } + + // Add cluster edges + for(Cluster c : cluster) { + + ClusterNode start = clusterNodes.get(c); + + for(Cluster succ : c.getSuccessors()) { + ClusterNode end = clusterNodes.get(succ); + if(end != null) { + ClusterEdge e = new ClusterEdge(start, end); + clusterEdges.add(e); + interClusterEdges.add(e); + } + } + } + + for(Vertex v : graph.getVertices()) { + + Cluster c = v.getCluster(); + clusterNodes.get(c).addSubNode(v); + + } + + for(Link l : graph.getLinks()) { + + Port fromPort = l.getFrom(); + Port toPort = l.getTo(); + Vertex fromVertex = fromPort.getVertex(); + Vertex toVertex = toPort.getVertex(); + Cluster fromCluster = fromVertex.getCluster(); + Cluster toCluster = toVertex.getCluster(); + + Port samePort = null; + if(combine == Combine.SAME_INPUTS) { + samePort = toPort; + } else if(combine == Combine.SAME_OUTPUTS) { + samePort = fromPort; + } + + assert listsConnection.containsKey(fromCluster); + assert listsConnection.containsKey(toCluster); + + if(fromCluster == toCluster) { + listsConnection.get(fromCluster).add(l); + clusterNodes.get(fromCluster).addSubEdge(l); + } else { + + ClusterInputSlotNode inputSlotNode = null; + ClusterOutputSlotNode outputSlotNode = null; + + if(samePort != null) { + outputSlotNode = clusterOutputSlotHash.get(fromCluster).get(samePort); + inputSlotNode = clusterInputSlotHash.get(toCluster).get(samePort); + } + + boolean needInterClusterConnection = (outputSlotNode == null || inputSlotNode == null); + + if(outputSlotNode == null) { + outputSlotNode = new ClusterOutputSlotNode(clusterNodes.get(fromCluster), "Out " + fromCluster.toString() + " " + samePort.toString()); + clusterOutputSlotSet.get(fromCluster).add(outputSlotNode); + ClusterOutgoingConnection conn = new ClusterOutgoingConnection(outputSlotNode, l); + outputSlotNode.setOutgoingConnection(conn); + clusterNodes.get(fromCluster).addSubEdge(conn); + if(samePort != null) { + clusterOutputSlotHash.get(fromCluster).put(samePort, outputSlotNode); + } + + linkClusterOutgoingConnection.put(l, conn); + } else { + linkClusterOutgoingConnection.put(l, outputSlotNode.getOutgoingConnection()); + } + + if(inputSlotNode == null) { + inputSlotNode = new ClusterInputSlotNode(clusterNodes.get(toCluster), "In " + toCluster.toString() + " " + samePort.toString()); + clusterInputSlotSet.get(toCluster).add(inputSlotNode); + } + + ClusterIngoingConnection conn = new ClusterIngoingConnection(inputSlotNode, l); + inputSlotNode.setIngoingConnection(conn); + clusterNodes.get(toCluster).addSubEdge(conn); + if(samePort != null) { + clusterInputSlotHash.get(toCluster).put(samePort, inputSlotNode); + } + + linkClusterIngoingConnection.put(l, conn); + /*} else { + linkClusterIngoingConnection.put(l, inputSlotNode.getIngoingConnection()); + }*/ + + InterClusterConnection interConn = new InterClusterConnection(outputSlotNode, inputSlotNode); + linkInterClusterConnection.put(l, interConn); + clusterEdges.add(interConn); + } + } + + for(Cluster c : cluster) { + ClusterNode n = clusterNodes.get(c); + subManager.doLayout(new LayoutGraph(n.getSubEdges(), n.getSubNodes()), clusterInputSlotSet.get(c), clusterOutputSlotSet.get(c)); + n.updateSize(); + } + + Set roots = new LayoutGraph(interClusterEdges).findRootVertices(); + for(Vertex v : roots) { + assert v instanceof ClusterNode; + ((ClusterNode)v).setRoot(true); + } + + manager.doLayout(new LayoutGraph(clusterEdges), new HashSet(), new HashSet(), interClusterEdges); + + + for(Link l : graph.getLinks()) { + + if(linkInterClusterConnection.containsKey(l)) { + ClusterOutgoingConnection conn1 = linkClusterOutgoingConnection.get(l); + InterClusterConnection conn2 = linkInterClusterConnection.get(l); + ClusterIngoingConnection conn3 = linkClusterIngoingConnection.get(l); + + assert conn1 != null; + assert conn2 != null; + assert conn3 != null; + + List points = new ArrayList(); + + points.addAll(conn1.getControlPoints()); + points.addAll(conn2.getControlPoints()); + points.addAll(conn3.getControlPoints()); + + l.setControlPoints(points); + } + + } + } + + + public void doRouting(LayoutGraph graph) { + } +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/HierarchicalLayoutManager.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/HierarchicalLayoutManager.java new file mode 100644 index 000000000000..7ee1e5ff7bbe --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/HierarchicalLayoutManager.java @@ -0,0 +1,1124 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.graphanalyzer.positioning; + +import at.ssw.positionmanager.LayoutGraph; +import at.ssw.positionmanager.LayoutManager; +import at.ssw.positionmanager.Link; +import at.ssw.positionmanager.Port; +import at.ssw.positionmanager.Vertex; +import at.ssw.graphanalyzer.positioning.Edge; +import at.ssw.graphanalyzer.positioning.Graph; +import at.ssw.graphanalyzer.positioning.Graph.BFSTraversalVisitor; +import at.ssw.graphanalyzer.positioning.Node; +import java.awt.Point; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * + * @author Thomas Wuerthinger + */ +public class HierarchicalLayoutManager implements LayoutManager{ + + public static final int DUMMY_WIDTH = 0; + public static final int DUMMY_HEIGHT = 0; + public static final int LAYER_OFFSET = 50; + public static final int OFFSET = 8; + + public static final boolean VERTICAL_LAYOUT = true; + public static final boolean ASSERT = false; + public static final boolean TRACE = false; + + private Combine combine; + + public enum Combine { + NONE, + SAME_INPUTS, + SAME_OUTPUTS + }; + + private class NodeData { + + private Map reversePositions; + private Vertex node; + private Link edge; + private int layer; + private int x; + private int y; + private int width; + + public NodeData(Vertex node) { + reversePositions = new HashMap(); + layer = -1; + this.node = node; + assert node != null; + + if(VERTICAL_LAYOUT) { + width = node.getSize().width; + } else { + width = node.getSize().height; + } + } + + public NodeData(Link edge) { + layer = -1; + this.edge = edge; + assert edge != null; + + if(VERTICAL_LAYOUT) { + width = DUMMY_WIDTH; + } else { + width = DUMMY_HEIGHT; + } + } + + public Vertex getNode() { + return node; + } + + public Link getEdge() { + return edge; + } + + public int getCoordinate() { + return x; + } + + public int getLayerCoordinate() { + return y; + } + + public void setCoordinate(int x) { + this.x = x; + } + + public int getX() { + if(VERTICAL_LAYOUT) { + return x; + } else { + return y; + } + } + + public int getY() { + if(VERTICAL_LAYOUT) { + return y; + } else { + return x; + } + } + + public void setLayerCoordinate(int y) { + this.y = y; + } + + public void setLayer(int x) { + layer = x; + } + + public int getLayer() { + return layer; + } + + public boolean isDummy() { + return edge != null; + } + + public int getWidth() { + return width; + } + + public void addReversedStartEdge(Edge e) { + assert e.getData().isReversed(); + Port port = e.getData().getEdge().getTo(); + int pos = addReversedPort(port); + Point start = e.getData().getRelativeStart(); + e.getData().addStartPoint(start); + int yCoord = node.getSize().height + width - node.getSize().width; + e.getData().addStartPoint(new Point(start.x, yCoord)); + e.getData().addStartPoint(new Point(pos, yCoord)); + e.getData().setRelativeStart(new Point(pos, 0)); + } + + private int addReversedPort(Port p) { + if(reversePositions.containsKey(p)) { + return reversePositions.get(p); + } else { + width += OFFSET; + reversePositions.put(p, width); + return width; + } + } + + public void addReversedEndEdge(Edge e) { + assert e.getData().isReversed(); + int pos = addReversedPort(e.getData().getEdge().getFrom()); + Point end = e.getData().getRelativeEnd(); + e.getData().setRelativeEnd(new Point(pos, node.getSize().height)); + int yCoord = 0 - width + node.getSize().width; + e.getData().addEndPoint(new Point(pos, yCoord)); + e.getData().addEndPoint(new Point(end.x, yCoord)); + e.getData().addEndPoint(end); + } + + public int getHeight() { + if(isDummy()) { + if(VERTICAL_LAYOUT) { + return DUMMY_HEIGHT; + } else { + return DUMMY_WIDTH; + } + + } else { + if(VERTICAL_LAYOUT) { + return node.getSize().height; + } else { + return node.getSize().width; + } + } + } + + public String toString() { + if(isDummy()) { + return edge.toString() + "(layer=" + layer + ")"; + } else { + return node.toString() + "(layer=" + layer + ")"; + } + } + } + + private class EdgeData { + + private Point relativeEnd; + private Point relativeStart; + private List startPoints; + private List endPoints; + private boolean important; + private boolean reversed; + private Link edge; + + public EdgeData(Link edge) { + this(edge, false); + } + + public EdgeData(Link edge, boolean rev) { + this.edge = edge; + reversed = rev; + relativeStart = edge.getFrom().getRelativePosition(); + relativeEnd = edge.getTo().getRelativePosition(); + assert relativeStart.x >= 0 && relativeStart.x <= edge.getFrom().getVertex().getSize().width; + assert relativeStart.y >= 0 && relativeStart.y <= edge.getFrom().getVertex().getSize().height; + assert relativeEnd.x >= 0 && relativeEnd.x <= edge.getTo().getVertex().getSize().width; + assert relativeEnd.y >= 0 && relativeEnd.y <= edge.getTo().getVertex().getSize().height; + startPoints = new ArrayList(); + endPoints = new ArrayList(); + this.important = true; + } + + public boolean isImportant() { + return important; + } + + public void setImportant(boolean b) { + this.important = b; + } + + public List getStartPoints() { + return startPoints; + } + + public List getEndPoints() { + return endPoints; + } + + public List getAbsoluteEndPoints() { + if(endPoints.size() == 0) return endPoints; + + List result = new ArrayList(); + Point point = edge.getTo().getVertex().getPosition(); + for(Point p : endPoints) { + Point p2 = new Point(p.x + point.x, p.y + point.y); + result.add(p2); + } + + return result; + } + + public List getAbsoluteStartPoints() { + if(startPoints.size() == 0) return startPoints; + + List result = new ArrayList(); + Point point = edge.getFrom().getVertex().getPosition(); + for(Point p : startPoints) { + Point p2 = new Point(p.x + point.x, p.y + point.y); + result.add(p2); + } + + return result; + } + + public void addEndPoint(Point p) { + endPoints.add(p); + } + + public void addStartPoint(Point p) { + startPoints.add(p); + } + + public Link getEdge() { + return edge; + } + + public void setRelativeEnd(Point p) { + relativeEnd = p; + } + + public void setRelativeStart(Point p) { + relativeStart = p; + } + + public Point getRelativeEnd() { + return relativeEnd; + } + + public Point getRelativeStart() { + return relativeStart; + } + + public boolean isReversed() { + return reversed; + } + + public void setReversed(boolean b) { + reversed = b; + } + + public String toString() { + return "EdgeData[reversed=" + reversed + "]"; + } + } + + private Graph graph; + private Map> nodeMap; + private int layerOffset; + + /** Creates a new instance of HierarchicalPositionManager */ + public HierarchicalLayoutManager(Combine combine) { + this(combine, LAYER_OFFSET); + } + + public HierarchicalLayoutManager(Combine combine, int layerOffset) { + this.combine = combine; + this.layerOffset = layerOffset; + } + + + public void doRouting(LayoutGraph graph) { + } + + //public void setPositions(PositionedNode rootNode, List nodes, List edges) { + + + public void doLayout(LayoutGraph layoutGraph) { + doLayout(layoutGraph, new HashSet(), new HashSet()); + } + + public void doLayout(LayoutGraph layoutGraph, Set firstLayerHint, Set lastLayerHint) { + doLayout(layoutGraph, firstLayerHint, lastLayerHint, new HashSet()); + } + + public void doLayout(LayoutGraph layoutGraph, Set firstLayerHint, Set lastLayerHint, Set importantLinksHint) { + + if(TRACE) System.out.println("HierarchicalPositionManager.doLayout called"); + + if(layoutGraph.getVertices().size() == 0) return; + + nodeMap = new HashMap>(); + + graph = new Graph(); + + Set> rootNodes = new HashSet>(); + Set startRootVertices = new HashSet(); + + for(Vertex v : layoutGraph.getVertices()) { + if(v.isRoot()) startRootVertices.add(v); + } + Set rootVertices = layoutGraph.findRootVertices(startRootVertices); + + for(Vertex node : layoutGraph.getVertices()) { + + NodeData data = new NodeData(node); + Node n = graph.createNode(data, node); + nodeMap.put(node, n); + + if(rootVertices.contains(node)) { + rootNodes.add(n); + } + } + + Set links = layoutGraph.getLinks(); + Link[] linkArr = new Link[links.size()]; + links.toArray(linkArr); + + List linkList = new ArrayList(); + for(Link l : linkArr) { + linkList.add(l); + } + + Collections.sort(linkList, new Comparator() { + public int compare(Link o1, Link o2) { + int result = o1.getFrom().getVertex().compareTo(o2.getFrom().getVertex()); + if(result == 0) { + return o1.getTo().getVertex().compareTo(o2.getTo().getVertex()); + } else { + return result; + } + } + }); + + for(Link edge : linkList) { + EdgeData data = new EdgeData(edge); + graph.createEdge(graph.getNode(edge.getFrom().getVertex()), graph.getNode(edge.getTo().getVertex()), data, data); + if(importantLinksHint.size() > 0 && !importantLinksHint.contains(edge)) { + data.setImportant(false); + } + } + + + // STEP 1: Remove cycles! + removeCycles(rootNodes); + if(ASSERT) assert checkRemoveCycles(); + + for(Node n : graph.getNodes()) { + List> edges = new ArrayList>(n.getOutEdges()); + Collections.sort(edges, new Comparator>() { + public int compare(Edge o1, Edge o2) { + return o2.getData().getRelativeEnd().x - o1.getData().getRelativeEnd().x; + }}); + + + for(Edge e : edges) { + + if(e.getData().isReversed()) { + e.getSource().getData().addReversedEndEdge(e); + } + } + } + + for(Node n : graph.getNodes()) { + List> edges = new ArrayList>(n.getInEdges()); + Collections.sort(edges, new Comparator>() { + public int compare(Edge o1, Edge o2) { + return o2.getData().getRelativeStart().x - o1.getData().getRelativeStart().x; + }}); + + + for(Edge e : edges) { + if(e.getData().isReversed()) { + e.getDest().getData().addReversedStartEdge(e); + } + } + } + + // STEP 2: Assign layers! + assignLayers(rootNodes, firstLayerHint, lastLayerHint); + if(ASSERT) assert checkAssignLayers(); + + // Put into layer array + int maxLayer = 0; + for(Node n : graph.getNodes()) { + maxLayer = Math.max(maxLayer, n.getData().getLayer()); + } + + + ArrayList> layers[] = new ArrayList[maxLayer+1]; + int layerSizes[] = new int[maxLayer + 1]; + for(int i=0; i>(); + } + + for(Node n : graph.getNodes()) { + int curLayer = n.getData().getLayer(); + layers[curLayer].add(n); + } + + // STEP 3: Insert dummy nodes! + insertDummyNodes(layers); + if(ASSERT) assert checkDummyNodes(); + + // STEP 4: Assign Y coordinates + assignLayerCoordinates(layers, layerSizes); + + // STEP 5: Crossing reduction + crossingReduction(layers); + + // STEP 6: Assign Y coordinates + assignCoordinates(layers); + + // Assign coordinates of nodes to real objects + for(Node n : graph.getNodes()) { + if(!n.getData().isDummy()) { + + Vertex node = n.getData().getNode(); + node.setPosition(new Point(n.getData().getX(), n.getData().getY())); + } + } + + for(Node n : graph.getNodes()) { + if(!n.getData().isDummy()) { + + Vertex node = n.getData().getNode(); + + List> outEdges = n.getOutEdges(); + for(Edge e : outEdges) { + Node succ = e.getDest(); + if(succ.getData().isDummy()) { + //PositionedEdge edge = succ.getData().getEdge(); + List points = new ArrayList(); + assignToRealObjects(layerSizes, succ, points); + } else { + List points = new ArrayList(); + + EdgeData otherEdgeData = e.getData(); + points.addAll(otherEdgeData.getAbsoluteStartPoints()); + Link otherEdge = otherEdgeData.getEdge(); + Point relFrom = new Point(otherEdgeData.getRelativeStart()); + Point from = otherEdge.getFrom().getVertex().getPosition(); + relFrom.move(relFrom.x + from.x, relFrom.y + from.y); + points.add(relFrom); + + Point relTo = new Point(otherEdgeData.getRelativeEnd()); + Point to = otherEdge.getTo().getVertex().getPosition(); + relTo.move(relTo.x + to.x, relTo.y + to.y); + assert from != null; + assert to != null; + points.add(relTo); + points.addAll(otherEdgeData.getAbsoluteEndPoints()); + e.getData().getEdge().setControlPoints(points); + } + } + } + } + + + } + + public boolean onOneLine(Point p1, Point p2, Point p3) { + int xoff1 = p1.x - p2.x; + int yoff1 = p1.y - p2.y; + int xoff2 = p3.x - p2.x; + int yoff2 = p3.y - p2.x; + + return (xoff1 * yoff2 - yoff1 * xoff2 == 0); + } + + public void assignToRealObjects(int layerSizes[], Node cur, List points) { + assert cur.getData().isDummy(); + + ArrayList otherPoints = new ArrayList(points); + + int size = layerSizes[cur.getData().getLayer()]; + otherPoints.add(new Point(cur.getData().getX(), cur.getData().getY() - size/2)); + if(otherPoints.size() >= 3 && onOneLine(otherPoints.get(otherPoints.size() - 1), otherPoints.get(otherPoints.size() - 2), otherPoints.get(otherPoints.size() - 3))) { + otherPoints.remove(otherPoints.size() - 2); + } + otherPoints.add(new Point(cur.getData().getX(), cur.getData().getY() + size/2)); + if(otherPoints.size() >= 3 && onOneLine(otherPoints.get(otherPoints.size() - 1), otherPoints.get(otherPoints.size() - 2), otherPoints.get(otherPoints.size() - 3))) { + otherPoints.remove(otherPoints.size() - 2); + } + + for(int i=0; i otherSucc = cur.getOutEdges().get(i).getDest(); + + if(otherSucc.getData().isDummy()) { + assignToRealObjects(layerSizes, otherSucc, otherPoints); + } else { + EdgeData otherEdgeData = cur.getOutEdges().get(i).getData(); + Link otherEdge = otherEdgeData.getEdge(); + + List middlePoints = new ArrayList(otherPoints); + if(cur.getOutEdges().get(i).getData().isReversed()) { + Collections.reverse(middlePoints); + } + + ArrayList copy = new ArrayList(); + Point relFrom = new Point(otherEdgeData.getRelativeStart()); + Point from = otherEdge.getFrom().getVertex().getPosition(); + //int moveUp = (size - otherEdge.getFrom().getVertex().getSize().height) / 2; + relFrom.move(relFrom.x + from.x, relFrom.y + from.y); + copy.addAll(otherEdgeData.getAbsoluteStartPoints()); + copy.add(relFrom); + copy.addAll(middlePoints); + + Point relTo = new Point(otherEdgeData.getRelativeEnd()); + Point to = otherEdge.getTo().getVertex().getPosition(); + relTo.move(relTo.x + to.x, relTo.y + to.y); + copy.add(relTo); + + copy.addAll(otherEdgeData.getAbsoluteEndPoints()); + + + otherEdge.setControlPoints(copy); + } + } + } + + + private boolean checkDummyNodes() { + for(Edge e : graph.getEdges()) { + if(e.getSource().getData().getLayer() != e.getDest().getData().getLayer() - 1) { + return false; + } + } + + return true; + } + + private void insertDummyNodes(ArrayList> layers[]) { + + int sum = 0; + List> nodes = new ArrayList>(graph.getNodes()); + int edgeCount = 0; + int innerMostLoop = 0; + + for(Node n : nodes) { + List> edges = new ArrayList>(n.getOutEdges()); + for(Edge e : edges) { + + edgeCount++; + Link edge = e.getData().getEdge(); + Node destNode = e.getDest(); + Node lastNode = n; + Edge lastEdge = e; + + boolean searchForNode = (combine != Combine.NONE); + for(int i=n.getData().getLayer()+1; i foundNode = null; + if(searchForNode) { + for(Node sameLayerNode : layers[i]) { + innerMostLoop++; + + if(combine == Combine.SAME_OUTPUTS) { + if(sameLayerNode.getData().isDummy() && sameLayerNode.getData().getEdge().getFrom() == edge.getFrom()) { + foundNode = sameLayerNode; + break; + } + } else if(combine == Combine.SAME_INPUTS) { + if(sameLayerNode.getData().isDummy() && sameLayerNode.getData().getEdge().getTo() == edge.getTo()) { + foundNode = sameLayerNode; + break; + } + } + } + } + + if(foundNode == null) { + searchForNode = false; + NodeData intermediateData = new NodeData(edge); + Node curNode = graph.createNode(intermediateData, null); + curNode.getData().setLayer(i); + layers[i].add(0, curNode); + sum++; + lastEdge.remove(); + graph.createEdge(lastNode, curNode, e.getData(), null); + assert lastNode.getData().getLayer() == curNode.getData().getLayer() - 1; + lastEdge = graph.createEdge(curNode, destNode, e.getData(), null); + lastNode = curNode; + } else { + lastEdge.remove(); + lastEdge = graph.createEdge(foundNode, destNode, e.getData(), null); + lastNode = foundNode; + } + + } + } + } + + if(TRACE) System.out.println("Number of edges: " + edgeCount); + if(TRACE) System.out.println("Dummy nodes inserted: " + sum); + } + + private void assignLayerCoordinates(ArrayList> layers[], int layerSizes[]) { + int cur = 0; + for(int i=0; i n : layers[i]) { + maxHeight = Math.max(maxHeight, n.getData().getHeight()); + } + + layerSizes[i] = maxHeight; + for(Node n : layers[i]) { + int curCoordinate = cur + (maxHeight - n.getData().getHeight())/2; + n.getData().setLayerCoordinate(curCoordinate); + } + cur += maxHeight + layerOffset; + + } + } + + private void assignCoordinates(ArrayList> layers[]) { + + // TODO: change this + for(int i=0; i> curArray = layers[i]; + int curY = 0; + for(Node n : curArray) { + + n.getData().setCoordinate(curY); + if(!n.getData().isDummy()) { + curY += n.getData().getWidth(); + } + curY += OFFSET; + + } + } + + int curSol = evaluateSolution(); + if(TRACE) System.out.println("First coordinate solution found: " + curSol); + + for(int i=0; i<2; i++) { + optimizeMedian(layers); + curSol = evaluateSolution(); + if(TRACE) System.out.println("Current coordinate solution found: " + curSol); + } + normalizeCoordinate(); + + } + + private void normalizeCoordinate() { + + int min = Integer.MAX_VALUE; + for(Node n : graph.getNodes()) { + min = Math.min(min, n.getData().getCoordinate()); + } + + for(Node n : graph.getNodes()) { + n.getData().setCoordinate(n.getData().getCoordinate() - min); + } + + } + + private void optimizeMedian(ArrayList> layers[]) { + + // Downsweep + for(int i=1; i> processingList = new ArrayList>(layers[i]); + Collections.sort(processingList, new Comparator>() { + public int compare(Node o1, Node o2) { + if(o2.getData().isDummy()) { + return 1; + } else if(o1.getData().isDummy()) { + return -1; + } + return o2.getInEdges().size() - o1.getInEdges().size(); + } + }); + + + ArrayList> alreadyAssigned = new ArrayList>(); + for(Node n : processingList) { + + + ArrayList> preds = new ArrayList>(n.getPredecessors()); + int pos = n.getData().getCoordinate(); + if(preds.size() > 0) { + + Collections.sort(preds, new Comparator>() { + public int compare(Node o1, Node o2) { + return o1.getData().getCoordinate() - o2.getData().getCoordinate(); + } + }); + + if(preds.size() % 2 == 0) { + assert preds.size() >= 2; + pos = (preds.get(preds.size() / 2).getData().getCoordinate() - calcRelativeCoordinate(preds.get(preds.size() / 2), n) + preds.get(preds.size() / 2-1).getData().getCoordinate() - calcRelativeCoordinate(preds.get(preds.size()/2-1), n))/2; + } else { + assert preds.size() >= 1; + pos = preds.get(preds.size() / 2).getData().getCoordinate() - calcRelativeCoordinate(preds.get(preds.size() / 2), n); + } + } + + tryAdding(alreadyAssigned, n, pos); + } + } + // Upsweep + for(int i=layers.length - 2; i >= 0; i--) { + ArrayList> processingList = new ArrayList>(layers[i]); + Collections.sort(processingList, new Comparator>() { + public int compare(Node o1, Node o2) { + if(o2.getData().isDummy()) { + return 1; + } else if(o1.getData().isDummy()) { + return -1; + } + return o2.getOutEdges().size() - o1.getOutEdges().size(); + } + }); + + ArrayList> alreadyAssigned = new ArrayList>(); + for(Node n : processingList) { + + ArrayList> succs = new ArrayList>(n.getSuccessors()); + int pos = n.getData().getCoordinate(); + if(succs.size() > 0) { + + Collections.sort(succs, new Comparator>() { + public int compare(Node o1, Node o2) { + return o1.getData().getCoordinate() - o2.getData().getCoordinate(); + } + }); + + if(succs.size() % 2 == 0) { + assert succs.size() >= 2; + pos = (succs.get(succs.size() / 2).getData().getCoordinate() - calcRelativeCoordinate(n, succs.get(succs.size() / 2)) + succs.get(succs.size()/2-1).getData().getCoordinate() - calcRelativeCoordinate(n, succs.get(succs.size()/2-1)))/2; + } else { + assert succs.size() >= 1; + pos = succs.get(succs.size() / 2).getData().getCoordinate() - calcRelativeCoordinate(n, succs.get(succs.size() / 2)); + } + } + + tryAdding(alreadyAssigned, n, pos); + } + } + } + + private int median(ArrayList arr) { + assert arr.size() > 0; + Collections.sort(arr); + if(arr.size() % 2 == 0) { + return (arr.get(arr.size() / 2) + arr.get(arr.size() / 2 - 1)) / 2; + } else { + return arr.get(arr.size() / 2); + } + } + + private int calcRelativeCoordinate(Node n, Node succ) { + + if(n.getData().isDummy() && succ.getData().isDummy()) return 0; + + int pos = 0; + int pos2 = 0; + ArrayList coords2 = new ArrayList(); + ArrayList coords = new ArrayList(); + /*if(!n.getData().isDummy())*/ { + for(Edge e : n.getOutEdges()) { + + //System.out.println("reversed: " + e.getData().isReversed()); + if(e.getDest() == succ) { + + if(e.getData().isReversed()) { + if(!n.getData().isDummy()) { + coords.add(e.getData().getRelativeEnd().x); + } + + if(!succ.getData().isDummy()) { + coords2.add(e.getData().getRelativeStart().x); + } + } else { + if(!n.getData().isDummy()) { + coords.add(e.getData().getRelativeStart().x); + } + + if(!succ.getData().isDummy()) { + coords2.add(e.getData().getRelativeEnd().x); + } + } + } + } + + // assert coords.size() > 0; + if(!n.getData().isDummy()) { + pos = median(coords); + } + + if(!succ.getData().isDummy()) { + pos2 = median(coords2); + } + } + //System.out.println("coords=" + coords); + //System.out.println("coords2=" + coords2); + + return pos - pos2; + } + + + private boolean intersect(int v1, int w1, int v2, int w2) { + if(v1 >= v2 && v1 < v2 + w2) return true; + if(v1 + w1 > v2 && v1 + w1 < v2 + w2) return true; + if(v1 < v2 && v1 + w1 > v2) return true; + return false; + } + + private boolean intersect(Node n1, Node n2) { + return intersect(n1.getData().getCoordinate(), n1.getData().getWidth() + OFFSET, n2.getData().getCoordinate(), n2.getData().getWidth() + OFFSET); + } + + private void tryAdding(List> alreadyAssigned, Node node, int pos) { + + boolean doesIntersect = false; + node.getData().setCoordinate(pos); + for(Node n : alreadyAssigned) { + if(intersect(node, n)) { + doesIntersect = true; + break; + } + } + + if(!doesIntersect) { + + // Everything find, just place the node + int insertPosition = 0; + int z = 0; + for(Node n : alreadyAssigned) { + if(pos > n.getData().getCoordinate()) { + insertPosition = z+1; + } else { + break; + } + z++; + } + + if(ASSERT) assert !findOverlap(alreadyAssigned, node); + alreadyAssigned.add(insertPosition, node); + + } else { + + assert alreadyAssigned.size() > 0; + + // Search for alternative location + int minOffset = Integer.MAX_VALUE; + int minIndex = -1; + int minPos = 0; + int w = node.getData().getWidth() + OFFSET; + + // Try bottom-most + minIndex = 0; + minPos = alreadyAssigned.get(0).getData().getCoordinate() - w; + minOffset = Math.abs(minPos - pos); + + // Try top-most + Node lastNode = alreadyAssigned.get(alreadyAssigned.size() - 1); + int lastPos = lastNode.getData().getCoordinate() + lastNode.getData().getWidth() + OFFSET; + int lastOffset = Math.abs(lastPos - pos); + if(lastOffset < minOffset) { + minPos = lastPos; + minOffset = lastOffset; + minIndex = alreadyAssigned.size(); + } + + // Try between + for(int i=0; i curNode = alreadyAssigned.get(i); + Node nextNode = alreadyAssigned.get(i+1); + + int start = curNode.getData().getCoordinate() + curNode.getData().getWidth() + OFFSET; + int end = nextNode.getData().getCoordinate() - OFFSET; + + if(end - start >= node.getData().getWidth()) { + // Node could fit here + int cand1 = start; + int cand2 = end - node.getData().getWidth(); + int off1 = Math.abs(cand1 - pos); + int off2 = Math.abs(cand2 - pos); + if(off1 < minOffset) { + minPos = cand1; + minOffset = off1; + minIndex = i+1; + } + + if(off2 < minOffset) { + minPos = cand2; + minOffset = off2; + minIndex = i+1; + } + } + } + + assert minIndex != -1; + node.getData().setCoordinate(minPos); + if(ASSERT) assert !findOverlap(alreadyAssigned, node); + alreadyAssigned.add(minIndex, node); + } + + } + + private boolean findOverlap(List> nodes, Node node) { + + for(Node n1 : nodes) { + if(intersect(n1, node)) { + return true; + } + } + + return false; + } + + private int evaluateSolution() { + + int sum = 0; + for(Edge e : graph.getEdges()) { + Node source = e.getSource(); + Node dest = e.getDest(); + int offset = 0; + offset = Math.abs(source.getData().getCoordinate() - dest.getData().getCoordinate()); + sum += offset; + } + + return sum; + } + + private void crossingReduction(ArrayList> layers[]) { + + for(int i=0; i> curNodes = layers[i]; + ArrayList> nextNodes = layers[i+1]; + for(Node n : curNodes) { + for(Node succ : n.getSuccessors()) { + if(ASSERT) assert nextNodes.contains(succ); + nextNodes.remove(succ); + nextNodes.add(succ); + } + } + + } + + } + + + + private void removeCycles(Set> rootNodes) { + final List> reversedEdges = new ArrayList>(); + + + int removedCount = 0; + int reversedCount = 0; + + Graph.DFSTraversalVisitor visitor = graph.new DFSTraversalVisitor() { + public boolean visitEdge(Edge e, boolean backEdge) { + if(backEdge) { + if(ASSERT) assert !reversedEdges.contains(e); + reversedEdges.add(e); + //System.out.println("Back edge: " + e); + e.getData().setReversed(!e.getData().isReversed()); + } + + return e.getData().isImportant(); + } + }; + Set> nodes = new HashSet>(); + nodes.addAll(rootNodes); + + assert nodes.size() > 0; + + this.graph.traverseDFS(nodes, visitor); + + for(Edge e : reversedEdges) { + if(e.isSelfLoop()) { + e.remove(); + removedCount++; + } else { + e.reverse(); + reversedCount++; + } + } + } + + private boolean checkRemoveCycles() { + return !graph.hasCycles(); + } + + // Only used by assignLayers + private int maxLayer; + + private void assignLayers(Set> rootNodes, Set firstLayerHints, Set lastLayerHints) { + this.maxLayer = -1; + for(Node n : graph.getNodes()) { + n.getData().setLayer(-1); + } + + Graph.BFSTraversalVisitor traverser = graph.new BFSTraversalVisitor() { + public void visitNode(Node n, int depth) { + if(depth > n.getData().getLayer()) { + n.getData().setLayer(depth); + maxLayer = Math.max(maxLayer, depth); + } + } + }; + + for(Node n : rootNodes) { + if(n.getData().getLayer() == -1) { + this.graph.traverseBFS(n, traverser, true); + } + } + + for(Vertex v : firstLayerHints) { + assert nodeMap.containsKey(v); + nodeMap.get(v).getData().setLayer(0); + } + + for(Vertex v : lastLayerHints) { + assert nodeMap.containsKey(v); + nodeMap.get(v).getData().setLayer(maxLayer); + } + } + + private boolean checkAssignLayers() { + + for(Edge e : graph.getEdges()) { + Node source = e.getSource(); + Node dest = e.getDest(); + + + if(source.getData().getLayer() >= dest.getData().getLayer()) { + return false; + } + } + int maxLayer = 0; + for(Node n : graph.getNodes()) { + assert n.getData().getLayer() >= 0; + if(n.getData().getLayer() > maxLayer) { + maxLayer = n.getData().getLayer(); + } + } + + int countPerLayer[] = new int[maxLayer+1]; + for(Node n : graph.getNodes()) { + countPerLayer[n.getData().getLayer()]++; + } + + if(TRACE) System.out.println("Number of layers: " + maxLayer); + return true; + } + + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/InterClusterConnection.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/InterClusterConnection.java new file mode 100644 index 000000000000..45b58e76a500 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/InterClusterConnection.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.graphanalyzer.positioning; + +import at.ssw.positionmanager.Link; +import at.ssw.positionmanager.Port; +import java.awt.Point; +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Thomas Wuerthinger + */ +public class InterClusterConnection implements Link{ + + private Port inputSlot; + private Port outputSlot; + private List intermediatePoints; + private ClusterInputSlotNode inputSlotNode; + private ClusterOutputSlotNode outputSlotNode; + + public InterClusterConnection(ClusterOutputSlotNode outputSlotNode, ClusterInputSlotNode inputSlotNode) { + this.outputSlotNode = outputSlotNode; + this.inputSlotNode = inputSlotNode; + this.inputSlot = inputSlotNode.getInputSlot(); + this.outputSlot = outputSlotNode.getOutputSlot(); + intermediatePoints = new ArrayList(); + } + + public ClusterOutputSlotNode getOutputSlotNode() { + return outputSlotNode; + } + + public Port getTo() { + return inputSlot; + } + + public Port getFrom() { + return outputSlot; + } + + public void setControlPoints(List p) { + this.intermediatePoints = p; + } + + public List getControlPoints() { + return intermediatePoints; + } + + public String toString() { + return "InterClusterConnection[from=" + getFrom() + ", to=" + getTo() + "]"; + } +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/LineGenerator.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/LineGenerator.java new file mode 100644 index 000000000000..6f9971d4cccb --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/LineGenerator.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.graphanalyzer.positioning; + +import java.awt.Point; +import java.util.List; + +/** + * + * @author Thomas Wuerthinger + */ +public interface LineGenerator { + + public List createLine(List line, Point startRefPoint, Point endRefPoint); + public String iconResource(); + public String getName(); + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/Node.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/Node.java new file mode 100644 index 000000000000..6b4c7b78148b --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/Node.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.graphanalyzer.positioning; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * + * @author Thomas Wuerthinger + */ +public class Node { + + private N data; + private List> inEdges; + private List> outEdges; + private boolean visited; + private boolean active; + private boolean reachable; + private Graph graph; + + protected boolean isVisited() { + return visited; + } + + protected void setVisited(boolean b) { + visited = b; + } + + protected boolean isReachable() { + return reachable; + } + + protected void setReachable(boolean b) { + reachable = b; + } + + protected boolean isActive() { + return active; + } + + protected void setActive(boolean b) { + active = b; + } + + public int getInDegree() { + return getInDegree(true); + } + + public int getInDegree(boolean countSelfLoops) { + if(countSelfLoops) { + return inEdges.size(); + } else { + int cnt = 0; + for(Edge e : inEdges) { + if(e.getSource() != this) { + cnt++; + } + } + return cnt; + } + } + + public int getOutDegree() { + return outEdges.size(); + } + + protected Node(Graph graph, N data) { + setData(data); + this.graph = graph; + inEdges = new ArrayList>(); + outEdges = new ArrayList>(); + } + + protected void addInEdge(Edge e) { + assert !inEdges.contains(e); + inEdges.add(e); + } + + protected void addOutEdge(Edge e) { + assert !outEdges.contains(e); + outEdges.add(e); + } + + protected void removeInEdge(Edge e) { + assert inEdges.contains(e); + inEdges.remove(e); + } + + protected void removeOutEdge(Edge e) { + assert outEdges.contains(e); + outEdges.remove(e); + } + + public List> getInEdges() { + return Collections.unmodifiableList(inEdges); + } + + public List> getOutEdges() { + return Collections.unmodifiableList(outEdges); + } + + public List> getSuccessors() { + ArrayList> succ = new ArrayList>(); + for(Edge e : getOutEdges()) { + Node n = e.getDest(); + if(!succ.contains(n)) { + succ.add(n); + } + } + return succ; + } + + public List> getPredecessors() { + ArrayList> pred = new ArrayList>(); + for(Edge e : getInEdges()) { + Node n = e.getSource(); + if(!pred.contains(n)) { + pred.add(n); + } + } + return pred; + } + + public N getData() { + return data; + } + + public void setData(N d) { + data = d; + } + + public String toString() { + return "Node: " + data; + } + +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/SplineLineGenerator.java b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/SplineLineGenerator.java new file mode 100644 index 000000000000..a1005dd0e40e --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/java/at/ssw/graphanalyzer/positioning/SplineLineGenerator.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.graphanalyzer.positioning; + +import java.awt.Point; +import java.util.List; + +/** + * + * @author Thomas Wuerthinger + */ +public class SplineLineGenerator implements LineGenerator{ + + public SplineLineGenerator() { + } + + public List createLine(List line, Point startRefPoint, Point endRefPoint) { + return Curves.bsplines(line, startRefPoint, endRefPoint, 30); + } + + public String iconResource() { + return "at/ssw/graphanalyzer/coordinator/images/spline_lines.gif"; + } + + public String getName() { + return "Splines"; + } +} diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..842e6ac1b39d --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/nbm/manifest.mf @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.dataflow +OpenIDE-Module-Localizing-Bundle: at/ssw/dataflow/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/GraphLayoutImpl/src/main/resources/at/ssw/dataflow/Bundle.properties b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/resources/at/ssw/dataflow/Bundle.properties new file mode 100644 index 000000000000..46629a573cb9 --- /dev/null +++ b/visualizer/C1Visualizer/GraphLayoutImpl/src/main/resources/at/ssw/dataflow/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=Graph Layout Impl diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/pom.xml b/visualizer/C1Visualizer/IntermediateCodeEditor/pom.xml new file mode 100644 index 000000000000..ce658f780088 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeEditor/pom.xml @@ -0,0 +1,123 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + IntermediateCodeEditor + 1.14-SNAPSHOT + nbm + IntermediateCodeEditor + + UTF-8 + + + + at.ssw.visualizer + VisualizerUI + ${project.version} + + + at.ssw.visualizer + CompilationModel + ${project.version} + + + at.ssw.visualizer + TextEditor + ${project.version} + + + org.netbeans.api + org-netbeans-modules-editor + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-fold + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-lib + ${netbeans.version} + + + org.netbeans.api + org-openide-awt + ${netbeans.version} + + + org.netbeans.api + org-openide-nodes + ${netbeans.version} + + + org.netbeans.api + org-openide-text + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + org.netbeans.api + org-openide-util-ui + ${netbeans.version} + + + org.netbeans.api + org-openide-windows + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.ir + at.ssw.visualizer.ir.model + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/IREditor.java b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/IREditor.java new file mode 100644 index 000000000000..b2948599596d --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/IREditor.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir; + +import at.ssw.visualizer.texteditor.Editor; +import org.openide.windows.CloneableTopComponent; + +/** + * The actual editor component for displaying IR text. + * + * @author Bernhard Stiftner + * @author Christian Wimmer + * @author Alexander Reder + */ +public class IREditor extends Editor { + + public IREditor(IREditorSupport support) { + super(support); + } + + @Override + protected CloneableTopComponent createClonedObject() { + IREditor editor = new IREditor((IREditorSupport) cloneableEditorSupport()); + editor.setActivatedNodes(getActivatedNodes()); + return editor; + } +} diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/IREditorKit.java b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/IREditorKit.java new file mode 100644 index 000000000000..536e32d03962 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/IREditorKit.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir; + +import at.ssw.visualizer.ir.model.IRScanner; +import at.ssw.visualizer.texteditor.EditorKit; +import java.util.Collection; +import javax.swing.Action; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import javax.swing.text.TextAction; +import org.netbeans.editor.Syntax; +import org.netbeans.modules.editor.NbEditorKit; +import org.openide.util.Lookup; +import org.openide.util.actions.CallableSystemAction; +import org.openide.util.lookup.Lookups; + +/** + * The IR Editor Kit, providing the syntax support. + * + * @author Bernhard Stiftner + * @author Christian Wimmer + */ +public class IREditorKit extends EditorKit { + + @Override + public String getContentType() { + return IREditorSupport.MIME_TYPE; + } + + @Override + public Syntax createSyntax(Document doc) { + return new IRScanner(); + } + + @Override + protected Action[] getCustomActions() { + return TextAction.augmentList(super.getCustomActions(), new Action[]{ + new GenerateFoldPopupAction() + }); + } + + public static class GenerateFoldPopupAction extends NbEditorKit.GenerateFoldPopupAction { + + @Override + public JMenuItem getPopupMenuItem(JTextComponent target) { + JMenuItem menu = super.getPopupMenuItem(target); + menu.add(new JPopupMenu.Separator()); + Lookup lookup = Lookups.forPath("IREditorFolding"); + Collection foldingActions = lookup.lookupAll(CallableSystemAction.class); + for (CallableSystemAction csa : foldingActions) { + menu.add(csa.getMenuPresenter()); + } + return menu; + } + } +} diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/IREditorSupport.java b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/IREditorSupport.java new file mode 100644 index 000000000000..a7a8dd398e18 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/IREditorSupport.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir; + +import at.ssw.visualizer.ir.icons.Icons; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.ir.model.IRTextBuilder; +import at.ssw.visualizer.texteditor.EditorSupport; +import org.openide.text.CloneableEditor; +import org.openide.util.ImageUtilities; + +/** + * Connects a ControlFlowGraph to a NetBeans text editor. + * + * @author Bernhard Stiftner + * @author Christian Wimmer + * @author Alexander Reder + */ +public class IREditorSupport extends EditorSupport { + + public static final String MIME_TYPE = "text/x-compilation-ir"; + + public IREditorSupport(ControlFlowGraph cfg) { + super(cfg); + this.text = new IRTextBuilder().buildDocument(cfg); + } + + @Override + public String getMimeType() { + return MIME_TYPE; + } + + @Override + protected CloneableEditor createCloneableEditor() { + return new IREditor(this); + } + + + @Override + protected void initializeCloneableEditor(CloneableEditor editor) { + super.initializeCloneableEditor(editor); + editor.setIcon(ImageUtilities.loadImage(Icons.IR)); + } + +} diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/action/CollapseAllAction.java b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/action/CollapseAllAction.java new file mode 100644 index 000000000000..b18bc44c23b1 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/action/CollapseAllAction.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir.action; + +import at.ssw.visualizer.ir.icons.Icons; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.fold.FoldHierarchy; +import org.netbeans.api.editor.fold.FoldUtilities; +import org.netbeans.editor.Utilities; +import org.openide.util.HelpCtx; +import org.openide.util.actions.CallableSystemAction; + +public final class CollapseAllAction extends CallableSystemAction { + public void performAction() { + JTextComponent comp = Utilities.getFocusedComponent(); + FoldHierarchy hierarchy = FoldHierarchy.get(comp); + FoldUtilities.collapseAll(hierarchy); + } + + public String getName() { + return "Collapse All"; + } + + @Override + protected String iconResource() { + return Icons.COLLAPSE_ALL; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + protected boolean asynchronous() { + return false; + } + +} diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/action/ExpandAllAction.java b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/action/ExpandAllAction.java new file mode 100644 index 000000000000..eb5514564455 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/action/ExpandAllAction.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir.action; + +import at.ssw.visualizer.ir.icons.Icons; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.fold.FoldHierarchy; +import org.netbeans.api.editor.fold.FoldUtilities; +import org.netbeans.editor.Utilities; +import org.openide.util.HelpCtx; +import org.openide.util.actions.CallableSystemAction; + +public final class ExpandAllAction extends CallableSystemAction { + public void performAction() { + JTextComponent comp = Utilities.getFocusedComponent(); + FoldHierarchy hierarchy = FoldHierarchy.get(comp); + FoldUtilities.expandAll(hierarchy); + } + + public String getName() { + return "Expand All"; + } + + @Override + protected String iconResource() { + return Icons.EXPAND_ALL; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + protected boolean asynchronous() { + return false; + } + +} diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/action/ExpandHIRAction.java b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/action/ExpandHIRAction.java new file mode 100644 index 000000000000..129451505d30 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/action/ExpandHIRAction.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir.action; + +import at.ssw.visualizer.ir.icons.Icons; +import at.ssw.visualizer.ir.model.IRTextBuilder; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.fold.FoldHierarchy; +import org.netbeans.api.editor.fold.FoldUtilities; +import org.netbeans.editor.Utilities; +import org.openide.util.HelpCtx; +import org.openide.util.actions.CallableSystemAction; + +public final class ExpandHIRAction extends CallableSystemAction { + public void performAction() { + JTextComponent comp = Utilities.getFocusedComponent(); + FoldHierarchy hierarchy = FoldHierarchy.get(comp); + FoldUtilities.collapseAll(hierarchy); + FoldUtilities.expand(hierarchy, IRTextBuilder.KIND_HIR); + FoldUtilities.expand(hierarchy, IRTextBuilder.KIND_STATE_WITH_PHIS); + FoldUtilities.expand(hierarchy, IRTextBuilder.KIND_BLOCK); + } + + public String getName() { + return "Expand HIR"; + } + + @Override + protected String iconResource() { + return Icons.EXPAND_HIR; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + protected boolean asynchronous() { + return false; + } + +} diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/action/ExpandLIRAction.java b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/action/ExpandLIRAction.java new file mode 100644 index 000000000000..35f1d1e54f5f --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/action/ExpandLIRAction.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir.action; + +import at.ssw.visualizer.ir.icons.Icons; +import at.ssw.visualizer.ir.model.IRTextBuilder; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.fold.FoldHierarchy; +import org.netbeans.api.editor.fold.FoldUtilities; +import org.netbeans.editor.Utilities; +import org.openide.util.HelpCtx; +import org.openide.util.actions.CallableSystemAction; + +public final class ExpandLIRAction extends CallableSystemAction { + public void performAction() { + JTextComponent comp = Utilities.getFocusedComponent(); + FoldHierarchy hierarchy = FoldHierarchy.get(comp); + FoldUtilities.collapseAll(hierarchy); + FoldUtilities.expand(hierarchy, IRTextBuilder.KIND_LIR); + FoldUtilities.expand(hierarchy, IRTextBuilder.KIND_BLOCK); + } + + public String getName() { + return "Expand LIR"; + } + + @Override + protected String iconResource() { + return Icons.EXPAND_LIR; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + protected boolean asynchronous() { + return false; + } + +} diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/action/ShowIREditorAction.java b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/action/ShowIREditorAction.java new file mode 100644 index 000000000000..97761ca9f861 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/action/ShowIREditorAction.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir.action; + +import at.ssw.visualizer.core.focus.Focus; +import at.ssw.visualizer.ir.IREditor; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.ir.IREditorSupport; +import at.ssw.visualizer.ir.icons.Icons; +import org.openide.nodes.Node; +import org.openide.util.HelpCtx; +import org.openide.util.actions.CookieAction; + +/** + * Opens the IR editor. + * + * @author Bernhard Stiftner + */ +public final class ShowIREditorAction extends CookieAction { + protected void performAction(Node[] activatedNodes) { + ControlFlowGraph cfg = activatedNodes[0].getLookup().lookup(ControlFlowGraph.class); + if (!Focus.findEditor(IREditor.class, cfg)) { + IREditorSupport editor = new IREditorSupport(cfg); + editor.open(); + } + } + + @Override + protected boolean enable(Node[] activatedNodes) { + if (!super.enable(activatedNodes)) { + return false; + } + ControlFlowGraph cfg = activatedNodes[0].getLookup().lookup(ControlFlowGraph.class); + return cfg.hasHir() || cfg.hasState() || cfg.hasLir(); + } + + public String getName() { + return "Open Intermediate Representation"; + } + + @Override + protected String iconResource() { + return Icons.IR; + } + + protected int mode() { + return CookieAction.MODE_EXACTLY_ONE; + } + + protected Class[] cookieClasses() { + return new Class[]{ControlFlowGraph.class}; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + protected boolean asynchronous() { + return false; + } +} diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/icons/Icons.java b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/icons/Icons.java new file mode 100644 index 000000000000..c8397cb97d27 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/icons/Icons.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir.icons; + +/** + * + * @author Christian Wimmer + */ +public class Icons { + private static final String PATH = "at/ssw/visualizer/ir/icons/"; + + public static final String IR = PATH + "ir.gif"; + public static final String EXPAND_HIR = PATH + "expandhir.gif"; + public static final String EXPAND_LIR = PATH + "expandlir.gif"; + public static final String EXPAND_ALL = PATH + "expandall.gif"; + public static final String COLLAPSE_ALL = PATH + "collapseall.gif"; + + private Icons() { + } +} diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/model/IRScanner.java b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/model/IRScanner.java new file mode 100644 index 000000000000..0490a19b7c33 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/model/IRScanner.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir.model; + +import at.ssw.visualizer.texteditor.model.Scanner; +import org.netbeans.editor.TokenID; + +/** + * Splits the textual intermediate representation into tokens for HIR and LIR operands. + * + * @author Christian Wimmer + */ +public class IRScanner extends Scanner { + public IRScanner() { + super("\n\r\t .,;()[]", IRTokenContext.contextPath); + } + + private boolean isBlock() { + return expectChar('B') && expectChar(DIGIT) && expectChars(DIGIT) && expectEnd(); + } + + private boolean isHir() { + return expectChar(LETTER) && expectChar(DIGIT) && expectChars(DIGIT) && expectEnd(); + } + + private boolean isLir() { + return expectChar(LETTER) && skipUntil('|') && expectChars(LETTER) + && isReferenceOrEmpty() && expectEnd(); + } + + private boolean isReferenceOrEmpty() { + if (ch != '[') + return true; + readNext(); + return expectChars(REFERENCE_CHARS) && expectChar(']'); + } + + @Override + protected TokenID parseToken() { + findTokenBegin(); + if (ch == EOF && offset + 1 >= stopOffset) { + // only except EOF if we are at the end of the buffer + return IRTokenContext.EOF_TOKEN; + } else if (isWhitespace()) { + return IRTokenContext.WHITESPACE_TOKEN; + } else if (isBlock()) { + return IRTokenContext.BLOCK_TOKEN; + } else if (isHir()) { + return IRTokenContext.HIR_TOKEN; + } else if (isLir()) { + return IRTokenContext.LIR_TOKEN; + } else { + readToWhitespace(); + return IRTokenContext.OTHER_TOKEN; + } + } + +} diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/model/IRTextBuilder.java b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/model/IRTextBuilder.java new file mode 100644 index 000000000000..fe64a3dd0d69 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/model/IRTextBuilder.java @@ -0,0 +1,507 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir.model; + +import at.ssw.visualizer.ir.IREditorSupport; +import at.ssw.visualizer.model.Compilation; +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.model.cfg.IRInstruction; +import at.ssw.visualizer.model.cfg.State; +import at.ssw.visualizer.model.cfg.StateEntry; +import at.ssw.visualizer.texteditor.model.BlockRegion; +import at.ssw.visualizer.texteditor.model.FoldingRegion; +import at.ssw.visualizer.texteditor.model.HoverParser; +import at.ssw.visualizer.texteditor.model.Text; +import at.ssw.visualizer.texteditor.model.TextBuilder; +import at.ssw.visualizer.texteditor.model.TextRegion; +import org.netbeans.api.editor.fold.FoldType; +import org.netbeans.editor.TokenID; + +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Christian Wimmer + */ +public class IRTextBuilder extends TextBuilder { + + public static final FoldType KIND_BLOCK = new FoldType("..."); + public static final FoldType KIND_STATE_WITH_PHIS = new FoldType("(State)"); + public static final FoldType KIND_STATE_WITHOUT_PHIS = new FoldType("(State)"); + public static final FoldType KIND_HIR = new FoldType("(HIR)"); + public static final FoldType KIND_LIR = new FoldType("(LIR)"); + public static final FoldType KIND_MULTILINE = new FoldType("..."); + private String[] hirColumnNames; + private int[] hirColumnStarts; + private String[] lirColumnNames; + private int[] lirColumnStarts; + + public IRTextBuilder() { + super(); + scanner = new IRScanner(); + } + + public String buildState(ControlFlowGraph cfg, BasicBlock[] blocks) { + defineColumns(cfg); + + Set blockSet = new HashSet(Arrays.asList(blocks)); + for (BasicBlock block : cfg.getBasicBlocks()) { + if (block.hasState() && blockSet.contains(block)) { + appendBlockDetails(block); + text.append("\n"); + appendStates(block); + text.append("\n"); + } + } + if (text.length() == 0) { + return "No State available\n"; + } + return text.toString(); + } + + public String buildHir(ControlFlowGraph cfg, BasicBlock[] blocks) { + defineColumns(cfg); + + Set blockSet = new HashSet(Arrays.asList(blocks)); + for (BasicBlock block : cfg.getBasicBlocks()) { + if (block.hasHir() && blockSet.contains(block)) { + appendBlockDetails(block); + text.append("\n"); + appendHir(block); + text.append("\n"); + } + } + if (text.length() == 0) { + return "No HIR available\n"; + } + return text.toString(); + } + + public String buildLir(ControlFlowGraph cfg, BasicBlock[] blocks) { + defineColumns(cfg); + + Set blockSet = new HashSet(Arrays.asList(blocks)); + for (BasicBlock block : cfg.getBasicBlocks()) { + if (block.hasLir() && blockSet.contains(block)) { + appendBlockDetails(block); + text.append("\n"); + appendLir(block); + text.append("\n"); + } + } + if (text.length() == 0) { + return "No LIR available\n"; + } + return text.toString(); + } + + public Text buildDocument(ControlFlowGraph cfg) { + Compilation compilation = cfg.getCompilation(); + DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM); + + text.append(compilation.getMethod()).append("\n"); + text.append(dateFormat.format(compilation.getDate())).append("\n"); + text.append(cfg.getName()).append("\n\n"); + + defineColumns(cfg); + + for (BasicBlock block : cfg.getBasicBlocks()) { + appendBlock(cfg, block); + } + return buildText(cfg, IREditorSupport.MIME_TYPE); + } + + protected void defineColumns(ControlFlowGraph cfg) { + ArrayList hirColumnNames = new ArrayList(); + ArrayList hirColumnWidths = new ArrayList(); + ArrayList lirColumnNames = new ArrayList(); + ArrayList lirColumnWidths = new ArrayList(); + + for (BasicBlock block : cfg.getBasicBlocks()) { + if (cfg.hasHir()) { + defineColumns(block.getHirInstructions(), hirColumnNames, hirColumnWidths); + } + if (cfg.hasLir()) { + defineColumns(block.getLirOperations(), lirColumnNames, lirColumnWidths); + } + } + + this.hirColumnNames = hirColumnNames.toArray(new String[hirColumnNames.size()]); + this.hirColumnStarts = new int[this.hirColumnNames.length]; + int start = 1; + for (int i = 0; i < this.hirColumnStarts.length; i++) { + this.hirColumnStarts[i] = start; + start += hirColumnWidths.get(i) + 2; + } + this.lirColumnNames = lirColumnNames.toArray(new String[lirColumnNames.size()]); + this.lirColumnStarts = new int[this.lirColumnNames.length]; + start = 1; + for (int i = 0; i < this.lirColumnStarts.length; i++) { + this.lirColumnStarts[i] = start; + start += lirColumnWidths.get(i) + 2; + } + + } + + protected void defineColumns(List instructions, List columnNames, List columnWidths) { + for (IRInstruction instr : instructions) { + int prevIdx = -1; + for (String name : instr.getNames()) { + int idx = columnNames.indexOf(name); + + int width = Math.min(HoverParser.firstLine(instr.getValue(name)).length(), 15); + + if (idx == -1) { + width = Math.max(width, name.length()); + columnNames.add(prevIdx + 1, name); + columnWidths.add(prevIdx + 1, width); + prevIdx = prevIdx + 1; + } else { + int oldWidth = columnWidths.get(idx); + if (width > oldWidth) { + columnWidths.set(idx, width); + } + prevIdx = idx; + } + } + } + } + + protected void buildHighlighting() { + // scan the entire content string because it is easier than + // computing information during construction of the string + HashMap> highlightingLists = new HashMap>(); + scanner.setText(text.toString(), 0, text.length()); + TokenID token = scanner.nextToken(); + while (token != null && token.getNumericID() != IRTokenContext.EOF_TOKEN_ID) { + if (token.getNumericID() == IRTokenContext.HIR_TOKEN_ID || token.getNumericID() == IRTokenContext.LIR_TOKEN_ID || token.getNumericID() == IRTokenContext.BLOCK_TOKEN_ID) { + String key = scanner.getTokenString(); + List list = highlightingLists.get(key); + if (list == null) { + list = new ArrayList(); + highlightingLists.put(key, list); + } + list.add(new TextRegion(scanner.getTokenOffset(), scanner.getOffset())); //getTokenStart() gteTokenEnd() + } + token = scanner.nextToken(); + } + + for (String key : highlightingLists.keySet()) { + List list = highlightingLists.get(key); + highlighting.put(key, list.toArray(new TextRegion[list.size()])); + } + } + + private void appendBlock(ControlFlowGraph cfg, BasicBlock block) { + int start = text.length(); + List blockFoldings = new ArrayList(); + + appendBlockDetails(block); + int bodyStart = text.length(); + text.append("\n"); + + if (block.hasState()) { + blockFoldings.add(appendStates(block)); + } + if (block.hasHir()) { + blockFoldings.add(appendHir(block)); + } + if (block.hasLir()) { + blockFoldings.add(appendLir(block)); + } + + // record foldings (no nested foldings if only one detail block is present) + if (blockFoldings.size() > 0) { + text.append(" \n"); + foldingRegions.add(new FoldingRegion(KIND_BLOCK, bodyStart, text.length() - 1, false)); + if (blockFoldings.size() > 1) { + for (FoldingRegion folding : blockFoldings) { + foldingRegions.add(folding); + } + } + } + + // record definition and hyperlink target + recordBlock(block, new TextRegion(start, start + block.getName().length())); + + // record block boundary information + blocks.put(block, new BlockRegion(block, start, text.length(), start, start + block.getName().length())); + } + + private void appendBlockDetails(StringBuilder sb, BasicBlock block) { + sb.append(blockDetails(block)); + } + + private void recordBlock(BasicBlock block, TextRegion hyperlinkTarget) { + StringBuilder blockText = new StringBuilder(); + appendBlockDetails(blockText, block); + recordDefinition(block.getName(), blockText.toString(), hyperlinkTarget); + } + + private FoldingRegion appendStates(BasicBlock block) { + boolean hasPhiOperands = false; + int start = text.length(); + + if (block.hasState()) { + for (State state : block.getStates()) { + hasPhiOperands |= appendState(block, state); + } + } + + return new FoldingRegion(hasPhiOperands ? KIND_STATE_WITH_PHIS : KIND_STATE_WITHOUT_PHIS, start, text.length(), !hasPhiOperands); + } + + private boolean appendState(BasicBlock block, State state) { + boolean hasPhiOperands = false; + + text.append(" ").append(state.getKind()).append(" size ").append(state.getSize()); + if (state.getMethod().length() > 0) { + text.append(" [").append(state.getMethod()).append("]"); + } + text.append("\n"); + + for (StateEntry entry : state.getEntries()) { + int lineStart = text.length(); + text.append(" "); + append(entry.getIndex(), 5); + append(entry.getName(), 5); + + if (appendPhiOperands(text, block, entry)) { + if (entry.getOperand() != null) { + text.append(" - ").append(entry.getOperand()); + } + + hasPhiOperands = true; + TextRegion hyperlinkTarget = new TextRegion(lineStart, text.length()); + recordPhiFunction(block, entry, hyperlinkTarget); + } + text.append("\n"); + } + + return hasPhiOperands; + } + + private void recordPhiFunction(BasicBlock block, StateEntry entry, TextRegion hyperlinkTarget) { + StringBuilder sb = new StringBuilder(); + sb.append(block.getName()).append(" - ").append(entry.getName()); + if (entry.getOperand() != null) { + sb.append(" ").append(entry.getOperand()); + } + sb.append(" : "); + + int start = sb.length(); + appendPhiOperands(sb, block, entry); + String s = sb.toString(); + + recordDefinition(entry.getName(), s, hyperlinkTarget); + if (entry.getOperand() != null) { + recordDefinition(entry.getOperand().substring(1, entry.getOperand().length() - 1), s, hyperlinkTarget); + } + recordUses(s, start, s.length() - start); + } + + private boolean appendPhiOperands(StringBuilder sb, BasicBlock block, StateEntry entry) { + if (entry.hasPhiOperands()) { + sb.append("["); + appendList(sb, "", entry.getPhiOperands()); + sb.append("]"); + return true; + } else if (block.getPredecessors().size() == 0) { + sb.append("[method parameter]"); + return true; + } else { + return false; + } + } + + private FoldingRegion appendHir(BasicBlock block) { + int start = text.length(); + appendColumnHeader(hirColumnNames, hirColumnStarts, " (HIR)"); + for (IRInstruction instruction : block.getHirInstructions()) { + int lineStart = text.length(); + appendColumn(instruction, hirColumnNames, hirColumnStarts); + TextRegion hyperlinkTarget = new TextRegion(lineStart, text.length() - 1); + recordHir(block, instruction, hyperlinkTarget); + } + + return new FoldingRegion(KIND_HIR, start, text.length(), false); + } + + protected void appendColumnHeader(String[] columnNames, int[] columnStarts, String descr) { + int lineStart = text.length(); + for (int i = 0; i < columnNames.length; i++) { + fillTo(columnStarts[i], lineStart, '_'); + text.append(columnNames[i]); + } + fillTo(80, lineStart, '_'); + text.append(descr).append("\n"); + } + + protected void appendColumn(IRInstruction instruction, String[] columnNames, int[] columnStarts) { + int lineStart = text.length(); + int foldStart = -1; + for (int i = 0; i < columnNames.length; i++) { + fillTo(columnStarts[i], lineStart, ' '); + String val = instruction.getValue(columnNames[i]); + if (val != null) { + HoverParser p = new HoverParser(val); + + while (p.hasNext()) { + int start = text.length(); + text.append(p.next()); + if (p.getHover() != null) { + regionHovers.put(new TextRegion(start, text.length()), p.getHover()); + } + if (p.isNewLine()) { + if (foldStart == -1) { + foldStart = text.length() - 1; + } + lineStart = text.length(); + fillTo(columnStarts[i], lineStart, ' '); + } + } + } + } + if (foldStart != -1) { + foldingRegions.add(new FoldingRegion(KIND_MULTILINE, foldStart, text.length(), false)); + } + text.append("\n"); + } + + private void recordHir(BasicBlock block, IRInstruction hir, TextRegion hyperlinkTarget) { + String irName = hir.getValue(IRInstruction.HIR_NAME); + String irText = HoverParser.firstLine(hir.getValue(IRInstruction.HIR_TEXT)); + String irOperand = hir.getValue(IRInstruction.HIR_OPERAND); + + StringBuilder sb = new StringBuilder(); + sb.append(block.getName()).append(" - ").append(irName); + if (irOperand != null) { + sb.append(" ").append(irOperand); + } + sb.append(" : "); + + int start = sb.length(); + sb.append(irText); + String s = sb.toString(); + + recordDefinition(irName, s, hyperlinkTarget); + if (irOperand != null) { + recordDefinition(irOperand.substring(1, irOperand.length() - 1), s, hyperlinkTarget); + } + recordUses(s, start, s.length() - start); + } + + private FoldingRegion appendLir(BasicBlock block) { + int start = text.length(); + appendColumnHeader(lirColumnNames, lirColumnStarts, " (LIR)"); + for (IRInstruction instruction : block.getLirOperations()) { + appendColumn(instruction, lirColumnNames, lirColumnStarts); + recordLir(block, instruction); + } + + return new FoldingRegion(KIND_LIR, start, text.length() - 1, false); + } + + private void recordLir(BasicBlock block, IRInstruction lir) { + String irNumber = lir.getValue(IRInstruction.LIR_NUMBER); + String irText = HoverParser.firstLine(lir.getValue(IRInstruction.LIR_TEXT)); + StringBuilder sb = new StringBuilder(); + sb.append(block.getName()).append(" - ").append(irNumber).append(" "); + + int start = sb.length(); + sb.append(irText); + + recordUses(sb.toString(), start, sb.length() - start); + } + + private void fillTo(int pos, int lineStart, char ch) { + for (int i = pos + lineStart - text.length(); i > 0; i--) { + text.append(ch); + } + } + + private void append(int value, int minLen) { + int oldLen = text.length(); + text.append(value); + + text.append(' '); + for (int i = oldLen + minLen - text.length(); i > 0; i--) { + text.append(' '); + } + } + + private void append(String value, int minLen) { + int oldLen = text.length(); + text.append(value); + text.append(' '); + for (int i = oldLen + minLen - text.length(); i > 0; i--) { + text.append(' '); + } + } + + private void appendList(StringBuilder sb, String prefix, List values) { + for (String value : values) { + sb.append(prefix).append(value); + prefix = ","; + } + } + + private void recordDefinition(String key, String value, TextRegion hyperlinkTarget) { + if (hoverDefinitions.containsKey(key)) { + System.out.println("WARNING: duplicate definition of '" + key + "': '" + value + "' and '" + hoverDefinitions.get(key) + "'"); + } + + hoverKeys.add(key); + hoverDefinitions.put(key, value); + hyperlinks.put(key, hyperlinkTarget); + } + + private void recordUse(String key, String value) { + hoverKeys.add(key); + List list = hoverReferences.get(key); + if (list == null) { + list = new ArrayList(); + hoverReferences.put(key, list); + } + if (!list.contains(value)) { + list.add(value); + } + } + + private void recordUses(String value, int offset, int length) { + scanner.setText(value, offset, length); + TokenID token = scanner.nextToken(); + while (token != null && token.getNumericID() != IRTokenContext.EOF_TOKEN_ID) { + if (token.getNumericID() == IRTokenContext.HIR_TOKEN_ID || token.getNumericID() == IRTokenContext.LIR_TOKEN_ID || token.getNumericID() == IRTokenContext.BLOCK_TOKEN_ID) { + recordUse(scanner.getTokenString(), value); + } + token = scanner.nextToken(); + } + } +} diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/model/IRTokenContext.java b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/model/IRTokenContext.java new file mode 100644 index 000000000000..8b236ff52ef2 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/java/at/ssw/visualizer/ir/model/IRTokenContext.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir.model; + +import org.netbeans.editor.BaseTokenID; +import org.netbeans.editor.TokenContext; +import org.netbeans.editor.TokenContextPath; +import org.netbeans.editor.TokenID; + +/** + * The one and only IR token context containing all possible tokens. + * + * @author Bernhard Stiftner + */ +public class IRTokenContext extends TokenContext { + + public static final int BLOCK_TOKEN_ID = 1; + public static final int HIR_TOKEN_ID = 2; + public static final int LIR_TOKEN_ID = 3; + public static final int OTHER_TOKEN_ID = -1; + public static final int WHITESPACE_TOKEN_ID = -2; + public static final int EOF_TOKEN_ID = -3; + + public static final TokenID BLOCK_TOKEN = new BaseTokenID("block", BLOCK_TOKEN_ID); + public static final TokenID HIR_TOKEN = new BaseTokenID("hir", HIR_TOKEN_ID); + public static final TokenID LIR_TOKEN = new BaseTokenID("lir", LIR_TOKEN_ID); + public static final TokenID OTHER_TOKEN = new BaseTokenID("other", OTHER_TOKEN_ID); + public static final TokenID WHITESPACE_TOKEN = new BaseTokenID("whitespace", WHITESPACE_TOKEN_ID); + public static final TokenID EOF_TOKEN = new BaseTokenID("eof", EOF_TOKEN_ID); + + public static final IRTokenContext context = new IRTokenContext(); + public static final TokenContextPath contextPath = context.getContextPath(); + + private IRTokenContext() { + super("ir-"); + addTokenID(BLOCK_TOKEN); + addTokenID(HIR_TOKEN); + addTokenID(LIR_TOKEN); + addTokenID(OTHER_TOKEN); + addTokenID(WHITESPACE_TOKEN); + addTokenID(EOF_TOKEN); + } +} diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..70a41b326cd1 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/nbm/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.ir +OpenIDE-Module-Layer: at/ssw/visualizer/ir/layer.xml +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/ir/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/Bundle.properties b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/Bundle.properties new file mode 100644 index 000000000000..f4dc89e61a5f --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/Bundle.properties @@ -0,0 +1,9 @@ +OpenIDE-Module-Name=Intermediate Code Editor + +text/x-compilation-ir=Intermediate Representation +ir-block=Block +ir-lir=LIR +ir-hir=HIR +ir-other=Other +ir-whitespace=Whitespace +ir-default=Default diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/icons/collapseall.gif b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/icons/collapseall.gif new file mode 100644 index 000000000000..a2d80a9044f3 Binary files /dev/null and b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/icons/collapseall.gif differ diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/icons/expandall.gif b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/icons/expandall.gif new file mode 100644 index 000000000000..0205b29176d4 Binary files /dev/null and b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/icons/expandall.gif differ diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/icons/expandhir.gif b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/icons/expandhir.gif new file mode 100644 index 000000000000..964170c6d3a3 Binary files /dev/null and b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/icons/expandhir.gif differ diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/icons/expandlir.gif b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/icons/expandlir.gif new file mode 100644 index 000000000000..c61bca4b620d Binary files /dev/null and b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/icons/expandlir.gif differ diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/icons/ir.gif b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/icons/ir.gif new file mode 100644 index 000000000000..17f927e9a4e2 Binary files /dev/null and b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/icons/ir.gif differ diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/layer.xml b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/layer.xml new file mode 100644 index 000000000000..fb24a66e676c --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/layer.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/model/NetBeans-IR-fontsColors.xml b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/model/NetBeans-IR-fontsColors.xml new file mode 100644 index 000000000000..1c8feb680c07 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeEditor/src/main/resources/at/ssw/visualizer/ir/model/NetBeans-IR-fontsColors.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/IntermediateCodeViews/pom.xml b/visualizer/C1Visualizer/IntermediateCodeViews/pom.xml new file mode 100644 index 000000000000..f3a5dcac13e5 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeViews/pom.xml @@ -0,0 +1,117 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + IntermediateCodeViews + 1.14-SNAPSHOT + nbm + IntermediateCodeViews + + UTF-8 + + + + at.ssw.visualizer + VisualizerUI + ${project.version} + + + at.ssw.visualizer + IntermediateCodeEditor + ${project.version} + + + at.ssw.visualizer + CompilationModel + ${project.version} + + + at.ssw.visualizer + TextEditor + ${project.version} + + + org.netbeans.api + org-netbeans-modules-editor + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-lib + ${netbeans.version} + + + org.netbeans.api + org-openide-filesystems + ${netbeans.version} + + + org.netbeans.api + org-openide-loaders + ${netbeans.version} + + + org.netbeans.api + org-openide-nodes + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + org.netbeans.api + org-openide-windows + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.ir.view + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/HIRViewTopComponent.java b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/HIRViewTopComponent.java new file mode 100644 index 000000000000..0460c057b666 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/HIRViewTopComponent.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir.view; + +import at.ssw.visualizer.ir.IREditorKit; +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.ir.model.IRTextBuilder; +import at.ssw.visualizer.texteditor.view.AbstractTextViewTopComponent; +import java.io.Serializable; +import org.openide.windows.TopComponent; +import org.openide.windows.WindowManager; + +/** + * TopComponent displaying the HIR view. + * + * @author Bernhard Stiftner + * @author Christian Wimmer + */ +final class HIRViewTopComponent extends AbstractTextViewTopComponent { + + private HIRViewTopComponent() { + super(new IREditorKit()); + setName("HIR"); + setToolTipText("High-level Intermediate Representation"); + } + + @Override + protected String getContent(ControlFlowGraph cfg, BasicBlock[] blocks) { + IRTextBuilder builder = new IRTextBuilder(); + return builder.buildHir(cfg, blocks); + } + + // + private static final String PREFERRED_ID = "HIRViewTopComponent"; + private static HIRViewTopComponent instance; + + public static synchronized HIRViewTopComponent getDefault() { + if (instance == null) { + instance = new HIRViewTopComponent(); + } + return instance; + } + + public static synchronized HIRViewTopComponent findInstance() { + return (HIRViewTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID); + } + + @Override + public int getPersistenceType() { + return TopComponent.PERSISTENCE_ALWAYS; + } + + @Override + protected String preferredID() { + return PREFERRED_ID; + } + + @Override + public Object writeReplace() { + return new ResolvableHelper(); + } + + static final class ResolvableHelper implements Serializable { + private static final long serialVersionUID = 1L; + public Object readResolve() { + return HIRViewTopComponent.getDefault(); + } + } + // +} diff --git a/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/LIRViewTopComponent.java b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/LIRViewTopComponent.java new file mode 100644 index 000000000000..42452370265d --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/LIRViewTopComponent.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir.view; + +import at.ssw.visualizer.ir.IREditorKit; +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.ir.model.IRTextBuilder; +import at.ssw.visualizer.texteditor.view.AbstractTextViewTopComponent; +import java.io.Serializable; +import org.openide.windows.TopComponent; +import org.openide.windows.WindowManager; + +/** + * TopComponent displaying the LIR view. + * + * @author Bernhard Stiftner + * @author Christian Wimmer + */ +final class LIRViewTopComponent extends AbstractTextViewTopComponent { + private LIRViewTopComponent() { + super(new IREditorKit()); + setName("LIR"); + setToolTipText("Low-level Intermediate Representation"); + } + + @Override + protected String getContent(ControlFlowGraph cfg, BasicBlock[] blocks) { + IRTextBuilder builder = new IRTextBuilder(); + return builder.buildLir(cfg, blocks); + } + + // + private static final String PREFERRED_ID = "LIRViewTopComponent"; + private static LIRViewTopComponent instance; + + public static synchronized LIRViewTopComponent getDefault() { + if (instance == null) { + instance = new LIRViewTopComponent(); + } + return instance; + } + + public static synchronized LIRViewTopComponent findInstance() { + return (LIRViewTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID); + } + + @Override + public int getPersistenceType() { + return TopComponent.PERSISTENCE_ALWAYS; + } + + @Override + protected String preferredID() { + return PREFERRED_ID; + } + + @Override + public Object writeReplace() { + return new ResolvableHelper(); + } + + static final class ResolvableHelper implements Serializable { + private static final long serialVersionUID = 1L; + public Object readResolve() { + return LIRViewTopComponent.getDefault(); + } + } + // +} diff --git a/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/ShowHIRViewAction.java b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/ShowHIRViewAction.java new file mode 100644 index 000000000000..caa1b07dac13 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/ShowHIRViewAction.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir.view; + +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; +import org.openide.windows.TopComponent; + +/** + * Action which shows the HIRView component. + * + * @author Bernhard Stiftner + */ +public class ShowHIRViewAction extends AbstractAction { + public ShowHIRViewAction() { + super("HIR View"); + } + + public void actionPerformed(ActionEvent event) { + TopComponent win = HIRViewTopComponent.findInstance(); + win.open(); + win.requestActive(); + } +} diff --git a/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/ShowLIRViewAction.java b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/ShowLIRViewAction.java new file mode 100644 index 000000000000..2bf5600b0c09 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/ShowLIRViewAction.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir.view; + +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; +import org.openide.windows.TopComponent; + +/** + * Action which shows the LIRView component. + * + * @author Bernhard Stiftner + */ +public class ShowLIRViewAction extends AbstractAction { + public ShowLIRViewAction() { + super("LIR View"); + } + + public void actionPerformed(ActionEvent event) { + TopComponent win = LIRViewTopComponent.findInstance(); + win.open(); + win.requestActive(); + } +} diff --git a/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/ShowStateViewAction.java b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/ShowStateViewAction.java new file mode 100644 index 000000000000..408a47ee2595 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/ShowStateViewAction.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir.view; + +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; +import org.openide.windows.TopComponent; + +/** + * Action which shows the StateView component. + * + * @author Bernhard Stiftner + */ +public class ShowStateViewAction extends AbstractAction { + public ShowStateViewAction() { + super("State View"); + } + + public void actionPerformed(ActionEvent event) { + TopComponent win = StateViewTopComponent.findInstance(); + win.open(); + win.requestActive(); + } +} diff --git a/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/StateViewTopComponent.java b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/StateViewTopComponent.java new file mode 100644 index 000000000000..54346725aa27 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/java/at/ssw/visualizer/ir/view/StateViewTopComponent.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.ir.view; + +import at.ssw.visualizer.ir.IREditorKit; +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.ir.model.IRTextBuilder; +import at.ssw.visualizer.texteditor.view.AbstractTextViewTopComponent; +import java.io.Serializable; +import org.openide.windows.TopComponent; +import org.openide.windows.WindowManager; + +/** + * TopComponent displaying the state view. + * + * @author Bernhard Stiftner + * @author Christian Wimmer + */ +final class StateViewTopComponent extends AbstractTextViewTopComponent { + private StateViewTopComponent() { + super(new IREditorKit()); + setName("State"); + setToolTipText("State of Local Variables and Operand Stack"); + } + + @Override + protected String getContent(ControlFlowGraph cfg, BasicBlock[] blocks) { + IRTextBuilder builder = new IRTextBuilder(); + return builder.buildState(cfg, blocks); + } + + // + private static final String PREFERRED_ID = "StateViewTopComponent"; + private static StateViewTopComponent instance; + + public static synchronized StateViewTopComponent getDefault() { + if (instance == null) { + instance = new StateViewTopComponent(); + } + return instance; + } + + public static synchronized StateViewTopComponent findInstance() { + return (StateViewTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID); + } + + @Override + public int getPersistenceType() { + return TopComponent.PERSISTENCE_ALWAYS; + } + + @Override + protected String preferredID() { + return PREFERRED_ID; + } + + @Override + public Object writeReplace() { + return new ResolvableHelper(); + } + + static final class ResolvableHelper implements Serializable { + private static final long serialVersionUID = 1L; + public Object readResolve() { + return StateViewTopComponent.getDefault(); + } + } + // +} diff --git a/visualizer/C1Visualizer/IntermediateCodeViews/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..0108407a2e84 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/nbm/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.ir.view +OpenIDE-Module-Layer: at/ssw/visualizer/ir/view/layer.xml +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/ir/view/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/Bundle.properties b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/Bundle.properties new file mode 100644 index 000000000000..1cd5dfc8c788 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=Intermediate Code Views diff --git a/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/HIRViewTopComponentSettings.xml b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/HIRViewTopComponentSettings.xml new file mode 100644 index 000000000000..dab039f8a03c --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/HIRViewTopComponentSettings.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/HIRViewTopComponentWstcref.xml b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/HIRViewTopComponentWstcref.xml new file mode 100644 index 000000000000..7edd9db7c96c --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/HIRViewTopComponentWstcref.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/LIRViewTopComponentSettings.xml b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/LIRViewTopComponentSettings.xml new file mode 100644 index 000000000000..692444020fb0 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/LIRViewTopComponentSettings.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/LIRViewTopComponentWstcref.xml b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/LIRViewTopComponentWstcref.xml new file mode 100644 index 000000000000..cb35fee1ac5d --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/LIRViewTopComponentWstcref.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/StateViewTopComponentSettings.xml b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/StateViewTopComponentSettings.xml new file mode 100644 index 000000000000..965772b3eb16 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/StateViewTopComponentSettings.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/StateViewTopComponentWstcref.xml b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/StateViewTopComponentWstcref.xml new file mode 100644 index 000000000000..4ba9933876d5 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/StateViewTopComponentWstcref.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/layer.xml b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/layer.xml new file mode 100644 index 000000000000..581cc8a53218 --- /dev/null +++ b/visualizer/C1Visualizer/IntermediateCodeViews/src/main/resources/at/ssw/visualizer/ir/view/layer.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/IntervalEditor/pom.xml b/visualizer/C1Visualizer/IntervalEditor/pom.xml new file mode 100644 index 000000000000..8b86df76e073 --- /dev/null +++ b/visualizer/C1Visualizer/IntervalEditor/pom.xml @@ -0,0 +1,107 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + IntervalEditor + 1.14-SNAPSHOT + nbm + IntervalEditor + + UTF-8 + + + + at.ssw.visualizer + VisualizerUI + ${project.version} + + + at.ssw.visualizer + CompilationModel + ${project.version} + + + at.ssw.visualizer + TextEditor + ${project.version} + + + org.netbeans.api + org-openide-awt + ${netbeans.version} + + + org.netbeans.api + org-openide-loaders + ${netbeans.version} + + + org.netbeans.api + org-openide-nodes + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + org.netbeans.api + org-openide-util-ui + ${netbeans.version} + + + org.netbeans.api + org-openide-windows + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.model.interval + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/IntervalCanvas.java b/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/IntervalCanvas.java new file mode 100644 index 000000000000..80ce76c5ff76 --- /dev/null +++ b/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/IntervalCanvas.java @@ -0,0 +1,577 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.interval; + +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.model.cfg.IRInstruction; +import at.ssw.visualizer.model.interval.ChildInterval; +import at.ssw.visualizer.model.interval.Interval; +import at.ssw.visualizer.model.interval.IntervalList; +import at.ssw.visualizer.model.interval.Range; +import at.ssw.visualizer.model.interval.UsePosition; +import at.ssw.visualizer.texteditor.model.HoverParser; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionAdapter; +import java.awt.event.MouseMotionListener; +import java.awt.geom.Rectangle2D; +import java.util.List; +import javax.swing.JComponent; +import javax.swing.JViewport; +import javax.swing.Scrollable; +import javax.swing.SwingConstants; +import javax.swing.UIManager; + +/** + * A Viewport showing an interval chart. Intended to be used in conjunction + * with a JScrollPane. + * + * @author Christian Wimmer + * @author Bernhard Stiftner + */ +public class IntervalCanvas extends JViewport { + + class ViewData { + + /** number of instructions/intervals hidden on left/top */ + public int offsetCol; + public int offsetRow; + /** number of instructions/intervals visible */ + public int sizeCol; + public int sizeRow; + /** total number of instructions/intervals */ + public int totalRow; + public int totalCol; + public int fontHeight; + public int fontAscent; + public int mouseX; + public int mouseY; + public int selectedCol; + public int selectedRow; + /** offset in pixels where first visible col/row starts */ + public int offsetX; + public int offsetY; + /** size in pixels of viewable area */ + public int sizeX; + public int sizeY; + public Interval selectedInterval; + public ChildInterval selectedChild; + public BasicBlock selectedBlock; + public IRInstruction selectedOperation; + + public int colToX(int col) { + return (col - offsetCol) * viewSettings.colWidth + offsetX; + } + + public int rowToY(int row) { + return (row - offsetRow) * viewSettings.rowHeight + offsetY; + } + } + + /** + * Just a dummy component which is used as the view of this viewport. + * It's just here to perform some layout calculations; it never gets painted. + */ + class PlaceHolder extends JComponent implements Scrollable { + + public PlaceHolder() { + setEnabled(false); + } + + public Dimension getPreferredScrollableViewportSize() { + return getPreferredSize(); + } + + public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { + switch (orientation) { + case SwingConstants.HORIZONTAL: + return 2 * viewSettings.colWidth; + case SwingConstants.VERTICAL: + return viewSettings.rowHeight; + } + throw new RuntimeException("illegal orientation"); + } + + public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { + switch (orientation) { + case SwingConstants.HORIZONTAL: + return viewSettings.colWidth * viewData.sizeCol * 3 / 8; + case SwingConstants.VERTICAL: + return viewSettings.rowHeight * viewData.sizeRow * 3 / 4; + } + throw new RuntimeException("illegal orientation"); + } + + public boolean getScrollableTracksViewportWidth() { + return false; + } + + public boolean getScrollableTracksViewportHeight() { + return false; + } + } + private IntervalEditorTopComponent editor; + private IntervalList intervals; + private ViewSettings viewSettings; + private ViewData viewData = new ViewData(); + /** inserted as view, just used for layout purposes */ + private PlaceHolder placeholder = new PlaceHolder(); + private MouseMotionListener mouseMotionListener = new MouseMotionAdapter() { + + @Override + public void mouseMoved(MouseEvent e) { + calcSelection(e.getX(), e.getY()); + } + }; + private MouseListener mouseListener = new MouseAdapter() { + + @Override + public void mouseEntered(MouseEvent e) { + calcSelection(e.getX(), e.getY()); + } + + @Override + public void mouseExited(MouseEvent e) { + calcSelection(-1, -1); + } + + @Override + public void mouseReleased(MouseEvent e) { + editor.updateCanvasSelection(); + } + }; + private ComponentListener componentListener = new ComponentAdapter() { + + @Override + public void componentResized(ComponentEvent e) { + calcViewData(); + } + + @Override + public void componentShown(ComponentEvent e) { + calcViewData(); + } + }; + + public IntervalCanvas(IntervalEditorTopComponent editor, ViewSettings viewSettings, IntervalList intervals) { + this.editor = editor; + this.intervals = intervals; + this.viewSettings = viewSettings; + + setView(placeholder); + setOpaque(true); + setBackground(UIManager.getColor("TextPane.background")); + setFocusable(true); + + addMouseListener(mouseListener); + addMouseMotionListener(mouseMotionListener); + addComponentListener(componentListener); + } + + public IntervalList getIntervals() { + return intervals; + } + + public BasicBlock getSelectedBlock() { + return viewData.selectedBlock; + } + + public ChildInterval getSelectedInterval() { + return viewData.selectedChild; + } + + public void ensureColumnVisible(int column) { + if (column < viewData.offsetCol) { + setViewPosition(new Point(column * viewSettings.colWidth, getViewPosition().y)); + } else if (column >= viewData.offsetCol + viewData.sizeCol - 4) { + setViewPosition(new Point((column - viewData.sizeCol + 4) * viewSettings.colWidth, getViewPosition().y)); + } + } + + public void ensureRowVisible(int row) { + if (row < viewData.offsetRow) { + setViewPosition(new Point(getViewPosition().x, row * viewSettings.rowHeight)); + } else if (row >= viewData.offsetRow + viewData.sizeRow - 1) { + setViewPosition(new Point(getViewPosition().x, (row - viewData.sizeRow + 1) * viewSettings.rowHeight)); + } + } + + public void calcViewData() { + + Graphics gc = getGraphics(); + if (gc == null) { + return; + } + + Dimension ca = getExtentSize(); + + viewData.totalRow = intervals.getIntervals().size(); + viewData.totalCol = intervals.getNumLIROperations(); + + gc.setFont(viewSettings.textFont); + FontMetrics fm = gc.getFontMetrics(); + viewData.fontHeight = fm.getHeight(); + viewData.fontAscent = fm.getAscent(); + viewData.offsetX = fm.stringWidth("v" + viewData.totalRow + "|a") + 15; + + viewData.offsetY = viewData.fontHeight * 2; + + placeholder.setPreferredSize(new Dimension(viewData.offsetX + (viewData.totalCol + 2) * viewSettings.colWidth + 2 + viewSettings.thickLineWidth, viewData.offsetY + (viewData.totalRow + 1) * viewSettings.rowHeight + 2)); + placeholder.setSize(placeholder.getPreferredSize()); + + viewData.offsetCol = getViewPosition().x / viewSettings.colWidth / 2 * 2; + viewData.offsetRow = getViewPosition().y / viewSettings.rowHeight; + + viewData.sizeCol = Math.min((ca.width - viewData.offsetX - 3) / viewSettings.colWidth / 2 * 2, viewData.totalCol - viewData.offsetCol); + viewData.sizeRow = Math.min((ca.height - viewData.offsetY - 3) / viewSettings.rowHeight, viewData.totalRow - viewData.offsetRow); + + viewData.sizeX = viewData.sizeCol * viewSettings.colWidth; + viewData.sizeY = viewData.sizeRow * viewSettings.rowHeight; + + calcSelection(); + repaint(); + } + + public void calcSelection(int mouseX, int mouseY) { + viewData.mouseX = mouseX; + viewData.mouseY = mouseY; + calcSelection(); + } + + public void calcSelection() { + int newCol; + int newRow; + if (viewData.mouseX != -1 && viewData.mouseY != -1) { + newCol = (viewData.mouseX - viewData.offsetX + 2 + viewSettings.colWidth * 2) / viewSettings.colWidth / 2 * 2 + viewData.offsetCol - 2; + newRow = (viewData.mouseY - viewData.offsetY + 2 + viewSettings.rowHeight) / viewSettings.rowHeight + viewData.offsetRow - 1; + } else { + newCol = -1; + newRow = -1; + } + + if (newCol < viewData.offsetCol || newCol >= viewData.offsetCol + viewData.sizeCol) { + newCol = -1; + } + if (newRow < viewData.offsetRow || newRow >= viewData.offsetRow + viewData.sizeRow) { + newRow = -1; + } + + if (viewData.selectedCol != newCol || viewData.selectedRow != newRow) { + viewData.selectedCol = newCol; + viewData.selectedRow = newRow; + + if (newRow != -1) { + viewData.selectedInterval = intervals.getIntervals().get(newRow); + viewData.selectedChild = getChildByLirId(viewData.selectedInterval, newCol); + } else { + viewData.selectedInterval = null; + viewData.selectedChild = null; + } + + if (newCol != -1) { + viewData.selectedBlock = getBlockByLirId(intervals.getControlFlowGraph(), newCol); + viewData.selectedOperation = getOperationByLirId(viewData.selectedBlock, newCol); + } else { + viewData.selectedBlock = null; + viewData.selectedOperation = null; + } + + repaint(); + updateStatusLine(); + } + } + + private BasicBlock getBlockByLirId(ControlFlowGraph cfg, int lirId) { + // TODO could do binary search + for (BasicBlock basicBlock : cfg.getBasicBlocks()) { + if (basicBlock.getFirstLirId() <= lirId && lirId <= basicBlock.getLastLirId()) { + return basicBlock; + } + } + return null; + } + + private IRInstruction getOperationByLirId(BasicBlock block, int lirId) { + if (block == null) { + return null; + } + // TODO could do binary search + for (IRInstruction operation : block.getLirOperations()) { + try { + if (Integer.parseInt(operation.getValue(IRInstruction.LIR_NUMBER)) == lirId) { + return operation; + } + } catch (NumberFormatException ex) { + // Silently ignore wrong numbers. + } + } + return null; + } + + public ChildInterval getChildByLirId(Interval interval, int lirId) { + if (interval == null) { + return null; + } + // TODO could do binary search + List children = interval.getChildren(); + for (ChildInterval child : children) { + for (Range range : child.getRanges()) { + if (range.getFrom() <= lirId && range.getTo() >= lirId) { + return child; + } + } + } + return children.get(0); + } + + private void updateStatusLine() { + String intervalText = ""; + String instructionText = ""; + String blockText = ""; + + ChildInterval child = viewData.selectedChild; + if (child != null) { + intervalText = child.getRegNum() + " " + child.getType() + " " + child.getOperand(); + } + + IRInstruction operation = viewData.selectedOperation; + if (operation != null) { + for (String name : operation.getNames()) { + instructionText = instructionText + HoverParser.firstLine(operation.getValue(name)) + " "; + } + } + + BasicBlock block = viewData.selectedBlock; + if (block != null) { + if (block.getLoopDepth() > 0) { + blockText = block.getName() + " (loop " + block.getLoopIndex() + " depth " + block.getLoopDepth() + ")"; + } else { + blockText = block.getName(); + } + } + + editor.setIntervalStatusText(intervalText); + editor.setInstructionStatusText(instructionText); + editor.setBlockStatusText(blockText); + } + + private int gridStart(int start, int grid) { + return grid - 1 - (start + grid - 1) % grid; + } + + private void drawXGrid(Graphics gc, int grid, Color color) { + gc.setColor(color); + int y1 = viewData.offsetY; + int y2 = viewData.offsetY + viewData.sizeY; + + for (int i = gridStart(viewData.offsetCol, grid); i < viewData.sizeCol; i += grid) { + int x = viewData.offsetX + i * viewSettings.colWidth; + gc.drawLine(x, y1, x, y2); + } + } + + private void drawXText(Graphics gc, int grid, Color color) { + gc.setColor(color); + int y = 0; + + for (int i = gridStart(viewData.offsetCol, grid); i < viewData.sizeCol; i += grid) { + int x = viewData.offsetX + i * viewSettings.colWidth + viewSettings.thickLineWidth + 2; + gc.drawString(String.valueOf(viewData.offsetCol + i), x, viewData.fontAscent + y); + } + } + + private void drawYGrid(Graphics gc, int grid, Color color) { + gc.setColor(color); + int x1 = viewData.offsetX; + int x2 = viewData.offsetX + viewData.sizeX; + + for (int i = gridStart(viewData.offsetRow, grid); i < viewData.sizeRow; i += grid) { + int y = viewData.offsetY + i * viewSettings.rowHeight; + gc.drawLine(x1, y, x2, y); + } + } + + private void drawYText(Graphics gc, int grid, Color color) { + gc.setColor(color); + int x = 5; + + for (int i = gridStart(viewData.offsetRow, grid); i < viewData.sizeRow; i += grid) { + Interval interval = intervals.getIntervals().get(i + viewData.offsetRow); + if (interval != null) { + int y = viewData.offsetY + i * viewSettings.rowHeight; + gc.drawString(String.valueOf(interval.getRegNum()), x, viewData.fontAscent + y); + } + } + } + + private void drawBorder(Graphics gc) { + gc.setColor(viewSettings.darkGridColor); + gc.drawRect(viewData.offsetX, viewData.offsetY, viewData.sizeX, viewData.sizeY); + } + + private void drawBlocks(Graphics gc) { + int y1 = viewData.fontHeight; + int y2 = viewData.offsetY + viewData.sizeY; + + for (BasicBlock basicBlock : intervals.getControlFlowGraph().getBasicBlocks()) { + if (basicBlock.getFirstLirId() >= viewData.offsetCol && basicBlock.getFirstLirId() <= viewData.offsetCol + viewData.sizeCol) { + int x = viewData.colToX(basicBlock.getFirstLirId()); + gc.setColor(viewSettings.darkGridColor); + gc.fillRect(x, y1, viewSettings.thickLineWidth, y2 - y1); + + if (basicBlock.getFirstLirId() < viewData.offsetCol + viewData.sizeCol) { + String text = basicBlock.getName(); + if (basicBlock.getLoopDepth() > 0) { + text += " (" + basicBlock.getLoopDepth() + ")"; + } + gc.setColor(getBackground()); + Rectangle2D stringBounds = gc.getFontMetrics().getStringBounds(text, gc); + gc.fillRect(x + viewSettings.thickLineWidth + 2, y1, (int) stringBounds.getWidth(), (int) stringBounds.getHeight()); + gc.setColor(viewSettings.textColor); + gc.drawString(text, x + viewSettings.thickLineWidth + 2, viewData.fontAscent + y1); + } + } + } + + gc.setColor(viewSettings.darkGridColor); + if (intervals.getNumLIROperations() <= viewData.offsetCol + viewData.sizeCol) { + int x = viewData.colToX(intervals.getNumLIROperations()); + gc.fillRect(x, y1, viewSettings.thickLineWidth, y2 - y1); + } + } + + public void drawInterval(Graphics gc, ChildInterval interval, int barY, int barHeight, int textY) { + gc.setColor(viewSettings.getIntervalColor(interval)); + + int textX = -1; + for (Range range : interval.getRanges()) { + if (range.getTo() > viewData.offsetCol && range.getFrom() < viewData.offsetCol + viewData.sizeCol) { + int x1 = Math.max(viewData.colToX(range.getFrom()) + viewSettings.thickLineWidth, viewData.offsetX); + int x2 = Math.min(viewData.colToX(range.getTo()), viewData.offsetX + viewData.sizeX); + gc.fillRect(x1, barY, x2 - x1, barHeight); + + if (textX == -1) { + textX = x1; + } + } + } + + for (UsePosition usePosition : interval.getUsePositions()) { + if (usePosition.getPosition() >= viewData.offsetCol && usePosition.getPosition() < viewData.offsetCol + viewData.sizeCol) { + gc.setColor(viewSettings.getUsePosColor(usePosition)); + + int x = viewData.colToX(usePosition.getPosition()); + gc.fillRect(x, barY, viewSettings.thickLineWidth, barHeight); + } + } + + gc.setColor(viewSettings.textColor); + if (viewSettings.showIntervalText && textX != -1) { + textX = Math.max(textX, viewData.offsetX + viewSettings.thickLineWidth); + String text = String.valueOf(interval.getRegNum()) + " " + interval.getOperand(); + gc.drawString(text, textX + 1, viewData.fontAscent + textY); + } + } + + public void drawIntervals(Graphics gc) { + gc.setColor(viewSettings.textColor); + int barHeight = viewSettings.rowHeight - viewSettings.barSeparation * 2 - 1; + + for (int i = 0; i < viewData.sizeRow; i++) { + Interval interval = intervals.getIntervals().get(i + viewData.offsetRow); + int textY = viewData.offsetY + i * viewSettings.rowHeight; + int barY = textY + 1 + viewSettings.barSeparation; + + for (ChildInterval child : interval.getChildren()) { + drawInterval(gc, child, barY, barHeight, textY); + } + } + } + + private void drawSelection(Graphics gc) { + gc.setColor(Color.RED); + Dimension ca = getExtentSize(); + + if (viewData.selectedCol != -1) { + int x = viewData.colToX(viewData.selectedCol); + gc.fillRect(x, 0, 3, ca.height); + } + if (viewData.selectedRow != -1) { + int y = viewData.rowToY(viewData.selectedRow); + gc.fillRect(0, y - 1, ca.width, 3); + } + } + + public void drawAll(Graphics gc) { + gc.setColor(getBackground()); + gc.fillRect(0, 0, getWidth(), getHeight()); + + gc.setFont(viewSettings.textFont); + + if (viewSettings.lightGridX > 0) { + drawXGrid(gc, viewSettings.lightGridX, viewSettings.lightGridColor); + } + if (viewSettings.lightGridY > 0) { + drawYGrid(gc, viewSettings.lightGridY, viewSettings.lightGridColor); + } + if (viewSettings.darkGridX > 0) { + drawXGrid(gc, viewSettings.darkGridX, viewSettings.darkGridColor); + } + if (viewSettings.darkGridY > 0) { + drawYGrid(gc, viewSettings.darkGridY, viewSettings.darkGridColor); + } + if (viewSettings.textGridX > 0) { + drawXText(gc, viewSettings.textGridX, viewSettings.textColor); + } + if (viewSettings.textGridY > 0) { + drawYText(gc, viewSettings.textGridY, viewSettings.textColor); + } + drawBorder(gc); + drawBlocks(gc); + drawIntervals(gc); + drawSelection(gc); + } + + @Override + public void paint(Graphics g) { + g.clearRect(0, 0, getSize().width, getSize().height); + drawAll(g); + } + + @Override + protected void fireStateChanged() { + calcViewData(); + super.fireStateChanged(); + } +} diff --git a/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/IntervalEditorSupport.java b/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/IntervalEditorSupport.java new file mode 100644 index 000000000000..dcc6f5b4c06b --- /dev/null +++ b/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/IntervalEditorSupport.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.interval; + +import at.ssw.visualizer.model.interval.IntervalList; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.beans.VetoableChangeListener; +import java.beans.VetoableChangeSupport; +import java.io.IOException; +import org.openide.cookies.OpenCookie; +import org.openide.windows.CloneableOpenSupport; +import org.openide.windows.CloneableTopComponent; + +/** + * Support class for opening interval editors. + * + * @author Bernhard Stiftner + * @author Christian Wimmer + */ +public class IntervalEditorSupport extends CloneableOpenSupport implements OpenCookie { + private IntervalList intervalList; + + public IntervalEditorSupport(IntervalList intervalList) { + super(new Env()); + ((Env) env).editorSupport = this; + this.intervalList = intervalList; + } + + protected CloneableTopComponent createCloneableTopComponent() { + return new IntervalEditorTopComponent(intervalList); + } + + public String messageOpened() { + return "Opened " + intervalList.getCompilation().getMethod() + " - " + intervalList.getName(); + } + + public String messageOpening() { + return "Opening " + intervalList.getCompilation().getMethod() + " - " + intervalList.getName(); + } + + + public static class Env implements CloneableOpenSupport.Env { + private PropertyChangeSupport prop = new PropertyChangeSupport(this); + private VetoableChangeSupport veto = new VetoableChangeSupport(this); + private IntervalEditorSupport editorSupport; + + public boolean isValid() { + return true; + } + + public boolean isModified() { + return false; + } + + public void markModified() throws IOException { + throw new IOException("Editor is readonly"); + } + + public void unmarkModified() { + // Nothing to do. + } + + public CloneableOpenSupport findCloneableOpenSupport() { + return editorSupport; + } + + public void addPropertyChangeListener(PropertyChangeListener l) { + prop.addPropertyChangeListener(l); + } + + public void removePropertyChangeListener(PropertyChangeListener l) { + prop.removePropertyChangeListener(l); + } + + public void addVetoableChangeListener(VetoableChangeListener l) { + veto.addVetoableChangeListener(l); + } + + public void removeVetoableChangeListener(VetoableChangeListener l) { + veto.removeVetoableChangeListener(l); + } + } +} diff --git a/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/IntervalEditorTopComponent.java b/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/IntervalEditorTopComponent.java new file mode 100644 index 000000000000..53b4b2545784 --- /dev/null +++ b/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/IntervalEditorTopComponent.java @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.interval; + +import at.ssw.visualizer.core.selection.Selection; +import at.ssw.visualizer.core.selection.SelectionManager; +import at.ssw.visualizer.core.selection.SelectionProvider; +import at.ssw.visualizer.interval.icons.Icons; +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.model.interval.ChildInterval; +import at.ssw.visualizer.model.interval.IntervalList; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JToggleButton; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import org.openide.awt.Toolbar; +import org.openide.util.ImageUtilities; +import org.openide.windows.CloneableTopComponent; +import org.openide.windows.TopComponent; + +/** + * TopComponent for displaying interval diagrams. + * + * @author Bernhard Stiftner + * @author Christian Wimmer + */ +public final class IntervalEditorTopComponent extends CloneableTopComponent implements SelectionProvider { + private static final String HSIZE_PROP = "visualizer.hsize"; + private static final String VSIZE_PROP = "visualizer.vsize"; + + private IntervalCanvas canvas; + private ViewSettings viewSettings; + private JToggleButton[] hsizeButtons; + private JToggleButton[] vsizeButtons; + private JLabel intervalStatusLabel; + private JLabel blockStatusLabel; + private JLabel instructionStatusLabel; + + private IntervalList intervals; + private Selection selection; + private boolean selectionUpdating; + + protected IntervalEditorTopComponent(IntervalList intervals) { + setName(intervals.getCompilation().getShortName()); + setToolTipText(intervals.getCompilation().getMethod() + " - " + intervals.getName()); + setIcon(ImageUtilities.loadImage(Icons.INTERVALS)); + + this.intervals = intervals; + selection = new Selection(); + selection.put(intervals); + selection.put(intervals.getControlFlowGraph()); + selection.addChangeListener(selectionChangeListener); + + viewSettings = new ViewSettings(); + canvas = new IntervalCanvas(this, viewSettings, intervals); + JScrollPane scrollPane = new JScrollPane(canvas); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + scrollPane.setViewportBorder(BorderFactory.createEmptyBorder()); + + hsizeButtons = new JToggleButton[3]; + hsizeButtons[0] = createSizeButton(Icons.HSIZE_SMALL, "Horizontal Size: Small", HSIZE_PROP, ViewSettings.SMALL); + hsizeButtons[1] = createSizeButton(Icons.HSIZE_MEDIUM, "Horizontal Size: Medium", HSIZE_PROP, ViewSettings.MEDIUM); + hsizeButtons[2] = createSizeButton(Icons.HSIZE_LARGE, "Horizontal Size: Large", HSIZE_PROP, ViewSettings.LARGE); + vsizeButtons = new JToggleButton[3]; + vsizeButtons[0] = createSizeButton(Icons.VSIZE_SMALL, "Vertical Size: Small", VSIZE_PROP, ViewSettings.SMALL); + vsizeButtons[1] = createSizeButton(Icons.VSIZE_MEDIUM, "Vertical Size: Medium", VSIZE_PROP, ViewSettings.MEDIUM); + vsizeButtons[2] = createSizeButton(Icons.VSIZE_LARGE, "Vertical Size: Large", VSIZE_PROP, ViewSettings.LARGE); + Toolbar toolbar = new Toolbar(); + toolbar.setBorder((Border) UIManager.get("Nb.Editor.Toolbar.border")); + toolbar.add(hsizeButtons[0]); + toolbar.add(hsizeButtons[1]); + toolbar.add(hsizeButtons[2]); + toolbar.addSeparator(); + toolbar.add(vsizeButtons[0]); + toolbar.add(vsizeButtons[1]); + toolbar.add(vsizeButtons[2]); + + intervalStatusLabel = createStatusLabel("Nb.Editor.Status.leftBorder", new Dimension(200, 18)); + instructionStatusLabel = createStatusLabel("Nb.Editor.Status.innerBorder", null); + blockStatusLabel = createStatusLabel("Nb.Editor.Status.rightBorder", new Dimension(200, 18)); + JPanel statusBar = new JPanel(new BorderLayout()); + statusBar.add(intervalStatusLabel, BorderLayout.WEST); + statusBar.add(blockStatusLabel, BorderLayout.EAST); + statusBar.add(instructionStatusLabel, BorderLayout.CENTER); + + setLayout(new BorderLayout()); + add(toolbar, BorderLayout.NORTH); + add(scrollPane, BorderLayout.CENTER); + add(statusBar, BorderLayout.SOUTH); + + hsizeButtons[2].doClick(); + vsizeButtons[2].doClick(); + } + + public Selection getSelection() { + return selection; + } + + private JToggleButton createSizeButton(String icon, String tooltip, String propName, int propValue) { + JToggleButton button = new JToggleButton(new ImageIcon(ImageUtilities.loadImage(icon))); + button.setToolTipText(tooltip); + button.putClientProperty(propName, propValue); + button.addActionListener(sizeButtonListener); + return button; + } + + private JLabel createStatusLabel(String border, Dimension dimension) { + JLabel label = new JLabel(" "); + label.setOpaque(true); + label.setBorder((Border) UIManager.get(border)); + if (dimension != null) { + label.setPreferredSize(dimension); + } + return label; + } + + @Override + public void componentActivated() { + super.componentActivated(); + canvas.requestFocus(); + SelectionManager.getDefault().setSelection(selection); + } + + @Override + public void componentClosed() { + super.componentClosed(); + SelectionManager.getDefault().removeSelection(selection); + } + + + private ActionListener sizeButtonListener = new ActionListener() { + public void actionPerformed(ActionEvent event) { + Object hsize = ((JComponent) event.getSource()).getClientProperty(HSIZE_PROP); + if (hsize instanceof Integer) { + viewSettings.setHorizontalSize((Integer) hsize); + } + Object vsize = ((JComponent) event.getSource()).getClientProperty(VSIZE_PROP); + if (vsize instanceof Integer) { + viewSettings.setVerticalSize((Integer) vsize); + } + + canvas.calcViewData(); + + for (int i = 0; i < hsizeButtons.length; i++) { + hsizeButtons[i].setSelected(viewSettings.hsize == i); + } + for (int i = 0; i < vsizeButtons.length; i++) { + vsizeButtons[i].setSelected(viewSettings.vsize == i); + } + } + }; + + + private ChangeListener selectionChangeListener = new ChangeListener() { + public void stateChanged(ChangeEvent event) { + if (selectionUpdating) { + return; + } + selectionUpdating = true; + updateBlockSelection(); + updateIntervalSelection(); + selectionUpdating = false; + } + }; + + private void updateBlockSelection() { + assert selection.get(ControlFlowGraph.class) == intervals.getControlFlowGraph(); + BasicBlock[] blocks = selection.get(BasicBlock[].class); + if (blocks == null || blocks.length == 0 || (blocks.length == 1 && blocks[0] == canvas.getSelectedBlock())) { + return; + } + + int leftCol = Integer.MAX_VALUE; + int rightCol = Integer.MIN_VALUE; + for (BasicBlock block : blocks) { + leftCol = Math.min(leftCol, block.getFirstLirId()); + rightCol = Math.max(rightCol, block.getLastLirId()); + } + canvas.ensureColumnVisible(rightCol); + canvas.ensureColumnVisible(leftCol); + } + + private void updateIntervalSelection() { + assert selection.get(IntervalList.class) == intervals; + ChildInterval child = selection.get(ChildInterval.class); + if (child == null || child == canvas.getSelectedInterval()) { + return; + } + + canvas.ensureColumnVisible(child.getFrom()); + canvas.ensureRowVisible(intervals.getIntervals().indexOf(child.getParent())); + } + + protected void updateCanvasSelection() { + if (selectionUpdating) { + return; + } + selectionUpdating = true; + if (canvas.getSelectedBlock() != null) { + selection.put(new BasicBlock[]{canvas.getSelectedBlock()}); + } + if (canvas.getSelectedInterval() != null) { + selection.put(canvas.getSelectedInterval()); + } + selectionUpdating = false; + } + + + protected void setIntervalStatusText(String text) { + intervalStatusLabel.setText(" " + text); + } + + protected void setBlockStatusText(String text) { + blockStatusLabel.setText(" " + text); + } + protected void setInstructionStatusText(String text) { + instructionStatusLabel.setText(" " + text); + } + + + @Override + protected CloneableTopComponent createClonedObject() { + return new IntervalEditorTopComponent(intervals); + } + + @Override + public int getPersistenceType() { + return TopComponent.PERSISTENCE_NEVER; + } +} diff --git a/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/ShowIntervalEditorAction.java b/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/ShowIntervalEditorAction.java new file mode 100644 index 000000000000..96c4df96f27c --- /dev/null +++ b/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/ShowIntervalEditorAction.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.interval; + +import at.ssw.visualizer.core.focus.Focus; +import at.ssw.visualizer.model.interval.IntervalList; +import at.ssw.visualizer.interval.icons.Icons; +import org.openide.nodes.Node; +import org.openide.util.HelpCtx; +import org.openide.util.actions.CookieAction; + +/** + * Opens a new interval visualization window. + * + * @author Bernhard Stiftner + * @author Christian Wimmer + */ +public final class ShowIntervalEditorAction extends CookieAction { + protected void performAction(Node[] activatedNodes) { + IntervalList intervalList = activatedNodes[0].getLookup().lookup(IntervalList.class); + if (!Focus.findEditor(IntervalEditorTopComponent.class, intervalList)) { + IntervalEditorSupport editor = new IntervalEditorSupport(intervalList); + editor.open(); + } + } + + public String getName() { + return "Open Intervals"; + } + + @Override + protected String iconResource() { + return Icons.INTERVALS; + } + + protected int mode() { + return CookieAction.MODE_EXACTLY_ONE; + } + + protected Class[] cookieClasses() { + return new Class[]{IntervalList.class}; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + protected boolean asynchronous() { + return false; + } +} diff --git a/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/ViewSettings.java b/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/ViewSettings.java new file mode 100644 index 000000000000..9348d3523475 --- /dev/null +++ b/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/ViewSettings.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.interval; + +import at.ssw.visualizer.model.interval.ChildInterval; +import at.ssw.visualizer.model.interval.UsePosition; +import java.awt.Color; +import java.awt.Font; +import java.util.HashMap; +import java.util.Map; + +/** + * Encapsulates display parameters for the interval visualization. + * + * @author Christian Wimmer + */ +public class ViewSettings { + public static final int SMALL = 0; + public static final int MEDIUM = 1; + public static final int LARGE = 2; + + // horizontal size + private final int[] _colWidth = {2, 4, 8}; + private final int[] _lightGridX = {-1, 10, 2}; + private final int[] _darkGridX = {-1, -1, 10}; + private final int[] _textGridX = {40, 20, 10}; + private final int[] _thickLineWidth = {2, 2, 3}; + + // vertical size + private final int[] _rowHeight = {3, 6, 14}; + private final int[] _lightGridY = {8, 4, 1}; + private final int[] _darkGridY = {-1, -1, 10}; + private final int[] _textGridY = {8, 4, 1}; + private final int[] _barSeparation = {0, 0, 1}; + + private final boolean[][] _showIntervalText = {{false, false, false}, {false, false, false}, {false, true, true}}; + + private static final Color DEFAULT_INT_COLOR = new Color(255, 255, 102); + + /** current size settings */ + public int hsize; + public int vsize; + + + public int colWidth; + public int rowHeight; + + /** space above and below each interval bar */ + public int barSeparation; + + /** width of block-separator lines, use-positions and indentation of ranges */ + public int thickLineWidth; + + public int lightGridX; + public int darkGridX; + public int textGridX; + public int lightGridY; + public int darkGridY; + public int textGridY; + + boolean showIntervalText; + + public Color lightGridColor; + public Color darkGridColor; + public Color blockGridColor; + public Color textColor; + + public Font textFont; + + private Color usePosColorL; + private Color usePosColorS; + private Color usePosColorM; + private Color usePosColorOther; + private Map typeIntervalColors; + private Color stackIntervalColor; + + + public ViewSettings() { + lightGridColor = Color.GRAY; + darkGridColor = Color.DARK_GRAY; + blockGridColor = Color.DARK_GRAY; + textColor = Color.BLACK; + + usePosColorL = new Color(192, 64, 64); + usePosColorS = new Color(255, 0, 192); + usePosColorM = new Color(255, 0, 0); + usePosColorOther = new Color(0, 255, 255); + + stackIntervalColor = new Color(255, 192, 64); + typeIntervalColors = new HashMap(); + typeIntervalColors.put("fixed", new Color(128, 128, 128)); + typeIntervalColors.put("object", new Color(192, 64, 255)); + typeIntervalColors.put("int", new Color(64, 192, 255)); + typeIntervalColors.put("long", new Color(0, 128, 255)); + typeIntervalColors.put("float", new Color(192, 255, 64)); + typeIntervalColors.put("double", new Color(128, 255, 0)); + + typeIntervalColors.put("byte", new Color(192, 64, 255)); + typeIntervalColors.put("word", new Color(192, 64, 255)); + typeIntervalColors.put("dword", new Color(64, 192, 255)); + typeIntervalColors.put("qword", new Color(0, 128, 255)); + typeIntervalColors.put("single", new Color(192, 255, 64)); + typeIntervalColors.put("double", new Color(128, 255, 0)); + + textFont = new Font("Dialog", Font.PLAIN, 11); + } + + public void setHorizontalSize(int hsize) { + this.hsize = hsize; + update(); + } + + public void setVerticalSize(int vsize) { + this.vsize = vsize; + update(); + } + + + private void update() { + colWidth = _colWidth[hsize]; + rowHeight = _rowHeight[vsize]; + lightGridX = _lightGridX[hsize]; + darkGridX = _darkGridX[hsize]; + textGridX = _textGridX[hsize]; + lightGridY = _lightGridY[vsize]; + darkGridY = _darkGridY[vsize]; + textGridY = _textGridY[vsize]; + thickLineWidth = _thickLineWidth[hsize]; + barSeparation = _barSeparation[vsize]; + showIntervalText = _showIntervalText[vsize][hsize]; + } + + public Color getIntervalColor(ChildInterval child) { + if (child.getOperand().contains("stack")) { + return stackIntervalColor; + } else { + final String typeName = child.getType().toLowerCase(); + final Color color = typeIntervalColors.get(typeName); + if (color == null) { + return DEFAULT_INT_COLOR; + } + return color; + } + } + + public Color getUsePosColor(UsePosition usePos) { + switch (usePos.getKind()) { + case 'L': + return usePosColorL; + case 'S': + return usePosColorS; + case 'M': + return usePosColorM; + default: + //throw new Error("illegal use kind"); + return usePosColorOther; + } + } +} diff --git a/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/icons/Icons.java b/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/icons/Icons.java new file mode 100644 index 000000000000..b6a03563c6a6 --- /dev/null +++ b/visualizer/C1Visualizer/IntervalEditor/src/main/java/at/ssw/visualizer/interval/icons/Icons.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.interval.icons; + +/** + * + * @author Christian Wimmer + */ +public class Icons { + private static final String PATH = "at/ssw/visualizer/interval/icons/"; + + public static final String INTERVALS = PATH + "intervals.gif"; + + public static final String HSIZE_LARGE = PATH + "hsizelarge.gif"; + public static final String HSIZE_MEDIUM = PATH + "hsizemedium.gif"; + public static final String HSIZE_SMALL = PATH + "hsizesmall.gif"; + public static final String VSIZE_LARGE = PATH + "vsizelarge.gif"; + public static final String VSIZE_MEDIUM = PATH + "vsizemedium.gif"; + public static final String VSIZE_SMALL = PATH + "vsizesmall.gif"; + + private Icons() { + } +} diff --git a/visualizer/C1Visualizer/IntervalEditor/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/IntervalEditor/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..a82c399dfbd7 --- /dev/null +++ b/visualizer/C1Visualizer/IntervalEditor/src/main/nbm/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.interval +OpenIDE-Module-Layer: at/ssw/visualizer/interval/layer.xml +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/interval/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/Bundle.properties b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/Bundle.properties new file mode 100644 index 000000000000..d79d161b97f9 --- /dev/null +++ b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=Interval Editor diff --git a/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/hsizelarge.gif b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/hsizelarge.gif new file mode 100644 index 000000000000..8afa7e666765 Binary files /dev/null and b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/hsizelarge.gif differ diff --git a/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/hsizemedium.gif b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/hsizemedium.gif new file mode 100644 index 000000000000..0d2706bc6ad1 Binary files /dev/null and b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/hsizemedium.gif differ diff --git a/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/hsizesmall.gif b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/hsizesmall.gif new file mode 100644 index 000000000000..e02277ad97fb Binary files /dev/null and b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/hsizesmall.gif differ diff --git a/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/intervals.gif b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/intervals.gif new file mode 100644 index 000000000000..5ef0ed7f13c7 Binary files /dev/null and b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/intervals.gif differ diff --git a/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/vsizelarge.gif b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/vsizelarge.gif new file mode 100644 index 000000000000..59970cb01916 Binary files /dev/null and b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/vsizelarge.gif differ diff --git a/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/vsizemedium.gif b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/vsizemedium.gif new file mode 100644 index 000000000000..74fb050b6db7 Binary files /dev/null and b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/vsizemedium.gif differ diff --git a/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/vsizesmall.gif b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/vsizesmall.gif new file mode 100644 index 000000000000..0f30937d5a32 Binary files /dev/null and b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/icons/vsizesmall.gif differ diff --git a/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/layer.xml b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/layer.xml new file mode 100644 index 000000000000..ff687d0b50cf --- /dev/null +++ b/visualizer/C1Visualizer/IntervalEditor/src/main/resources/at/ssw/visualizer/interval/layer.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/IntervalView/pom.xml b/visualizer/C1Visualizer/IntervalView/pom.xml new file mode 100644 index 000000000000..dd1600e2aaa6 --- /dev/null +++ b/visualizer/C1Visualizer/IntervalView/pom.xml @@ -0,0 +1,97 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + IntervalView + 1.14-SNAPSHOT + nbm + IntervalView + + UTF-8 + + + + at.ssw.visualizer + VisualizerUI + ${project.version} + + + at.ssw.visualizer + CompilationModel + ${project.version} + + + org.netbeans.api + org-openide-actions + ${netbeans.version} + + + org.netbeans.api + org-openide-loaders + ${netbeans.version} + + + org.netbeans.api + org-openide-nodes + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + org.netbeans.api + org-openide-windows + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.IntervalView + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/IntervalView/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/IntervalView/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..cade72e44f21 --- /dev/null +++ b/visualizer/C1Visualizer/IntervalView/src/main/nbm/manifest.mf @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.interval.view +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/interval/view/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 \ No newline at end of file diff --git a/visualizer/C1Visualizer/IntervalView/src/main/resources/at/ssw/visualizer/interval/view/Bundle.properties b/visualizer/C1Visualizer/IntervalView/src/main/resources/at/ssw/visualizer/interval/view/Bundle.properties new file mode 100644 index 000000000000..5fe72303fd99 --- /dev/null +++ b/visualizer/C1Visualizer/IntervalView/src/main/resources/at/ssw/visualizer/interval/view/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=Interval View diff --git a/visualizer/C1Visualizer/NativeCodeEditor/pom.xml b/visualizer/C1Visualizer/NativeCodeEditor/pom.xml new file mode 100644 index 000000000000..771623279e8a --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeEditor/pom.xml @@ -0,0 +1,128 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + NativeCodeEditor + 1.14-SNAPSHOT + nbm + NativeCodeEditor + + UTF-8 + + + + at.ssw.visualizer + VisualizerUI + ${project.version} + + + at.ssw.visualizer + CompilationModel + ${project.version} + + + at.ssw.visualizer + TextEditor + ${project.version} + + + org.netbeans.api + org-netbeans-modules-editor + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-fold + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-lib + ${netbeans.version} + + + org.netbeans.api + org-openide-awt + ${netbeans.version} + + + org.netbeans.api + org-openide-nodes + ${netbeans.version} + + + org.netbeans.api + org-openide-text + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + org.netbeans.api + org-openide-util-ui + ${netbeans.version} + + + org.netbeans.api + org-openide-windows + ${netbeans.version} + + + org.netbeans.api + org-netbeans-libs-jna + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.nc + at.ssw.visualizer.nc.model + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 17 + 17 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/NCEditor.java b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/NCEditor.java new file mode 100644 index 000000000000..97e356751a3c --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/NCEditor.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.nc; + +import at.ssw.visualizer.texteditor.Editor; +import org.openide.windows.CloneableTopComponent; + +/** + * + * @author Alexander Reder + */ +public class NCEditor extends Editor { + + public NCEditor(NCEditorSupport support) { + super(support); + } + + @Override + protected CloneableTopComponent createClonedObject() { + NCEditor editor = new NCEditor((NCEditorSupport) cloneableEditorSupport()); + editor.setActivatedNodes(getActivatedNodes()); + return editor; + } + +} diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/NCEditorKit.java b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/NCEditorKit.java new file mode 100644 index 000000000000..b0d9f2e1a792 --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/NCEditorKit.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.nc; + +import at.ssw.visualizer.nc.model.NCScanner; +import at.ssw.visualizer.texteditor.EditorKit; +import java.util.Collection; +import javax.swing.Action; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import javax.swing.text.TextAction; +import org.netbeans.editor.Syntax; +import org.netbeans.modules.editor.NbEditorKit; +import org.openide.util.Lookup; +import org.openide.util.actions.CallableSystemAction; +import org.openide.util.lookup.Lookups; + +/** + * + * @author Alexander Reder + */ +public class NCEditorKit extends EditorKit { + + @Override + public Syntax createSyntax(Document document) { + return new NCScanner(); + } + + @Override + public String getContentType() { + return NCEditorSupport.MIME_TYPE; + } + + /** + * Add a costum CodeFoldingPopupAction to enable code folding of + * the LIR Comments in the native code. + */ + @Override + protected Action[] getCustomActions() { + return TextAction.augmentList(super.getCustomActions(), new Action[]{ + new GenerateFoldPopupAction() + }); + } + + /** + * Extend the existing GenerateFoldPopupAction with the + * expand and collapse lir comment actions. + */ + public static class GenerateFoldPopupAction extends NbEditorKit.GenerateFoldPopupAction { + + @Override + public JMenuItem getPopupMenuItem(JTextComponent target) { + JMenuItem menu = super.getPopupMenuItem(target); + menu.add(new JPopupMenu.Separator()); + Lookup lookup = Lookups.forPath("Actions/View/NCFolding"); + Collection foldingActions = lookup.lookupAll(CallableSystemAction.class); + for (CallableSystemAction csa : foldingActions) { + menu.add(csa.getMenuPresenter()); + } + return menu; + } + } +} diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/NCEditorSupport.java b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/NCEditorSupport.java new file mode 100644 index 000000000000..90ca8b992894 --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/NCEditorSupport.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.nc; + +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.nc.icons.Icons; +import at.ssw.visualizer.nc.model.NCTextBuilder; +import at.ssw.visualizer.texteditor.EditorSupport; +import org.openide.text.CloneableEditor; +import org.openide.util.ImageUtilities; + + +/** + * + * @author Alexander Reder + */ +public class NCEditorSupport extends EditorSupport { + + public static final String MIME_TYPE = "text/x-compilation-nc"; + + public NCEditorSupport(ControlFlowGraph cfg) { + super(cfg); + this.text = new NCTextBuilder().buildDocument(cfg); + } + + public String getMimeType() { + return MIME_TYPE; + } + + @Override + protected CloneableEditor createCloneableEditor() { + return new NCEditor(this); + } + + @Override + protected void initializeCloneableEditor(CloneableEditor editor) { + super.initializeCloneableEditor(editor); + editor.setIcon(ImageUtilities.loadImage(Icons.NATIVECODE)); + } + +} diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/action/CollapseCommentsAction.java b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/action/CollapseCommentsAction.java new file mode 100644 index 000000000000..e0059202827b --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/action/CollapseCommentsAction.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.nc.action; + +import at.ssw.visualizer.nc.icons.Icons; +import at.ssw.visualizer.nc.model.NCTextBuilder; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.fold.FoldHierarchy; +import org.netbeans.api.editor.fold.FoldUtilities; +import org.netbeans.editor.Utilities; +import org.openide.util.HelpCtx; +import org.openide.util.actions.CallableSystemAction; + +public final class CollapseCommentsAction extends CallableSystemAction { + + public void performAction() { + JTextComponent comp = Utilities.getFocusedComponent(); + FoldHierarchy hierarchy = FoldHierarchy.get(comp); + FoldUtilities.collapse(hierarchy, NCTextBuilder.LIR_BLOCK); + } + + public String getName() { + return "Collapse LIR Comments"; + } + + @Override + protected String iconResource() { + return Icons.COLLAPSE_COMMENTS; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + protected boolean asynchronous() { + return false; + } +} diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/action/ExpandCommentsAction.java b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/action/ExpandCommentsAction.java new file mode 100644 index 000000000000..53049c1a133e --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/action/ExpandCommentsAction.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.nc.action; + +import at.ssw.visualizer.nc.icons.Icons; +import at.ssw.visualizer.nc.model.NCTextBuilder; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.fold.FoldHierarchy; +import org.netbeans.api.editor.fold.FoldUtilities; +import org.netbeans.editor.Utilities; +import org.openide.util.HelpCtx; +import org.openide.util.actions.CallableSystemAction; + +public final class ExpandCommentsAction extends CallableSystemAction { + + public void performAction() { + JTextComponent comp = Utilities.getFocusedComponent(); + FoldHierarchy hierarchy = FoldHierarchy.get(comp); + FoldUtilities.expand(hierarchy, NCTextBuilder.LIR_BLOCK); + } + + public String getName() { + return "Expand LIR Comments"; + } + + @Override + protected String iconResource() { + return Icons.EXPAND_COMMENTS; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + @Override + protected boolean asynchronous() { + return false; + } +} diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/action/ShowNCEditorAction.java b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/action/ShowNCEditorAction.java new file mode 100644 index 000000000000..c3bf9fa19f4b --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/action/ShowNCEditorAction.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.nc.action; + +import at.ssw.visualizer.core.focus.Focus; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.nc.NCEditor; +import at.ssw.visualizer.nc.NCEditorSupport; +import at.ssw.visualizer.nc.icons.Icons; +import org.openide.nodes.Node; +import org.openide.util.HelpCtx; +import org.openide.util.actions.CookieAction; + +/** + * + * @author Alexander Reder + */ +public final class ShowNCEditorAction extends CookieAction { + + protected void performAction(Node[] activatedNodes) { + ControlFlowGraph cfg = activatedNodes[0].getLookup().lookup(ControlFlowGraph.class); + if (!Focus.findEditor(NCEditor.class, cfg)) { + NCEditorSupport support = new NCEditorSupport(cfg); + support.open(); + } + } + + @Override + protected boolean enable(Node[] activatedNodes) { + if(activatedNodes == null || activatedNodes.length == 0) { + return false; + } + ControlFlowGraph cfg = activatedNodes[0].getLookup().lookup(ControlFlowGraph.class); + if(cfg == null) { + return false; + } + return cfg.getNativeMethod() != null; + } + + protected int mode() { + return CookieAction.MODE_EXACTLY_ONE; + } + + public String getName() { + return "Open Native code"; + } + + protected Class[] cookieClasses() { + return new Class[]{ControlFlowGraph.class}; + } + + @Override + protected String iconResource() { + return Icons.NATIVECODE; + } + + public HelpCtx getHelpCtx() { + return HelpCtx.DEFAULT_HELP; + } + + protected @Override + boolean asynchronous() { + return false; + } +} + diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/icons/Icons.java b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/icons/Icons.java new file mode 100644 index 000000000000..eafc3c5063d7 --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/icons/Icons.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.nc.icons; + +/** + * + * @author Alexander Reder + */ +public class Icons { + private static final String PATH = "at/ssw/visualizer/nc/icons/"; + + public static final String NATIVECODE = PATH + "nativecode.gif"; + public static final String EXPAND_COMMENTS = PATH + "expandlir.gif"; + public static final String COLLAPSE_COMMENTS = PATH + "collapselir.gif"; +} diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/model/NCScanner.java b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/model/NCScanner.java new file mode 100644 index 000000000000..c64b2605096e --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/model/NCScanner.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.nc.model; + +import at.ssw.visualizer.texteditor.model.Scanner; +import java.util.HashSet; +import java.util.Set; +import org.netbeans.editor.TokenID; + +/** + * + * @author Alexander Reder + * @author Christian Wimmer + */ +public class NCScanner extends Scanner { + private Set registerNames; + + public NCScanner() { + super("\n\r\t ,;:()$[]", NCTokenContext.contextPath); + + registerNames = new HashSet(); + registerNames.add("eax"); + registerNames.add("ebx"); + registerNames.add("ecx"); + registerNames.add("edx"); + registerNames.add("esi"); + registerNames.add("edi"); + registerNames.add("esp"); + registerNames.add("ebp"); + registerNames.add("rax"); + registerNames.add("rbx"); + registerNames.add("rcx"); + registerNames.add("rdx"); + registerNames.add("rsi"); + registerNames.add("rdi"); + registerNames.add("rsp"); + registerNames.add("rbp"); + registerNames.add("r8"); + registerNames.add("r9"); + registerNames.add("r10"); + registerNames.add("r11"); + registerNames.add("r12"); + registerNames.add("r13"); + registerNames.add("r14"); + registerNames.add("r15"); + registerNames.add("r8d"); + registerNames.add("r9d"); + registerNames.add("r10d"); + registerNames.add("r11d"); + registerNames.add("r12d"); + registerNames.add("r13d"); + registerNames.add("r14d"); + registerNames.add("r15d"); + registerNames.add("xmm0"); + registerNames.add("xmm1"); + registerNames.add("xmm2"); + registerNames.add("xmm3"); + registerNames.add("xmm4"); + registerNames.add("xmm5"); + registerNames.add("xmm6"); + registerNames.add("xmm7"); + registerNames.add("xmm8"); + registerNames.add("xmm9"); + registerNames.add("xmm10"); + registerNames.add("xmm11"); + registerNames.add("xmm12"); + registerNames.add("xmm13"); + registerNames.add("xmm14"); + registerNames.add("xmm15"); + } + + private boolean isBlock() { + return (expectChar('B') && expectChar(DIGIT) && expectChars(DIGIT) && expectEnd()) || + (expectChar('L') && expectChar(DIGIT) && expectChars(DIGIT) && expectEnd()); + } + + private boolean isAddress() { + return expectChar('0') && expectChar('x') && expectChar(HEX) && expectChars(HEX) && expectEnd(); + } + + private boolean isRegister() { + return (expectChar('%') && expectChar(LETTER) && expectChars(LETTER_DIGIT) && expectEnd()) || + isKeyword(registerNames); + } + + private boolean isInstruction() { + boolean result = (beforeChar(':') || beforeChars(DIGIT)) && expectChar(LC_LETTER) && expectChars(LC_LETTER_DIGIT); + while (result && ch == ' ' && offset + 1 < stopOffset && LC_LETTER.get(buffer[offset + 1])) { + readNext(); + result = expectChars(LC_LETTER_DIGIT); + } + return result && expectEnd(); + } + + private boolean isComment() { + int curOffset = offset; + boolean startFound = false; + while (curOffset >= 0 && buffer[curOffset] != '\n') { + if (buffer[curOffset] == ';') { + startFound = true; + tokenOffset = curOffset; + } + curOffset--; + } + if (!startFound) { + return false; + } + while (ch != '\n' && ch != EOF) { + readNext(); + } + return true; + } + + @Override + protected TokenID parseToken() { + findTokenBegin(); + if (ch == EOF) { + return NCTokenContext.EOF_TOKEN; + } else if (isComment()) { + return NCTokenContext.COMMENT_TOKEN; + } else if (isWhitespace()) { + return NCTokenContext.WHITESPACE_TOKEN; + } else if (isBlock()) { + return NCTokenContext.BLOCK_TOKEN; + } else if (isAddress()) { + return NCTokenContext.ADDRESS_TOKEN; + } else if (isRegister()) { + return NCTokenContext.REGISTER_TOKEN; + } else if (isInstruction()) { + return NCTokenContext.INSTRUCTION_TOKEN; + } else { + readToWhitespace(); + return NCTokenContext.OTHER_TOKEN; + } + } + +} diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/model/NCTextBuilder.java b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/model/NCTextBuilder.java new file mode 100644 index 000000000000..163af68641ef --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/model/NCTextBuilder.java @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.nc.model; + +import at.ssw.visualizer.model.Compilation; +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.nc.NCEditorSupport; +import at.ssw.visualizer.texteditor.model.BlockRegion; +import at.ssw.visualizer.texteditor.model.FoldingRegion; +import at.ssw.visualizer.texteditor.model.HoverParser; +import at.ssw.visualizer.texteditor.model.Text; +import at.ssw.visualizer.texteditor.model.TextBuilder; +import at.ssw.visualizer.texteditor.model.TextRegion; +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.netbeans.api.editor.fold.FoldType; +import org.netbeans.editor.TokenID; + +/** + * + * @author Alexander Reder + */ +public class NCTextBuilder extends TextBuilder { + + public static final FoldType KIND_BLOCK = new FoldType("..."); + public static final FoldType LIR_BLOCK = new FoldType(""); + private CodeBlock codeBlock = null; + private CommentBlock commentBlock = null; + + public NCTextBuilder() { + super(); + scanner = new NCScanner(); + } + + public Text buildDocument(ControlFlowGraph cfg) { + Compilation compilation = cfg.getCompilation(); + DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM); + + text.append(compilation.getMethod()).append("\n"); + text.append(dateFormat.format(compilation.getDate())).append("\n\n"); + + return buildDocument(cfg, false); + } + + private Text buildDocument(ControlFlowGraph cfg, boolean skipLIR) { + String text = cfg.getNativeMethod().getMethodText().trim(); + if (text.startsWith("<<>>")) { + // There is no builtin disassembler so just pass it through for now + append(text); + return buildText(cfg, NCEditorSupport.MIME_TYPE); + } + String[] methodText = text.split("\n"); + + String l; + for (String s : methodText) { + l = s.trim(); + if (l.length() > 0) { + switch (l.charAt(0)) { + case '[': + checkComment(); + append(s); + append("\n"); + break; + case ';': + parseComment(cfg, s, skipLIR); + break; + case '0': + checkComment(); + parseCodeLine(l); + append(l); + append("\n"); + break; + } + } + } + if (codeBlock != null) { + foldingRegions.add(new FoldingRegion(KIND_BLOCK, codeBlock.codeStart + 1, codeBlock.start + codeBlock.length - 1, false)); + } + + return buildText(cfg, NCEditorSupport.MIME_TYPE); + } + + public String buildView(ControlFlowGraph cfg, BasicBlock[] blocks) { + if (cfg.getNativeMethod() == null) { + return "No native code available\n"; + } + Text t = buildDocument(cfg, true); + String contents = t.getText(); + StringBuilder view = new StringBuilder(); + BlockRegion reg; + for (BasicBlock bb : blocks) { + reg = t.getBlocks().get(bb); + if (reg != null) { + view.append(contents.substring(reg.getStart(), reg.getEnd())); + } + } + return view.toString(); + } + + private void parseComment(ControlFlowGraph cfg, String s, boolean skipComments) { + int i = 0; + String l = s.trim(); + while (i < l.length() && (l.charAt(i) == ';' || l.charAt(i) == ' ')) { + i++; + } + if (l.length() > i + 5 && l.substring(i, i + 5).equals("block")) { // Block beginning + if (codeBlock != null) { // append last parsed block + appendBlock(); + } + codeBlock = new CodeBlock(l.substring(i + 6, l.indexOf(' ', i + 6)), cfg); + } else if (l.contains("slow case") && codeBlock != null && codeBlock.name != null) { + appendBlock(); + codeBlock = new CodeBlock(null, cfg); + append(s); + append("\n"); + } else { // any other comment + if (commentBlock == null) { + commentBlock = new CommentBlock(); + } + if (!skipComments) { + append(s); + append("\n"); + commentBlock.length += s.length() + 1; + } + } + } + + private void parseCodeLine(String s) { + int start = text.length() + 2; + boolean refAdr = false; + String addr = null; + scanner.setText(s, 0, s.length()); + TokenID token = scanner.nextToken(); + while (token != null && token != NCTokenContext.EOF_TOKEN) { + switch (token.getNumericID()) { + case NCTokenContext.ADDRESS_TOKEN_ID: + addr = scanner.getTokenString(); + if (refAdr) { + addReference(normalizeAddr(addr), s); + addReference(trimAddr(addr), s); + } else { + hyperlinks.put(trimAddr(addr), new TextRegion(start, start + addr.length())); + hoverKeys.add(normalizeAddr(addr)); + hoverKeys.add(trimAddr(addr)); + addDefinition(normalizeAddr(addr), s); + addDefinition(trimAddr(addr), s); + refAdr = true; + } + break; + case NCTokenContext.REGISTER_TOKEN_ID: + String reg = scanner.getTokenString(); + hoverKeys.add(reg); + addReference(reg, s); + break; + } + token = scanner.nextToken(); + } + } + + private void addReference(String key, String s) { + if (!hoverReferences.containsKey(key)) { + hoverReferences.put(key, new ArrayList()); + } + String bn = codeBlock == null || codeBlock.name == null ? "" : codeBlock.name + ":\t"; + hoverReferences.get(key).add(bn + s.trim()); + } + + private void addDefinition(String key, String s) { + String bn = codeBlock == null || codeBlock.name == null ? "" : codeBlock.name + ":\t"; + hoverDefinitions.put(key, bn + s.trim()); + } + + private static boolean isAddressParseableAsSignedLong(String addr) { + return addr.length() < (16 + 2) && Character.isDigit(addr.charAt(2)); + } + + private String normalizeAddr(String addr) { + if (!isAddressParseableAsSignedLong(addr)) { + return addr; + } + StringBuilder addrString = new StringBuilder(Long.toString(Long.decode(addr), 16)); + for (int i = addrString.length(); i < 8; i++) { + addrString.insert(0, 0); + } + addrString.insert(0, "0x"); + return addrString.toString(); + } + + private String trimAddr(String addr) { + if (!isAddressParseableAsSignedLong(addr)) { + return addr; + } + return "0x" + Long.toString(Long.decode(addr), 16); + } + + private void appendBlock() { + foldingRegions.add(new FoldingRegion(KIND_BLOCK, codeBlock.codeStart, codeBlock.start + codeBlock.length - 1, false)); + if (codeBlock != null && codeBlock.basicBlock != null) { + blocks.put(codeBlock.basicBlock, + new BlockRegion(codeBlock.basicBlock, + codeBlock.start, + codeBlock.start + codeBlock.length, + codeBlock.start, + codeBlock.start + codeBlock.basicBlock.getName().length())); + } + hyperlinks.put(codeBlock.name, new TextRegion(codeBlock.start, codeBlock.start + codeBlock.name.length())); + codeBlock = null; + } + + private void checkComment() { + if (commentBlock != null) { + foldingRegions.add(new FoldingRegion(LIR_BLOCK, commentBlock.start, commentBlock.start + commentBlock.length + 1, false)); + commentBlock = null; + } + } + + private void append(String s) { + HoverParser p = new HoverParser(s); + while (p.hasNext()) { + int start = text.length(); + String part = p.next(); + text.append(part); + if (codeBlock != null) { + codeBlock.length += part.length(); + } + + if (p.getHover() != null) { + regionHovers.put(new TextRegion(start, text.length()), p.getHover()); + } + } + } + + protected void buildHighlighting() { + scanner.setText(text.toString(), 0, text.length()); + TokenID token = scanner.nextToken(); + Map> highlightings = new HashMap>(); + String name; + while (token != null && token != NCTokenContext.EOF_TOKEN) { + switch (token.getNumericID()) { + case NCTokenContext.ADDRESS_TOKEN_ID: + name = trimAddr(scanner.getTokenString()); + if (!highlightings.containsKey(scanner.getTokenString()) && !highlightings.containsKey(name)) { + List tr = new ArrayList(); + highlightings.put(normalizeAddr(name), tr); + highlightings.put(name, tr); + } + highlightings.get(normalizeAddr(name)).add(new TextRegion(scanner.getTokenOffset(), scanner.getOffset())); + highlightings.get(name).add(new TextRegion(scanner.getTokenOffset(), scanner.getOffset())); + break; + case NCTokenContext.BLOCK_TOKEN_ID: + case NCTokenContext.REGISTER_TOKEN_ID: + if (!highlightings.containsKey(scanner.getTokenString())) { + highlightings.put(scanner.getTokenString(), new ArrayList()); + } + highlightings.get(scanner.getTokenString()).add(new TextRegion(scanner.getTokenOffset(), scanner.getOffset())); + break; + } + token = scanner.nextToken(); + } + for (String key : highlightings.keySet()) { + List regions = highlightings.get(key); + highlighting.put(key, regions.toArray(new TextRegion[regions.size()])); + } + } + + private class CodeBlock { + + private String name; + private BasicBlock basicBlock; + private int start; + private int codeStart; + private int length; + + private CodeBlock(String name, ControlFlowGraph cfg) { + this.name = name; + basicBlock = name == null ? null : cfg.getBasicBlockByName(name); + start = text.length(); + if (basicBlock != null) { + appendBlockDetails(basicBlock); + append("\n"); + } + codeStart = text.length() - 1; + length = text.length() - start; + } + } + + private class CommentBlock { + + int start; + int length; + + private CommentBlock() { + start = text.length(); + length = 0; + } + } +} diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/model/NCTokenContext.java b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/model/NCTokenContext.java new file mode 100644 index 000000000000..cbc8c25ffd80 --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/at/ssw/visualizer/nc/model/NCTokenContext.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.nc.model; + +import org.netbeans.editor.BaseTokenID; +import org.netbeans.editor.TokenContext; +import org.netbeans.editor.TokenContextPath; +import org.netbeans.editor.TokenID; + +/** + * + * @author Alexander Reder + */ +public class NCTokenContext extends TokenContext { + + public static final int EOF_TOKEN_ID = -1; + public static final int WHITESPACE_TOKEN_ID = -2; + public static final int OTHER_TOKEN_ID = -3; + public static final int ADDRESS_TOKEN_ID = 4; + public static final int INSTRUCTION_TOKEN_ID = 5; + public static final int REGISTER_TOKEN_ID = 6; + public static final int BLOCK_TOKEN_ID = 7; + public static final int COMMENT_TOKEN_ID = 8; + + public static final TokenID EOF_TOKEN = new BaseTokenID("eof", EOF_TOKEN_ID); + public static final TokenID WHITESPACE_TOKEN = new BaseTokenID("whitespace", WHITESPACE_TOKEN_ID); + public static final TokenID OTHER_TOKEN = new BaseTokenID("other", OTHER_TOKEN_ID); + public static final TokenID ADDRESS_TOKEN = new BaseTokenID("address", ADDRESS_TOKEN_ID); + public static final TokenID INSTRUCTION_TOKEN = new BaseTokenID("instruction", INSTRUCTION_TOKEN_ID); + public static final TokenID REGISTER_TOKEN = new BaseTokenID("register", REGISTER_TOKEN_ID); + public static final TokenID BLOCK_TOKEN = new BaseTokenID("block", BLOCK_TOKEN_ID); + public static final TokenID COMMENT_TOKEN = new BaseTokenID("comment", COMMENT_TOKEN_ID); + + public static final NCTokenContext context = new NCTokenContext(); + public static final TokenContextPath contextPath = context.getContextPath(); + + private NCTokenContext() { + super("nc-"); + addTokenID(WHITESPACE_TOKEN); + addTokenID(OTHER_TOKEN); + addTokenID(ADDRESS_TOKEN); + addTokenID(INSTRUCTION_TOKEN); + addTokenID(REGISTER_TOKEN); + addTokenID(BLOCK_TOKEN); + addTokenID(COMMENT_TOKEN); + } + +} diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/com/oracle/max/hcfdis/HexCodeFile.java b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/com/oracle/max/hcfdis/HexCodeFile.java new file mode 100644 index 000000000000..85843a46086b --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeEditor/src/main/java/com/oracle/max/hcfdis/HexCodeFile.java @@ -0,0 +1,558 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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. + */ +/* + * Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ +package com.oracle.max.hcfdis; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.Serializable; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A HexCodeFile is a textual format for representing a chunk of machine code along with extra + * information that can be used to enhance a disassembly of the code. + *

+ * A pseudo grammar for a HexCodeFile is given below. + * + *

+ *     HexCodeFile ::= Platform Delim HexCode Delim (OptionalSection Delim)*
+ *
+ *     OptionalSection ::= Comment | OperandComment | JumpTable | LookupTable
+ *
+ *     Platform ::= "Platform" ISA WordWidth
+ *
+ *     HexCode ::= "HexCode" StartAddress HexDigits
+ *
+ *     Comment ::= "Comment" Position String
+ *
+ *     OperandComment ::= "OperandComment" Position String
+ *
+ *     EntryFormat ::= 4 | 8 | "OFFSET" | "KEY2_OFFSET"
+ *
+ *     JumpTable ::= "JumpTable" Position EntryFormat Low High
+ *
+ *     LookupTable ::= "LookupTable" Position NPairs KeySize OffsetSize
+ *
+ *     Position, EntrySize, Low, High, NPairs KeySize OffsetSize ::= int
+ *
+ *     Delim := "<||@"
+ * 
+ *

+ * There must be exactly one HexCode and Platform part in a HexCodeFile. The length of HexDigits + * must be even as each pair of digits represents a single byte. + *

+ * Below is an example of a valid Code input: + * + *

+ *
+ *  Platform AMD64 64  <||@
+ *  HexCode 0 e8000000009090904883ec084889842410d0ffff48893c24e800000000488b3c24488bf0e8000000004883c408c3  <||@
+ *  Comment 24 frame-ref-map: +0 {0}
+ *  at java.lang.String.toLowerCase(String.java:2496) [bci: 1]
+ *              |0
+ *     locals:  |stack:0:a
+ *     stack:   |stack:0:a
+ *    <||@
+ *  OperandComment 24 {java.util.Locale.getDefault()}  <||@
+ *  Comment 36 frame-ref-map: +0 {0}
+ *  at java.lang.String.toLowerCase(String.java:2496) [bci: 4]
+ *              |0
+ *     locals:  |stack:0:a
+ *    <||@
+ *  OperandComment 36 {java.lang.String.toLowerCase(Locale)}  <||@
+ *
+ * 
+ */ +public class HexCodeFile { + /** + * Provides extra information about instructions or data at specific positions in the target + * code. This is optional information that can be used to enhance a disassembly of the code. + */ + public abstract static class CodeAnnotation implements Serializable { + private static final long serialVersionUID = 928798596620568715L; + public final int position; + + public CodeAnnotation(int position) { + this.position = position; + } + + public int getPosition() { + return position; + } + } + + /** + * Describes a table of signed offsets embedded in the code. The offsets are relative to the + * starting address of the table. This type of table maybe generated when translating a + * multi-way branch based on a key value from a dense value set (e.g. the {@code tableswitch} + * JVM instruction). + *

+ * The table is indexed by the contiguous range of integers from {@link #low} to {@link #high} + * inclusive. + */ + public static final class JumpTable extends CodeAnnotation { + private static final long serialVersionUID = -6520017513070589383L; + + /** + * Constants denoting the format and size of each entry in a jump table. + */ + public enum EntryFormat { + /** + * Each entry is a 4 byte offset. The base of the offset is platform dependent. + */ + OFFSET(4), + + /** + * Each entry is a secondary key value followed by a 4 byte offset. The base of the + * offset is platform dependent. + */ + KEY2_OFFSET(8); + + EntryFormat(int size) { + this.size = size; + } + + /** + * Gets the size of an entry in bytes. + */ + public final int size; + } + + /** + * The low value in the key range (inclusive). + */ + public final int low; + + /** + * The high value in the key range (inclusive). + */ + public final int high; + + /** + * The size (in bytes) of each table entry. + */ + public final EntryFormat entryFormat; + + public JumpTable(int position, int low, int high, EntryFormat entryFormat) { + super(position); + if (high <= low) { + throw new IllegalArgumentException(String.format("low (%d) is not less than high(%d)", low, high)); + } + this.low = low; + this.high = high; + this.entryFormat = entryFormat; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "@" + position + ": [" + low + " .. " + high + "]"; + } + } + + /** + * Describes a table of key and offset pairs. The offset in each table entry is relative to the + * address of the table. This type of table maybe generated when translating a multi-way branch + * based on a key value from a sparse value set (e.g. the {@code lookupswitch} JVM instruction). + */ + public static final class LookupTable extends CodeAnnotation { + private static final long serialVersionUID = 6797908737446993328L; + /** + * The number of entries in the table. + */ + public final int npairs; + + /** + * The size (in bytes) of entry's key. + */ + public final int keySize; + + /** + * The size (in bytes) of entry's offset value. + */ + public final int offsetSize; + + public LookupTable(int position, int npairs, int keySize, int offsetSize) { + super(position); + this.npairs = npairs; + this.keySize = keySize; + this.offsetSize = offsetSize; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "@" + position + ": [npairs=" + npairs + ", keySize=" + keySize + ", offsetSize=" + offsetSize + "]"; + } + } + + public static final String NEW_LINE = "\n"; + public static final String SECTION_DELIM = " <||@"; + public static final Pattern SECTION = Pattern.compile("(\\S+)\\s+(.*)", Pattern.DOTALL); + public static final Pattern COMMENT = Pattern.compile("(\\d+)\\s+(.*)", Pattern.DOTALL); + public static final Pattern OPERAND_COMMENT = COMMENT; + public static final Pattern JUMP_TABLE = Pattern.compile("(\\d+)\\s+(\\S+)\\s+(-{0,1}\\d+)\\s+(-{0,1}\\d+)\\s*"); + public static final Pattern LOOKUP_TABLE = Pattern.compile("(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s*"); + public static final Pattern HEX_CODE = Pattern.compile("(\\p{XDigit}+)(?:\\s+(\\p{XDigit}*))?"); + public static final Pattern PLATFORM = Pattern.compile("(\\S+)\\s+(\\S+)", Pattern.DOTALL); + + /** + * Delimiter placed before a HexCodeFile when embedded in a string/stream. + */ + public static final String EMBEDDED_HCF_OPEN = "<<> comments = new TreeMap<>(); + + /** + * Map from a machine code position to a comment for the operands of the instruction at the + * position. + */ + public final Map> operandComments = new TreeMap<>(); + + public final byte[] code; + + public final ArrayList jumpTables = new ArrayList<>(); + + public final ArrayList lookupTables = new ArrayList<>(); + + public final String isa; + + public final int wordWidth; + + public final long startAddress; + + public HexCodeFile(byte[] code, long startAddress, String isa, int wordWidth) { + this.code = code; + this.startAddress = startAddress; + this.isa = isa; + this.wordWidth = wordWidth; + } + + /** + * Parses a string in the format produced by {@link #toString()} to produce a + * {@link HexCodeFile} object. + */ + public static HexCodeFile parse(String input, int sourceOffset, String source, String sourceName) { + return new Parser(input, sourceOffset, source, sourceName).hcf; + } + + /** + * Formats this HexCodeFile as a string that can be parsed with {@link #parse}. + */ + @Override + public String toString() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + writeTo(baos); + return baos.toString(); + } + + public void writeTo(OutputStream out) { + PrintStream ps = out instanceof PrintStream ? (PrintStream) out : new PrintStream(out); + ps.printf("Platform %s %d %s%n", isa, wordWidth, SECTION_DELIM); + ps.printf("HexCode %x %s %s%n", startAddress, HexCodeFile.hexCodeString(code), SECTION_DELIM); + + for (JumpTable table : jumpTables) { + ps.printf("JumpTable %d %d %d %d %s%n", table.position, table.entryFormat, table.low, table.high, SECTION_DELIM); + } + + for (LookupTable table : lookupTables) { + ps.printf("LookupTable %d %d %d %d %s%n", table.position, table.npairs, table.keySize, table.keySize, SECTION_DELIM); + } + + for (Map.Entry> e : comments.entrySet()) { + int pos = e.getKey(); + for (String comment : e.getValue()) { + ps.printf("Comment %d %s %s%n", pos, comment, SECTION_DELIM); + } + } + + for (Map.Entry> e : operandComments.entrySet()) { + int pos = e.getKey(); + for (String comment : e.getValue()) { + ps.printf("OperandComment %d %s %s%n", pos, comment, SECTION_DELIM); + } + } + ps.flush(); + } + + /** + * Formats a byte array as a string of hex digits. + */ + public static String hexCodeString(byte[] code) { + StringBuilder sb = new StringBuilder(code.length * 2); + for (int b : code) { + String hex = Integer.toHexString(b & 0xff); + if (hex.length() == 1) { + sb.append('0'); + } + sb.append(hex); + } + return sb.toString(); + } + + /** + * Adds a comment to the list of comments for a given position. + */ + public void addComment(int pos, String comment) { + List list = comments.computeIfAbsent(pos, k -> new ArrayList<>()); + list.add(encodeString(comment)); + } + + /** + * Sets an operand comment for a given position. + */ + public void addOperandComment(int pos, String comment) { + List list = operandComments.computeIfAbsent(pos, k -> new ArrayList<>()); + list.add(encodeString(comment)); + } + + /** + * Modifies a string to mangle any substrings matching {@link #SECTION_DELIM}. + */ + public static String encodeString(String string) { + int index; + String s = string; + while ((index = s.indexOf(SECTION_DELIM)) != -1) { + s = s.substring(0, index) + " < |@" + s.substring(index + SECTION_DELIM.length()); + } + return s; + } + + /** + * Helper class to parse a string in the format produced by {@link HexCodeFile#toString()} and + * produce a {@link HexCodeFile} object. + */ + static class Parser { + + final String input; + final String inputSource; + String isa; + int wordWidth; + byte[] code; + long startAddress; + HexCodeFile hcf; + + Parser(String input, int sourceOffset, String source, String sourceName) { + this.input = input; + this.inputSource = sourceName; + parseSections(sourceOffset, source); + } + + void makeHCF() { + if (hcf == null) { + if (isa != null && wordWidth != 0 && code != null) { + hcf = new HexCodeFile(code, startAddress, isa, wordWidth); + } + } + } + + void checkHCF(String section, int offset) { + check(hcf != null, offset, section + " section must be after Platform and HexCode section"); + } + + void check(boolean condition, int offset, String message) { + if (!condition) { + throw error(offset, message); + } + } + + Error error(int offset, String message) { + throw new Error(errorMessage(offset, message)); + } + + String errorMessage(int offset, String message) { + assert offset < input.length(); + InputPos inputPos = filePos(offset); + int lineEnd = input.indexOf(HexCodeFile.NEW_LINE, offset); + int lineStart = offset - inputPos.col; + String line = lineEnd == -1 ? input.substring(lineStart) : input.substring(lineStart, lineEnd); + return String.format("%s:%d: %s%n%s%n%" + (inputPos.col + 1) + "s", inputSource, inputPos.line, message, line, "^"); + } + + static class InputPos { + final int line; + final int col; + + InputPos(int line, int col) { + this.line = line; + this.col = col; + } + } + + InputPos filePos(int index) { + assert input != null; + int lineStart = input.lastIndexOf(HexCodeFile.NEW_LINE, index) + 1; + + int pos = input.indexOf(HexCodeFile.NEW_LINE); + int line = 1; + while (pos > 0 && pos < index) { + line++; + pos = input.indexOf(HexCodeFile.NEW_LINE, pos + 1); + } + return new InputPos(line, index - lineStart); + } + + void parseSections(int offset, String source) { + assert input.startsWith(source, offset); + int index = 0; + int endIndex = source.indexOf(SECTION_DELIM); + while (endIndex != -1) { + while (source.charAt(index) <= ' ') { + index++; + } + String section = source.substring(index, endIndex).trim(); + parseSection(offset + index, section); + index = endIndex + SECTION_DELIM.length(); + endIndex = source.indexOf(SECTION_DELIM, index); + } + } + + int parseInt(int offset, String value) { + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + throw error(offset, "Not a valid integer: " + value); + } + } + + void parseSection(int offset, String section) { + if (section.isEmpty()) { + return; + } + assert input.startsWith(section, offset); + Matcher m = HexCodeFile.SECTION.matcher(section); + check(m.matches(), offset, "Section does not match pattern " + HexCodeFile.SECTION); + + String header = m.group(1); + String body = m.group(2); + int headerOffset = offset + m.start(1); + int bodyOffset = offset + m.start(2); + + switch (header) { + case "Platform": + check(isa == null, bodyOffset, "Duplicate Platform section found"); + m = HexCodeFile.PLATFORM.matcher(body); + check(m.matches(), bodyOffset, "Platform does not match pattern " + HexCodeFile.PLATFORM); + isa = m.group(1); + wordWidth = parseInt(bodyOffset + m.start(2), m.group(2)); + makeHCF(); + break; + case "HexCode": + check(code == null, bodyOffset, "Duplicate Code section found"); + m = HexCodeFile.HEX_CODE.matcher(body); + check(m.matches(), bodyOffset, "Code does not match pattern " + HexCodeFile.HEX_CODE); + String hexAddress = m.group(1); + startAddress = new BigInteger(hexAddress, 16).longValue(); + String hexCode = m.group(2); + if (hexCode == null) { + code = new byte[0]; + } else { + check((hexCode.length() % 2) == 0, bodyOffset, "Hex code length must be even"); + code = new byte[hexCode.length() / 2]; + for (int i = 0; i < code.length; i++) { + String hexByte = hexCode.substring(i * 2, (i + 1) * 2); + code[i] = (byte) Integer.parseInt(hexByte, 16); + } + } + makeHCF(); + break; + case "Comment": { + checkHCF("Comment", headerOffset); + m = HexCodeFile.COMMENT.matcher(body); + check(m.matches(), bodyOffset, "Comment does not match pattern " + HexCodeFile.COMMENT); + int pos = parseInt(bodyOffset + m.start(1), m.group(1)); + String comment = m.group(2); + hcf.addComment(pos, comment); + break; + } + case "OperandComment": { + checkHCF("OperandComment", headerOffset); + m = HexCodeFile.OPERAND_COMMENT.matcher(body); + check(m.matches(), bodyOffset, "OperandComment does not match pattern " + HexCodeFile.OPERAND_COMMENT); + int pos = parseInt(bodyOffset + m.start(1), m.group(1)); + String comment = m.group(2); + hcf.addOperandComment(pos, comment); + break; + } + case "JumpTable": { + checkHCF("JumpTable", headerOffset); + m = HexCodeFile.JUMP_TABLE.matcher(body); + check(m.matches(), bodyOffset, "JumpTable does not match pattern " + HexCodeFile.JUMP_TABLE); + int pos = parseInt(bodyOffset + m.start(1), m.group(1)); + JumpTable.EntryFormat entryFormat = parseJumpTableEntryFormat(m, bodyOffset); + int low = parseInt(bodyOffset + m.start(3), m.group(3)); + int high = parseInt(bodyOffset + m.start(4), m.group(4)); + hcf.jumpTables.add(new JumpTable(pos, low, high, entryFormat)); + break; + } + case "LookupTable": { + checkHCF("LookupTable", headerOffset); + m = HexCodeFile.LOOKUP_TABLE.matcher(body); + check(m.matches(), bodyOffset, "LookupTable does not match pattern " + HexCodeFile.LOOKUP_TABLE); + int pos = parseInt(bodyOffset + m.start(1), m.group(1)); + int npairs = parseInt(bodyOffset + m.start(2), m.group(2)); + int keySize = parseInt(bodyOffset + m.start(3), m.group(3)); + int offsetSize = parseInt(bodyOffset + m.start(4), m.group(4)); + hcf.lookupTables.add(new LookupTable(pos, npairs, keySize, offsetSize)); + break; + } + default: + throw error(offset, "Unknown section header: " + header); + } + } + + private JumpTable.EntryFormat parseJumpTableEntryFormat(Matcher m, int bodyOffset) throws Error { + String entryFormatName = m.group(2); + JumpTable.EntryFormat entryFormat; + if ("4".equals(entryFormatName)) { + entryFormat = JumpTable.EntryFormat.OFFSET; + } else if ("8".equals(entryFormatName)) { + entryFormat = JumpTable.EntryFormat.KEY2_OFFSET; + } else { + try { + entryFormat = JumpTable.EntryFormat.valueOf(entryFormatName); + } catch (IllegalArgumentException e) { + throw error(bodyOffset + m.start(2), "Not a valid " + JumpTable.EntryFormat.class.getSimpleName() + " value: " + entryFormatName); + } + } + return entryFormat; + } + + } +} diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/NativeCodeEditor/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..9be25d9e6494 --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeEditor/src/main/nbm/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.nc +OpenIDE-Module-Layer: at/ssw/visualizer/nc/layer.xml +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/nc/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/Bundle.properties b/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/Bundle.properties new file mode 100644 index 000000000000..8f185ffe92a1 --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/Bundle.properties @@ -0,0 +1,10 @@ +OpenIDE-Module-Name=Native Code Editor + +text/x-compilation-nc=Native Code +nc-whitespace=Whitespace +nc-other=Other +nc-address=Address +nc-instruction=Instruction +nc-register=Register +nc-block=Block +nc-comment=Comment diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/icons/collapselir.gif b/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/icons/collapselir.gif new file mode 100644 index 000000000000..b7e9733e07da Binary files /dev/null and b/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/icons/collapselir.gif differ diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/icons/expandlir.gif b/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/icons/expandlir.gif new file mode 100644 index 000000000000..c61bca4b620d Binary files /dev/null and b/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/icons/expandlir.gif differ diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/icons/nativecode.gif b/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/icons/nativecode.gif new file mode 100644 index 000000000000..1854ca7fb364 Binary files /dev/null and b/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/icons/nativecode.gif differ diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/layer.xml b/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/layer.xml new file mode 100644 index 000000000000..579e1b5cb97e --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/layer.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/model/NetBeans-NC-fontsColors.xml b/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/model/NetBeans-NC-fontsColors.xml new file mode 100644 index 000000000000..42c5b3f353a4 --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeEditor/src/main/resources/at/ssw/visualizer/nc/model/NetBeans-NC-fontsColors.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/NativeCodeView/pom.xml b/visualizer/C1Visualizer/NativeCodeView/pom.xml new file mode 100644 index 000000000000..d55a0ccdc6b6 --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeView/pom.xml @@ -0,0 +1,102 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + NativeCodeView + 1.14-SNAPSHOT + nbm + NativeCodeView + + UTF-8 + + + + at.ssw.visualizer + CompilationModel + ${project.version} + + + at.ssw.visualizer + NativeCodeEditor + ${project.version} + + + at.ssw.visualizer + TextEditor + ${project.version} + + + org.netbeans.api + org-netbeans-modules-editor + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-lib + ${netbeans.version} + + + org.netbeans.api + org-openide-awt + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + org.netbeans.api + org-openide-windows + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.NativeCodeView + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/NativeCodeView/src/main/java/at/ssw/visualizer/nc/view/NCViewAction.java b/visualizer/C1Visualizer/NativeCodeView/src/main/java/at/ssw/visualizer/nc/view/NCViewAction.java new file mode 100644 index 000000000000..4a90ee7950ec --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeView/src/main/java/at/ssw/visualizer/nc/view/NCViewAction.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.nc.view; + +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; +import org.openide.util.NbBundle; +import org.openide.windows.TopComponent; + +/** + * + * @author Alexander Reder + */ +public class NCViewAction extends AbstractAction { + + public NCViewAction() { + super(NbBundle.getMessage(NCViewAction.class, "CTL_NCViewAction")); + } + + public void actionPerformed(ActionEvent evt) { + TopComponent win = NCViewTopComponent.findInstance(); + win.open(); + win.requestActive(); + } +} diff --git a/visualizer/C1Visualizer/NativeCodeView/src/main/java/at/ssw/visualizer/nc/view/NCViewTopComponent.java b/visualizer/C1Visualizer/NativeCodeView/src/main/java/at/ssw/visualizer/nc/view/NCViewTopComponent.java new file mode 100644 index 000000000000..03e75e3a8765 --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeView/src/main/java/at/ssw/visualizer/nc/view/NCViewTopComponent.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.nc.view; + +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.nc.NCEditorKit; +import at.ssw.visualizer.nc.model.NCTextBuilder; +import at.ssw.visualizer.texteditor.view.AbstractTextViewTopComponent; +import java.io.Serializable; +import org.openide.windows.TopComponent; +import org.openide.windows.WindowManager; + +/** + * + * @author Alexander Reder + */ +final class NCViewTopComponent extends AbstractTextViewTopComponent { + + private static NCViewTopComponent instance; + private static final String PREFERRED_ID = "NCViewTopComponent"; + + private NCViewTopComponent() { + super(new NCEditorKit()); + setName("Native Code"); + setToolTipText("Native Code"); + + } + + @Override + protected String getContent(ControlFlowGraph cfg, BasicBlock[] blocks) { + NCTextBuilder builder = new NCTextBuilder(); + return builder.buildView(cfg, blocks); + } + + // + public static synchronized NCViewTopComponent getDefault() { + if (instance == null) { + instance = new NCViewTopComponent(); + } + return instance; + } + + public static synchronized NCViewTopComponent findInstance() { + return (NCViewTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID); + } + + @Override + public int getPersistenceType() { + return TopComponent.PERSISTENCE_ALWAYS; + } + + @Override + public void componentOpened() { + // TODO add custom code on component opening + } + + @Override + public void componentClosed() { + // TODO add custom code on component closing + } + + /** replaces this in object stream */ + @Override + public Object writeReplace() { + return new ResolvableHelper(); + } + + @Override + protected String preferredID() { + return PREFERRED_ID; + } + + final static class ResolvableHelper implements Serializable { + + private static final long serialVersionUID = 1L; + + public Object readResolve() { + return NCViewTopComponent.getDefault(); + } + } + // + +} diff --git a/visualizer/C1Visualizer/NativeCodeView/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/NativeCodeView/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..ffbe37e2ec47 --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeView/src/main/nbm/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.nc.view +OpenIDE-Module-Layer: at/ssw/visualizer/nc/view/layer.xml +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/nc/view/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/NativeCodeView/src/main/resources/at/ssw/visualizer/nc/view/Bundle.properties b/visualizer/C1Visualizer/NativeCodeView/src/main/resources/at/ssw/visualizer/nc/view/Bundle.properties new file mode 100644 index 000000000000..c3c0cf25b76e --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeView/src/main/resources/at/ssw/visualizer/nc/view/Bundle.properties @@ -0,0 +1,4 @@ +CTL_NCViewAction=NCView +CTL_NCViewTopComponent=NCView Window +HINT_NCViewTopComponent=This is a NCView window +OpenIDE-Module-Name=Native Code View diff --git a/visualizer/C1Visualizer/NativeCodeView/src/main/resources/at/ssw/visualizer/nc/view/NCViewTopComponentSettings.xml b/visualizer/C1Visualizer/NativeCodeView/src/main/resources/at/ssw/visualizer/nc/view/NCViewTopComponentSettings.xml new file mode 100644 index 000000000000..9bd4eba5524f --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeView/src/main/resources/at/ssw/visualizer/nc/view/NCViewTopComponentSettings.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/visualizer/C1Visualizer/NativeCodeView/src/main/resources/at/ssw/visualizer/nc/view/NCViewTopComponentWstcref.xml b/visualizer/C1Visualizer/NativeCodeView/src/main/resources/at/ssw/visualizer/nc/view/NCViewTopComponentWstcref.xml new file mode 100644 index 000000000000..27564a7c4dd4 --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeView/src/main/resources/at/ssw/visualizer/nc/view/NCViewTopComponentWstcref.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/visualizer/C1Visualizer/NativeCodeView/src/main/resources/at/ssw/visualizer/nc/view/layer.xml b/visualizer/C1Visualizer/NativeCodeView/src/main/resources/at/ssw/visualizer/nc/view/layer.xml new file mode 100644 index 000000000000..4ea3297c74df --- /dev/null +++ b/visualizer/C1Visualizer/NativeCodeView/src/main/resources/at/ssw/visualizer/nc/view/layer.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/TextEditor/pom.xml b/visualizer/C1Visualizer/TextEditor/pom.xml new file mode 100644 index 000000000000..0136c4dc7cc9 --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/pom.xml @@ -0,0 +1,139 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + TextEditor + 1.14-SNAPSHOT + nbm + TextEditor + + UTF-8 + + + + at.ssw.visualizer + VisualizerUI + ${project.version} + + + at.ssw.visualizer + CompilationModel + ${project.version} + + + org.netbeans.api + org-netbeans-modules-editor + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-fold + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-lib + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-lib2 + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-mimelookup + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-settings + ${netbeans.version} + + + org.netbeans.api + org-netbeans-modules-editor-util + ${netbeans.version} + + + org.netbeans.api + org-openide-actions + ${netbeans.version} + + + org.netbeans.api + org-openide-awt + ${netbeans.version} + + + org.netbeans.api + org-openide-nodes + ${netbeans.version} + + + org.netbeans.api + org-openide-text + ${netbeans.version} + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + org.netbeans.api + org-openide-windows + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.texteditor + at.ssw.visualizer.texteditor.model + at.ssw.visualizer.texteditor.view + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/Editor.java b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/Editor.java new file mode 100644 index 000000000000..c8d60379c04b --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/Editor.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.texteditor; + +import at.ssw.visualizer.core.selection.Selection; +import at.ssw.visualizer.core.selection.SelectionManager; +import at.ssw.visualizer.core.selection.SelectionProvider; +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.texteditor.model.BlockRegion; +import at.ssw.visualizer.texteditor.model.Text; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import org.openide.text.CloneableEditor; +import org.openide.windows.TopComponent; + +/** + * Abstract template class of a Editor class of the Visualizer. + * + * Must be initialized with a custom EditorSupport class and the + * method creatClonedObject must be overwritten by + * the Editor implementation. + * + * @author Alexander Reder + */ +public abstract class Editor extends CloneableEditor implements SelectionProvider { + + protected Selection selection; + private boolean selectionUpdating; + private BasicBlock[] curBlocks; + private boolean initialized; + + protected Editor(EditorSupport support) { + super(support); + selection = new Selection(); + selection.put(support.getControlFlowGraph()); + selection.addChangeListener(selectionListener); + } + + public Selection getSelection() { + return selection; + } + + @Override + protected void componentShowing() { + super.componentShowing(); + if (!initialized) { + getEditorPane().addCaretListener(caretListener); + initialized = true; + } + } + + @Override + protected void componentActivated() { + super.componentActivated(); + SelectionManager.getDefault().setSelection(selection); + } + + @Override + protected void componentClosed() { + super.componentClosed(); + SelectionManager.getDefault().removeSelection(selection); + } + + @Override + public int getPersistenceType() { + return TopComponent.PERSISTENCE_NEVER; + } + + private ChangeListener selectionListener = new ChangeListener() { + public void stateChanged(ChangeEvent event) { + if (selectionUpdating) { + return; + } + selectionUpdating = true; + + Text text = (Text) getEditorPane().getDocument().getProperty(Text.class); + BasicBlock[] newBlocks = selection.get(BasicBlock[].class); + + if (newBlocks != null && newBlocks.length > 0 && !Arrays.equals(curBlocks, newBlocks)) { + BlockRegion r = text.getBlocks().get(newBlocks[0]); + int startOffset = r.getNameStart(); + int endOffset = r.getNameEnd(); + + if (newBlocks.length > 1) { + for (BasicBlock b : newBlocks) { + r = text.getBlocks().get(b); + startOffset = Math.min(startOffset, r.getStart()); + endOffset = Math.max(endOffset, r.getEnd()); + } + } + + getEditorPane().select(startOffset, endOffset); + } + curBlocks = newBlocks; + selectionUpdating = false; + } + }; + + + private CaretListener caretListener = new CaretListener() { + public void caretUpdate(CaretEvent event) { + if (selectionUpdating) { + return; + } + selectionUpdating = true; + + Text text = (Text) getEditorPane().getDocument().getProperty(Text.class); + List newBlocks = new ArrayList(); + int startOffset = Math.min(event.getDot(), event.getMark()); + int endOffset = Math.max(event.getDot(), event.getMark()); + + for (BlockRegion region : text.getBlocks().values()) { + if (region.getStart() <= endOffset && region.getEnd() > startOffset) { + newBlocks.add(region.getBlock()); + } + } + + curBlocks = newBlocks.toArray(new BasicBlock[newBlocks.size()]); + selection.put(curBlocks); + selectionUpdating = false; + } + }; + +} diff --git a/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/EditorKit.java b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/EditorKit.java new file mode 100644 index 000000000000..b867ce8987c6 --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/EditorKit.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.texteditor; + +import at.ssw.visualizer.texteditor.tooltip.ToolTipAction; +import javax.swing.Action; +import javax.swing.text.TextAction; +import org.netbeans.modules.editor.NbEditorKit; + +/** + * Abstract template class of a EditorKit class of the Visualizer. + * + * The scanner field must be initialized with the + * custom Scanner implementation and the method + * getContentType must be overwritten and return the mime type + * for the editor. + * + * @author Alexander Reder + */ +public abstract class EditorKit extends NbEditorKit { + + @Override + protected Action[] getCustomActions() { + Action[] prev = super.getCustomActions(); + Action[] added = new Action[]{new ToolTipAction()}; + if (prev != null) { + return TextAction.augmentList(prev, added); + } else { + return added; + } + } + +} diff --git a/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/EditorSupport.java b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/EditorSupport.java new file mode 100644 index 000000000000..2fb92441802e --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/EditorSupport.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.texteditor; + +import at.ssw.visualizer.model.Compilation; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.texteditor.model.Text; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.beans.VetoableChangeListener; +import java.beans.VetoableChangeSupport; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Date; +import javax.swing.text.EditorKit; +import javax.swing.text.StyledDocument; +import org.openide.cookies.EditorCookie; +import org.openide.cookies.EditCookie; +import org.openide.text.CloneableEditorSupport; +import org.openide.windows.CloneableOpenSupport; + +/** + * Abstract template class of a EditorSupport class of the + * Visulizer. + * + * The text field must be initialized by the implementing class + * and the methods createCloneableEditor and + * initializeCloneableEditor must be overwritten by the implenting class. + * createCloneableEditor must return a custom implementation + * of the Editor class. + * initializeClonableEditor is used to set the icon, e.g. + * editor.setIcon(Utilities.loadImage(IconsImage)); . + * + * @author Bernhard Stiftner + * @author Christian Wimmer + * @author Alexander Reder + */ +public abstract class EditorSupport extends CloneableEditorSupport implements EditCookie, EditorCookie, EditorCookie.Observable { + + protected ControlFlowGraph cfg; + protected Text text; + + protected EditorSupport(ControlFlowGraph cfg) { + super(new Env()); + ((Env) this.env).editorSupport = this; + this.cfg = cfg; + } + + public ControlFlowGraph getControlFlowGraph() { + return cfg; + } + + @Override + protected StyledDocument createStyledDocument(EditorKit kit) { + StyledDocument doc = super.createStyledDocument(kit); + + // Back-link from Document to our internal data model. + doc.putProperty(Text.class, text); + doc.putProperty(Compilation.class, cfg.getCompilation()); + doc.putProperty(ControlFlowGraph.class, cfg); + + return doc; + } + + public abstract String getMimeType(); + + protected String messageOpening() { + return "Opening " + messageToolTip(); + } + + protected String messageOpened() { + return "Opened " + messageToolTip(); + } + + protected String messageSave() { + throw new UnsupportedOperationException("Not supported yet."); + } + + protected String messageName() { + return cfg.getCompilation().getShortName(); + } + + protected String messageToolTip() { + return cfg.getCompilation().getMethod() + " - " + cfg.getName(); + } + + public static class Env implements CloneableEditorSupport.Env { + + private PropertyChangeSupport prop = new PropertyChangeSupport(this); + private VetoableChangeSupport veto = new VetoableChangeSupport(this); + + /** + * Back-link to outer class EditorSupport. Env must be a static class + * because it is passed to super constructor of EditorSupport. + */ + private EditorSupport editorSupport; + + public InputStream inputStream() throws IOException { + return new ByteArrayInputStream(editorSupport.text.getText().getBytes()); + } + + public OutputStream outputStream() throws IOException { + throw new IOException("Editor is readonly"); + } + + public Date getTime() { + return editorSupport.cfg.getCompilation().getDate(); + } + + public String getMimeType() { + return editorSupport.getMimeType(); + } + + public boolean isValid() { + return true; + } + + public boolean isModified() { + return false; + } + + public void markModified() throws IOException { + throw new IOException("Editor is readonly"); + } + + public void unmarkModified() { + // Nothing to do. + } + + public CloneableOpenSupport findCloneableOpenSupport() { + return editorSupport; + } + + public void addPropertyChangeListener(PropertyChangeListener l) { + prop.addPropertyChangeListener(l); + } + + public void removePropertyChangeListener(PropertyChangeListener l) { + prop.removePropertyChangeListener(l); + } + + public void addVetoableChangeListener(VetoableChangeListener l) { + veto.addVetoableChangeListener(l); + } + + public void removeVetoableChangeListener(VetoableChangeListener l) { + veto.removeVetoableChangeListener(l); + } + } + +} diff --git a/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/fold/FoldManager.java b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/fold/FoldManager.java new file mode 100644 index 000000000000..e8b488aa151f --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/fold/FoldManager.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.texteditor.fold; + +import at.ssw.visualizer.texteditor.model.FoldingRegion; +import at.ssw.visualizer.texteditor.model.Text; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JComponent; +import javax.swing.event.DocumentEvent; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.fold.Fold; +import org.netbeans.editor.CodeFoldingSideBar; +import org.netbeans.spi.editor.fold.FoldHierarchyTransaction; +import org.netbeans.spi.editor.fold.FoldOperation; + +/** + * + * @author Alexander Reder + */ +public class FoldManager implements org.netbeans.spi.editor.fold.FoldManager { + + protected FoldOperation operation; + + public void init(FoldOperation operation) { + this.operation = operation; + } + + public void initFolds(FoldHierarchyTransaction transaction) { + Document document = operation.getHierarchy().getComponent().getDocument(); + Text text = (Text) document.getProperty(Text.class); + if (document.getLength() == 0 || text == null) { + return; + } + + try { + for (FoldingRegion fr : text.getFoldings()) { + operation.addToHierarchy(fr.getKind(), fr.getKind().toString(), fr.isInitiallyCollapsed(), fr.getStart(), fr.getEnd(), 0, 0, null, transaction); + } + } catch (BadLocationException ex) { + Logger logger = Logger.getLogger(FoldManager.class.getName()); + logger.log(Level.SEVERE, ex.getMessage(), ex); + } + } + + public void insertUpdate(DocumentEvent arg0, FoldHierarchyTransaction arg1) { + } + + public void removeUpdate(DocumentEvent arg0, FoldHierarchyTransaction arg1) { + } + + public void changedUpdate(DocumentEvent arg0, FoldHierarchyTransaction arg1) { + } + + public void removeEmptyNotify(Fold arg0) { + } + + public void removeDamagedNotify(Fold arg0) { + } + + public void expandNotify(Fold arg0) { + } + + public void release() { + } + + public static class FoldManagerFactory implements org.netbeans.spi.editor.fold.FoldManagerFactory { + + public FoldManager createFoldManager() { + return new FoldManager(); + } + } + + public static class SideBarFactory implements org.netbeans.editor.SideBarFactory { + + public JComponent createSideBar(JTextComponent target) { + return new CodeFoldingSideBar(target); + } + } + +} diff --git a/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/highlight/HighlightsContainer.java b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/highlight/HighlightsContainer.java new file mode 100644 index 000000000000..78c3605261b4 --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/highlight/HighlightsContainer.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.texteditor.highlight; + +import at.ssw.visualizer.texteditor.model.Scanner; +import at.ssw.visualizer.texteditor.model.Text; +import at.ssw.visualizer.texteditor.model.TextRegion; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; +import javax.swing.text.AttributeSet; +import javax.swing.text.Caret; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import javax.swing.text.SimpleAttributeSet; +import org.netbeans.api.editor.mimelookup.MimeLookup; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.api.editor.settings.FontColorSettings; +import org.netbeans.editor.TokenID; +import org.netbeans.spi.editor.highlighting.HighlightsSequence; +import org.netbeans.spi.editor.highlighting.ZOrder; +import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer; +import org.openide.util.WeakListeners; + +/** + * + * @author Christian Wimmer + * @author Alexander Reder + */ +public class HighlightsContainer extends AbstractHighlightsContainer { + + private static final String HIGHLIGHT_COLORING = "at-ssw-visualizer-highlight"; + + protected final JTextComponent component; + protected final Document document; + protected final AttributeSet highlightColoring; + protected final Scanner scanner; + + protected TextRegion[] curRegions = null; + + private final CaretListener caretListener = new CaretListener() { + + public void caretUpdate(CaretEvent event) { + TextRegion[] newRegions = findRegions(); + if (newRegions != curRegions) { + curRegions = newRegions; + fireHighlightsChange(0, document.getLength()); + } + } + + }; + + protected HighlightsContainer(JTextComponent component, Document document) { + this.document = document; + this.component = component; + component.addCaretListener(WeakListeners.create(CaretListener.class, caretListener, component)); + + // Load the coloring. + Text t = (Text) document.getProperty(Text.class); + MimePath mimePath = MimePath.parse(t.getMimeType()); + FontColorSettings fcs = MimeLookup.getLookup(mimePath).lookup(FontColorSettings.class); + AttributeSet highlight = fcs.getFontColors(HIGHLIGHT_COLORING); + highlightColoring = highlight != null ? highlight : SimpleAttributeSet.EMPTY; + + scanner = t.getScanner(); + scanner.setText(document); + curRegions = findRegions(); + } + + public HighlightsSequence getHighlights(int startOffset, int endOffset) { + return new RegionSequence(); + } + + protected TextRegion[] findRegions() { + Text text = (Text) document.getProperty(Text.class); + Caret caret = component.getCaret(); + if (text == null || caret == null) { + return null; + } + scanner.findTokenBegin(caret.getDot()); + TokenID token = scanner.nextToken(); + if (token.getNumericID() < 0) { + return null; + } + + return text.getHighlighting(scanner.getTokenString()); + } + + protected class RegionSequence implements HighlightsSequence { + + private int idx = -1; + + public boolean moveNext() { + idx++; + return curRegions != null && idx < curRegions.length; + } + + public int getStartOffset() { + return curRegions[idx].getStart(); + } + + public int getEndOffset() { + return curRegions[idx].getEnd(); + } + + public AttributeSet getAttributes() { + return highlightColoring; + } + } + + public static final class HighlightsLayerFactory implements org.netbeans.spi.editor.highlighting.HighlightsLayerFactory { + + public org.netbeans.spi.editor.highlighting.HighlightsLayer[] createLayers(Context context) { + Text t = (Text) context.getDocument().getProperty(Text.class); + if(t == null) { + return new org.netbeans.spi.editor.highlighting.HighlightsLayer[0]; + } + return new org.netbeans.spi.editor.highlighting.HighlightsLayer[]{org.netbeans.spi.editor.highlighting.HighlightsLayer.create("at-ssw-visualizer-highlighting", ZOrder.SHOW_OFF_RACK, true, new HighlightsContainer(context.getComponent(), context.getDocument()))}; + } + + } +} diff --git a/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/hyperlink/HyperlinkProvider.java b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/hyperlink/HyperlinkProvider.java new file mode 100644 index 000000000000..d04184e85e80 --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/hyperlink/HyperlinkProvider.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.texteditor.hyperlink; + +import at.ssw.visualizer.texteditor.model.Scanner; +import at.ssw.visualizer.texteditor.model.Text; +import at.ssw.visualizer.texteditor.model.TextRegion; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import org.netbeans.editor.TokenID; +import org.netbeans.editor.Utilities; + +/** + * + * @author Bernhard Stiftner + * @author Christian Wimmer + * @author Alexander Reder + */ +public class HyperlinkProvider implements org.netbeans.lib.editor.hyperlink.spi.HyperlinkProvider { + + protected Scanner scanner = null; + + protected TextRegion findTarget(Document doc, int offset) { + Text text = (Text) doc.getProperty(Text.class); + if (text == null) { + return null; + } + + scanner = text.getScanner(); + scanner.setText(doc); + scanner.findTokenBegin(offset); + TokenID token = scanner.nextToken(); + if (token.getNumericID() < 0) { + return null; + } + + return text.getHyperlinkTarget(scanner.getTokenString()); + } + + public boolean isHyperlinkPoint(Document doc, int offset) { + return findTarget(doc, offset) != null; + } + + public int[] getHyperlinkSpan(Document doc, int offset) { + if (findTarget(doc, offset) != null) { + return new int[]{scanner.getTokenOffset(), scanner.getTokenOffset() + scanner.getTokenLength()}; + } + return null; + } + + public void performClickAction(Document doc, int offset) { + TextRegion target = findTarget(doc, offset); + if (target != null) { + JTextComponent editor = Utilities.getFocusedComponent(); + editor.select(target.getStart(), target.getEnd()); + } + } + +} diff --git a/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/BlockRegion.java b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/BlockRegion.java new file mode 100644 index 000000000000..b61922553ea3 --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/BlockRegion.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.texteditor.model; + +import at.ssw.visualizer.model.cfg.BasicBlock; + +/** + * + * @author Christian Wimmer + */ +public class BlockRegion extends TextRegion { + + private BasicBlock block; + + private int nameStart; + private int nameEnd; + + public BlockRegion(BasicBlock block, int start, int end, int nameStart, int nameEnd) { + super(start, end); + this.block = block; + this.nameStart = nameStart; + this.nameEnd = nameEnd; + } + + + public BasicBlock getBlock() { + return block; + } + + public int getNameStart() { + return nameStart; + } + + public int getNameEnd() { + return nameEnd; + } +} diff --git a/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/FoldingRegion.java b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/FoldingRegion.java new file mode 100644 index 000000000000..838e7484916e --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/FoldingRegion.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.texteditor.model; + +import org.netbeans.api.editor.fold.FoldType; + +/** + * + * @author Christian Wimmer + * @author Alexander Reder + */ +public class FoldingRegion extends TextRegion { + + + private FoldType kind; + private boolean initallyCollapsed; + + + public FoldingRegion(FoldType kind, int start, int end, boolean initiallyCollapsed) { + super(start, end); + this.kind = kind; + this.initallyCollapsed = initiallyCollapsed; + } + + + public FoldType getKind() { + return kind; + } + + public boolean isInitiallyCollapsed() { + return initallyCollapsed; + } + +} diff --git a/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/HoverParser.java b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/HoverParser.java new file mode 100644 index 000000000000..ff6cb0be7bdc --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/HoverParser.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.texteditor.model; + +import java.util.Iterator; + +/** + * + * @author ChristianWimmer + */ +public class HoverParser implements Iterator { + + private static String HOVER_START = "<@"; + private static String HOVER_SEP = "|@"; + private static String HOVER_END = ">@"; + private String text; + private int curPos; + private String curText; + private String curHover; + private boolean curNewLine; + + public static String firstLine(String text) { + if (text == null) { + return ""; + } + HoverParser p = new HoverParser(text); + StringBuilder result = new StringBuilder(text.length()); + while (p.hasNext()) { + String part = p.next(); + if (p.isNewLine()) { + break; + } + result.append(part); + } + return result.toString(); + } + + public HoverParser(String text) { + this.text = text; + } + + private void advance() { + int lineStart = text.indexOf('\n', curPos); + int nextStart = text.indexOf(HOVER_START, curPos); + + if (lineStart == curPos) { + curText = "\n"; + curHover = null; + curPos = lineStart + 1; + curNewLine = true; + while (curPos < text.length() && text.charAt(curPos) <= ' ') { + curPos++; + } + return; + } + curNewLine = false; + if (lineStart != -1 && (nextStart == -1 || lineStart < nextStart)) { + curText = text.substring(curPos, lineStart); + curHover = null; + curPos = lineStart; + return; + } + + if (nextStart == curPos) { + int nextSep = text.indexOf(HOVER_SEP, nextStart); + if (nextSep != -1) { + int nextEnd = text.indexOf(HOVER_END, nextSep); + if (nextEnd != -1) { + curText = text.substring(nextStart + HOVER_START.length(), nextSep); + curHover = text.substring(nextSep + HOVER_SEP.length(), nextEnd); + while (curHover.endsWith("\n")) { + curHover = curHover.substring(0, curHover.length() - 1); + } + curPos = nextEnd + HOVER_END.length(); + return; + } + } + } + + if (nextStart == curPos) { + // Incomplete hover sequence. Make sure we make progress by just advancing to the next chararter. + nextStart++; + } + + if (nextStart != -1) { + curText = text.substring(curPos, nextStart); + curHover = null; + curPos = nextStart; + } else if (curPos < text.length()) { + curText = text.substring(curPos); + curHover = null; + curPos = text.length(); + } else { + curText = null; + curHover = null; + } + } + + public boolean hasNext() { + return curPos < text.length(); + } + + public String next() { + advance(); + return curText; + } + + public String getHover() { + return curHover; + } + + public boolean isNewLine() { + return curNewLine; + } + + public void remove() { + throw new UnsupportedOperationException("Not supported."); + } +} diff --git a/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/Scanner.java b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/Scanner.java new file mode 100644 index 000000000000..9d7a988198a6 --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/Scanner.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.texteditor.model; + +import java.util.BitSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import org.netbeans.editor.Syntax; +import org.netbeans.editor.TokenContextPath; + +/** + * The implementing class must specify the used TokenIDs and + * implement the scanner for the specified text. + * + * @author Alexander Reder + * @author Christian Wimmer + */ +public abstract class Scanner extends Syntax { + protected static final int EOF = 0; + protected static final BitSet DIGIT = charsOf("0123456789"); + protected static final BitSet HEX = charsOf("0123456789abcdefABCDEF"); + protected static final BitSet LETTER = charsOf("_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + protected static final BitSet LETTER_DIGIT = charsOf("0123456789_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + protected static final BitSet LC_LETTER = charsOf("_abcdefghijklmnopqrstuvwxyz"); + protected static final BitSet LC_LETTER_DIGIT = charsOf("0123456789_abcdefghijklmnopqrstuvwxyz"); + protected static final BitSet REFERENCE_CHARS = charsOf("*. "); + + protected static BitSet charsOf(String s) { + BitSet result = new BitSet(); + for (int i = 0; i < s.length(); i++) { + result.set(s.charAt(i)); + } + return result; + } + + protected BitSet whitespace; + protected char ch; + + public Scanner(String whitespace, TokenContextPath tokenContextPath) { + this.whitespace = charsOf(whitespace); + this.whitespace.set(EOF); + + this.tokenContextPath = tokenContextPath; + } + + public void setText(Document document) { + try { + setText(document.getText(0, document.getLength()), 0, document.getLength()); + } catch (BadLocationException ex) { + Logger logger = Logger.getLogger(Scanner.class.getName()); + logger.log(Level.SEVERE, ex.getMessage(), ex); + } + } + + public void setText(String s, int offset, int length) { + this.buffer = s.toCharArray(); + this.offset = offset; + this.tokenOffset = offset; + this.stopOffset = Math.min(buffer.length, offset + length); + } + + public String getTokenString() { + return new String(buffer, getTokenOffset(), getTokenLength()); + } + + public void findTokenBegin(int offset) { + this.offset = Math.max(offset, 0); + findTokenBegin(); + } + + /** + * If offset is in a token this method will read backwards until a + * whitespace character occurs. + */ + protected void findTokenBegin() { + if (offset >= stopOffset) { + offset = stopOffset - 1; + } + + if (!whitespace.get(buffer[offset])) { + while (offset > 0 && !whitespace.get(buffer[offset - 1])) { + offset--; + } + } + ch = buffer[offset]; + tokenOffset = offset; + } + + /** + * Reads the next character. + */ + protected void readNext() { + offset++; + if (offset < stopOffset) { + ch = buffer[offset]; + } else { + ch = EOF; + } + } + + protected boolean isWhitespace() { + boolean result = false; + while (whitespace.get(ch) && ch != EOF) { + result = true; + readNext(); + } + return result; + } + + /** + * Read to the next whitespace + */ + protected void readToWhitespace() { + do { + readNext(); + } while (!whitespace.get(ch)); + } + + private boolean readNextOrRestart(boolean result, boolean readNext) { + if (result) { + if (readNext) { + readNext(); + } + } else { + offset = tokenOffset; + ch = buffer[offset]; + } + return result; + } + + protected boolean isKeyword(Set keywords) { + int beginOffset = offset; + int endOffset = offset; + while (endOffset < stopOffset && !whitespace.get(buffer[endOffset])) { + endOffset++; + } + String word = new String(buffer, beginOffset, endOffset - beginOffset); + if (!keywords.contains(word)) { + return false; + } + + offset = endOffset - 1; + readNext(); + return true; + } + + protected boolean expectEnd() { + return readNextOrRestart(whitespace.get(ch), false); + } + + protected boolean expectEnd(char expected) { + return readNextOrRestart(ch == expected, false) && offset + 1 < stopOffset && whitespace.get(buffer[offset + 1]); + } + + protected boolean expectChar(char expected) { + return readNextOrRestart(ch == expected, true); + } + + protected boolean expectChar(BitSet expected) { + return readNextOrRestart(expected.get(ch), true); + } + + protected boolean expectChars(BitSet expected) { + while (expected.get(ch)) { + readNext(); + } + return true; + } + + protected boolean skipUntil(char expected) { + while (ch != expected && !whitespace.get(ch)) { + readNext(); + } + return expectChar(expected); + } + + protected boolean beforeChar(char before) { + int curOffset = offset - 1; + while (curOffset >= 0 && buffer[curOffset] != before && whitespace.get(buffer[curOffset])) { + curOffset--; + } + return curOffset >= 0 && buffer[curOffset] == before; + } + + protected boolean beforeChars(BitSet before) { + int curOffset = offset - 1; + while (curOffset >= 0 && !before.get(buffer[curOffset]) && whitespace.get(buffer[curOffset])) { + curOffset--; + } + return curOffset >= 0 && before.get(buffer[curOffset]); + } +} diff --git a/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/Text.java b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/Text.java new file mode 100644 index 000000000000..364bf2fc94fc --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/Text.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.texteditor.model; + +import at.ssw.visualizer.model.Compilation; +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import java.util.Map; + +/** + * @author Christian Wimmer + * @author Alexander Reder + */ +public class Text { + + private Compilation compilation; + private ControlFlowGraph cfg; + + private String text; + private FoldingRegion[] foldings; + private Map hyperlinks; + private Map stringHovers; + private Map regionHovers; + private Map highlighting; + private Map blocks; + private Scanner scanner; + private String mimeType; + + + public Text(ControlFlowGraph cfg, String text, FoldingRegion[] foldings, Map hyperlinks, Map stringHovers, Map regionHovers, Map highlighting, Map blocks, Scanner scanner, String mimeType) { + this.compilation = cfg.getCompilation(); + this.cfg = cfg; + this.text = text; + this.foldings = foldings; + this.hyperlinks = hyperlinks; + this.stringHovers = stringHovers; + this.regionHovers = regionHovers; + this.highlighting = highlighting; + this.blocks = blocks; + this.scanner = scanner; + this.mimeType = mimeType; + } + + + public Compilation getCompilation() { + return compilation; + } + + public ControlFlowGraph getCfg() { + return cfg; + } + + public String getText() { + return text; + } + + public FoldingRegion[] getFoldings() { + return foldings; + } + + public TextRegion getHyperlinkTarget(String key) { + return hyperlinks.get(key); + } + + public String getStringHover(String key) { + return stringHovers.get(key); + } + + public String getRegionHover(int position) { + for (TextRegion r : regionHovers.keySet()) { + if (r.getStart() <= position && r.getEnd() >= position) { + return regionHovers.get(r); + } + } + return null; + } + + public TextRegion[] getHighlighting(String key) { + return highlighting.get(key); + } + + public Map getBlocks() { + return blocks; + } + + public Scanner getScanner() { + return scanner; + } + + public String getMimeType() { + return mimeType; + } +} diff --git a/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/TextBuilder.java b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/TextBuilder.java new file mode 100644 index 000000000000..99fb03c3d34c --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/TextBuilder.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.texteditor.model; + +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * + * @author Alexander Reder + */ +public abstract class TextBuilder { + + protected StringBuilder text; + protected Scanner scanner; + protected List foldingRegions; + protected Map hyperlinks; + protected Map stringHovers; + protected Map regionHovers; + protected Map highlighting; + protected Map blocks; + protected Set hoverKeys; + protected Map hoverDefinitions; + protected Map> hoverReferences; + + public TextBuilder() { + text = new StringBuilder(4 * 1024); + foldingRegions = new ArrayList(); + hyperlinks = new HashMap(); + stringHovers = new HashMap(); + regionHovers = new HashMap(); + highlighting = new HashMap(); + blocks = new HashMap(); + hoverKeys = new HashSet(); + hoverDefinitions = new HashMap(); + hoverReferences = new HashMap>(); + } + + public abstract Text buildDocument(ControlFlowGraph cfg); + + protected abstract void buildHighlighting(); + + protected Text buildText(ControlFlowGraph cfg, String mimeType) { + buildHovers(); + buildHighlighting(); + return new Text(cfg, text.toString(), foldingRegions.toArray(new FoldingRegion[foldingRegions.size()]), hyperlinks, stringHovers, regionHovers, highlighting, blocks, scanner, mimeType); + } + + protected void appendBlockDetails(BasicBlock block) { + text.append(blockDetails(block)); + } + + protected String blockDetails(BasicBlock block) { + StringBuilder sb = new StringBuilder(); + sb.append(block.getName()); + hoverKeys.add(block.getName()); + appendBlockList(sb, " <- ", block.getPredecessors()); + appendBlockList(sb, " -> ", block.getSuccessors()); + appendBlockList(sb, " xh ", block.getXhandlers()); + if (block.getDominator() != null) { + sb.append(" dom ").append(block.getDominator().getName()); + } + sb.append(" [").append(block.getFromBci()).append(", ").append(block.getToBci()).append("]"); + appendList(sb, " ", block.getFlags()); + + if (block.getLoopDepth() > 0) { + sb.append(" (loop ").append(block.getLoopIndex()).append(" depth ").append(block.getLoopDepth()).append(")"); + } + return sb.toString(); + } + + protected void appendBlockList(StringBuilder sb, String prefix, List blocks) { + for (BasicBlock block : blocks) { + sb.append(prefix); + prefix = ","; + sb.append(block.getName()); + } + } + + private void appendList(StringBuilder sb, String prefix, List values) { + for (String value : values) { + sb.append(prefix).append(value); + prefix = ","; + } + } + + protected void buildHovers() { + StringBuilder sb; + for(String key : hoverKeys) { + sb = new StringBuilder(); + if(hoverDefinitions.containsKey(key) && hoverReferences.containsKey(key)) { + sb.append("Definition;\n"); + sb.append(hoverDefinitions.get(key)); + sb.append("\n"); + } + if(hoverReferences.containsKey(key)) { + sb.append("References:\n"); + for(String ref : hoverReferences.get(key)) { + sb.append(ref); + sb.append("\n"); + } + } + if(sb.length() > 0) { + stringHovers.put(key, sb.toString().substring(0, sb.length() - 1)); + } + } + } + +} diff --git a/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/TextRegion.java b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/TextRegion.java new file mode 100644 index 000000000000..266c75dc030d --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/model/TextRegion.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.texteditor.model; + +/** + * + * @author Christian Wimmer + * @author Alexander Reder + */ +public class TextRegion { + + private int start; + private int end; + + public TextRegion(int start, int end) { + this.start = start; + this.end = end; + } + + + public int getStart() { + return start; + } + + public int getEnd() { + return end; + } + +} diff --git a/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/tooltip/StyledToolTip.java b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/tooltip/StyledToolTip.java new file mode 100644 index 000000000000..e2c6dee30c35 --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/tooltip/StyledToolTip.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.texteditor.tooltip; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Graphics; +import javax.swing.JEditorPane; +import javax.swing.JPanel; +import javax.swing.border.LineBorder; +import javax.swing.text.EditorKit; + +/** + * + * @author Bernhard Stiftner + * @author Christian Wimmer + * @author Alexander Reder + */ +public class StyledToolTip extends JPanel { + + private final String text; + private final JEditorPane toolTipPane; + + public StyledToolTip(String text, EditorKit editorKit) { + this.text = text; + + setBackground(new Color(235, 235, 163)); + setBorder(new LineBorder(Color.BLACK)); + setOpaque(true); + + toolTipPane = new JEditorPane(); + toolTipPane.putClientProperty("document-view-accurate-span", true); + toolTipPane.setEditorKit(editorKit); + toolTipPane.setText(text); + + setLayout(new BorderLayout()); + add(toolTipPane, BorderLayout.CENTER); + } + + @Override + protected void paintComponent(Graphics g) { + // Workaround: Add a new line at the end so that the caret is not in the visible area. + toolTipPane.setText(text + "\n"); + super.paintComponent(g); + } +} diff --git a/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/tooltip/ToolTipAction.java b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/tooltip/ToolTipAction.java new file mode 100644 index 000000000000..a4bb61d15b93 --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/tooltip/ToolTipAction.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.texteditor.tooltip; + +import at.ssw.visualizer.texteditor.model.Scanner; +import at.ssw.visualizer.texteditor.model.Text; +import java.awt.event.ActionEvent; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.EditorActionRegistration; +import org.netbeans.editor.BaseDocument; +import org.netbeans.editor.EditorUI; +import org.netbeans.editor.PopupManager; +import org.netbeans.editor.TokenID; +import org.netbeans.editor.Utilities; +import org.netbeans.editor.ext.ExtKit; +import org.netbeans.editor.ext.ToolTipSupport; +import org.netbeans.modules.editor.NbEditorKit; + +/** + * + * @author Christian Wimmer + * @author Alexander Reder + */ +public class ToolTipAction extends NbEditorKit.NbBuildToolTipAction { + public ToolTipAction() { + putValue(NAME, ExtKit.buildToolTipAction); + } + + @Override + public void actionPerformed(ActionEvent evt, JTextComponent target) { + if (!showTooltip(target)) { + super.actionPerformed(evt, target); + } + } + + private boolean showTooltip(JTextComponent target) { + BaseDocument document = Utilities.getDocument(target); + Text text = (Text) document.getProperty(Text.class); + if (text == null) { + return false; + } + + EditorUI ui = Utilities.getEditorUI(target); + ToolTipSupport tts = ui.getToolTipSupport(); + int offset = target.viewToModel(tts.getLastMouseEvent().getPoint()); + + String toolTipText = text.getRegionHover(offset); + + if (toolTipText == null) { + Scanner scanner = text.getScanner(); + scanner.setText(document); + scanner.findTokenBegin(offset); + TokenID token = scanner.nextToken(); + if (token.getNumericID() < 0) { + return false; + } + + toolTipText = text.getStringHover(scanner.getTokenString()); + if (toolTipText == null) { + return false; + } + } + + StyledToolTip tooltip = new StyledToolTip(toolTipText, target.getUI().getEditorKit(target)); + + tts.setToolTip(tooltip, PopupManager.ViewPortBounds, PopupManager.Largest, 0, 0); + return true; + } +} diff --git a/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/view/AbstractTextViewTopComponent.java b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/view/AbstractTextViewTopComponent.java new file mode 100644 index 000000000000..db29a2d6146f --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/view/AbstractTextViewTopComponent.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.texteditor.view; + +import at.ssw.visualizer.core.selection.Selection; +import at.ssw.visualizer.core.selection.SelectionManager; +import at.ssw.visualizer.model.cfg.BasicBlock; +import at.ssw.visualizer.model.cfg.ControlFlowGraph; +import at.ssw.visualizer.texteditor.EditorKit; +import java.awt.BorderLayout; +import java.util.Arrays; +import javax.swing.BorderFactory; +import javax.swing.JEditorPane; +import javax.swing.JScrollPane; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import org.openide.windows.TopComponent; + +/** + * + * @author Alexander Reder + */ +public abstract class AbstractTextViewTopComponent extends TopComponent { + + protected ControlFlowGraph curCFG; + protected BasicBlock[] curBlocks; + + private JEditorPane editorPane; + + // EditorKit must be set in the imlementation class + public AbstractTextViewTopComponent(EditorKit kit) { + editorPane = new JEditorPane(); + editorPane.setEditorKit(kit); + editorPane.setEditable(false); + JScrollPane scrollPane = new JScrollPane(editorPane); + scrollPane.setViewportBorder(BorderFactory.createEmptyBorder()); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + setLayout(new BorderLayout()); + add(scrollPane); + } + + @Override + protected void componentShowing() { + super.componentShowing(); + SelectionManager.getDefault().addChangeListener(selectionChangeListener); + updateContent(); + } + + @Override + protected void componentHidden() { + super.componentHidden(); + SelectionManager.getDefault().removeChangeListener(selectionChangeListener); + curCFG = null; + curBlocks = null; + } + + + private ChangeListener selectionChangeListener = new ChangeListener() { + public void stateChanged(ChangeEvent event) { + updateContent(); + } + }; + + protected void updateContent() { + Selection selection = SelectionManager.getDefault().getCurSelection(); + ControlFlowGraph newCFG = selection.get(ControlFlowGraph.class); + BasicBlock[] newBlocks = selection.get(BasicBlock[].class); + + if (newCFG == null || newBlocks == null || newBlocks.length == 0) { + editorPane.setText("No block selected\n"); + } else if (curCFG != newCFG || !Arrays.equals(curBlocks, newBlocks)) { + editorPane.setText(getContent(newCFG, newBlocks)); + } + curCFG = newCFG; + curBlocks = newBlocks; + } + + protected abstract String getContent(ControlFlowGraph cfg, BasicBlock[] blocks); + +} diff --git a/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/view/PaintBugWorkaroundBar.java b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/view/PaintBugWorkaroundBar.java new file mode 100644 index 000000000000..3b280085ceb8 --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/java/at/ssw/visualizer/texteditor/view/PaintBugWorkaroundBar.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.texteditor.view; + +import java.awt.Dimension; +import javax.swing.JComponent; +import javax.swing.text.JTextComponent; +import org.netbeans.api.editor.fold.FoldUtilities; +import org.netbeans.spi.editor.SideBarFactory; + +/** + * This is a workaround for NETBEANS-6232. NB platform wrongly computes sidebar + * (line numbers, code folding) after fold expansion - until the bug is fixed, this + * component will force the sidebar's height to the text view's height. + * It is safe to remove this class + layer.xml registration after NETBEANS-6232 is fixed and + * the fixed platform is consumed in C1V. + * @author sdedic + */ +public class PaintBugWorkaroundBar extends JComponent { + private final JTextComponent target; + + PaintBugWorkaroundBar(JTextComponent target) { + this.target = target; + } + + @Override + public Dimension getPreferredSize() { + Dimension d = new Dimension(); + d.height = target.getPreferredScrollableViewportSize().height; + return d; + } + + /** + * Factory for the sidebars. Use {@link FoldUtilities#getFoldingSidebarFactory()} to + * obtain an instance. + */ + public static class Factory implements SideBarFactory { + @Override + public JComponent createSideBar(JTextComponent target) { + return new PaintBugWorkaroundBar(target); + } + } +} diff --git a/visualizer/C1Visualizer/TextEditor/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/TextEditor/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..7a64f87674e1 --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/nbm/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer.texteditor +OpenIDE-Module-Layer: at/ssw/visualizer/texteditor/layer.xml +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/texteditor/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/TextEditor/src/main/resources/at/ssw/visualizer/texteditor/Bundle.properties b/visualizer/C1Visualizer/TextEditor/src/main/resources/at/ssw/visualizer/texteditor/Bundle.properties new file mode 100644 index 000000000000..d0b97b7ed427 --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/resources/at/ssw/visualizer/texteditor/Bundle.properties @@ -0,0 +1 @@ +OpenIDE-Module-Name=Text Editor diff --git a/visualizer/C1Visualizer/TextEditor/src/main/resources/at/ssw/visualizer/texteditor/layer.xml b/visualizer/C1Visualizer/TextEditor/src/main/resources/at/ssw/visualizer/texteditor/layer.xml new file mode 100644 index 000000000000..fa61fdc49e67 --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/resources/at/ssw/visualizer/texteditor/layer.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/TextEditor/src/main/resources/at/ssw/visualizer/texteditor/preferences.xml b/visualizer/C1Visualizer/TextEditor/src/main/resources/at/ssw/visualizer/texteditor/preferences.xml new file mode 100644 index 000000000000..c69c6f9d3186 --- /dev/null +++ b/visualizer/C1Visualizer/TextEditor/src/main/resources/at/ssw/visualizer/texteditor/preferences.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/visualizer/C1Visualizer/VisualizerUI/pom.xml b/visualizer/C1Visualizer/VisualizerUI/pom.xml new file mode 100644 index 000000000000..0cba6d67c4c0 --- /dev/null +++ b/visualizer/C1Visualizer/VisualizerUI/pom.xml @@ -0,0 +1,73 @@ + + 4.0.0 + + C1Visualizer-parent + at.ssw.visualizer + 1.14-SNAPSHOT + + at.ssw.visualizer + VisualizerUI + 1.14-SNAPSHOT + nbm + VisualizerUI + + UTF-8 + + + + org.netbeans.api + org-openide-util + ${netbeans.version} + + + org.netbeans.api + org-openide-util-lookup + ${netbeans.version} + + + org.netbeans.api + org-openide-windows + ${netbeans.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + + at.ssw.visualizer.core.selection + at.ssw.visualizer.core.focus + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/VisualizerUI/src/main/java/at/ssw/visualizer/core/focus/Focus.java b/visualizer/C1Visualizer/VisualizerUI/src/main/java/at/ssw/visualizer/core/focus/Focus.java new file mode 100644 index 000000000000..59b342b42b50 --- /dev/null +++ b/visualizer/C1Visualizer/VisualizerUI/src/main/java/at/ssw/visualizer/core/focus/Focus.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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. + */ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package at.ssw.visualizer.core.focus; + +import at.ssw.visualizer.core.selection.SelectionProvider; +import org.openide.windows.TopComponent; + +public class Focus { + public static interface SelectionClosure { + public boolean matches(TopComponent tc); + } + + public static boolean findEditor(Class c, Object data) { + for (TopComponent tc : TopComponent.getRegistry().getOpened()) { + if (tc.getClass() == c && ((SelectionProvider)tc).getSelection().get(data.getClass()) == data) { +// WindowManager wm = WindowManager.getDefault(); +// Mode mode = wm.findMode(tc); +// if(mode != null && mode != wm.getCurrentMaximizedMode()) { +// wm.switchMaximizedMode(null); +// } + tc.requestActive(); + return true; + } + } + return false; + } +} diff --git a/visualizer/C1Visualizer/VisualizerUI/src/main/java/at/ssw/visualizer/core/selection/Selection.java b/visualizer/C1Visualizer/VisualizerUI/src/main/java/at/ssw/visualizer/core/selection/Selection.java new file mode 100644 index 000000000000..7f3008978e0a --- /dev/null +++ b/visualizer/C1Visualizer/VisualizerUI/src/main/java/at/ssw/visualizer/core/selection/Selection.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.core.selection; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.swing.Timer; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +/** + * + * @author Christian Wimmer + */ +public class Selection { + private Map elements; + private List listeners; + private Timer eventTimer; + + private ActionListener eventTimerListener = new ActionListener() { + public void actionPerformed(ActionEvent event) { + doFireChangeEvent(); + } + }; + + public Selection() { + elements = new HashMap(); + listeners = new ArrayList(); + eventTimer = new Timer(100, eventTimerListener); + eventTimer.setRepeats(false); + } + + private void doPut(Class clazz, Object element) { + elements.put(clazz, element); + for (Class i : clazz.getInterfaces()) { + doPut(i, element); + } + } + + public void put(Object element) { + doPut(element.getClass(), element); + fireChangeEvent(); + SelectionManager.getDefault().fireChangeEvent(); + } + + @SuppressWarnings(value = "unchecked") + public T get(Class clazz) { + return (T) elements.get(clazz); + } + + + protected void doFireChangeEvent() { + ChangeEvent event = new ChangeEvent(this); + for (ChangeListener listener : listeners.toArray(new ChangeListener[listeners.size()])) { + listener.stateChanged(event); + } + } + + protected void fireChangeEvent() { + eventTimer.restart(); + } + + public void addChangeListener(ChangeListener listener) { + listeners.add(listener); + } + + public void removeChangeListener(ChangeListener listener) { + listeners.remove(listener); + } +} diff --git a/visualizer/C1Visualizer/VisualizerUI/src/main/java/at/ssw/visualizer/core/selection/SelectionManager.java b/visualizer/C1Visualizer/VisualizerUI/src/main/java/at/ssw/visualizer/core/selection/SelectionManager.java new file mode 100644 index 000000000000..94dbe2594f83 --- /dev/null +++ b/visualizer/C1Visualizer/VisualizerUI/src/main/java/at/ssw/visualizer/core/selection/SelectionManager.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.core.selection; + +import javax.swing.event.ChangeListener; + +/** + * + * @author Christian Wimmer + */ +public class SelectionManager { + private static final SelectionManager SINGLETON = new SelectionManager(); + + public static SelectionManager getDefault() { + return SINGLETON; + } + + + /** Default selection returned when no TopComponent is active. + * It is also used to maintain listeners added to the selection manager. */ + private final Selection emptySelection; + private Selection curSelection; + + private SelectionManager() { + emptySelection = new Selection(); + curSelection = emptySelection; + } + + public Selection getCurSelection() { + return curSelection; + } + + public void setSelection(Selection sel) { + if (curSelection != sel) { + curSelection = sel; + fireChangeEvent(); + } + } + + public void removeSelection(Selection sel) { + if (curSelection == sel) { + curSelection = emptySelection; + fireChangeEvent(); + } + } + + protected void fireChangeEvent() { + emptySelection.fireChangeEvent(); + } + + public void addChangeListener(ChangeListener listener) { + emptySelection.addChangeListener(listener); + } + + public void removeChangeListener(ChangeListener listener) { + emptySelection.removeChangeListener(listener); + } +} diff --git a/visualizer/C1Visualizer/VisualizerUI/src/main/java/at/ssw/visualizer/core/selection/SelectionProvider.java b/visualizer/C1Visualizer/VisualizerUI/src/main/java/at/ssw/visualizer/core/selection/SelectionProvider.java new file mode 100644 index 000000000000..8829b664fc0c --- /dev/null +++ b/visualizer/C1Visualizer/VisualizerUI/src/main/java/at/ssw/visualizer/core/selection/SelectionProvider.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017, 2025, 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. + * + * 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 at.ssw.visualizer.core.selection; + +/** + * + * @author Christian Wimmer + */ +public interface SelectionProvider { + public Selection getSelection(); +} diff --git a/visualizer/C1Visualizer/VisualizerUI/src/main/nbm/manifest.mf b/visualizer/C1Visualizer/VisualizerUI/src/main/nbm/manifest.mf new file mode 100644 index 000000000000..b83b2bb50196 --- /dev/null +++ b/visualizer/C1Visualizer/VisualizerUI/src/main/nbm/manifest.mf @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +OpenIDE-Module: at.ssw.visualizer +OpenIDE-Module-Layer: at/ssw/visualizer/layer.xml +OpenIDE-Module-Localizing-Bundle: at/ssw/visualizer/Bundle.properties +OpenIDE-Module-Specification-Version: 1.0 + diff --git a/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/Bundle.properties b/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/Bundle.properties new file mode 100644 index 000000000000..5c3e441e9896 --- /dev/null +++ b/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/Bundle.properties @@ -0,0 +1,23 @@ +OpenIDE-Module-Name=Visualizer UI + +# Coloring profile name +Editors/FontsColors/NetBeans=Default + +# Syntax Colorings +default=Default + +# Highlighting +selection=Selected Text +guarded=Guarded Block +block-search=Search Block +status-bar=Status Bar +status-bar-bold=Bold Status Bar +inc-search=Incremental Search +line-number=Line Number +code-folding-bar=Code Folding Bar +code-folding=Code Folding +highlight-search=Highlight Search +highlight-caret-row=Highlight Caret Row +caret-color-insert-mode=Caret Color +text-limit-line-color=Text Limit Line +at-ssw-visualizer-highlight=Reference Highlights diff --git a/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/NetBeans-editor.xml b/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/NetBeans-editor.xml new file mode 100644 index 000000000000..5086088e79ea --- /dev/null +++ b/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/NetBeans-editor.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/NetBeans-fontsColors.xml b/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/NetBeans-fontsColors.xml new file mode 100644 index 000000000000..7597a125b1ee --- /dev/null +++ b/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/NetBeans-fontsColors.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/bottomLeftWsmode.xml b/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/bottomLeftWsmode.xml new file mode 100644 index 000000000000..aa9272b22dbb --- /dev/null +++ b/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/bottomLeftWsmode.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/bottomRightWsmode.xml b/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/bottomRightWsmode.xml new file mode 100644 index 000000000000..b11a01168fb6 --- /dev/null +++ b/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/bottomRightWsmode.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/layer.xml b/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/layer.xml new file mode 100644 index 000000000000..16376f194a68 --- /dev/null +++ b/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/layer.xml @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/leftWsmode.xml b/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/leftWsmode.xml new file mode 100644 index 000000000000..5927985a41b9 --- /dev/null +++ b/visualizer/C1Visualizer/VisualizerUI/src/main/resources/at/ssw/visualizer/leftWsmode.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + diff --git a/visualizer/C1Visualizer/application/pom.xml b/visualizer/C1Visualizer/application/pom.xml new file mode 100644 index 000000000000..039fe3284212 --- /dev/null +++ b/visualizer/C1Visualizer/application/pom.xml @@ -0,0 +1,1257 @@ + + + + + 4.0.0 + + at.ssw.visualizer + C1Visualizer-parent + 1.14-SNAPSHOT + + C1Visualizer-app + nbm-application + C1Visualizer-app + + UTF-8 + ${project.build.directory}/${brandingToken} + + + + org.netbeans.cluster + ide + ${netbeans.version} + pom + + + org.netbeans.api + org-netbeans-libs-tomlj + + + org.netbeans.modules + org-netbeans-modules-project-dependency + + + org.netbeans.modules + org-netbeans-modules-languages-toml + + + org.netbeans.modules + org-netbeans-modules-languages-go + + + org.netbeans.modules + org-netbeans-modules-languages-hcl + + + org.netbeans.external + com-jcraft-jsch + + + org.netbeans.external + com-jcraft-jzlib + + + org.netbeans.external + org-apache-commons-httpclient + + + org.netbeans.external + org-apache-ws-commons-util + + + org.netbeans.api + org-apache-xml-resolver + + + org.netbeans.external + org-apache-xmlrpc + + + org.netbeans.external + org-eclipse-core-net + + + org.netbeans.external + org-eclipse-core-runtime-compatibility-auth + + + org.netbeans.external + org-eclipse-equinox-preferences + + + org.netbeans.external + org-eclipse-equinox-registry + + + org.netbeans.external + org-eclipse-equinox-security + + + org.netbeans.external + org-eclipse-jgit + + + org.netbeans.external + org-eclipse-mylyn-bugzilla-core + + + org.netbeans.external + org-eclipse-mylyn-commons-core + + + org.netbeans.external + org-eclipse-mylyn-commons-net + + + org.netbeans.external + org-eclipse-mylyn-commons-xmlrpc + + + org.netbeans.external + org-eclipse-mylyn-tasks-core + + + org.netbeans.api + org-netbeans-api-debugger + + + org.netbeans.api + org-netbeans-api-java-classpath + + + org.netbeans.api + org-netbeans-api-xml-ui + + + org.netbeans.api + org-netbeans-api-xml + + + org.netbeans.modules + org-netbeans-core-browser-webview + + + org.netbeans.modules + org-netbeans-core-browser + + + org.netbeans.api + org-netbeans-core-ide + + + org.netbeans.modules + org-netbeans-core-multitabs-project + + + org.netbeans.modules + org-netbeans-lib-terminalemulator + + + org.netbeans.api + org-netbeans-libs-antlr3-runtime + + + org.netbeans.api + org-netbeans-libs-commons_net + + + org.netbeans.modules + org-netbeans-libs-commons_compress + + + org.netbeans.modules + org-netbeans-libs-freemarker + + + org.netbeans.api + org-netbeans-libs-git + + + org.netbeans.modules + org-netbeans-libs-ini4j + + + org.netbeans.modules + org-netbeans-libs-jaxb + + + org.netbeans.modules + org-netbeans-libs-jsch-agentproxy + + + org.netbeans.api + org-netbeans-libs-lucene + + + org.netbeans.api + org-netbeans-libs-smack + + + org.netbeans.modules + org-netbeans-libs-svnClientAdapter-javahl + + + org.netbeans.api + org-netbeans-libs-svnClientAdapter + + + org.netbeans.api + org-netbeans-libs-xerces + + + org.netbeans.modules + org-netbeans-modules-bugtracking-bridge + + + org.netbeans.modules + org-netbeans-modules-bugtracking-commons + + + org.netbeans.api + org-netbeans-modules-bugtracking + + + org.netbeans.modules + org-netbeans-modules-bugzilla + + + org.netbeans.modules + org-netbeans-modules-code-analysis + + + org.netbeans.api + org-netbeans-modules-csl-api + + + org.netbeans.api + org-netbeans-modules-csl-types + + + org.netbeans.modules + org-netbeans-modules-css-editor + + + org.netbeans.modules + org-netbeans-modules-css-lib + + + org.netbeans.modules + org-netbeans-modules-css-model + + + org.netbeans.modules + org-netbeans-modules-css-prep + + + org.netbeans.modules + org-netbeans-modules-css-visual + + + org.netbeans.api + org-netbeans-modules-db-core + + + org.netbeans.modules + org-netbeans-modules-db-dataview + + + org.netbeans.modules + org-netbeans-modules-db-drivers + + + org.netbeans.modules + org-netbeans-modules-db-kit + + + org.netbeans.api + org-netbeans-modules-db-metadata-model + + + org.netbeans.modules + org-netbeans-modules-db-mysql + + + org.netbeans.modules + org-netbeans-modules-db-sql-editor + + + org.netbeans.modules + org-netbeans-modules-db-sql-visualeditor + + + org.netbeans.api + org-netbeans-modules-db + + + org.netbeans.modules + org-netbeans-modules-dbapi + + + org.netbeans.modules + org-netbeans-modules-defaults + + + org.netbeans.modules + org-netbeans-modules-derby + + + org.netbeans.api + org-netbeans-modules-diff + + + org.netbeans.modules + org-netbeans-modules-dlight-nativeexecution-nb + + + org.netbeans.modules + org-netbeans-modules-dlight-nativeexecution + + + org.netbeans.modules + org-netbeans-modules-dlight-terminal + + + org.netbeans.modules + org-netbeans-modules-docker-api + + + org.netbeans.modules + org-netbeans-modules-docker-editor + + + org.netbeans.modules + org-netbeans-modules-docker-ui + + + org.netbeans.modules + org-netbeans-modules-editor-bookmarks + + + org.netbeans.api + org-netbeans-modules-editor-bracesmatching + + + org.netbeans.modules + org-netbeans-modules-editor-breadcrumbs + + + org.netbeans.api + org-netbeans-modules-editor-codetemplates + + + org.netbeans.api + org-netbeans-modules-editor-completion + + + org.netbeans.api + org-netbeans-modules-editor-errorstripe-api + + + org.netbeans.modules + org-netbeans-modules-editor-errorstripe + + + org.netbeans.modules + org-netbeans-modules-editor-global-format + + + org.netbeans.api + org-netbeans-modules-editor-guards + + + org.netbeans.api + org-netbeans-modules-editor-indent-project + + + org.netbeans.api + org-netbeans-modules-editor-indent + + + org.netbeans.modules + org-netbeans-modules-editor-kit + + + org.netbeans.modules + org-netbeans-modules-editor-macros + + + org.netbeans.api + org-netbeans-modules-editor-plain-lib + + + org.netbeans.modules + org-netbeans-modules-editor-plain + + + org.netbeans.modules + org-netbeans-modules-editor-search + + + org.netbeans.modules + org-netbeans-modules-editor-structure + + + org.netbeans.api + org-netbeans-modules-editor-tools-storage + + + org.netbeans.modules + org-netbeans-modules-extbrowser + + + org.netbeans.modules + org-netbeans-modules-extexecution-impl + + + org.netbeans.api + org-netbeans-modules-extexecution + + + org.netbeans.modules + org-netbeans-modules-git + + + org.netbeans.modules + org-netbeans-modules-gototest + + + org.netbeans.modules + org-netbeans-modules-gsf-codecoverage + + + org.netbeans.modules + org-netbeans-modules-gsf-testrunner-ui + + + org.netbeans.api + org-netbeans-modules-gsf-testrunner + + + org.netbeans.modules + org-netbeans-modules-html-custom + + + org.netbeans.modules + org-netbeans-modules-html-editor-lib + + + org.netbeans.modules + org-netbeans-modules-html-editor + + + org.netbeans.api + org-netbeans-modules-html-indexing + + + org.netbeans.api + org-netbeans-modules-html-lexer + + + org.netbeans.modules + org-netbeans-modules-html-parser + + + org.netbeans.modules + org-netbeans-modules-html-validation + + + org.netbeans.modules + org-netbeans-modules-html + + + org.netbeans.modules + org-netbeans-modules-httpserver + + + org.netbeans.modules + org-netbeans-modules-hudson-git + + + org.netbeans.modules + org-netbeans-modules-hudson-mercurial + + + org.netbeans.modules + org-netbeans-modules-hudson-subversion + + + org.netbeans.modules + org-netbeans-modules-hudson-tasklist + + + org.netbeans.modules + org-netbeans-modules-hudson-ui + + + org.netbeans.modules + org-netbeans-modules-hudson + + + org.netbeans.modules + org-netbeans-modules-ide-kit + + + org.netbeans.modules + org-netbeans-modules-image + + + org.netbeans.modules + org-netbeans-modules-javascript2-debug-ui + + + org.netbeans.modules + org-netbeans-modules-javascript2-debug + + + org.netbeans.api + org-netbeans-modules-jellytools-ide + + + org.netbeans.api + org-netbeans-modules-jumpto + + + org.netbeans.modules + org-netbeans-modules-languages-diff + + + org.netbeans.modules + org-netbeans-modules-languages-manifest + + + org.netbeans.modules + org-netbeans-modules-languages-yaml + + + org.netbeans.api + org-netbeans-modules-languages + + + org.netbeans.modules + org-netbeans-modules-localhistory + + + org.netbeans.modules + org-netbeans-modules-localtasks + + + org.netbeans.api + org-netbeans-modules-lsp-client + + + org.netbeans.modules + org-netbeans-modules-markdown + + + org.netbeans.modules + org-netbeans-modules-mercurial + + + org.netbeans.modules + org-netbeans-modules-mylyn-util + + + org.netbeans.modules + org-netbeans-modules-nativeimage-api + + + org.netbeans.modules + org-netbeans-modules-notifications + + + org.netbeans.api + org-netbeans-modules-parsing-api + + + org.netbeans.api + org-netbeans-modules-parsing-indexing + + + org.netbeans.modules + org-netbeans-modules-parsing-lucene + + + org.netbeans.modules + org-netbeans-modules-parsing-nb + + + org.netbeans.modules + org-netbeans-modules-parsing-ui + + + org.netbeans.modules + org-netbeans-modules-print-editor + + + org.netbeans.api + org-netbeans-modules-project-ant-compat8 + + + org.netbeans.api + org-netbeans-modules-project-ant-ui + + + org.netbeans.api + org-netbeans-modules-project-ant + + + org.netbeans.modules + org-netbeans-modules-project-indexingbridge + + + org.netbeans.api + org-netbeans-modules-project-libraries-ui + + + org.netbeans.api + org-netbeans-modules-project-libraries + + + org.netbeans.modules + org-netbeans-modules-project-spi-intern-impl + + + org.netbeans.modules + org-netbeans-modules-project-spi-intern + + + org.netbeans.modules + org-netbeans-modules-projectapi-nb + + + org.netbeans.api + org-netbeans-modules-projectapi + + + org.netbeans.modules + org-netbeans-modules-projectui-buildmenu + + + org.netbeans.modules + org-netbeans-modules-projectui + + + org.netbeans.api + org-netbeans-modules-projectuiapi-base + + + org.netbeans.api + org-netbeans-modules-projectuiapi + + + org.netbeans.api + org-netbeans-modules-properties-syntax + + + org.netbeans.modules + org-netbeans-modules-properties + + + org.netbeans.api + org-netbeans-modules-refactoring-api + + + org.netbeans.api + org-netbeans-modules-schema2beans + + + org.netbeans.modules + org-netbeans-modules-selenium2-server + + + org.netbeans.modules + org-netbeans-modules-selenium2 + + + org.netbeans.api + org-netbeans-modules-server + + + org.netbeans.api + org-netbeans-modules-servletapi + + + org.netbeans.api + org-netbeans-modules-spellchecker-apimodule + + + org.netbeans.modules + org-netbeans-modules-spellchecker-bindings-htmlxml + + + org.netbeans.modules + org-netbeans-modules-spellchecker-bindings-properties + + + org.netbeans.modules + org-netbeans-modules-spellchecker-dictionary_en + + + org.netbeans.modules + org-netbeans-modules-spellchecker-kit + + + org.netbeans.modules + org-netbeans-modules-spellchecker + + + org.netbeans.modules + org-netbeans-modules-subversion + + + org.netbeans.modules + org-netbeans-modules-swing-validation + + + org.netbeans.modules + org-netbeans-modules-target-iterator + + + org.netbeans.modules + org-netbeans-modules-tasklist-kit + + + org.netbeans.modules + org-netbeans-modules-tasklist-projectint + + + org.netbeans.modules + org-netbeans-modules-tasklist-todo + + + org.netbeans.modules + org-netbeans-modules-tasklist-ui + + + org.netbeans.modules + org-netbeans-modules-team-commons + + + org.netbeans.modules + org-netbeans-modules-team-ide + + + org.netbeans.modules + org-netbeans-modules-terminal-nb + + + org.netbeans.modules + org-netbeans-modules-terminal + + + org.netbeans.modules + org-netbeans-modules-usersguide + + + org.netbeans.modules + org-netbeans-modules-utilities-project + + + org.netbeans.modules + org-netbeans-modules-versioning-core + + + org.netbeans.modules + org-netbeans-modules-versioning-indexingbridge + + + org.netbeans.modules + org-netbeans-modules-versioning-masterfs + + + org.netbeans.modules + org-netbeans-modules-versioning-system-cvss-installer + + + org.netbeans.modules + org-netbeans-modules-versioning-ui + + + org.netbeans.modules + org-netbeans-modules-versioning-util + + + org.netbeans.api + org-netbeans-modules-versioning + + + org.netbeans.modules + org-netbeans-modules-web-browser-api + + + org.netbeans.modules + org-netbeans-modules-web-common-ui + + + org.netbeans.modules + org-netbeans-modules-web-common + + + org.netbeans.modules + org-netbeans-modules-web-indent + + + org.netbeans.modules + org-netbeans-modules-web-webkit-debugging + + + org.netbeans.modules + org-netbeans-modules-xml-axi + + + org.netbeans.api + org-netbeans-modules-xml-catalog-ui + + + org.netbeans.api + org-netbeans-modules-xml-catalog + + + org.netbeans.api + org-netbeans-modules-xml-core + + + org.netbeans.api + org-netbeans-modules-xml-jaxb-api + + + org.netbeans.api + org-netbeans-modules-xml-lexer + + + org.netbeans.modules + org-netbeans-modules-xml-multiview + + + org.netbeans.api + org-netbeans-modules-xml-retriever + + + org.netbeans.api + org-netbeans-modules-xml-schema-completion + + + org.netbeans.api + org-netbeans-modules-xml-schema-model + + + org.netbeans.modules + org-netbeans-modules-xml-tax + + + org.netbeans.api + org-netbeans-modules-xml-text-obsolete90 + + + org.netbeans.modules + org-netbeans-modules-xml-text + + + org.netbeans.modules + org-netbeans-modules-xml-tools + + + org.netbeans.api + org-netbeans-modules-xml-wsdl-model + + + org.netbeans.api + org-netbeans-modules-xml-xam + + + org.netbeans.api + org-netbeans-modules-xml-xdm + + + org.netbeans.modules + org-netbeans-modules-xml + + + org.netbeans.modules + org-netbeans-modules-xsl + + + org.netbeans.api + org-netbeans-spi-debugger-ui + + + org.netbeans.api + org-netbeans-spi-editor-hints-projects + + + org.netbeans.api + org-netbeans-spi-editor-hints + + + org.netbeans.api + org-netbeans-spi-navigator + + + org.netbeans.api + org-netbeans-spi-palette + + + org.netbeans.api + org-netbeans-spi-tasklist + + + org.netbeans.api + org-netbeans-spi-viewmodel + + + org.netbeans.modules + org-netbeans-swing-dirchooser + + + org.netbeans.api + org-openidex-util + + + + + org.netbeans.cluster + platform + ${netbeans.version} + pom + + + org.netbeans.external + org-apache-commons-codec + + + org.netbeans.external + org-apache-commons-logging + + + org.netbeans.api + org-netbeans-core-multiview + + + org.netbeans.modules + org-netbeans-core-nativeaccess + + + org.netbeans.api + org-netbeans-core-netigso + + + org.netbeans.modules + org-netbeans-core-osgi + + + org.netbeans.modules + org-netbeans-core-output2 + + + org.netbeans.modules + org-netbeans-libs-felix + + + org.netbeans.api + org-netbeans-libs-jsr223 + + + org.netbeans.api + org-netbeans-libs-junit4 + + + org.netbeans.api + org-netbeans-libs-osgi + + + org.netbeans.api + org-netbeans-libs-testng + + + org.netbeans.modules + org-netbeans-modules-autoupdate-cli + + + org.netbeans.api + org-netbeans-modules-autoupdate-services + + + org.netbeans.api + org-netbeans-modules-autoupdate-ui + + + org.netbeans.modules + org-netbeans-modules-core-kit + + + org.netbeans.modules + org-netbeans-modules-favorites + + + org.netbeans.api + org-netbeans-modules-javahelp + + + org.netbeans.modules + org-netbeans-modules-junitlib + + + org.netbeans.modules + org-netbeans-modules-keyring-impl + + + org.netbeans.api + org-netbeans-modules-netbinox + + + org.netbeans.api + org-netbeans-modules-print + + + org.netbeans.api + org-netbeans-modules-sendopts + + + org.netbeans.modules + org-netbeans-modules-templates + + + org.netbeans.api + org-openide-compat + + + org.netbeans.api + org-apache-commons-commons_io + + + + + ${project.groupId} + branding + ${project.version} + + + ${project.groupId} + BlockView + ${project.version} + + + ${project.groupId} + BytecodeEditor + ${project.version} + + + ${project.groupId} + BytecodeModel + ${project.version} + + + ${project.groupId} + BytecodeView + ${project.version} + + + ${project.groupId} + CompilationModel + ${project.version} + + + ${project.groupId} + CompilationView + ${project.version} + + + ${project.groupId} + ControlFlowEditor + ${project.version} + + + ${project.groupId} + DataFlowEditor + ${project.version} + + + ${project.groupId} + DataFlowGraph + ${project.version} + + + ${project.groupId} + DataFlowView + ${project.version} + + + ${project.groupId} + GraphHelper + ${project.version} + + + ${project.groupId} + GraphLayoutAPI + ${project.version} + + + ${project.groupId} + GraphLayoutImpl + ${project.version} + + + ${project.groupId} + IntermediateCodeEditor + ${project.version} + + + ${project.groupId} + IntermediateCodeViews + ${project.version} + + + ${project.groupId} + IntervalEditor + ${project.version} + + + ${project.groupId} + IntervalView + ${project.version} + + + ${project.groupId} + NativeCodeEditor + ${project.version} + + + ${project.groupId} + NativeCodeView + ${project.version} + + + ${project.groupId} + TextEditor + ${project.version} + + + ${project.groupId} + VisualizerUI + ${project.version} + + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + + src/main/resources/${brandingToken}.conf + src/main/resources/${brandingToken}.clusters + src/main/resources/bin + + + + default-standalone-zip + + standalone-zip + + + ${brandingToken}-${project.version} + + + + + + maven-resources-plugin + 2.6 + + + install-mac-icon + package + + copy-resources + + + ${basedir}/target/${brandingToken} + UTF-8 + + + ${basedir}/src/main/resources + + ${brandingToken}.icns + + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.6 + + + zip-artifacts + package + + run + + + + + + + + + + + + + + + + deployment + + + + org.apache.netbeans.utilities + nbm-maven-plugin + + + extra + + autoupdate + webstart-app + build-installers + + + + + + + + + diff --git a/visualizer/C1Visualizer/application/src/main/resources/bin/c1visualizer b/visualizer/C1Visualizer/application/src/main/resources/bin/c1visualizer new file mode 100755 index 000000000000..8bd327ef04b5 --- /dev/null +++ b/visualizer/C1Visualizer/application/src/main/resources/bin/c1visualizer @@ -0,0 +1,199 @@ +#!/bin/sh + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# +# resolve symlinks +# + +PRG=$0 + +while [ -h "$PRG" ]; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '^.*-> \(.*\)$' 2>/dev/null` + if expr "$link" : '^/' 2> /dev/null >/dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi +done + +progdir=`dirname "$PRG"` +APPNAME=`basename "$PRG"` +if [ -z "$APP_DOCK_NAME" ] ; then + APP_DOCK_NAME="$APPNAME" +fi + +case "`uname`" in + Darwin*) + # set default userdir and cachedir on Mac OS X + DEFAULT_USERDIR_ROOT="${HOME}/Library/Application Support/${APPNAME}" + DEFAULT_CACHEDIR_ROOT=${HOME}/Library/Caches/${APPNAME} + ;; + *) + # set default userdir and cachedir on unix systems + DEFAULT_USERDIR_ROOT=${HOME}/.${APPNAME} + DEFAULT_CACHEDIR_ROOT=${HOME}/.cache/${APPNAME} + ;; +esac + +if [ -f "$progdir/../etc/$APPNAME".conf ] ; then + . "$progdir/../etc/$APPNAME".conf +fi + +# XXX does not correctly deal with spaces in non-userdir params +args="" + +case "`uname`" in + Darwin*) + if [ ! -z "$default_mac_userdir" ]; then + userdir="${default_mac_userdir}" + else + userdir="${default_userdir}" + fi + ;; + *) + userdir="${default_userdir}" + ;; +esac +while [ $# -gt 0 ] ; do + case "$1" in + --userdir) shift; if [ $# -gt 0 ] ; then userdir="$1"; fi + ;; + *) + if [[ "$1" == *\$* ]]; then + echo "Error: Argument '$1' contains a \$ character which cannot be safely handled the launcher script" >&2 + exit 1 + fi + args="$args \"$1\"" + ;; + esac + shift +done + +cachedir="${default_cachedir}" + +if [ -f "${userdir}/etc/$APPNAME".conf ] ; then + . "${userdir}/etc/$APPNAME".conf +fi + +if [ -n "$jdkhome" -a \! -d "$jdkhome" -a -d "$progdir/../$jdkhome" ]; then + # #74333: permit jdkhome to be defined as relative to app dir + jdkhome="$progdir/../$jdkhome" +fi + +readClusters() { + if [ -x /usr/ucb/echo ]; then + echo=/usr/ucb/echo + else + echo=echo + fi + while read X; do + if [ "$X" \!= "" ]; then + $echo "$progdir/../$X" + fi + done +} + +absolutize_paths() { + while read path; do + if [ -d "$path" ]; then + (cd "$path" 2>/dev/null && pwd) + else + echo "$path" + fi + done +} + +clusters=`(cat "$progdir/../etc/$APPNAME".clusters; echo) | readClusters | absolutize_paths | tr '\012' ':'` + +if [ ! -z "$extra_clusters" ] ; then + clusters="$clusters:$extra_clusters" +fi + +nbexec=`echo "$progdir"/../platform*/lib/nbexec` + +case "`uname`" in + Darwin*) + eval exec sh '"$nbexec"' \ + --jdkhome '"$jdkhome"' \ + -J-Xdock:name='"$APP_DOCK_NAME"' \ + '"-J-Xdock:icon=$progdir/../$APPNAME.icns"' \ + --clusters '"$clusters"' \ + --userdir '"${userdir}"' \ + --cachedir '"${cachedir}"' \ + ${default_options} \ + "$args" + ;; + *) + sh=sh + # #73162: Ubuntu uses the ancient Bourne shell, which does not implement trap well. + if [ -x /bin/bash ] + then + sh=/bin/bash + fi + + # See longer comments in nb/ide.launcher/unix/netbeans. + if [ "`command xrdb -query 2> /dev/null | grep Xft.dpi | cut -d ':' -f2 | xargs`" = 192 ] + then + echo "Detected 2x HiDPI scaling in Xft.dpi setting; setting GDK_SCALE=2" + export GDK_SCALE=2 + fi + if [ "`command xdpyinfo 2> /dev/null | grep 'resolution:.*dots per inch' | cut -d ':' -f2 | cut -d 'x' -f1 | sort -u | xargs`" = 192 ] + then + echo "Detected 192 DPI on all screens in xdpyinfo; setting GDK_SCALE=2" + export GDK_SCALE=2 + fi + + extra_automatic_options="" + + # See longer comments in nb/ide.launcher/unix/netbeans. + if [ ! -z "$KDE_FULL_SESSION" ] ; then + case "`command xrdb -query 2> /dev/null | grep Xft.rgba | cut -d ':' -f2 | xargs`" in + rgb) + extra_automatic_options="-J-Dawt.useSystemAAFontSettings=lcd_hrgb" + ;; + bgr) + extra_automatic_options="-J-Dawt.useSystemAAFontSettings=lcd_hbgr" + ;; + vrgb) + extra_automatic_options="-J-Dawt.useSystemAAFontSettings=lcd_vrgb" + ;; + vbgr) + extra_automatic_options="-J-Dawt.useSystemAAFontSettings=lcd_vbgr" + ;; + *) + extra_automatic_options="-J-Dawt.useSystemAAFontSettings=on" + ;; + esac + echo "Detected KDE; use explicit setting for font antialiasing ($extra_automatic_options)" + fi + + # Add extra_automatic_options before default_options, to allow system + # property definitions from the configuration file to take precedence. + eval exec $sh '"$nbexec"' \ + --jdkhome '"$jdkhome"' \ + --clusters '"$clusters"' \ + --userdir '"${userdir}"' \ + --cachedir '"${cachedir}"' \ + ${extra_automatic_options} \ + ${default_options} \ + "$args" + exit 1 + ;; +esac diff --git a/visualizer/C1Visualizer/application/src/main/resources/c1visualizer.clusters b/visualizer/C1Visualizer/application/src/main/resources/c1visualizer.clusters new file mode 100644 index 000000000000..f1bb4b1abf03 --- /dev/null +++ b/visualizer/C1Visualizer/application/src/main/resources/c1visualizer.clusters @@ -0,0 +1,2 @@ +c1visualizer +ide diff --git a/visualizer/C1Visualizer/application/src/main/resources/c1visualizer.conf b/visualizer/C1Visualizer/application/src/main/resources/c1visualizer.conf new file mode 100644 index 000000000000..ed65b83298bc --- /dev/null +++ b/visualizer/C1Visualizer/application/src/main/resources/c1visualizer.conf @@ -0,0 +1,37 @@ +# Copyright (c) 2013, 2022, 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. +# +# 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. + +# ${HOME} will be replaced by user home directory according to platform + +default_userdir="${DEFAULT_USERDIR_ROOT}/0.31" +default_cachedir="${DEFAULT_CACHEDIR_ROOT}/0.31" + +# options used by the launcher by default, can be overridden by explicit +# command line switches +default_options="--branding c1visualizer -J-XX:+UseStringDeduplication -J-DRepositoryUpdate.increasedLogLevel=800 -J-client -J-Xss2m -J-Xms32m -J-Dnetbeans.logger.console=true -J-Djdk.gtk.version=2.2 -J-Dapple.laf.useScreenMenuBar=true -J-Dapple.awt.graphics.UseQuartz=true -J-Dsun.java2d.noddraw=true -J-Dsun.java2d.dpiaware=true -J-Dsun.zip.disableMemoryMapping=true -J-Dplugin.manager.check.updates=false -J--add-opens=java.base/java.net=ALL-UNNAMED -J--add-opens=java.base/java.lang.ref=ALL-UNNAMED -J--add-opens=java.base/java.lang=ALL-UNNAMED -J--add-opens=java.base/java.security=ALL-UNNAMED -J--add-opens=java.base/java.util=ALL-UNNAMED -J--add-opens=java.desktop/javax.swing.plaf.basic=ALL-UNNAMED -J--add-opens=java.desktop/javax.swing.text=ALL-UNNAMED -J--add-opens=java.desktop/javax.swing=ALL-UNNAMED -J--add-opens=java.desktop/java.awt=ALL-UNNAMED -J--add-opens=java.desktop/java.awt.event=ALL-UNNAMED -J--add-opens=java.prefs/java.util.prefs=ALL-UNNAMED -J--add-opens=jdk.jshell/jdk.jshell=ALL-UNNAMED -J--add-modules=jdk.jshell -J--add-exports=java.desktop/sun.awt=ALL-UNNAMED -J--add-exports=java.desktop/java.awt.peer=ALL-UNNAMED -J--add-exports=java.desktop/com.sun.beans.editors=ALL-UNNAMED -J--add-exports=java.desktop/sun.swing=ALL-UNNAMED -J--add-exports=java.desktop/sun.awt.im=ALL-UNNAMED -J--add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED -J--add-exports=java.management/sun.management=ALL-UNNAMED -J--add-exports=java.base/sun.reflect.annotation=ALL-UNNAMED -J--enable-native-access=ALL-UNNAMED -J-XX:+IgnoreUnrecognizedVMOptions" + +# for development purposes you may wish to append: -J-Dnetbeans.logger.console=true -J-ea + +# default location of JDK/JRE, can be overridden by using --jdkhome

switch +#jdkhome="/path/to/jdk" + +# clusters' paths separated by path.separator (semicolon on Windows, colon on Unices) +#extra_clusters= diff --git a/visualizer/C1Visualizer/application/src/main/resources/c1visualizer.icns b/visualizer/C1Visualizer/application/src/main/resources/c1visualizer.icns new file mode 100644 index 000000000000..7ab2b6abb3c0 Binary files /dev/null and b/visualizer/C1Visualizer/application/src/main/resources/c1visualizer.icns differ diff --git a/visualizer/C1Visualizer/branding/pom.xml b/visualizer/C1Visualizer/branding/pom.xml new file mode 100644 index 000000000000..c77ae61f4cc3 --- /dev/null +++ b/visualizer/C1Visualizer/branding/pom.xml @@ -0,0 +1,97 @@ + + + + + 4.0.0 + + at.ssw.visualizer + C1Visualizer-parent + 1.14-SNAPSHOT + + at.ssw.visualizer + branding + 1.14-SNAPSHOT + nbm + branding + + UTF-8 + + + + + ${basedir}/src/main/nbm-branding + + + **/* + + true + ${basedir}/target/filtered-nbm-branding + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.1 + + ${project.build.sourceEncoding} + + gif + png + + + + + org.apache.netbeans.utilities + nbm-maven-plugin + + + ${basedir}/target/filtered-nbm-branding + + + + org.apache.maven.plugins + maven-jar-plugin + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + diff --git a/visualizer/C1Visualizer/branding/src/main/nbm-branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/visualizer/C1Visualizer/branding/src/main/nbm-branding/core/core.jar/org/netbeans/core/startup/Bundle.properties new file mode 100644 index 000000000000..e6d83cc4b8cf --- /dev/null +++ b/visualizer/C1Visualizer/branding/src/main/nbm-branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -0,0 +1,8 @@ +#Updated by 'ant build-brand' +#Mon, 01 Nov 2021 20:27:18 +0100 +currentVersion=Java HotSpot Client Compiler Visualizer ${project.version} +LBL_splash_window_title=Starting Java HotSpot Client Compiler Visualizer +SplashProgressBarBounds=0,258,472,3 +SplashProgressBarColor=0x80 +SplashRunningTextBounds=15,245,452,12 +SplashRunningTextColor=0x80 diff --git a/visualizer/C1Visualizer/branding/src/main/nbm-branding/core/core.jar/org/netbeans/core/startup/frame.gif b/visualizer/C1Visualizer/branding/src/main/nbm-branding/core/core.jar/org/netbeans/core/startup/frame.gif new file mode 100644 index 000000000000..3882bd93ba1b Binary files /dev/null and b/visualizer/C1Visualizer/branding/src/main/nbm-branding/core/core.jar/org/netbeans/core/startup/frame.gif differ diff --git a/visualizer/C1Visualizer/branding/src/main/nbm-branding/core/core.jar/org/netbeans/core/startup/frame48.gif b/visualizer/C1Visualizer/branding/src/main/nbm-branding/core/core.jar/org/netbeans/core/startup/frame48.gif new file mode 100644 index 000000000000..038172e2b708 Binary files /dev/null and b/visualizer/C1Visualizer/branding/src/main/nbm-branding/core/core.jar/org/netbeans/core/startup/frame48.gif differ diff --git a/visualizer/C1Visualizer/branding/src/main/nbm-branding/core/core.jar/org/netbeans/core/startup/splash.gif b/visualizer/C1Visualizer/branding/src/main/nbm-branding/core/core.jar/org/netbeans/core/startup/splash.gif new file mode 100644 index 000000000000..2d219c518763 Binary files /dev/null and b/visualizer/C1Visualizer/branding/src/main/nbm-branding/core/core.jar/org/netbeans/core/startup/splash.gif differ diff --git a/visualizer/C1Visualizer/branding/src/main/nbm-branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/visualizer/C1Visualizer/branding/src/main/nbm-branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties new file mode 100644 index 000000000000..9ca12fa37970 --- /dev/null +++ b/visualizer/C1Visualizer/branding/src/main/nbm-branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -0,0 +1,4 @@ +#Updated by 'ant build-brand' +#Mon, 01 Nov 2021 20:27:18 +0100 +CTL_MainWindow_Title=Java HotSpot Client Compiler Visualizer ${project.version} +CTL_MainWindow_Title_No_Project=Java HotSpot Client Compiler Visualizer ${project.version} diff --git a/visualizer/C1Visualizer/pom.xml b/visualizer/C1Visualizer/pom.xml new file mode 100644 index 000000000000..c901bc74ede0 --- /dev/null +++ b/visualizer/C1Visualizer/pom.xml @@ -0,0 +1,150 @@ + + + + + 4.0.0 + at.ssw.visualizer + C1Visualizer-parent + 1.14-SNAPSHOT + pom + C1Visualizer-parent + + + scm:git:git@github.com:oracle/graal.git + + HEAD + + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.1 + + + org.apache.netbeans.utilities + nbm-maven-plugin + ${nbmmvnplugin.version} + true + + ${brandingToken} + ${brandingToken} + + + + org.apache.maven.plugins + maven-compiler-plugin + ${mvncompilerplugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + ${mvnjarplugin.version} + + + org.apache.maven.plugins + maven-release-plugin + 3.1.1 + + true + false + c1visualizer-@{project.version} + + + + org.apache.maven.plugins + maven-enforcer-plugin + ${mvnenforcerplugin.version} + + + enforce-java + + enforce + + + + + [17,) + IdealGraphVisualizer requires a JDK version greater than or equal to 17 + + + + + + + + + + + BlockView + BytecodeEditor + BytecodeModel + BytecodeView + CompilationModel + CompilationView + ControlFlowEditor + DataFlowEditor + DataFlowGraph + DataFlowView + GraphHelper + GraphLayoutAPI + GraphLayoutImpl + IntermediateCodeEditor + IntermediateCodeViews + IntervalEditor + IntervalView + NativeCodeEditor + NativeCodeView + TextEditor + VisualizerUI + branding + application + + + RELEASE260 + 1.0.2 + 4.8 + 3.10.1 + 3.2.2 + 3.3.0 + 4.13.2 + 1.14 + 1.3.29 + 1.5.8 + c1visualizer + + diff --git a/visualizer/IdealGraphVisualizer/Coordinator/src/main/java/org/graalvm/visualizer/coordinator/impl/BinaryGraphDataObject.java b/visualizer/IdealGraphVisualizer/Coordinator/src/main/java/org/graalvm/visualizer/coordinator/impl/BinaryGraphDataObject.java index 904cc35beb12..c1342390b492 100644 --- a/visualizer/IdealGraphVisualizer/Coordinator/src/main/java/org/graalvm/visualizer/coordinator/impl/BinaryGraphDataObject.java +++ b/visualizer/IdealGraphVisualizer/Coordinator/src/main/java/org/graalvm/visualizer/coordinator/impl/BinaryGraphDataObject.java @@ -96,7 +96,6 @@ public void importPrimaryFile() { return; } try { - Thread.dumpStack(); FileImporter.asyncImportDocument(f.toPath(), true, true, null); } catch (IOException ex) { Exceptions.printStackTrace( diff --git a/visualizer/IdealGraphVisualizer/JSONExporter/pom.xml b/visualizer/IdealGraphVisualizer/JSONExporter/pom.xml deleted file mode 100644 index 49e3e2777bdc..000000000000 --- a/visualizer/IdealGraphVisualizer/JSONExporter/pom.xml +++ /dev/null @@ -1,38 +0,0 @@ - - 4.0.0 - - org.graalvm.visualizer - IdealGraphVisualizer-parent - 1.24-SNAPSHOT - - JSONExporter - nbm - - - - org.apache.netbeans.utilities - nbm-maven-plugin - true - - - org.apache.maven.plugins - maven-jar-plugin - - - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - - - - - - - ${project.groupId} - Data - ${project.version} - - - - UTF-8 - - diff --git a/visualizer/IdealGraphVisualizer/JSONExporter/src/main/java/org/graalvm/visualizer/JSONExporter.java b/visualizer/IdealGraphVisualizer/JSONExporter/src/main/java/org/graalvm/visualizer/JSONExporter.java deleted file mode 100644 index fde4941f29fb..000000000000 --- a/visualizer/IdealGraphVisualizer/JSONExporter/src/main/java/org/graalvm/visualizer/JSONExporter.java +++ /dev/null @@ -1,559 +0,0 @@ -/* - * Copyright (c) 2021, 2022, 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. - * - * 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 org.graalvm.visualizer; - -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.net.URL; -import java.nio.file.Paths; -import java.util.*; - -import jdk.graal.compiler.graphio.parsing.*; -import jdk.graal.compiler.graphio.parsing.model.*; - -public class JSONExporter { - - private static final String ID = "id"; - private static final String NAME = "name"; - private static final String GRAPH_TYPE = "graph_type"; - private static final String PART = "part"; - - private static final String NODES = "nodes"; - private static final String EDGES = "edges"; - private static final String BLOCKS = "blocks"; - - private static final String JAVA = "Java"; - - private static final int JSON_SIZE_LIMIT = (int) Math.pow(2, 28); // ~270 MB per json file - - public static void main(String[] args) throws IOException { - if (args.length == 0 || !args[0].endsWith(".bgv") || args[0].contentEquals("-h") || args[0].contentEquals("--help")) { - printHelp(); - return; - } - boolean addBlocks = false; - List graphNames = new ArrayList<>(); - if (args.length > 1) { - int i = 1; - while (i < args.length) { - switch (args[i]) { - case "-b": - addBlocks = true; - break; - case "-n": - if (++i == args.length) { - printHelp(); - return; - } - String gn = args[i]; - if (gn.length() > 3 && (gn.charAt(0) == '"' || gn.charAt(0) == '\'')) { - gn = gn.substring(1, gn.length() - 1); - } - graphNames.add(gn.toLowerCase()); - break; - default: - printHelp(); - return; - } - i++; - } - } - exportToJSON(args[0], graphNames, addBlocks); - } - - private static void printHelp() { - System.out.println("usage:"); - System.out.println(" mx igv-json [-b][-n GRAPH_NAME]"); - } - - @SuppressWarnings("deprecation") - private static void fillGraphsList(URL url, List graphs) throws IOException { - ModelBuilder mb = new ModelBuilder(new GraphDocument(), null) { - @Override - public InputGraph startGraph(int dumpId, String format, Object[] args) { - InputGraph g = super.startGraph(dumpId, format, args); - graphs.add(g); - return g; - } - }; - new BinaryReader(new StreamSource(url.openStream()), mb).parse(); - } - - private static boolean isGraphNameAccepted(List graphNames, String n) { - for (String name : graphNames) { - if (n.toLowerCase().contains(name)) { - return true; - } - } - return false; - } - - private static JSONHelper.JSONObjectBuilder createRoot(int dumpID, String graphType, String graphName) { - JSONHelper.JSONObjectBuilder root = JSONHelper.object(); - root.add(ID, dumpID); - root.add(NAME, graphName); - root.add(GRAPH_TYPE, graphType); - return root; - } - - public static void exportToJSON(String in, List graphNames, boolean addBlocks) throws IOException { - URL url = new URL(new URL("file:"), in); - List graphs = new ArrayList<>(); - fillGraphsList(url, graphs); - for (InputGraph graph : graphs) { - if (graph.getProperties().size() <= 1) { - // This is most likely an IR graph that is not needed - continue; - } - String graphName = graph.getProperties().get(NAME, String.class); - if (graphNames.size() > 0 && !isGraphNameAccepted(graphNames, graphName)) { - continue; - } - String graphType = graph.getGraphType(); - int dumpID = graph.getDumpId(); - int currentPart = -1; - JSONHelper.JSONObjectBuilder root = createRoot(dumpID, graphType, graphName); - JSONHelper.JSONReadyObject nodesJson = JSONHelper.readyObject(0); - for (InputNode node : graph.getNodes()) { - if (nodesJson.getSize() > JSON_SIZE_LIMIT) { - root.add(NODES, nodesJson); - root.add(PART, ++currentPart); - write(in, graphType, graphName, currentPart, root.toString()); - root = createRoot(dumpID, graphType, graphName); - nodesJson = JSONHelper.readyObject(0); - } - nodesJson.add(node.getId() + "", getNode(node)); - } - root.add(NODES, nodesJson); - - JSONHelper.JSONReadyArray edgesJson = JSONHelper.readyArray(nodesJson.getSize()); - for (InputEdge e : graph.getEdges()) { - if (edgesJson.getSize() > JSON_SIZE_LIMIT) { - root.add(EDGES, edgesJson); - root.add(PART, ++currentPart); - write(in, graphType, graphName, currentPart, root.toString()); - root = createRoot(dumpID, graphType, graphName); - edgesJson = JSONHelper.readyArray(0); - } - edgesJson.add(getEdge(e)); - } - root.add(EDGES, edgesJson); - - if (addBlocks) { - /* Blocks can be reconstructed using nodes properties */ - JSONHelper.JSONReadyObject blocksJson = JSONHelper.readyObject(edgesJson.getSize()); - for (InputBlock b : graph.getBlocks()) { - if (edgesJson.getSize() > JSON_SIZE_LIMIT) { - root.add(BLOCKS, blocksJson); - root.add(PART, ++currentPart); - write(in, graphType, graphName, currentPart, root.toString()); - root = createRoot(dumpID, graphType, graphName); - edgesJson = JSONHelper.readyArray(0); - } - blocksJson.add(b.getName(), getBlock(b)); - } - root.add(BLOCKS, blocksJson); - } - - if (currentPart > -1) { - root.add(PART, ++currentPart); - } - write(in, graphType, graphName, currentPart, root.toString()); - } - } - - private static void write(String in, String graphType, String graphName, int part, String content) throws IOException { - String out = in.substring(0, in.lastIndexOf('.')) + '_'; - String outputPath = Paths.get(out + createFileName(graphType, graphName, part)).toString(); - System.out.println("Exporting to " + outputPath); - try (PrintWriter writer = new PrintWriter(new File(outputPath))) { - writer.write(content); - } - } - - private static JSONHelper.JSONObjectBuilder getNode(InputNode node) { - JSONHelper.JSONObjectBuilder nodeJson = JSONHelper.object(); - for (Property p : node.getProperties()) { - Object o = p.getValue(); - if (o != null) { - String key = p.getName(); - if (o instanceof LocationStackFrame) { - nodeJson.add(key, stacktrace(((LocationStackFrame) o))); - } else { - nodeJson.add(key, o.toString()); - } - } - } - return nodeJson; - } - - public static JSONHelper.JSONArrayBuilder getEdge(InputEdge edge) { - JSONHelper.JSONArrayBuilder e = JSONHelper.array(); - e.add(edge.getFrom()).add(edge.getTo()); - e.add(edge.getLabel()).add(edge.getType()); - return e; - } - - public static JSONHelper.JSONObjectBuilder getBlock(InputBlock b) { - JSONHelper.JSONObjectBuilder block = JSONHelper.object(); - JSONHelper.JSONArrayBuilder ids = JSONHelper.array(); - b.getNodes().forEach((bb) -> ids.add(bb.getId())); - block.add(NODES, ids); - JSONHelper.JSONArrayBuilder edges = JSONHelper.array(); - b.getSuccessors().forEach((ss) -> edges.add(ss.getName())); - block.add(EDGES, edges); - return block; - } - - private static String createFileName(String graphType, String graphName, int part) { - String p = (part == -1) ? "" : ("." + part); - String gt = graphType.replaceAll("[^\\p{Alnum}]", "") + '_'; - String gn = graphName.replaceAll("[^\\p{Alnum}]", "_"); - return gt + gn + p + ".json"; - } - - private static JSONHelper.JSONObjectBuilder stacktrace(LocationStackFrame lsf) { - HashMap> langStack = new HashMap<>(); - HashSet memo = new HashSet<>(); - - for (LocationStackFrame t = lsf; t != null; t = t.getParent()) { - for (LocationStratum s : t.getStrata()) { - if (!memo.contains(s)) { - memo.add(s); - String lang = s.language; - String methodName = t.getFullMethodName(); - boolean isJava = lang.contentEquals(JAVA); - ArrayList stack = langStack.getOrDefault(lang, new ArrayList<>()); - methodName = (isJava && methodName != null) ? ("(" + methodName + ") ") : ""; - stack.add(methodName + stratumToString(s)); - langStack.put(lang, stack); - } - } - } - JSONHelper.JSONObjectBuilder st = JSONHelper.object(); - for (String s : langStack.keySet()) { - JSONHelper.JSONArrayBuilder stack = JSONHelper.array(); - langStack.get(s).forEach(stack::add); - st.add(s, stack); - } - - return st; - } - - private static String stratumToString(LocationStratum s) { - String str = (s.uri != null ? s.uri : s.file) + ":" + s.line; - // bci needed ? - return str + stratumPosToString(s); - } - - private static String stratumPosToString(LocationStratum s) { - if (s.startOffset > -1 && s.endOffset > -1) { - return "(" + s.startOffset + "-" + s.endOffset + ")"; - } - if (s.startOffset > -1) { - return "(" + s.startOffset + "-?)"; - } - if (s.endOffset > -1) { - return "(?-" + s.endOffset + ")"; - } - return ""; - } -} - -// modified version of com.oracle.truffle.api.utilities.JSONHelper - -final class JSONHelper { - - private JSONHelper() { - } - - private static String quote(CharSequence value) { - StringBuilder builder = new StringBuilder(value.length() + 2); - builder.append('"'); - for (int i = 0; i < value.length(); i++) { - char c = value.charAt(i); - switch (c) { - case '"': - builder.append("\\\""); - break; - case '\\': - builder.append("\\\\"); - break; - case '\b': - builder.append("\\b"); - break; - case '\f': - builder.append("\\f"); - break; - case '\n': - builder.append("\\n"); - break; - case '\r': - builder.append("\\r"); - break; - case '\t': - builder.append("\\t"); - break; - default: { - if (c < ' ') { - builder.append("\\u00"); - builder.append(Character.forDigit((c >> 4) & 0xF, 16)); - builder.append(Character.forDigit(c & 0xF, 16)); - } else { - builder.append(c); - } - } - } - } - builder.append('"'); - return builder.toString(); - } - - public static JSONObjectBuilder object() { - return new JSONObjectBuilder(); - } - - public static JSONReadyObject readyObject(int startSize) { - return new JSONReadyObject(startSize); - } - - public static JSONArrayBuilder array() { - return new JSONArrayBuilder(); - } - - public static JSONReadyArray readyArray(int startSize) { - return new JSONReadyArray(startSize); - } - - public abstract static class JSONStringBuilder { - - private JSONStringBuilder() { - } - - @Override - public final String toString() { - StringBuilder sb = new StringBuilder(); - appendTo(sb); - return sb.toString(); - } - - protected abstract void appendTo(StringBuilder sb); - - protected static void appendValue(StringBuilder sb, Object value) { - if (value instanceof JSONStringBuilder) { - ((JSONStringBuilder) value).appendTo(sb); - } else if (value instanceof Integer || value instanceof Boolean || value == null) { - sb.append(value); - } else { - sb.append(quote(String.valueOf(value))); - } - } - } - - public static final class JSONObjectBuilder extends JSONStringBuilder { - private final Map contents = new LinkedHashMap<>(); - - private JSONObjectBuilder() { - } - - public JSONObjectBuilder add(String key, String value) { - contents.put(key, value); - return this; - } - - public JSONObjectBuilder add(String key, Number value) { - contents.put(key, value); - return this; - } - - public JSONObjectBuilder add(String key, JSONStringBuilder value) { - contents.put(key, value); - return this; - } - - @Override - protected void appendTo(StringBuilder sb) { - sb.append("{"); - boolean comma = false; - for (Map.Entry entry : contents.entrySet()) { - if (comma) { - sb.append(", "); - } - sb.append(quote(entry.getKey())); - sb.append(": "); - appendValue(sb, entry.getValue()); - comma = true; - } - sb.append("}"); - } - } - - public static final class JSONReadyObject extends JSONStringBuilder { - private final Map contents = new LinkedHashMap<>(); - private int size; - - private JSONReadyObject(int startSize) { - size = startSize + 2 /* '{' '}' */; - } - - public JSONReadyObject add(String key, String value) { - put(key, quote(value)); - return this; - } - - public JSONReadyObject add(String key, Number value) { - put(key, value.toString()); - return this; - } - - public JSONReadyObject add(String key, Boolean value) { - put(key, value.toString()); - return this; - } - - public JSONReadyObject add(String key, JSONStringBuilder value) { - put(key, value.toString()); - return this; - } - - private void put(String key, String v) { - size += key.length() + v.length() + 4 /* ", " ": " */; - contents.put(key, v); - } - - public int getSize() { - return size; - } - - @Override - protected void appendTo(StringBuilder sb) { - sb.append("{"); - boolean comma = false; - for (Map.Entry entry : contents.entrySet()) { - if (comma) { - sb.append(", "); - } - sb.append(quote(entry.getKey())); - sb.append(": "); - sb.append(entry.getValue()); - comma = true; - } - sb.append("}"); - } - } - - public static final class JSONArrayBuilder extends JSONStringBuilder { - private final List contents = new ArrayList<>(); - - private JSONArrayBuilder() { - } - - public JSONArrayBuilder add(String value) { - contents.add(value); - return this; - } - - public JSONArrayBuilder add(Number value) { - contents.add(value); - return this; - } - - public JSONArrayBuilder add(Boolean value) { - contents.add(value); - return this; - } - - public JSONArrayBuilder add(JSONStringBuilder value) { - contents.add(value); - return this; - } - - @Override - protected void appendTo(StringBuilder sb) { - sb.append("["); - boolean comma = false; - for (Object value : contents) { - if (comma) { - sb.append(", "); - } - appendValue(sb, value); - comma = true; - } - sb.append("]"); - } - } - - public static final class JSONReadyArray extends JSONStringBuilder { - private final List contents = new ArrayList<>(); - private int size; - - private JSONReadyArray(int startSize) { - size = startSize + 2 /* '[' ']' */; - } - - public JSONReadyArray add(String value) { - addElement(quote(value)); - return this; - } - - public JSONReadyArray add(Number value) { - addElement(value.toString()); - return this; - } - - public JSONReadyArray add(Boolean value) { - addElement(value.toString()); - return this; - } - - public JSONReadyArray add(JSONStringBuilder value) { - addElement(value.toString()); - return this; - } - - private void addElement(String value) { - size += value.length() + 2 /* ", " */; - contents.add(value); - } - - public int getSize() { - return size; - } - - @Override - protected void appendTo(StringBuilder sb) { - sb.append("["); - boolean comma = false; - for (Object value : contents) { - if (comma) { - sb.append(", "); - } - sb.append(value); - comma = true; - } - sb.append("]"); - } - } - -} diff --git a/visualizer/IdealGraphVisualizer/JSONExporter/src/main/nbm/manifest.mf b/visualizer/IdealGraphVisualizer/JSONExporter/src/main/nbm/manifest.mf deleted file mode 100644 index 3bfbca7a1a56..000000000000 --- a/visualizer/IdealGraphVisualizer/JSONExporter/src/main/nbm/manifest.mf +++ /dev/null @@ -1,3 +0,0 @@ -Manifest-Version: 1.0 -OpenIDE-Module-Localizing-Bundle: org/graalvm/visualizer/jsonexporter/Bundle.properties -OpenIDE-Module-Hide-Classpath-Packages: jdk.graal.compiler.graphio.** diff --git a/visualizer/IdealGraphVisualizer/JSONExporter/src/main/resources/org/graalvm/visualizer/jsonexporter/Bundle.properties b/visualizer/IdealGraphVisualizer/JSONExporter/src/main/resources/org/graalvm/visualizer/jsonexporter/Bundle.properties deleted file mode 100644 index adee06c2e90e..000000000000 --- a/visualizer/IdealGraphVisualizer/JSONExporter/src/main/resources/org/graalvm/visualizer/jsonexporter/Bundle.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Localized module labels. Defaults taken from POM (, , ) if unset. -#OpenIDE-Module-Name= -#OpenIDE-Module-Short-Description= -#OpenIDE-Module-Long-Description= -#OpenIDE-Module-Display-Category= -#Fri Mar 22 12:20:09 PDT 2024 diff --git a/visualizer/IdealGraphVisualizer/pom.xml b/visualizer/IdealGraphVisualizer/pom.xml index ad7ced889976..9e729f303cff 100644 --- a/visualizer/IdealGraphVisualizer/pom.xml +++ b/visualizer/IdealGraphVisualizer/pom.xml @@ -80,7 +80,7 @@ - [17,24) + [17,24] The IGV Netbeans platform requires a JDK version between 17 and 24 @@ -112,7 +112,6 @@ Graal Graph GraphSearch - JSONExporter JavaSources NetworkConnection SelectionCoordinator diff --git a/visualizer/mx.visualizer/mx_visualizer.py b/visualizer/mx.visualizer/mx_visualizer.py index 28d87114e638..b8d7d5ccc4bb 100644 --- a/visualizer/mx.visualizer/mx_visualizer.py +++ b/visualizer/mx.visualizer/mx_visualizer.py @@ -20,7 +20,6 @@ # questions. import mx -import mx_util import os import shutil from os.path import join, exists @@ -29,6 +28,7 @@ _suite = mx.suite('visualizer') igvDir = os.path.join(_suite.dir, 'IdealGraphVisualizer') +c1vDir = os.path.join(_suite.dir, 'C1Visualizer') class NetBeansProject(mx.ArchivableProject, mx.ClasspathDependency): def __init__(self, suite, name, deps, workingSets, theLicense, mxLibs=None, **args): @@ -44,11 +44,11 @@ def __init__(self, suite, name, deps, workingSets, theLicense, mxLibs=None, **ar if not hasattr(self, 'buildDependencies'): self.buildDependencies = [] self.buildDependencies += self.mxLibs - self.output_dirs = ['idealgraphvisualizer'] if self.dist else [] + self.output_dirs = [self.name.lower()] if self.dist else [] # by default skip the unit tests in a build self.build_commands = ["package", "-DskipTests"] - self.subDir = args.get('subDir') or None - self.baseDir = os.path.join(self.dir, self.subDir or 'IdealGraphVisualizer') + self.subDir = args['subDir'] + self.baseDir = os.path.join(self.dir, self.subDir) def is_test_project(self): return False @@ -57,13 +57,13 @@ def source_dirs(self): return [] def classpath_repr(self, resolve=True): - src = os.path.join(self.dir, 'IdealGraphVisualizer', self.name, 'src') + src = os.path.join(self.dir, self.name, self.name, 'src') if not os.path.exists(src): mx.abort(f"Cannot find {src}") return src def output_dir(self, relative=False): - outdir = os.path.join(_suite.dir, 'IdealGraphVisualizer', 'application', 'target') + outdir = os.path.join(_suite.dir, self.name, 'application', 'target') return outdir def source_gen_dir(self): @@ -84,7 +84,7 @@ def getBuildTask(self, args): return NetBeansBuildTask(self, args, None, None) def archive_prefix(self): - return 'igv' + return '' def annotation_processors(self): return [] @@ -116,33 +116,21 @@ def newestOutput(self): return None def build(self): - if self.subject.mxLibs: - libs_dir = os.path.join(self.subject.dir, self.subject.subDir or 'IdealGraphVisualizer', self.subject.name, 'lib') - mx_util.ensure_dir_exists(libs_dir) - for lib in self.subject.mxLibs: - lib_path = lib.classpath_repr(resolve=False) - link_name = os.path.join(libs_dir, os.path.basename(lib_path)) - from_path = os.path.relpath(lib_path, os.path.dirname(link_name)) - mx.log(f"Symlink {from_path}, {link_name}") - os.symlink(from_path, link_name) - mx.log('Symlinks must be copied!') - env = os.environ.copy() - env["MAVEN_OPTS"] = "-Djava.awt.headless=true -Dpolyglot.engine.WarnInterpreterOnly=false" + env["MAVEN_OPTS"] = "-Djava.awt.headless=true -Dpolyglot.engine.WarnInterpreterOnly=false --enable-native-access=ALL-UNNAMED" mx.logv(f"Setting PATH to {os.environ['PATH']}") - mx.log(f"Invoking maven for {' '.join(self.subject.build_commands)} for {self.subject.name} in {self.subject.baseDir}") run_maven(self.subject.build_commands, nonZeroIsFatal=True, cwd=self.subject.baseDir, env=env) for output_dir in self.subject.output_dirs: - os.chmod(os.path.join(self.subject.output_dir(), output_dir, 'bin', 'idealgraphvisualizer'), 0o755) + os.chmod(os.path.join(self.subject.output_dir(), output_dir, 'bin', self.subject.name.lower()), 0o755) mx.log(f'...finished build of {self.subject}') def clean(self, forBuild=False): if self.subject.mxLibs: - libs_dir = os.path.join(self.subject.dir, self.subject.subDir or 'IdealGraphVisualizer', self.subject.name, 'lib') + libs_dir = os.path.join(self.subject.dir, self.subject.subDir, self.subject.name, 'lib') for lib in self.subject.mxLibs: lib_path = lib.classpath_repr(resolve=False) link_name = os.path.join(libs_dir, os.path.basename(lib_path)) @@ -151,33 +139,48 @@ def clean(self, forBuild=False): if forBuild: return env = os.environ.copy() - env["MAVEN_OPTS"] = "-Djava.awt.headless=true" - run_maven(['clean', '--quiet'], resolve=False, nonZeroIsFatal=True, cwd=igvDir, env=env) + env["MAVEN_OPTS"] = "-Djava.awt.headless=true --enable-native-access=ALL-UNNAMED" + run_maven(['clean', '--quiet'], resolve=False, nonZeroIsFatal=True, cwd=os.path.join(_suite.dir, self.name), env=env) def run_maven(args, resolve=True, **kwargs): return mx.run(['mvn'] + args, **kwargs) -@mx.command(_suite.name, 'igv') -def igv(args): - """run the newly built Ideal Graph Visualizer""" +def build_and_run_netbeans(title, name, args, launchDir): # force a build if it hasn't been built yet - if not os.path.exists(os.path.join(_suite.dir, 'IdealGraphVisualizer', 'application', 'target', 'idealgraphvisualizer')): - mx.build(['--dependencies', 'IGV']) + if not os.path.exists(os.path.join(_suite.dir, title, 'application', 'target', name)): + mx.build(['--dependencies', title.upper()]) if mx.get_opts().verbose: args.append('-J-Dnetbeans.logger.console=true') - mx.run([join(igvDir, 'application', 'target', 'idealgraphvisualizer', 'bin', 'idealgraphvisualizer.exe' if mx.is_windows() else 'idealgraphvisualizer'), '--jdkhome', mx.get_jdk().home] + args) + mx.run([join(launchDir, 'application', 'target', name, 'bin', name + '.exe' if mx.is_windows() else name), '--jdkhome', mx.get_jdk().home] + args) + + +@mx.command(_suite.name, 'igv') +def igv(args): + """run the newly built Ideal Graph Visualizer""" + build_and_run_netbeans('IdealGraphVisualizer', 'idealgraphvisualizer', args, igvDir) + +@mx.command(_suite.name, 'c1visualizer') +def c1visualizer(args): + """run the newly built C1Visualizer""" + build_and_run_netbeans('C1Visualizer', 'c1visualizer', args, c1vDir) @mx.command(_suite.name, 'unittest') def test(args): """Run Ideal Graph Visualizer unit tests""" run_maven(['package'], nonZeroIsFatal=True, cwd=igvDir) + # C1Visualizer current has no unit tests -@mx.command(_suite.name, 'release') -def release(args): - """Build a released version using mvn release:prepare""" +@mx.command(_suite.name, 'releaseigv') +def releaseigv(args): + """Build a released version of IdealGraphVisualizer using mvn release:prepare""" run_maven(['-B', 'release:clean', 'release:prepare'], nonZeroIsFatal=True, cwd=igvDir) +@mx.command(_suite.name, 'releasec1v') +def releasec1v(args): + """Build a released version of C1Visualizer using mvn release:prepare""" + run_maven(['-B', 'release:clean', 'release:prepare'], nonZeroIsFatal=True, cwd=c1vDir) + @mx.command(_suite.name, 'verify-graal-graphio') def verify_graal_graphio(args): """Verify org.graalvm.graphio is unchanged between the compiler and visualizer folders""" diff --git a/visualizer/mx.visualizer/suite.py b/visualizer/mx.visualizer/suite.py index 187fc216468c..8c1585a816ff 100644 --- a/visualizer/mx.visualizer/suite.py +++ b/visualizer/mx.visualizer/suite.py @@ -42,14 +42,28 @@ "class": "NetBeansProject", "dist" : "true", }, + "C1Visualizer" : { + "subDir" : "C1Visualizer", + "sourceDirs" : ["src"], + "checkstyle" : "Data", + "class": "NetBeansProject", + "dist" : "true", + }, }, "distributions": { - "IGV": { + "IDEALGRAPHVISUALIZER": { "native" : True, "relpath" : True, "dependencies" : [ "IdealGraphVisualizer", ], }, + "C1VISUALIZER": { + "native" : True, + "relpath" : True, + "dependencies" : [ + "C1Visualizer", + ], + }, }, }