/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * SourceModelVisualizer.java * Created: Sep 4, 2007 * By: Joseph Wong */ package org.openquark.cal.compiler; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.Rectangle; import java.awt.Toolkit; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTextPane; import javax.swing.JTree; import javax.swing.SwingUtilities; import javax.swing.WindowConstants; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeModel; import org.openquark.cal.compiler.IdentifierOccurrenceFinder.FinderState; import org.openquark.cal.compiler.IdentifierResolver.SymbolTable; import org.openquark.cal.compiler.IdentifierResolver.VisitorArgument; import org.openquark.cal.services.BasicCALServices; import org.openquark.cal.services.Status; import org.openquark.cal.services.WorkspaceManager; import org.openquark.util.Pair; /** * A visualizer of source ranges for {@link SourceModel} and {@link IdentifierOccurrence}. * * This application will likely need -Xmx256m or more to allow the CAL workspace to successfully compile. * * To change the workspace being explored, specify it as a command line argument. * To change the default workspace being explored, edit the {@link #WORKSPACE_NAME} constant. * * @author Joseph Wong * @author James Wright */ public class SourceModelVisualizer extends JFrame { public static final String WORKSPACE_NAME = "cal.platform.test.cws"; private static final long serialVersionUID = 5738413354269889573L; /** * A utility class for finding all non static fields (even protected and private ones) in a * class and all its superclasses. * * @author Joseph Wong */ static final class ReflectiveFieldsFinder { /** Memoized results from previous runs. */ private final Map<Class<?>, Field[]> classToFields = new HashMap<Class<?>, Field[]>(); /** * A map mapping a Field to its original accessibility flag, for use during cleanup when the * accessibility flags should be reset back to their original values. */ private final Map<Field, Boolean> fieldAccessibility = new HashMap<Field, Boolean>(); /** The singleton instance. */ static final ReflectiveFieldsFinder INSTANCE = new ReflectiveFieldsFinder(); /** Private constructor. */ private ReflectiveFieldsFinder() { } /** * Gets an array of the non-static fields of the given class and all its superclasses. * * @param cls * the Class to query. * @return all the non-static fields (even private and protected ones) of the class and its * superclasses. */ Field[] getAllNonStaticFields(Class<?> cls) { Field[] fields = classToFields.get(cls); if (fields != null) { return fields; } List<Field> fieldList = new ArrayList<Field>(); Class<?> currentClass = cls; while (currentClass != null) { Field[] curClassFields = currentClass.getDeclaredFields(); for (final Field f : curClassFields) { if (!Modifier.isStatic(f.getModifiers())) { fieldAccessibility.put(f, Boolean.valueOf(f.isAccessible())); f.setAccessible(true); fieldList.add(f); } } currentClass = currentClass.getSuperclass(); } fields = fieldList.toArray(new Field[0]); classToFields.put(cls, fields); return fields; } /** * Cleans up by reseting the accessibility flags modified during invocations to * getAllNonStaticFields(). */ void cleanup() { for (final Map.Entry<Field, Boolean> entry : fieldAccessibility.entrySet()) { Field field = entry.getKey(); Boolean isAccessible = entry.getValue(); field.setAccessible(isAccessible.booleanValue()); } classToFields.clear(); fieldAccessibility.clear(); } @Override public void finalize() { cleanup(); } } /** * Provides a modeless dialogue for displaying the source of a module with a hit highlighted. * * @author Jawright */ private static final class SearchResultsDialog extends JDialog { private static final long serialVersionUID = -122928860414946366L; /** Used for finding and loading/saving modules based on their names */ private final WorkspaceManager workspaceManager; /** Text area where the source is displayed and (potentially) edited */ private final JTextPane editorPane = new JTextPane(); /** Provides scroll functionality for the editorPane */ private final JScrollPane scrollPane = new JScrollPane(editorPane); /** Module name of the current search hit */ private ModuleName currentModuleName = null; /** Size of the parent frame */ private final Rectangle parentBounds; /** * Construct a new SearchResultsDialog. * * @param parent * Parent window for this dialog * @param workspaceManager * workspace manager to obtain Workspace from for searching */ SearchResultsDialog(JFrame parent, WorkspaceManager workspaceManager) { super(parent); this.workspaceManager = workspaceManager; parentBounds = parent.getBounds(); initialize(); } /** * Set up the UI elements */ private void initialize() { setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); JPanel mainPane = new JPanel(new BorderLayout()); mainPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); setContentPane(mainPane); Box buttonBox = Box.createHorizontalBox(); buttonBox.add(Box.createHorizontalGlue()); buttonBox.add(Box.createHorizontalStrut(10)); buttonBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 2, 2)); final Container pane = getContentPane(); pane.add(buttonBox, "South"); pane.add(scrollPane, "Center"); editorPane.setFont(new Font("Monospaced", Font.PLAIN, 12)); scrollPane.setMinimumSize(new Dimension(600, 600)); scrollPane.setPreferredSize(new Dimension(600, 600)); scrollPane.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)); /* addWindowListener(new WindowAdapter() { @Override public void windowActivated(WindowEvent e) { // Force the focus to the editor component (rather than, say, the // Close button) to ensure that the selection is visible. editorPane.requestFocusInWindow(); } }); */ pack(); setModal(false); //setLocation(parentBounds.x + parentBounds.width - getWidth(), parentBounds.y); } /** * Sets the text and selection of the dialog based on a SourcePosition. */ void setResult(ModuleName moduleName, SourceRange sourceRange) { // Update text if necessary if (currentModuleName == null || !currentModuleName.equals(moduleName)) { Reader sourceReader = workspaceManager.getWorkspace().getSourceDefinition(moduleName).getSourceReader(new Status("reading source for search hit display")); if (sourceReader == null) { System.err.println("Could not read source definition for source: " + moduleName); return; } sourceReader = new BufferedReader(sourceReader); try { editorPane.select(0, 0); editorPane.setCharacterAttributes(SimpleAttributeSet.EMPTY, true); editorPane.read(sourceReader, null); } catch (IOException e) { e.printStackTrace(); return; } finally { try { sourceReader.close(); } catch (IOException e) { } } } currentModuleName = moduleName; setTitle("Source of " + moduleName.toSourceText()); if (sourceRange == null) { // deselects final int previousPosition = editorPane.getSelectionStart(); editorPane.select(previousPosition, previousPosition); return; } selectTargetAtCurrentPosition(sourceRange); editorPane.setEditable(false); } void selectTargetAtCurrentPosition(SourceRange sourceRange) { Document doc = editorPane.getDocument(); SourcePosition startPosition = sourceRange.getStartSourcePosition(); SourcePosition endPosition = sourceRange.getEndSourcePosition(); try { int len = doc.getLength(); String text = doc.getText(0, len); int startIndex = startPosition.getPosition(text); int endIndex = endPosition.getPosition(text, startPosition, startIndex); editorPane.select(startIndex, endIndex); editorPane.setSelectedTextColor(Color.BLUE); } catch (BadLocationException e1) { e1.printStackTrace(); } } void highlightDoc(List<SourceRange> sourceRanges) { if (sourceRanges.isEmpty()) { return; } Document doc = editorPane.getDocument(); int len = doc.getLength(); try { String text = doc.getText(0, len); SourcePosition lastPosition = sourceRanges.get(0).getStartSourcePosition(); int lastIndex = lastPosition.getPosition(text); for (final SourceRange sourceRange : sourceRanges) { SourcePosition startPosition = sourceRange.getStartSourcePosition(); SourcePosition endPosition = sourceRange.getEndSourcePosition(); int startIndex = startPosition.getPosition(text, lastPosition, lastIndex); int endIndex = endPosition.getPosition(text, startPosition, startIndex); editorPane.select(startIndex, endIndex); final SimpleAttributeSet attributeSet = new SimpleAttributeSet(editorPane.getCharacterAttributes()); attributeSet.addAttribute(StyleConstants.Foreground, new Color(0x00, 0x99, 0xcc)); editorPane.setCharacterAttributes(attributeSet, false); // we keep the start position because those are guaranteed to be monotonically increasing // and at least less than or equal to all subsequent start-end pairs lastPosition = startPosition; lastIndex = startIndex; } } catch (BadLocationException e) { e.printStackTrace(); } } } /** * The CAL services to use. */ private final BasicCALServices calServices; /* * GUI components */ private JSplitPane horizontalSplitPane; private JSplitPane verticalSplitPane; private JList moduleList; private JTree sourceModelTree; private JTree identifierOccurrenceTree; private SearchResultsDialog dialog; private JLabel sourceModelTitleLabel; private JLabel identifierOccurrencesTitleLabel; /** * Constructs an instance of this class. */ private SourceModelVisualizer(String cwsName) { super("Source Model Visualizer"); MessageLogger messageLogger = new MessageLogger(); calServices = BasicCALServices.makeCompiled(cwsName, messageLogger); if (messageLogger.getNErrors() > 0) { System.err.println(messageLogger); System.exit(1); } } /** * Initialize the GUI and hook up the various components with the backend. */ private void init() { dialog = new SearchResultsDialog(this, calServices.getWorkspaceManager()); final ModuleName[] moduleNamesInProgram = calServices.getWorkspaceManager().getModuleNamesInProgram(); Arrays.sort(moduleNamesInProgram); moduleList = new JList(moduleNamesInProgram); final JPanel moduleListPane = new JPanel(new BorderLayout()); moduleListPane.add(new JLabel("Modules"), BorderLayout.NORTH); moduleListPane.add(new JScrollPane(moduleList), BorderLayout.CENTER); moduleList.setSelectedIndex(moduleNamesInProgram.length - 1); final ModuleName firstModule = moduleNamesInProgram[moduleNamesInProgram.length - 1]; sourceModelTitleLabel = new JLabel("Source Model of " + firstModule); identifierOccurrencesTitleLabel = new JLabel("Identifier Occurrences of " + firstModule); sourceModelTree = new JTree(); final JPanel sourceModelTreePane = new JPanel(new BorderLayout()); sourceModelTreePane.add(sourceModelTitleLabel, BorderLayout.NORTH); sourceModelTreePane.add(new JScrollPane(sourceModelTree), BorderLayout.CENTER); identifierOccurrenceTree = new JTree(); final JPanel identifierOccurrenceTreePane = new JPanel(new BorderLayout()); identifierOccurrenceTreePane.add(identifierOccurrencesTitleLabel, BorderLayout.NORTH); identifierOccurrenceTreePane.add(new JScrollPane(identifierOccurrenceTree), BorderLayout.CENTER); dialog.setResult((ModuleName)moduleList.getSelectedValue(), null); final Pair<TreeModel, TreeModel> treeModels = makeTreeModels(firstModule); sourceModelTree.setModel(treeModels.fst()); identifierOccurrenceTree.setModel(treeModels.snd()); horizontalSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, moduleListPane, sourceModelTreePane); verticalSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, horizontalSplitPane, identifierOccurrenceTreePane); getContentPane().add(verticalSplitPane, BorderLayout.CENTER); moduleList.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { if (moduleList.getSelectedIndex() != -1) { final ModuleName moduleName = (ModuleName)moduleList.getSelectedValue(); dialog.setResult(moduleName, null); dialog.setVisible(true); final Pair<TreeModel, TreeModel> treeModels = makeTreeModels(moduleName); sourceModelTitleLabel.setText("Source Model of " + moduleName.toSourceText()); sourceModelTree.setModel(treeModels.fst()); identifierOccurrencesTitleLabel.setText("Identifier Occurrences in " + moduleName.toSourceText()); identifierOccurrenceTree.setModel(treeModels.snd()); } } } }); sourceModelTree.addTreeSelectionListener(new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent e) { if (sourceModelTree.getSelectionCount() > 0) { final DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)sourceModelTree.getSelectionPath().getLastPathComponent(); final Object userObject = selectedNode.getUserObject(); if (userObject instanceof SourceModelTreeNodeData) { SourceModelTreeNodeData data = (SourceModelTreeNodeData)userObject; dialog.setResult((ModuleName)moduleList.getSelectedValue(), data.getElement().getSourceRange()); dialog.setVisible(true); SourceModelVisualizer.this.requestFocus(); if (selectedNode.getChildCount() > 0) { sourceModelTree.expandPath(sourceModelTree.getSelectionPath()); } } else { if (selectedNode.getChildCount() > 0) { sourceModelTree.setSelectionPath(sourceModelTree.getSelectionPath().pathByAddingChild(selectedNode.getFirstChild())); } } } } }); identifierOccurrenceTree.addTreeSelectionListener(new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent e) { if (identifierOccurrenceTree.getSelectionCount() > 0) { final DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode)identifierOccurrenceTree.getSelectionPath().getLastPathComponent(); final Object userObject = selectedNode.getUserObject(); if (userObject instanceof IdentifierOccurrenceTreeNodeData) { IdentifierOccurrenceTreeNodeData data = (IdentifierOccurrenceTreeNodeData)userObject; dialog.setResult((ModuleName)moduleList.getSelectedValue(), data.getIdentifierOccurrence().getSourceRange()); dialog.setVisible(true); SourceModelVisualizer.this.requestFocus(); } if (selectedNode.getChildCount() > 0) { identifierOccurrenceTree.expandPath(identifierOccurrenceTree.getSelectionPath()); } } } }); setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("/Resources/cal_32x32.jpg"))); setDefaultCloseOperation(EXIT_ON_CLOSE); pack(); SwingUtilities.invokeLater(new Runnable() { public void run() { dialog.setVisible(true); }}); } /** * Builds the two tree models - one for the source model and one for identifier occurrences. * @param moduleName the name of the module off which the models will be built. * @return the pair of tree models. */ private Pair<TreeModel, TreeModel> makeTreeModels(ModuleName moduleName) { ModuleSourceDefinition moduleSource = calServices.getCALWorkspace().getSourceDefinition(moduleName); Status status = new Status("Proccessing module " + moduleSource.getModuleName()); Reader reader = moduleSource.getSourceReader(status); BufferedReader bufferedReader = new BufferedReader(reader); StringBuilder stringBuf = new StringBuilder(); String line = null; try { while ((line = bufferedReader.readLine()) != null) { stringBuf.append(line).append('\n'); } } catch (IOException e) { e.printStackTrace(System.err); } String origSource = stringBuf.toString(); SourceModel.SourceElement moduleDefn = SourceModelUtilities.TextParsing.parseModuleDefnIntoSourceModel(origSource); // the source model tree DefaultMutableTreeNode sourceModelTreeTop = new DefaultMutableTreeNode(new SourceModelTreeNodeData(moduleDefn)); final DefaultTreeModel sourceModelTreeModel = new DefaultTreeModel(sourceModelTreeTop); buildSourceModelTree(sourceModelTreeTop, moduleDefn); // the identifier occurrence tree DefaultMutableTreeNode occurrenceTreeTop = new DefaultMutableTreeNode(); final DefaultTreeModel occurrenceTreeModel = new DefaultTreeModel(occurrenceTreeTop); IdentifierOccurrenceCollector<Void> collector = new IdentifierOccurrenceCollector<Void>(moduleName, true, true, true, true, true); moduleDefn.accept(collector, VisitorArgument.make(SymbolTable.makeRoot(moduleName, IdentifierResolver.makeContext(calServices.getModuleTypeInfo(moduleName))), FinderState.make())); final List<IdentifierOccurrence<?>> occurrences = collector.getCollectedOccurrences(); buildIdentifierOccurrenceTree(occurrenceTreeTop, occurrences); return Pair.<TreeModel, TreeModel>make(sourceModelTreeModel, occurrenceTreeModel); } /** * The data object for nodes in the occurrence tree. * * @author Joseph Wong */ private static final class IdentifierOccurrenceTreeNodeData { private final IdentifierOccurrence<?> occurrence; private final String prefix; IdentifierOccurrenceTreeNodeData(IdentifierOccurrence<?> occurrence, String prefix) { this.occurrence = occurrence; this.prefix = prefix; } public String toString() { return prefix + occurrence.getClass().getName().replaceAll("^org.openquark.cal.compiler.IdentifierOccurrence.", "").replace('$', '.') + "<" + occurrence.getIdentifierInfo().getClass().getName().replaceAll("^org.openquark.cal.compiler.IdentifierInfo.", "").replace('$', '.') + "> " + occurrence.getSourceRange(); } IdentifierOccurrence<?> getIdentifierOccurrence() { return occurrence; } } /** * Builds the occurrence tree. * @param parent the parent node. * @param occurrences the occurrences to display. */ private void buildIdentifierOccurrenceTree(DefaultMutableTreeNode parent, List<IdentifierOccurrence<?>> occurrences) { List<SourceRange> sourceRanges = new ArrayList<SourceRange>(); SourceRange lastSourceRange = new SourceRange(new SourcePosition(0, 0), new SourcePosition(0, 0)); for (final IdentifierOccurrence<?> occurrence : occurrences) { if (occurrence.getSourceRange() != null) { if (lastSourceRange.contains(occurrence.getSourceRange()) && occurrence.getSourceRange().contains(lastSourceRange)) { System.err.println("Duplicate source range: " + lastSourceRange); System.err.println(occurrence.getSourceElement()); } } buildIdentifierOccurrenceNode(parent, occurrence); if (occurrence.getSourceRange() != null) { sourceRanges.add(occurrence.getSourceRange()); } if (occurrence.getSourceRange() != null) { lastSourceRange = occurrence.getSourceRange(); } } dialog.highlightDoc(sourceRanges); } /** * Builds a node for an occurrence. * @param parent the parent node. * @param occurrence an occurrence. */ private void buildIdentifierOccurrenceNode(DefaultMutableTreeNode parent, final IdentifierOccurrence<?> occurrence) { DefaultMutableTreeNode occurrenceNode = new DefaultMutableTreeNode(new IdentifierOccurrenceTreeNodeData(occurrence, "")); parent.add(occurrenceNode); buildSubtreeForObjectWithFieldsForIdentifierOccurrenceTree(occurrenceNode, occurrence); } /** * Builds a subtree for an object with fields for the occurrence tree. * @param parent the parent node. * @param objectWithFields the object with fields. */ private void buildSubtreeForObjectWithFieldsForIdentifierOccurrenceTree(DefaultMutableTreeNode parent, Object objectWithFields) { Class<?> objectClass = objectWithFields.getClass(); Field[] objectFields = ReflectiveFieldsFinder.INSTANCE.getAllNonStaticFields(objectClass); for (final Field field : objectFields) { try { final String fieldName = field.getName(); Object fieldValue = field.get(objectWithFields); buildNodeForObjectWithFieldsForIdentifierOccurrenceTree(parent, fieldName, fieldValue); } catch (Exception e) { e.printStackTrace(System.err); } } } /** * Builds a node for an object with fields for the occurrence tree. * @param parent the parent node. * @param fieldName the field name. * @param fieldValue the value. */ private void buildNodeForObjectWithFieldsForIdentifierOccurrenceTree(DefaultMutableTreeNode parent, final String fieldName, Object fieldValue) { if (fieldValue instanceof IdentifierOccurrence<?>) { final IdentifierOccurrence<?> occurrence = (IdentifierOccurrence<?>)fieldValue; DefaultMutableTreeNode fieldNode = new DefaultMutableTreeNode(new IdentifierOccurrenceTreeNodeData(occurrence, fieldName + " = ")); parent.add(fieldNode); buildSubtreeForObjectWithFieldsForIdentifierOccurrenceTree(fieldNode, occurrence); } else if (fieldValue instanceof IdentifierInfo) { IdentifierInfo identifierInfo = (IdentifierInfo)fieldValue; DefaultMutableTreeNode identifierInfoNode = new DefaultMutableTreeNode(fieldName + " = " + identifierInfo.getClass().getName().replaceAll("^org.openquark.cal.compiler.IdentifierInfo.", "").replace('$', '.')); parent.add(identifierInfoNode); buildSubtreeForObjectWithFieldsForIdentifierOccurrenceTree(identifierInfoNode, identifierInfo); } else if (fieldValue instanceof SourceModel.SourceElement) { DefaultMutableTreeNode fieldNode = new DefaultMutableTreeNode( fieldName + " :: " + displayNameForClassOfSourceElement((SourceModel.SourceElement)fieldValue)); parent.add(fieldNode); } else if (fieldValue instanceof Iterable<?>){ DefaultMutableTreeNode listNode = new DefaultMutableTreeNode("[" + fieldName + "]"); parent.add(listNode); for (final Object element : (Iterable<?>)fieldValue) { buildNodeForObjectWithFieldsForIdentifierOccurrenceTree(listNode, "...", element); } } else { DefaultMutableTreeNode fieldNode = new DefaultMutableTreeNode(fieldName + " = " + fieldValue); parent.add(fieldNode); } } /** * The data object for nodes in the source model tree. * * @author Joseph Wong */ private static final class SourceModelTreeNodeData { private final SourceModel.SourceElement element; SourceModelTreeNodeData(SourceModel.SourceElement element) { this.element = element; } public String toString() { return displayNameForClassOfSourceElement(element); } SourceModel.SourceElement getElement() { return element; } } /** * @return the display name for a SourceElement subclass. */ private static String displayNameForClassOfSourceElement(SourceModel.SourceElement sourceElement) { return sourceElement.getClass().getName().replaceAll("^org.openquark.cal.compiler.SourceModel.", "").replace('$', '.'); } /** * Builds the source model tree. * @param node the root node. * @param element the source element to represent. */ void buildSourceModelTree(DefaultMutableTreeNode node, SourceModel.SourceElement element) { if (element == null) { return; } Class<? extends SourceModel.SourceElement> elementClass = element.getClass(); Field[] fields = ReflectiveFieldsFinder.INSTANCE.getAllNonStaticFields(elementClass); for (final Field field : fields) { try { Object fieldValue = field.get(element); if (fieldValue instanceof SourceModel.SourceElement) { final SourceModel.SourceElement fieldElement = (SourceModel.SourceElement)fieldValue; if (fieldElement != null) { DefaultMutableTreeNode fieldNameNode = new DefaultMutableTreeNode("(" + field.getName() + ")"); node.add(fieldNameNode); DefaultMutableTreeNode fieldElementNode = new DefaultMutableTreeNode(new SourceModelTreeNodeData(fieldElement)); fieldNameNode.add(fieldElementNode); // recurse buildSourceModelTree(fieldElementNode, fieldElement); } } else if (fieldValue instanceof SourceModel.SourceElement[]) { final SourceModel.SourceElement[] fieldElements = (SourceModel.SourceElement[])fieldValue; if (fieldElements.length > 0) { DefaultMutableTreeNode fieldNameNode = new DefaultMutableTreeNode("[" + field.getName() + "]"); node.add(fieldNameNode); for (final SourceModel.SourceElement fieldElement : fieldElements) { DefaultMutableTreeNode fieldElementNode = new DefaultMutableTreeNode(new SourceModelTreeNodeData(fieldElement)); fieldNameNode.add(fieldElementNode); // recurse buildSourceModelTree(fieldElementNode, fieldElement); } } } } catch (Exception e) { e.printStackTrace(System.err); } } } /** * The main method. * @param args */ public static void main(String[] args) { final String cwsName; if (args.length > 0) { cwsName = args[0]; } else { cwsName = WORKSPACE_NAME; } System.out.println("Starting the Source Model Visualizer"); System.out.println("Initializing CAL workspace..."); final SourceModelVisualizer instance = new SourceModelVisualizer(cwsName); instance.init(); System.out.println("...done"); instance.setVisible(true); } }