1 19 20 package org.netbeans.modules.java.actions; 21 22 import java.awt.BorderLayout ; 23 import java.awt.Component ; 24 import java.awt.Color ; 25 import java.awt.Container ; 26 import java.awt.Dialog ; 27 import java.awt.Dimension ; 28 import java.awt.Rectangle ; 29 import java.awt.event.ActionEvent ; 30 import java.awt.event.ActionListener ; 31 import java.awt.event.WindowAdapter ; 32 import java.awt.event.WindowEvent ; 33 import java.io.IOException ; 34 import java.util.ArrayList ; 35 import java.util.ArrayList ; 36 import java.util.Collections ; 37 import java.util.EnumSet ; 38 import java.util.HashSet ; 39 import java.util.List ; 40 import java.util.Set ; 41 import java.util.logging.Level ; 42 import java.util.logging.Logger ; 43 import javax.lang.model.element.ElementKind; 44 import javax.lang.model.element.TypeElement; 45 import javax.swing.AbstractAction ; 46 import javax.swing.DefaultListModel ; 47 import javax.swing.JButton ; 48 import javax.swing.ListCellRenderer ; 49 import javax.swing.DefaultListCellRenderer ; 50 import javax.swing.Icon ; 51 import javax.swing.JEditorPane ; 52 import javax.swing.JLabel ; 53 import javax.swing.JList ; 54 import javax.swing.JList ; 55 import javax.swing.JPanel ; 56 import javax.swing.JViewport ; 57 import javax.swing.ListModel ; 58 import javax.swing.SwingUtilities ; 59 import javax.swing.event.ChangeEvent ; 60 import javax.swing.event.ChangeListener ; 61 import org.netbeans.api.java.classpath.ClassPath; 62 import org.netbeans.api.java.queries.SourceForBinaryQuery; 63 import org.netbeans.api.java.source.ClassIndex; 64 import org.netbeans.api.java.source.ClassIndex.NameKind; 65 import org.netbeans.api.java.source.ClasspathInfo; 66 import org.netbeans.api.java.source.ElementHandle; 67 import org.netbeans.api.java.source.UiUtils; 68 import org.netbeans.api.project.FileOwnerQuery; 69 import org.netbeans.api.project.Project; 70 import org.netbeans.api.project.ProjectInformation; 71 import org.netbeans.api.project.ProjectInformation; 72 import org.netbeans.api.project.ProjectUtils; 73 import org.netbeans.modules.java.source.usages.RepositoryUpdater; 74 import org.netbeans.modules.java.source.util.Models; 75 import org.netbeans.spi.java.classpath.support.ClassPathSupport; 76 import org.openide.DialogDescriptor; 77 import org.openide.DialogDisplayer; 78 import org.openide.ErrorManager; 79 import org.openide.cookies.EditorCookie; 80 import org.openide.filesystems.FileObject; 81 import org.openide.filesystems.FileStateInvalidException; 82 import org.openide.filesystems.FileUtil; 83 import org.openide.nodes.Node; 84 import org.openide.util.HelpCtx; 85 import org.openide.util.NbBundle; 86 import org.openide.util.RequestProcessor; 87 import org.openide.util.Utilities; 88 import org.openide.windows.TopComponent; 89 90 94 public class GoToTypeAction extends AbstractAction implements GoToPanel.ContentProvider { 95 96 private static final Logger LOGGER = Logger.getLogger(GoToTypeAction.class.getName()); 97 private static final ClassPath EMPTY_CLASSPATH = ClassPathSupport.createClassPath( new FileObject[0] ); 98 private ClassIndex.NameKind nameKind; 99 private static ListModel EMPTY_LIST_MODEL = new DefaultListModel (); 100 private static final RequestProcessor rp = new RequestProcessor ("GoToTypeAction-RequestProcessor",1); 101 private Worker running; 102 private RequestProcessor.Task task; 103 private GoToPanel panel; 104 private Set <CacheItem> cache; 105 private Dialog dialog; 106 private JButton okButton; 107 108 109 110 public GoToTypeAction() { 111 super( NbBundle.getMessage( GoToTypeAction.class,"TXT_GoToType") ); 112 putValue("PopupMenuText", NbBundle.getBundle(GoToTypeAction.class).getString("editor-popup-TXT_GoToType")); } 114 115 public void actionPerformed( ActionEvent e ) { 116 try { 117 cache = null; 118 119 panel = new GoToPanel( this ); 120 dialog = createDialog(panel); 121 SwingUtilities.invokeLater( new Runnable () { 122 public void run() { 123 dialog.setVisible(true); 124 } 125 } ); 126 127 Node[] arr = TopComponent.getRegistry ().getActivatedNodes(); 128 String initSearchText = null; 129 if (arr.length > 0) { 130 EditorCookie ec = arr[0].getCookie (EditorCookie.class); 131 if (ec != null) { 132 JEditorPane [] openedPanes = ec.getOpenedPanes (); 133 if (openedPanes != null) { 134 initSearchText = org.netbeans.editor.Utilities.getSelectionOrIdentifier(openedPanes [0]); 135 if (initSearchText != null && org.openide.util.Utilities.isJavaIdentifier(initSearchText)) { 136 panel.setInitialText(initSearchText); 137 } 138 } 139 } 140 } 141 142 } catch (IOException ex) { 143 ErrorManager.getDefault().notify(ex); 144 } 145 } 146 147 148 149 151 152 public ListCellRenderer getListCellRenderer( JList list ) { 153 return new Renderer ( list ); 154 } 155 156 157 public void setListModel( GoToPanel panel, String text ) { 158 if (okButton != null) { 159 okButton.setEnabled (false); 160 } 161 if ( running != null ) { 162 running.cancel(); 163 task.cancel(); 164 running = null; 165 } 166 167 if ( text == null ) { 168 panel.setModel(EMPTY_LIST_MODEL); 169 return; 170 } 171 172 text = text.trim(); 173 174 if ( text.length() == 0) { 175 panel.setModel(EMPTY_LIST_MODEL); 176 return; 177 } 178 179 if (isAllUpper(text)) { 180 nameKind = NameKind.CAMEL_CASE; 181 } 182 else if (containsWildCard(text) != -1) { 183 if (Character.isJavaIdentifierStart(text.charAt(0))) { 184 nameKind = panel.isCaseSensitive() ? NameKind.REGEXP : NameKind.CASE_INSENSITIVE_REGEXP; 185 } 186 else { 187 panel.setModel(EMPTY_LIST_MODEL); 188 return; 189 } 190 191 } 192 else { 193 nameKind = panel.isCaseSensitive() ? NameKind.PREFIX : NameKind.CASE_INSENSITIVE_PREFIX; 194 } 195 196 198 synchronized( this ) { 199 running = new Worker( text ); 200 task = rp.post( running, 220); 201 } 202 } 203 204 public void closeDialog() { 205 dialog.setVisible( false ); 206 cleanup(); 207 } 208 209 public boolean hasValidContent () { 210 return this.okButton != null && this.okButton.isEnabled(); 211 } 212 213 215 private static boolean isAllUpper( String text ) { 216 for( int i = 0; i < text.length(); i++ ) { 217 if ( !Character.isUpperCase( text.charAt( i ) ) ) { 218 return false; 219 } 220 } 221 222 return true; 223 } 224 225 private static int containsWildCard( String text ) { 226 for( int i = 0; i < text.length(); i++ ) { 227 if ( text.charAt( i ) == '?' || text.charAt( i ) == '*' ) { 228 return i; 229 } 230 } 231 return -1; 232 } 233 234 235 237 private Dialog createDialog( final GoToPanel panel) { 238 239 okButton = new JButton (NbBundle.getMessage(GoToTypeAction.class, "CTL_OK")); 240 okButton.setEnabled (false); 241 panel.getAccessibleContext().setAccessibleName( NbBundle.getMessage( GoToTypeAction.class, "AN_GoToType") ); panel.getAccessibleContext().setAccessibleDescription( NbBundle.getMessage( GoToTypeAction.class, "AD_GoToType") ); 244 DialogDescriptor dialogDescriptor = new DialogDescriptor( 245 panel, NbBundle.getMessage( GoToTypeAction.class, "DLG_GoToType" ), true, 248 new Object [] {okButton, DialogDescriptor.CANCEL_OPTION}, 249 okButton, 250 DialogDescriptor.DEFAULT_ALIGN, 251 HelpCtx.DEFAULT_HELP, 252 new DialogButtonListener( panel ) ); 254 dialogDescriptor.setClosingOptions(new Object [] {okButton, DialogDescriptor.CANCEL_OPTION}); 255 256 261 Dialog d = DialogDisplayer.getDefault().createDialog( dialogDescriptor ); 262 263 d.setPreferredSize( new Dimension ( UiOptions.GoToTypeDialog.getWidth(), 265 UiOptions.GoToTypeDialog.getHeight() ) ); 266 267 Rectangle r = Utilities.getUsableScreenBounds(); 269 int maxW = (r.width * 9) / 10; 270 int maxH = (r.height * 9) / 10; 271 Dimension dim = d.getPreferredSize(); 272 dim.width = Math.min(dim.width, maxW); 273 dim.height = Math.min(dim.height, maxH); 274 d.setBounds(Utilities.findCenterBounds(dim)); 275 276 d.addWindowListener(new WindowAdapter () { 277 public void windowClosed(WindowEvent e) { 278 cleanup(); 279 } 280 }); 281 282 return d; 283 284 } 285 286 private void cleanup() { 287 290 if ( GoToTypeAction.this.dialog != null ) { 292 UiOptions.GoToTypeDialog.setHeight(dialog.getHeight()); 294 UiOptions.GoToTypeDialog.setWidth(dialog.getWidth()); 295 296 GoToTypeAction.this.dialog.dispose(); 298 GoToTypeAction.this.dialog = null; 299 GoToTypeAction.this.cache = null; 300 } 301 } 302 303 305 306 private class Worker implements Runnable { 307 308 private volatile boolean isCanceled = false; 309 private final String text; 310 311 public Worker( String text ) { 312 this.text = text; 313 } 314 315 public void run() { 316 List <TypeDescription> types = getTypeNames( text ); 317 if ( isCanceled ) { 318 return; 319 } 320 ListModel model = Models.fromList( types ); 321 if ( isCanceled ) { 322 return; 323 } 324 325 if ( !isCanceled && model != null ) { 326 panel.setModel(model); 327 if (okButton != null && !types.isEmpty()) { 328 okButton.setEnabled (true); 329 } 330 } 331 } 332 333 public void cancel() { 334 isCanceled = true; 335 } 336 337 private List <TypeDescription> getTypeNames( String text ) { 338 339 long time; 340 341 long cp, gss, gsb, sfb, gtn, add, sort; 342 cp = gss = gsb = sfb = gtn = add = sort = 0; 343 344 if (cache == null) { 345 LOGGER.fine("GoToTypeAction.getTypeNames recreates cache\n"); 346 time = System.currentTimeMillis(); 348 ClassPath scp = RepositoryUpdater.getDefault().getScannedSources(); 349 FileObject roots[] = scp.getRoots(); 350 gss += System.currentTimeMillis() - time; 351 FileObject root[] = new FileObject[1]; 352 Set <CacheItem> sources = new HashSet <CacheItem>( roots.length ); 353 for (int i = 0; i < roots.length; i++ ) { 354 root[0] = roots[i]; 355 time = System.currentTimeMillis(); 356 ClasspathInfo ci = ClasspathInfo.create( EMPTY_CLASSPATH, EMPTY_CLASSPATH, ClassPathSupport.createClassPath(root)); LOGGER.fine("GoToTypeAction.getTypeNames created ClasspathInfo for source: " + FileUtil.getFileDisplayName(roots[i])+"\n"); 358 if ( isCanceled ) { 359 return null; 360 } 361 else { 362 sources.add( new CacheItem( roots[i], ci, false ) ); 363 } 364 cp += System.currentTimeMillis() - time; 365 } 366 367 368 369 time = System.currentTimeMillis(); 371 scp = RepositoryUpdater.getDefault().getScannedBinaries(); 372 roots = scp.getRoots(); 373 gsb += System.currentTimeMillis() - time; 374 root = new FileObject[1]; 375 for (int i = 0; i < roots.length; i++ ) { 376 try { 377 time = System.currentTimeMillis(); 378 SourceForBinaryQuery.Result result = SourceForBinaryQuery.findSourceRoots(roots[i].getURL()); 379 if ( result.getRoots().length == 0 ) { 380 continue; 381 } 382 sfb += System.currentTimeMillis() - time; 383 time = System.currentTimeMillis(); 384 root[0] = roots[i]; 385 ClasspathInfo ci = ClasspathInfo.create(ClassPathSupport.createClassPath(root), EMPTY_CLASSPATH, EMPTY_CLASSPATH ); LOGGER.fine("GoToTypeAction.getTypeNames created ClasspathInfo for binary: " + FileUtil.getFileDisplayName(roots[i])+"\n"); 387 sources.add( new CacheItem( roots[i], ci, true ) ); 388 cp += System.currentTimeMillis() - time; 389 } 390 catch ( FileStateInvalidException e ) { 391 continue; 392 } 393 } 394 if ( !isCanceled ) { 395 cache = sources; 396 } 397 else { 398 return null; 399 } 400 401 } 402 LOGGER.fine("GoToTypeAction.getTypeNames collected : " + cache.size() +" elements\n"); 403 404 ArrayList <TypeDescription> types = new ArrayList <TypeDescription>(cache.size() * 20); 405 406 for( CacheItem ci : cache ) { 407 time = System.currentTimeMillis(); 408 409 String textForQuery; 410 switch( nameKind ) { 411 case REGEXP: 412 case CASE_INSENSITIVE_REGEXP: 413 String pattern = text + "*"; pattern = pattern.replace( "*", ".*" ).replace( '?', '.' ); 415 textForQuery = pattern; 416 break; 417 default: 418 textForQuery = text; 419 } 420 LOGGER.fine("GoToTypeAction.getTypeNames queries usages of: " + ci.classpathInfo+"\n"); 421 Set <ElementHandle<TypeElement>> names = ci.classpathInfo.getClassIndex().getDeclaredTypes(textForQuery, nameKind, EnumSet.of( ci.isBinary ? ClassIndex.SearchScope.DEPENDENCIES : ClassIndex.SearchScope.SOURCE )); 422 if ( isCanceled ) { 423 return null; 424 } 425 426 gtn += System.currentTimeMillis() - time; 427 time = System.currentTimeMillis(); 428 429 for (ElementHandle<TypeElement> name : names) { 432 TypeDescription td = new TypeDescription(ci, name ); 435 types.add(td); 436 if ( isCanceled ) { 438 return null; 439 } 440 } 441 add += System.currentTimeMillis() - time; 442 } 443 444 if ( !isCanceled ) { 445 time = System.currentTimeMillis(); 446 Collections.sort(types); 447 sort += System.currentTimeMillis() - time; 448 Logger.getLogger(GoToTypeAction.class.getName()).log(Level.INFO, "PERF - " + " GSS: " + gss + " GSB " + gsb + " CP: " + cp + " SFB: " + sfb + " GTN: " + gtn + " ADD: " + add + " SORT: " + sort ); 449 return types; 450 } 451 else { 452 return null; 453 } 454 } 455 456 } 457 458 459 474 private static class Renderer extends DefaultListCellRenderer implements ChangeListener { 475 476 private JPanel rendererComponent; 477 private JLabel jlName = new JLabel (); 478 private JLabel jlPkg = new JLabel (); 479 private JLabel jlPrj = new JLabel (); 480 private int DARKER_COLOR_COMPONENT = 5; 481 private int LIGHTER_COLOR_COMPONENT = 80; 482 private Color fgColor; 483 private Color fgColorLighter; 484 private Color bgColor; 485 private Color bgColorDarker; 486 private Color bgSelectionColor; 487 private Color fgSelectionColor; 488 489 private JList jList; 490 491 public Renderer( JList list ) { 492 493 jList = list; 494 495 Container container = list.getParent(); 496 if ( container instanceof JViewport ) { 497 ((JViewport )container).addChangeListener(this); 498 stateChanged(new ChangeEvent (container)); 499 } 500 501 rendererComponent = new JPanel (); 502 rendererComponent.setLayout(new BorderLayout ()); 503 rendererComponent.add( jlName, BorderLayout.WEST ); 504 rendererComponent.add( jlPkg, BorderLayout.CENTER); 505 rendererComponent.add( jlPrj, BorderLayout.EAST ); 506 507 508 jlName.setOpaque(false); 509 jlPkg.setOpaque(false); 510 jlPrj.setOpaque(false); 511 512 jlName.setFont(list.getFont()); 513 jlPkg.setFont(list.getFont()); 514 jlPrj.setFont(list.getFont()); 515 516 517 jlPrj.setHorizontalAlignment(RIGHT); 518 jlPrj.setHorizontalTextPosition(LEFT); 519 520 fgColor = list.getForeground(); 522 fgColorLighter = new Color ( 523 Math.min( 255, fgColor.getRed() + LIGHTER_COLOR_COMPONENT), 524 Math.min( 255, fgColor.getGreen() + LIGHTER_COLOR_COMPONENT), 525 Math.min( 255, fgColor.getBlue() + LIGHTER_COLOR_COMPONENT) 526 ); 527 528 bgColor = list.getBackground(); 529 bgColorDarker = new Color ( 530 Math.abs(bgColor.getRed() - DARKER_COLOR_COMPONENT), 531 Math.abs(bgColor.getGreen() - DARKER_COLOR_COMPONENT), 532 Math.abs(bgColor.getBlue() - DARKER_COLOR_COMPONENT) 533 ); 534 bgSelectionColor = list.getSelectionBackground(); 535 fgSelectionColor = list.getSelectionForeground(); 536 } 537 538 public Component getListCellRendererComponent( JList list, 539 Object value, 540 int index, 541 boolean isSelected, 542 boolean hasFocus) { 543 544 546 int height = list.getFixedCellHeight(); 547 int width = list.getFixedCellWidth() - 1; 548 549 width = width < 200 ? 200 : width; 550 551 553 Dimension size = new Dimension ( width, height ); 554 rendererComponent.setMaximumSize(size); 555 rendererComponent.setPreferredSize(size); 556 557 if ( isSelected ) { 558 jlName.setForeground(fgSelectionColor); 559 jlPkg.setForeground(fgSelectionColor); 560 jlPrj.setForeground(fgSelectionColor); 561 rendererComponent.setBackground(bgSelectionColor); 562 } 563 else { 564 jlName.setForeground(fgColor); 565 jlPkg.setForeground(fgColorLighter); 566 jlPrj.setForeground(fgColor); 567 rendererComponent.setBackground( index % 2 == 0 ? bgColor : bgColorDarker ); 568 } 569 570 if ( value instanceof TypeDescription ) { 571 TypeDescription td = (TypeDescription)value; 572 jlName.setIcon(td.getIcon()); 573 jlName.setText(td.getTypeName()); 574 jlPkg.setText(td.getPackageName()); 575 jlPrj.setText(td.getProjectName()); 576 jlPrj.setIcon(td.getProjectIcon()); 577 rendererComponent.setToolTipText( FileUtil.getFileDisplayName(td.getFileObject())); 578 } 579 else { 580 jlName.setText( value.toString() ); 581 } 582 583 return rendererComponent; 584 } 585 586 public void stateChanged(ChangeEvent event) { 587 588 JViewport jv = (JViewport )event.getSource(); 589 590 jlName.setText( "Sample" ); jlName.setIcon(UiUtils.getElementIcon(ElementKind.CLASS, null)); 592 593 jList.setFixedCellHeight(jlName.getPreferredSize().height); 594 jList.setFixedCellWidth(jv.getExtentSize().width); 595 } 596 597 } 598 599 static class CacheItem { 600 601 public final boolean isBinary; 602 public final FileObject fileObject; 603 public final ClasspathInfo classpathInfo; 604 public String projectName; 605 public Icon projectIcon; 606 private ClassPath.Entry defEntry; 607 608 public CacheItem ( FileObject fileObject, ClasspathInfo classpathInfo, boolean isBinary ) { 609 this.isBinary = isBinary; 610 this.fileObject = fileObject; 611 this.classpathInfo = classpathInfo; 612 } 613 614 631 @Override 632 public int hashCode () { 633 return this.fileObject == null ? 0 : this.fileObject.hashCode(); 634 } 635 636 @Override 637 public boolean equals (Object other) { 638 if (other instanceof CacheItem) { 639 CacheItem otherItem = (CacheItem) other; 640 return this.fileObject == null ? otherItem.fileObject == null : this.fileObject.equals(otherItem.fileObject); 641 } 642 return false; 643 } 644 645 public FileObject getRoot() { 646 return fileObject; 647 } 648 649 public boolean isBinary() { 650 return isBinary; 651 } 652 653 public synchronized String getProjectName() { 654 if ( !isBinary && projectName == null) { 655 initProjectInfo(); 656 } 657 return projectName; 658 } 659 660 public synchronized Icon getProjectIcon() { 661 if ( !isBinary && projectIcon == null ) { 662 initProjectInfo(); 663 } 664 return projectIcon; 665 } 666 667 private void initProjectInfo() { 668 Project p = FileOwnerQuery.getOwner(fileObject); 669 if (p != null) { 670 ProjectInformation pi = ProjectUtils.getInformation( p ); 671 projectName = pi.getDisplayName(); 672 projectIcon = pi.getIcon(); 673 } 674 } 675 676 } 677 678 679 private class DialogButtonListener implements ActionListener { 680 681 private GoToPanel panel; 682 683 public DialogButtonListener( GoToPanel panel ) { 684 this.panel = panel; 685 } 686 687 public void actionPerformed(ActionEvent e) { 688 if ( e.getSource() == okButton) { 689 panel.openSelectedItem(); 690 } 691 } 692 693 } 694 695 } 696 | Popular Tags |