org.netbeans.modules.languages.features.ASTBrowserTopComponent Maven / Gradle / Ivy
The newest version!
/*
* 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.
*/
package org.netbeans.modules.languages.features;
import org.netbeans.api.languages.ASTItem;
import org.netbeans.api.languages.ASTItem;
import org.netbeans.api.languages.ASTNode;
import org.netbeans.api.languages.ASTPath;
import org.netbeans.api.languages.ParserManager;
import org.netbeans.modules.editor.NbEditorDocument;
import org.netbeans.modules.languages.ParserManagerImpl;
import org.openide.ErrorManager;
import org.openide.cookies.EditorCookie;
import org.openide.nodes.Node;
import org.openide.util.NbBundle;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import javax.swing.tree.TreePath;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.text.Document;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.swing.JEditorPane;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
/**
* Top component which displays something.
*/
final class ASTBrowserTopComponent extends TopComponent {
private static final long serialVersionUID = 1L;
private static ASTBrowserTopComponent instance;
private static final String PREFERRED_ID = "ASTBrowserTopComponent";
private JTree tree;
private Listener listener;
private HighlighterSupport highlighting = new HighlighterSupport (Color.yellow);
private ASTBrowserTopComponent () {
initComponents ();
setLayout (new BorderLayout ());
tree = new JTree ();
tree.addTreeSelectionListener (new TreeSelectionListener () {
public void valueChanged (TreeSelectionEvent e) {
mark ();
}
});
tree.addFocusListener (new FocusListener () {
public void focusGained (FocusEvent e) {
mark ();
}
public void focusLost (FocusEvent e) {
mark ();
}
});
tree.setShowsRootHandles (true);
tree.setModel (new DefaultTreeModel (new TNode (null, null)));
tree.setCellRenderer (new Renderer ());
add (new JScrollPane (tree), BorderLayout.CENTER);
setName (NbBundle.getMessage (ASTBrowserTopComponent.class, "CTL_ASTBrowserTopComponent"));
setToolTipText (NbBundle.getMessage (ASTBrowserTopComponent.class, "HINT_ASTBrowserTopComponent"));
// setIcon(Utilities.loadImage(ICON_PATH, true));
}
/** 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() {
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 300, Short.MAX_VALUE)
);
}// //GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
/**
* Gets default instance. Do not use directly: reserved for *.settings files only,
* i.e. deserialization routines; otherwise you could get a non-deserialized instance.
* To obtain the singleton instance, use {@link findInstance}.
*/
public static synchronized ASTBrowserTopComponent getDefault () {
if (instance == null) {
instance = new ASTBrowserTopComponent ();
}
return instance;
}
/**
* Obtain the ASTBrowserTopComponent instance. Never call {@link #getDefault} directly!
*/
public static synchronized ASTBrowserTopComponent findInstance () {
TopComponent win = WindowManager.getDefault ().findTopComponent (PREFERRED_ID);
if (win == null) {
ErrorManager.getDefault ().log (ErrorManager.WARNING, "Cannot find ASTBrowser component. It will not be located properly in the window system.");
return getDefault ();
}
if (win instanceof ASTBrowserTopComponent) {
return (ASTBrowserTopComponent)win;
}
ErrorManager.getDefault ().log (ErrorManager.WARNING, "There seem to be multiple components with the '" + PREFERRED_ID + "' ID. That is a potential source of errors and unexpected behavior.");
return getDefault ();
}
public int getPersistenceType () {
return TopComponent.PERSISTENCE_ALWAYS;
}
public void componentShowing () {
// TODO add custom code on component opening
refresh ();
if (listener == null)
listener = new Listener (this);
}
public void componentHidden () {
// TODO add custom code on component closing
if (listener != null) {
listener.remove ();
listener = null;
}
}
/** replaces this in object stream */
public Object writeReplace () {
return new ResolvableHelper ();
}
protected String preferredID () {
return PREFERRED_ID;
}
private void mark () {
if (tree.isFocusOwner ()) {
Node[] ns = TopComponent.getRegistry ().getActivatedNodes ();
if (ns.length == 1) {
EditorCookie editorCookie = ns [0].getLookup ().
lookup (EditorCookie.class);
if (editorCookie != null) {
TNode tn = (TNode) tree.getLastSelectedPathComponent ();
if (tn != null) {
ASTItem item = tn.getASTItem ();
if (item == null) return;
highlighting.highlight (
editorCookie.getDocument (),
item.getOffset (),
item.getEndOffset ()
);
return;
}
}
}
}
highlighting.removeHighlight ();
}
private CaretListener caretListener;
private JEditorPane lastPane;
private ASTNode rootNode;
private void refresh () {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ASTNode nr = getCurrentRootNode ();
if (nr == rootNode) return;
rootNode = nr;
DefaultTreeModel model = new DefaultTreeModel (new TNode (null, rootNode));
tree.setModel (model);
}
});
}
private ASTNode getCurrentRootNode () {
Node[] ns = TopComponent.getRegistry ().getActivatedNodes ();
if (ns.length != 1) return null;
EditorCookie editorCookie = ns [0].getLookup ().
lookup (EditorCookie.class);
if (editorCookie == null) return null;
if (editorCookie.getOpenedPanes () == null) return null;
if (editorCookie.getOpenedPanes ().length < 1) return null;
JEditorPane pane = editorCookie.getOpenedPanes () [0];
if (caretListener == null)
caretListener = new CListener ();
if (lastPane != null && lastPane != pane) {
lastPane.removeCaretListener (caretListener);
lastPane = null;
}
if (lastPane == null) {
pane.addCaretListener (caretListener);
lastPane = pane;
}
Document document = editorCookie.getDocument ();
if (!(document instanceof NbEditorDocument)) return null;
return ParserManagerImpl.getImpl (document).getAST ();
}
// innerclasses ............................................................
static class TNode implements TreeNode {
private TNode parent;
private ASTItem astItem;
private List children;
private Map map;
TNode (TNode parent, ASTItem astItem) {
this.parent = parent;
this.astItem = astItem;
}
private void initChildren () {
if (children != null) return;
children = new ArrayList ();
map = new HashMap ();
if (astItem == null) return;
List chList = astItem.getChildren ();
if (chList != null) {
Iterator it = chList.iterator ();
while (it.hasNext ()) {
ASTItem item = it.next ();
TreeNode tn = new TNode (this, item);
children.add (tn);
map.put (item, tn);
}
}
}
String getName () {
if (astItem == null)
return "No syntax definition.";
if (astItem instanceof ASTNode) {
String nt = ((ASTNode) astItem).getNT ();
if (nt.equals ("S"))
nt += " (" + astItem.getMimeType () + ")";
return nt; // + " [" + astItem.getOffset () + ":" + astItem.getEndOffset () + "]";
}
return astItem.toString (); // + " [" + astItem.getOffset () + ":" + astItem.getEndOffset () + "]";
}
ASTItem getASTItem () {
return astItem;
}
TreeNode getTreeNode (Object o) {
initChildren ();
return map.get (o);
}
public TreeNode getChildAt (int childIndex) {
initChildren ();
return children.get (childIndex);
}
public int getChildCount () {
initChildren ();
return children.size ();
}
public TreeNode getParent () {
return parent;
}
public int getIndex (TreeNode node) {
initChildren ();
return children.indexOf (node);
}
public boolean getAllowsChildren () {
return true;
}
public boolean isLeaf () {
if (astItem == null)
return false;
return astItem.getChildren ().isEmpty ();
}
public Enumeration children () {
return new Enumeration () {
private Iterator it = children.iterator ();
public boolean hasMoreElements () {
return it.hasNext ();
}
public Object nextElement () {
return it.next ();
}
};
}
}
class CListener implements CaretListener {
public void caretUpdate (CaretEvent e) {
if (rootNode == null) return;
ASTPath path = rootNode.findPath (e.getDot ());
if (path == null) return;
TreeNode tNode = (TreeNode) tree.getModel ().getRoot ();
List treePath = new ArrayList ();
Iterator it = path.listIterator ();
if (!it.hasNext ()) return;
it.next ();
treePath.add (tNode);
while (tNode instanceof TNode && it.hasNext ()) {
tNode = ((TNode) tNode).getTreeNode (it.next ());
if (tNode == null) throw new NullPointerException ();
treePath.add (tNode);
}
TreePath treePath2 = new TreePath (treePath.toArray ());
DefaultTreeModel model = new DefaultTreeModel ((TreeNode) tree.getModel ().getRoot ());
tree.setModel (model);
tree.setSelectionPath (treePath2);
tree.scrollPathToVisible (treePath2);
}
}
private static class Renderer extends DefaultTreeCellRenderer {
public Component getTreeCellRendererComponent (
JTree tree,
Object value,
boolean sel,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus
) {
return super.getTreeCellRendererComponent (
tree,
((TNode) value).getName (),
sel, expanded, leaf, row, hasFocus
);
}
}
static final class ResolvableHelper implements Serializable {
private static final long serialVersionUID = 1L;
public Object readResolve () {
return ASTBrowserTopComponent.getDefault ();
}
}
private static class Listener implements PropertyChangeListener {
private WeakReference component;
Listener (ASTBrowserTopComponent c) {
component = new WeakReference (c);
TopComponent.getRegistry ().addPropertyChangeListener (this);
}
ASTBrowserTopComponent getComponent () {
ASTBrowserTopComponent c = component.get ();
if (c != null) return c;
remove ();
return null;
}
void remove () {
TopComponent.getRegistry ().removePropertyChangeListener (this);
}
public void propertyChange (PropertyChangeEvent evt) {
ASTBrowserTopComponent c = getComponent ();
if (c == null) return;
c.refresh ();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy