1 19 20 package org.netbeans.modules.languages.studio; 21 22 import org.netbeans.api.languages.ASTItem; 23 import org.netbeans.api.languages.ASTItem; 24 import org.netbeans.api.languages.ASTNode; 25 import org.netbeans.api.languages.ASTPath; 26 import org.netbeans.api.languages.ParseException; 27 import org.netbeans.api.languages.ParserManager; 28 import org.netbeans.api.languages.ASTToken; 29 import org.netbeans.modules.editor.NbEditorDocument; 30 import org.openide.ErrorManager; 31 import org.openide.cookies.EditorCookie; 32 import org.openide.nodes.Node; 33 import org.openide.util.NbBundle; 34 import org.openide.windows.TopComponent; 35 import org.openide.windows.WindowManager; 36 import java.awt.BorderLayout ; 37 import java.awt.Color ; 38 import java.awt.Component ; 39 import javax.swing.tree.TreePath ; 40 import javax.swing.event.CaretEvent ; 41 import javax.swing.event.CaretListener ; 42 import javax.swing.event.TreeSelectionEvent ; 43 import javax.swing.event.TreeSelectionListener ; 44 import javax.swing.text.Document ; 45 import javax.swing.tree.DefaultTreeCellRenderer ; 46 import javax.swing.tree.DefaultTreeModel ; 47 import javax.swing.tree.TreeNode ; 48 import java.awt.event.FocusEvent ; 49 import java.awt.event.FocusListener ; 50 import java.beans.PropertyChangeEvent ; 51 import java.beans.PropertyChangeListener ; 52 import java.io.Serializable ; 53 import java.lang.ref.WeakReference ; 54 import java.util.ArrayList ; 55 import java.util.Enumeration ; 56 import java.util.HashMap ; 57 import java.util.Iterator ; 58 import java.util.List ; 59 import java.util.Map ; 60 import javax.swing.JEditorPane ; 61 import javax.swing.JScrollPane ; 62 import javax.swing.JTree ; 63 import javax.swing.SwingUtilities ; 64 65 66 69 final class ASTBrowserTopComponent extends TopComponent { 70 71 private static final long serialVersionUID = 1L; 72 private static ASTBrowserTopComponent instance; 73 private static final String PREFERRED_ID = "ASTBrowserTopComponent"; 74 75 private JTree tree; 76 private Listener listener; 77 private HighlighterSupport highlighting = new HighlighterSupport (Color.yellow); 78 79 80 private ASTBrowserTopComponent () { 81 initComponents (); 82 setLayout (new BorderLayout ()); 83 tree = new JTree (); 84 tree.setCellRenderer (new Renderer ()); 85 tree.addTreeSelectionListener (new TreeSelectionListener () { 86 public void valueChanged (TreeSelectionEvent e) { 87 mark (); 88 } 89 }); 90 tree.addFocusListener (new FocusListener () { 91 public void focusGained (FocusEvent e) { 92 mark (); 93 } 94 public void focusLost (FocusEvent e) { 95 mark (); 96 } 97 }); 98 tree.setShowsRootHandles (true); 99 tree.setModel (new DefaultTreeModel (new TNode (null, null))); 100 add (new JScrollPane (tree), BorderLayout.CENTER); 101 setName (NbBundle.getMessage (ASTBrowserTopComponent.class, "CTL_ASTBrowserTopComponent")); 102 setToolTipText (NbBundle.getMessage (ASTBrowserTopComponent.class, "HINT_ASTBrowserTopComponent")); 103 } 105 106 111 private void initComponents() { 113 114 org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); 115 this.setLayout(layout); 116 layout.setHorizontalGroup( 117 layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) 118 .add(0, 400, Short.MAX_VALUE) 119 ); 120 layout.setVerticalGroup( 121 layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) 122 .add(0, 300, Short.MAX_VALUE) 123 ); 124 } 126 127 130 135 public static synchronized ASTBrowserTopComponent getDefault () { 136 if (instance == null) { 137 instance = new ASTBrowserTopComponent (); 138 } 139 return instance; 140 } 141 142 145 public static synchronized ASTBrowserTopComponent findInstance () { 146 TopComponent win = WindowManager.getDefault ().findTopComponent (PREFERRED_ID); 147 if (win == null) { 148 ErrorManager.getDefault ().log (ErrorManager.WARNING, "Cannot find ASTBrowser component. It will not be located properly in the window system."); 149 return getDefault (); 150 } 151 if (win instanceof ASTBrowserTopComponent) { 152 return (ASTBrowserTopComponent)win; 153 } 154 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."); 155 return getDefault (); 156 } 157 158 public int getPersistenceType () { 159 return TopComponent.PERSISTENCE_ALWAYS; 160 } 161 162 public void componentShowing () { 163 refresh (); 165 if (listener == null) 166 listener = new Listener (this); 167 } 168 169 public void componentHidden () { 170 if (listener != null) { 172 listener.remove (); 173 listener = null; 174 } 175 } 176 177 178 public Object writeReplace () { 179 return new ResolvableHelper (); 180 } 181 182 protected String preferredID () { 183 return PREFERRED_ID; 184 } 185 186 private void mark () { 187 if (tree.isFocusOwner ()) { 188 Node[] ns = TopComponent.getRegistry ().getActivatedNodes (); 189 if (ns.length == 1) { 190 EditorCookie editorCookie = (EditorCookie) ns [0].getLookup (). 191 lookup (EditorCookie.class); 192 if (editorCookie != null) { 193 TNode tn = (TNode) tree.getLastSelectedPathComponent (); 194 if (tn != null) { 195 ASTItem item = tn.getASTItem (); 196 if (item == null) return; 197 highlighting.highlight ( 198 editorCookie.getDocument (), 199 item 200 ); 201 return; 202 } 203 } 204 } 205 } 206 highlighting.removeHighlight (); 207 } 208 209 private CaretListener caretListener; 210 private JEditorPane lastPane; 211 private ASTNode rootNode; 212 213 214 private void refresh () { 215 SwingUtilities.invokeLater(new Runnable () { 216 public void run() { 217 ASTNode nr = getCurrentRootNode (); 218 if (nr == rootNode) return; 219 rootNode = nr; 220 DefaultTreeModel model = new DefaultTreeModel (new TNode (null, rootNode)); 221 tree.setModel (model); 222 } 223 }); 224 } 225 226 private ASTNode getCurrentRootNode () { 227 Node[] ns = TopComponent.getRegistry ().getActivatedNodes (); 228 if (ns.length != 1) return null; 229 EditorCookie editorCookie = (EditorCookie) ns [0].getLookup (). 230 lookup (EditorCookie.class); 231 if (editorCookie == null) return null; 232 if (editorCookie.getOpenedPanes () == null) return null; 233 if (editorCookie.getOpenedPanes ().length < 1) return null; 234 JEditorPane pane = editorCookie.getOpenedPanes () [0]; 235 if (caretListener == null) 236 caretListener = new CListener (); 237 if (lastPane != null && lastPane != pane) { 238 lastPane.removeCaretListener (caretListener); 239 lastPane = null; 240 } 241 if (lastPane == null) { 242 pane.addCaretListener (caretListener); 243 lastPane = pane; 244 } 245 Document doc = editorCookie.getDocument (); 246 if (doc == null || !(doc instanceof NbEditorDocument)) return null; 247 String mimeType = (String ) doc.getProperty ("mimeType"); 248 try { 249 return ParserManager.get ((NbEditorDocument) doc).getAST (); 250 } catch (ParseException ex) { 251 return ex.getASTNode (); 252 } 253 } 254 255 256 258 static class TNode implements TreeNode { 259 260 private TNode parent; 261 private ASTItem astItem; 262 private List children; 263 private Map map; 264 265 TNode (TNode parent, ASTItem astItem) { 266 this.parent = parent; 267 this.astItem = astItem; 268 } 269 270 private void initChildren () { 271 if (children != null) return; 272 children = new ArrayList (); 273 map = new HashMap (); 274 if (astItem == null) return; 275 List <ASTItem> chList = astItem.getChildren (); 276 277 if (chList != null) { 278 Iterator <ASTItem> it = chList.iterator (); 279 while (it.hasNext ()) { 280 ASTItem item = it.next (); 281 TreeNode tn = new TNode (this, item); 282 children.add (tn); 283 map.put (item, tn); 284 } 285 } 286 } 287 288 String getName () { 289 if (astItem == null) 290 return "No syntax definition."; 291 if (astItem instanceof ASTNode) 292 return ((ASTNode) astItem).getNT (); 293 return astItem.toString (); 294 } 295 296 ASTItem getASTItem () { 297 return astItem; 298 } 299 300 TreeNode getTreeNode (Object o) { 301 initChildren (); 302 return (TreeNode ) map.get (o); 303 } 304 305 public TreeNode getChildAt (int childIndex) { 306 initChildren (); 307 return (TreeNode ) children.get (childIndex); 308 } 309 310 public int getChildCount () { 311 initChildren (); 312 return children.size (); 313 } 314 315 public TreeNode getParent () { 316 return parent; 317 } 318 319 public int getIndex (TreeNode node) { 320 initChildren (); 321 return children.indexOf (node); 322 } 323 324 public boolean getAllowsChildren () { 325 return true; 326 } 327 328 public boolean isLeaf () { 329 if (astItem == null) 330 return false; 331 return astItem.getChildren ().isEmpty (); 332 } 333 334 public Enumeration children () { 335 return new Enumeration () { 336 private Iterator it = children.iterator (); 337 public boolean hasMoreElements () { 338 return it.hasNext (); 339 } 340 341 public Object nextElement () { 342 return it.next (); 343 } 344 }; 345 } 346 } 347 348 class CListener implements CaretListener { 349 public void caretUpdate (CaretEvent e) { 350 if (rootNode == null) return; 351 ASTPath path = rootNode.findPath (e.getDot ()); 352 if (path == null) return; 353 TreeNode tNode = (TreeNode ) tree.getModel ().getRoot (); 354 List treePath = new ArrayList (); 355 Iterator it = path.listIterator (); 356 if (!it.hasNext ()) return; 357 it.next (); 358 treePath.add (tNode); 359 while (tNode instanceof TNode && it.hasNext ()) { 360 tNode = ((TNode) tNode).getTreeNode (it.next ()); 361 if (tNode == null) throw new NullPointerException (); 362 treePath.add (tNode); 363 } 364 TreePath treePath2 = new TreePath (treePath.toArray ()); 365 DefaultTreeModel model = new DefaultTreeModel ((TreeNode ) tree.getModel ().getRoot ()); 366 tree.setModel (model); 367 tree.setSelectionPath (treePath2); 368 tree.scrollPathToVisible (treePath2); 369 } 370 } 371 372 private static class Renderer extends DefaultTreeCellRenderer { 373 374 public Component getTreeCellRendererComponent ( 375 JTree tree, 376 Object value, 377 boolean sel, 378 boolean expanded, 379 boolean leaf, 380 int row, 381 boolean hasFocus 382 ) { 383 return super.getTreeCellRendererComponent ( 384 tree, 385 ((TNode) value).getName (), 386 sel, expanded, leaf, row, hasFocus 387 ); 388 } 389 } 390 391 final static class ResolvableHelper implements Serializable { 392 private static final long serialVersionUID = 1L; 393 public Object readResolve () { 394 return ASTBrowserTopComponent.getDefault (); 395 } 396 } 397 398 private static class Listener implements PropertyChangeListener { 399 400 private WeakReference component; 401 402 403 Listener (ASTBrowserTopComponent c) { 404 component = new WeakReference (c); 405 TopComponent.getRegistry ().addPropertyChangeListener (this); 406 } 407 408 ASTBrowserTopComponent getComponent () { 409 ASTBrowserTopComponent c = (ASTBrowserTopComponent) component.get (); 410 if (c != null) return c; 411 remove (); 412 return null; 413 } 414 415 void remove () { 416 TopComponent.getRegistry ().removePropertyChangeListener (this); 417 } 418 419 public void propertyChange (PropertyChangeEvent evt) { 420 ASTBrowserTopComponent c = getComponent (); 421 if (c == null) return; 422 c.refresh (); 423 } 424 } 425 } 426 | Popular Tags |