1 19 20 package org.netbeans.modules.project.ui; 21 22 import java.awt.Dimension ; 23 import java.awt.EventQueue ; 24 import java.awt.Frame ; 25 import java.awt.Rectangle ; 26 import java.beans.PropertyChangeEvent ; 27 import java.beans.PropertyChangeListener ; 28 import java.beans.PropertyChangeSupport ; 29 import java.io.File ; 30 import java.io.IOException ; 31 import java.lang.ref.WeakReference ; 32 import java.net.URL ; 33 import java.text.Collator ; 34 import java.util.ArrayList ; 35 import java.util.Arrays ; 36 import java.util.Collection ; 37 import java.util.Collections ; 38 import java.util.Comparator ; 39 import java.util.HashMap ; 40 import java.util.Iterator ; 41 import java.util.LinkedHashSet ; 42 import java.util.LinkedList ; 43 import java.util.List ; 44 import java.util.Map ; 45 import java.util.Set ; 46 import java.util.StringTokenizer ; 47 import javax.swing.Icon ; 48 import javax.swing.JDialog ; 49 import javax.swing.SwingUtilities ; 50 import org.netbeans.api.progress.ProgressHandle; 51 import org.netbeans.api.progress.ProgressHandleFactory; 52 import org.netbeans.api.project.Project; 53 import org.netbeans.api.project.ProjectManager; 54 import org.netbeans.api.project.ProjectUtils; 55 import org.netbeans.modules.project.ui.api.UnloadedProjectInformation; 56 import org.netbeans.modules.project.uiapi.ProjectOpenedTrampoline; 57 import org.netbeans.spi.project.SubprojectProvider; 58 import org.netbeans.spi.project.ui.PrivilegedTemplates; 59 import org.netbeans.spi.project.ui.ProjectOpenedHook; 60 import org.netbeans.spi.project.ui.RecommendedTemplates; 61 import org.openide.ErrorManager; 62 import org.openide.filesystems.FileChangeAdapter; 63 import org.openide.filesystems.FileEvent; 64 import org.openide.filesystems.FileObject; 65 import org.openide.filesystems.FileStateInvalidException; 66 import org.openide.filesystems.FileSystem; 67 import org.openide.filesystems.FileUtil; 68 import org.openide.filesystems.Repository; 69 import org.openide.filesystems.URLMapper; 70 import org.openide.loaders.DataObject; 71 import org.openide.loaders.DataObjectNotFoundException; 72 import org.openide.modules.ModuleInfo; 73 import org.openide.util.Lookup; 74 import org.openide.util.Mutex; 75 import org.openide.util.Mutex.Action; 76 import org.openide.util.NbBundle; 77 import org.openide.util.RequestProcessor; 78 import org.openide.windows.WindowManager; 79 80 84 public final class OpenProjectList { 85 86 public static final Comparator <Project> PROJECT_BY_DISPLAYNAME = new ProjectByDisplayNameComparator(); 87 88 public static final String PROPERTY_OPEN_PROJECTS = "OpenProjects"; 90 public static final String PROPERTY_MAIN_PROJECT = "MainProject"; 91 public static final String PROPERTY_RECENT_PROJECTS = "RecentProjects"; 92 93 private static OpenProjectList INSTANCE; 94 95 private static final int NUM_TEMPLATES = 15; 97 98 private static final ErrorManager ERR = ErrorManager.getDefault().getInstance(OpenProjectList.class.getName()); 99 private static final RequestProcessor OPENING_RP = new RequestProcessor("Opening projects", 1); 100 101 102 private List <Project> openProjects; 103 private HashMap <ModuleInfo, List <Project>> openProjectsModuleInfos; 104 105 106 private Project mainProject; 107 108 109 private final RecentProjectList recentProjects; 110 111 112 private List <String > recentTemplates; 113 114 115 private final PropertyChangeSupport pchSupport; 116 117 private ProjectDeletionListener deleteListener = new ProjectDeletionListener(); 118 119 private PropertyChangeListener infoListener; 120 121 OpenProjectList() { 122 openProjects = new ArrayList <Project>(); 123 openProjectsModuleInfos = new HashMap <ModuleInfo, List <Project>>(); 124 infoListener = new PropertyChangeListener () { 125 public void propertyChange(PropertyChangeEvent evn) { 126 if (ModuleInfo.PROP_ENABLED.equals(evn.getPropertyName())) { 127 checkModuleInfo((ModuleInfo)evn.getSource()); 128 } 129 } 130 }; 131 pchSupport = new PropertyChangeSupport ( this ); 132 recentProjects = new RecentProjectList(10); } 134 135 136 138 public static OpenProjectList getDefault() { 139 boolean needNotify = false; 140 141 synchronized ( OpenProjectList.class ) { 142 if ( INSTANCE == null ) { 143 needNotify = true; 144 INSTANCE = new OpenProjectList(); 145 INSTANCE.openProjects = loadProjectList(); 146 INSTANCE.recentTemplates = new ArrayList <String >( OpenProjectListSettings.getInstance().getRecentTemplates() ); 147 URL mainProjectURL = OpenProjectListSettings.getInstance().getMainProjectURL(); 148 INSTANCE.recentProjects.load(); 150 for( Iterator it = INSTANCE.openProjects.iterator(); it.hasNext(); ) { 151 Project p = (Project)it.next(); 152 INSTANCE.addModuleInfo(p); 153 try { 155 if ( mainProjectURL != null && 156 mainProjectURL.equals( p.getProjectDirectory().getURL() ) ) { 157 INSTANCE.mainProject = p; 158 } 159 } 160 catch( FileStateInvalidException e ) { 161 } 163 } 164 } 165 } 166 if ( needNotify ) { 167 for(Project p: new ArrayList <Project>(INSTANCE.openProjects)) { 169 notifyOpened(p); 170 } 171 172 } 173 174 return INSTANCE; 175 } 176 177 public void open( Project p ) { 178 open( new Project[] {p}, false ); 179 } 180 181 public void open (Project p, boolean openSubprojects ) { 182 open( new Project[] {p}, openSubprojects ); 183 } 184 185 public void open( Project[] projects, boolean openSubprojects ) { 186 open(projects, openSubprojects, false); 187 } 188 189 public void open(final Project[] projects, final boolean openSubprojects, final boolean asynchronously ) { 190 if (projects.length == 0) { 191 return ; 193 } 194 195 long start = System.currentTimeMillis(); 196 197 if (asynchronously) { 198 if (!EventQueue.isDispatchThread()) { EventQueue.invokeLater(new Runnable () { 200 public void run() { 201 open(projects, openSubprojects, asynchronously); 202 } 203 }); 204 return; 205 } 206 final ProgressHandle handle = ProgressHandleFactory.createHandle(NbBundle.getMessage(OpenProjectList.class, "CAP_Opening_Projects")); 207 final Frame mainWindow = WindowManager.getDefault().getMainWindow(); 208 final JDialog dialog = new JDialog (mainWindow, NbBundle.getMessage(OpenProjectList.class, "LBL_Opening_Projects_Progress"), true); 209 final OpeningProjectPanel panel = new OpeningProjectPanel(handle); 210 211 dialog.getContentPane().add(panel); 212 dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); dialog.pack(); 214 215 Rectangle bounds = mainWindow.getBounds(); 216 217 int middleX = bounds.x + bounds.width / 2; 218 int middleY = bounds.y + bounds.height / 2; 219 220 Dimension size = dialog.getPreferredSize(); 221 222 dialog.setBounds(middleX - size.width / 2, middleY - size.height / 2, size.width, size.height); 223 224 OPENING_RP.post(new Runnable () { 225 public void run() { 226 try { 227 doOpen(projects, openSubprojects, handle, panel); 228 } finally { 229 SwingUtilities.invokeLater(new Runnable () { 230 public void run() { 231 try { 233 Thread.currentThread().sleep(50); 234 } catch (InterruptedException e) { 235 } 237 dialog.setVisible(false); 238 dialog.dispose(); 239 } 240 }); 241 } 242 } 243 }); 244 245 dialog.setVisible(true); 246 } else { 247 doOpen(projects, openSubprojects, null, null); 248 } 249 250 long end = System.currentTimeMillis(); 251 252 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 253 ERR.log(ErrorManager.INFORMATIONAL, "opening projects took: " + (end - start) + "ms"); 254 } 255 } 256 257 private void doOpen(Project[] projects, boolean openSubprojects, ProgressHandle handle, OpeningProjectPanel panel) { 258 assert !Arrays.asList(projects).contains(null) : "Projects can't be null"; 259 260 boolean recentProjectsChanged = false; 261 int maxWork = 1000; 262 int workForSubprojects = maxWork / 2; 263 double currentWork = 0; 264 Collection <Project> projectsToOpen = new LinkedHashSet <Project>(); 265 266 if (handle != null) { 267 handle.start(maxWork); 268 handle.progress(0); 269 } 270 271 if (panel != null) { 272 assert projects.length > 0 : "at least one project to open"; 273 274 panel.setProjectName(ProjectUtils.getInformation(projects[0]).getDisplayName()); 275 } 276 277 Map <Project,Set <? extends Project>> subprojectsCache = new HashMap <Project,Set <? extends Project>>(); 279 List <Project> toHandle = new LinkedList <Project>(Arrays.asList(projects)); 280 281 while (!toHandle.isEmpty()) { 282 Project p = toHandle.remove(0); 283 Set <? extends Project> subprojects = openSubprojects ? subprojectsCache.get(p) : Collections.<Project>emptySet(); 284 285 if (subprojects == null) { 286 SubprojectProvider spp = p.getLookup().lookup(SubprojectProvider.class); 287 if (spp != null) { 288 subprojects = spp.getSubprojects(); 289 } else { 290 subprojects = Collections.emptySet(); 291 } 292 subprojectsCache.put(p, subprojects); 293 } 294 295 projectsToOpen.add(p); 296 297 for (Project sub : subprojects) { 298 if (!projectsToOpen.contains(sub) && !toHandle.contains(sub)) { 299 toHandle.add(sub); 300 } 301 } 302 303 double workPerOneProject = (workForSubprojects - currentWork) / (toHandle.size() + 1); 304 int lastState = (int) currentWork; 305 306 currentWork += workPerOneProject; 307 308 if (handle != null && lastState < (int) currentWork) { 309 handle.progress((int) currentWork); 310 } 311 } 312 313 double workPerProject = (maxWork - workForSubprojects) / projectsToOpen.size(); 314 315 for (Project p: projectsToOpen) { 316 317 if (panel != null) { 318 panel.setProjectName(ProjectUtils.getInformation(p).getDisplayName()); 319 } 320 321 recentProjectsChanged |= doOpenProject(p); 322 323 int lastState = (int) currentWork; 324 325 currentWork += workPerProject; 326 327 if (handle != null && lastState < (int) currentWork) { 328 handle.progress((int) currentWork); 329 } 330 } 331 332 synchronized ( this ) { 333 saveProjectList( openProjects ); 334 if ( recentProjectsChanged ) { 335 recentProjects.save(); 336 } 337 } 338 339 if (handle != null) { 340 handle.finish(); 341 } 342 343 final boolean recentProjectsChangedCopy = recentProjectsChanged; 344 345 Mutex.EVENT.readAccess(new Action<Void >() { 346 public Void run() { 347 pchSupport.firePropertyChange( PROPERTY_OPEN_PROJECTS, null, null ); 348 if ( recentProjectsChangedCopy ) { 349 pchSupport.firePropertyChange( PROPERTY_RECENT_PROJECTS, null, null ); 350 } 351 352 return null; 353 } 354 }); 355 } 356 357 public void close( Project projects[], boolean notifyUI ) { 358 if (!ProjectUtilities.closeAllDocuments (projects, notifyUI )) { 359 return; 360 } 361 362 boolean mainClosed = false; 363 boolean someClosed = false; 364 synchronized ( this ) { 365 for( int i = 0; i < projects.length; i++ ) { 366 if ( !openProjects.contains( projects[i] ) ) { 367 continue; } 369 if ( !mainClosed ) { 370 mainClosed = isMainProject( projects[i] ); 371 } 372 openProjects.remove( projects[i] ); 373 removeModuleInfo(projects[i]); 374 375 projects[i].getProjectDirectory().removeFileChangeListener(deleteListener); 376 377 recentProjects.add( projects[i] ); 378 notifyClosed( projects[i] ); 379 someClosed = true; 380 } 381 if ( someClosed ) { 382 saveProjectList( openProjects ); 383 } 384 if ( mainClosed ) { 385 this.mainProject = null; 386 saveMainProject( mainProject ); 387 } 388 if ( someClosed ) { 389 recentProjects.save(); 390 } 391 } 392 if ( someClosed ) { 393 pchSupport.firePropertyChange( PROPERTY_OPEN_PROJECTS, null, null ); 394 } 395 if ( mainClosed ) { 396 pchSupport.firePropertyChange( PROPERTY_MAIN_PROJECT, null, null ); 397 } 398 if ( someClosed ) { 399 pchSupport.firePropertyChange( PROPERTY_RECENT_PROJECTS, null, null ); 400 } 401 for (int i = 0; i < projects.length; i++) { 403 try { 404 ProjectManager.getDefault().saveProject(projects[i]); 405 } catch (IOException e) { 406 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); 407 } 408 } 409 } 410 411 public synchronized Project[] getOpenProjects() { 412 Project projects[] = new Project[ openProjects.size() ]; 413 openProjects.toArray( projects ); 414 return projects; 415 } 416 417 public synchronized boolean isOpen( Project p ) { 418 for( Iterator it = openProjects.iterator(); it.hasNext(); ) { 420 Project cp = (Project)it.next(); 421 if ( p.getProjectDirectory().equals( cp.getProjectDirectory() ) ) { 422 return true; 423 } 424 } 425 return false; 426 } 427 428 public synchronized boolean isMainProject( Project p ) { 429 430 if ( mainProject != null && p != null && 431 mainProject.getProjectDirectory().equals( p.getProjectDirectory() ) ) { 432 return true; 433 } 434 else { 435 return false; 436 } 437 438 } 439 440 public synchronized Project getMainProject() { 441 return mainProject; 442 } 443 444 public void setMainProject( Project mainProject ) { 445 synchronized ( this ) { 446 if (mainProject != null && !openProjects.contains(mainProject)) { 447 throw new IllegalArgumentException ("Project " + ProjectUtils.getInformation(mainProject).getDisplayName() + " is not open and cannot be set as main."); 448 } 449 450 this.mainProject = mainProject; 451 saveMainProject( mainProject ); 452 } 453 pchSupport.firePropertyChange( PROPERTY_MAIN_PROJECT, null, null ); 454 } 455 456 public synchronized List getRecentProjects() { 457 return recentProjects.getProjects(); 458 } 459 460 public synchronized boolean isRecentProjectsEmpty() { 461 return recentProjects.isEmpty(); 462 } 463 464 public synchronized List getRecentProjectsInformation() { 465 return recentProjects.getRecentProjectsInfo(); 466 } 467 468 471 472 public void addPropertyChangeListener( PropertyChangeListener l ) { 473 pchSupport.addPropertyChangeListener( l ); 474 } 475 476 public void removePropertyChangeListener( PropertyChangeListener l ) { 477 pchSupport.removePropertyChangeListener( l ); 478 } 479 480 481 public List <DataObject> getTemplatesLRU( Project project ) { 483 List <FileObject> pLRU = getTemplateNamesLRU( project ); 484 List <DataObject> templates = new ArrayList <DataObject>(); 485 FileSystem sfs = Repository.getDefault().getDefaultFileSystem(); 486 for( Iterator <FileObject> it = pLRU.iterator(); it.hasNext(); ) { 487 FileObject fo = it.next(); 488 if ( fo != null ) { 489 try { 490 DataObject dobj = DataObject.find( fo ); 491 templates.add( dobj ); 492 } 493 catch ( DataObjectNotFoundException e ) { 494 it.remove(); 495 org.openide.ErrorManager.getDefault().notify( org.openide.ErrorManager.INFORMATIONAL, e ); 496 } 497 } 498 else { 499 it.remove(); 500 } 501 } 502 503 return templates; 504 } 505 506 507 public void updateTemplatesLRU( FileObject template ) { 509 510 String templateName = template.getPath(); 511 512 if ( recentTemplates.contains( templateName ) ) { 513 recentTemplates.remove( templateName ); 514 } 515 recentTemplates.add( 0, templateName ); 516 517 if ( recentTemplates.size() > 100 ) { 518 recentTemplates.remove( 100 ); 519 } 520 521 OpenProjectListSettings.getInstance().setRecentTemplates( new ArrayList <String >( recentTemplates ) ); 522 } 523 524 525 527 static void shutdown() { 529 if (INSTANCE != null) { 530 Iterator it = INSTANCE.openProjects.iterator(); 531 while (it.hasNext()) { 532 Project p = (Project)it.next(); 533 notifyClosed(p); 534 } 535 } 536 } 537 538 public static Project fileToProject( File projectDir ) { 540 541 try { 542 543 FileObject fo = FileUtil.toFileObject(projectDir); 544 if (fo != null && fo.isFolder()) { 545 return ProjectManager.getDefault().findProject(fo); 546 } else { 547 return null; 548 } 549 550 } 551 catch ( IOException e ) { 552 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); 553 return null; 554 } 555 556 } 557 558 559 560 562 private static List <Project> URLs2Projects( Collection <URL > URLs ) { 563 ArrayList <Project> result = new ArrayList <Project>( URLs.size() ); 564 565 for(URL url: URLs) { 566 FileObject dir = URLMapper.findFileObject( url ); 567 if ( dir != null && dir.isFolder() ) { 568 try { 569 Project p = ProjectManager.getDefault().findProject( dir ); 570 if ( p != null ) { 571 result.add( p ); 572 } 573 } 574 catch ( Throwable t ) { 575 if (t instanceof ThreadDeath ) { 579 throw (ThreadDeath ) t; 580 } 581 582 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, t); 583 } 584 } 585 } 586 587 return result; 588 } 589 590 private static List <URL > projects2URLs( Collection <Project> projects ) { 591 ArrayList <URL > URLs = new ArrayList <URL >( projects.size() ); 592 for(Project p: projects) { 593 try { 594 URL root = p.getProjectDirectory().getURL(); 595 if ( root != null ) { 596 URLs.add( root ); 597 } 598 } 599 catch( FileStateInvalidException e ) { 600 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); 601 } 602 } 603 604 return URLs; 605 } 606 607 608 private static void notifyOpened(Project p) { 609 for (Iterator i = p.getLookup().lookupAll(ProjectOpenedHook.class).iterator(); i.hasNext(); ) { 610 ProjectOpenedHook hook = (ProjectOpenedHook) i.next(); 611 612 try { 613 ProjectOpenedTrampoline.DEFAULT.projectOpened(hook); 614 } catch (RuntimeException e) { 615 ErrorManager.getDefault().notify(e); 616 INSTANCE.openProjects.remove(p); 618 INSTANCE.removeModuleInfo(p); 619 } catch (Error e) { 620 ErrorManager.getDefault().notify(e); 621 INSTANCE.openProjects.remove(p); 622 INSTANCE.removeModuleInfo(p); 623 } 624 } 625 } 626 627 private static void notifyClosed(Project p) { 628 for (Iterator i = p.getLookup().lookupAll(ProjectOpenedHook.class).iterator(); i.hasNext(); ) { 629 ProjectOpenedHook hook = (ProjectOpenedHook) i.next(); 630 631 try { 632 ProjectOpenedTrampoline.DEFAULT.projectClosed(hook); 633 } catch (RuntimeException e) { 634 ErrorManager.getDefault().notify(e); 635 } catch (Error e) { 636 ErrorManager.getDefault().notify(e); 637 } 638 } 639 } 640 641 private boolean doOpenProject(final Project p) { 642 boolean recentProjectsChanged; 643 644 synchronized (this) { 645 if (openProjects.contains(p)) { 646 return false; 647 } 648 openProjects.add(p); 649 addModuleInfo(p); 650 651 p.getProjectDirectory().addFileChangeListener(deleteListener); 652 recentProjectsChanged = recentProjects.remove(p); 653 } 654 655 notifyOpened(p); 657 658 Mutex.EVENT.readAccess(new Action<Void >() { 659 public Void run() { 660 ProjectUtilities.openProjectFiles(p); 662 663 return null; 664 } 665 }); 666 667 return recentProjectsChanged; 668 } 669 670 private static List <Project> loadProjectList() { 671 List <URL > URLs = OpenProjectListSettings.getInstance().getOpenProjectsURLs(); 672 List <Project> projects = URLs2Projects( URLs ); 673 return projects; 674 } 675 676 677 private static void saveProjectList( List <Project> projects ) { 678 List <URL > URLs = projects2URLs( projects ); 679 OpenProjectListSettings.getInstance().setOpenProjectsURLs( URLs ); 680 } 681 682 private static void saveMainProject( Project mainProject ) { 683 try { 684 URL mainRoot = mainProject == null ? null : mainProject.getProjectDirectory().getURL(); 685 OpenProjectListSettings.getInstance().setMainProjectURL( mainRoot ); 686 } 687 catch ( FileStateInvalidException e ) { 688 OpenProjectListSettings.getInstance().setMainProjectURL( null ); 689 } 690 } 691 692 private ArrayList <FileObject> getTemplateNamesLRU( Project project ) { 693 696 ArrayList <FileObject> result = new ArrayList <FileObject>(NUM_TEMPLATES); 697 698 RecommendedTemplates rt = project.getLookup().lookup( RecommendedTemplates.class ); 699 String rtNames[] = rt == null ? new String [0] : rt.getRecommendedTypes(); 700 PrivilegedTemplates pt = project.getLookup().lookup( PrivilegedTemplates.class ); 701 String ptNames[] = pt == null ? null : pt.getPrivilegedTemplates(); 702 ArrayList <String > privilegedTemplates = new ArrayList <String >( Arrays.asList( pt == null ? new String [0]: ptNames ) ); 703 FileSystem sfs = Repository.getDefault().getDefaultFileSystem(); 704 705 Iterator <String > it = recentTemplates.iterator(); 706 for( int i = 0; i < NUM_TEMPLATES && it.hasNext(); i++ ) { 707 String templateName = it.next(); 708 FileObject fo = sfs.findResource( templateName ); 709 if ( fo == null ) { 710 it.remove(); } 712 else if ( isRecommended( project, fo ) ) { 713 result.add( fo ); 714 privilegedTemplates.remove( templateName ); } 716 else { 717 continue; 718 } 719 } 720 721 it = privilegedTemplates.iterator(); 723 for( int i = result.size(); i < NUM_TEMPLATES && it.hasNext(); i++ ) { 724 String path = it.next(); 725 FileObject fo = sfs.findResource( path ); 726 if ( fo != null ) { 727 result.add( fo ); 728 } 729 } 730 731 return result; 732 733 } 734 735 static boolean isRecommended (Project p, FileObject primaryFile) { 736 if (getRecommendedTypes (p) == null || getRecommendedTypes (p).length == 0) { 737 return true; 739 } 740 741 Object o = primaryFile.getAttribute ("templateCategory"); if (o != null) { 743 assert o instanceof String : primaryFile + " attr templateCategory = " + o; 744 Iterator categoriesIt = getCategories ((String )o).iterator (); 745 boolean ok = false; 746 while (categoriesIt.hasNext ()) { 747 String category = (String )categoriesIt.next (); 748 if (Arrays.asList (getRecommendedTypes (p)).contains (category)) { 749 ok = true; 750 break; 751 } 752 } 753 return ok; 754 } else { 755 return true; 758 } 759 } 760 761 private static String [] getRecommendedTypes (Project project) { 762 RecommendedTemplates rt = project.getLookup().lookup(RecommendedTemplates.class); 763 return rt == null ? null :rt.getRecommendedTypes(); 764 } 765 766 private static List <String > getCategories (String source) { 767 ArrayList <String > categories = new ArrayList <String > (); 768 StringTokenizer cattok = new StringTokenizer (source, ","); while (cattok.hasMoreTokens ()) { 770 categories.add (cattok.nextToken ().trim ()); 771 } 772 return categories; 773 } 774 775 777 779 private static class RecentProjectList { 780 781 private List <ProjectReference> recentProjects; 782 private List <UnloadedProjectInformation> recentProjectsInfos; 783 784 private int size; 785 786 789 public RecentProjectList( int size ) { 790 this.size = size; 791 recentProjects = new ArrayList <ProjectReference>( size ); 792 recentProjectsInfos = new ArrayList <UnloadedProjectInformation>(size); 793 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 794 ERR.log("created a RecentProjectList: size=" + size); 795 } 796 } 797 798 public void add( Project p ) { 799 int index = getIndex( p ); 800 801 if ( index == -1 ) { 802 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 804 ERR.log("add new recent project: " + p); 805 } 806 if ( recentProjects.size() == size ) { 807 recentProjects.remove( size - 1 ); 809 recentProjectsInfos.remove(size - 1); 810 } 811 recentProjects.add( 0, new ProjectReference( p ) ); 812 try { 813 recentProjectsInfos.add(0, ProjectInfoAccessor.DEFAULT.getProjectInfo( 814 ProjectUtils.getInformation(p).getDisplayName(), 815 ProjectUtils.getInformation(p).getIcon(), 816 p.getProjectDirectory().getURL())); 817 } catch(FileStateInvalidException ex) { 818 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); 819 } 820 } 821 else { 822 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 823 ERR.log("re-add recent project: " + p); 824 } 825 recentProjects.remove( index ); 827 recentProjects.add( 0, new ProjectReference( p ) ); 828 recentProjectsInfos.remove(index); 829 try { 830 recentProjectsInfos.add(0, ProjectInfoAccessor.DEFAULT.getProjectInfo( 831 ProjectUtils.getInformation(p).getDisplayName(), 832 ProjectUtils.getInformation(p).getIcon(), 833 p.getProjectDirectory().getURL())); 834 } catch(FileStateInvalidException ex) { 835 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); 836 } 837 } 838 } 839 840 public boolean remove( Project p ) { 841 int index = getIndex( p ); 842 if ( index != -1 ) { 843 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 844 ERR.log("remove recent project: " + p); 845 } 846 recentProjects.remove( index ); 847 recentProjectsInfos.remove(index); 848 return true; 849 } 850 return false; 851 } 852 853 854 public List <Project> getProjects() { 855 List <Project> result = new ArrayList <Project>( recentProjects.size() ); 856 List <ProjectReference> references = new ArrayList <ProjectReference>( recentProjects ); 858 for ( Iterator <ProjectReference> it = references.iterator(); it.hasNext(); ) { 859 ProjectReference pRef = it.next(); 860 Project p = pRef.getProject(); 861 if ( p == null || !p.getProjectDirectory().isValid() ) { 862 remove( p ); if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 864 ERR.log("removing dead recent project: " + p); 865 } 866 } 867 else { 868 result.add( p ); 869 } 870 } 871 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 872 ERR.log("recent projects: " + result); 873 } 874 return result; 875 } 876 877 878 public boolean isEmpty() { 879 boolean empty = recentProjects.isEmpty(); 880 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 881 ERR.log("recent projects empty? " + empty); 882 } 883 return empty; 884 } 885 886 public void load() { 887 List <URL > URLs = OpenProjectListSettings.getInstance().getRecentProjectsURLs(); 888 List <String > names = OpenProjectListSettings.getInstance().getRecentProjectsDisplayNames(); 889 List <ExtIcon> icons = OpenProjectListSettings.getInstance().getRecentProjectsIcons(); 890 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 891 ERR.log("recent project list load: " + URLs); 892 } 893 recentProjects.clear(); 894 for ( Iterator it = URLs.iterator(); it.hasNext(); ) { 895 recentProjects.add( new ProjectReference( (URL )it.next() ) ); 896 } 897 recentProjectsInfos.clear(); 898 for (Iterator iterNames = names.iterator(), iterURLs = URLs.iterator(), iterIcons = icons.iterator(); 899 (iterNames.hasNext() && iterURLs.hasNext() && iterIcons.hasNext()); ) { 900 String name = (String ) iterNames.next(); 901 URL url = (URL ) iterURLs.next(); 902 Icon icon = ((ExtIcon) iterIcons.next()).getIcon(); 903 recentProjectsInfos.add(ProjectInfoAccessor.DEFAULT.getProjectInfo(name, icon, url)); 904 } 905 if (recentProjects.size() != recentProjectsInfos.size()) { 909 recentProjects.clear(); 910 recentProjectsInfos.clear(); 911 } 912 } 913 914 public void save() { 915 List <URL > URLs = new ArrayList <URL >( recentProjects.size() ); 916 for (ProjectReference pRef: recentProjects) { 917 URL pURL = pRef.getURL(); 918 if ( pURL != null ) { 919 URLs.add( pURL ); 920 } 921 } 922 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 923 ERR.log("recent project list save: " + URLs); 924 } 925 OpenProjectListSettings.getInstance().setRecentProjectsURLs( URLs ); 926 int listSize = recentProjectsInfos.size(); 927 List <String > names = new ArrayList <String >(listSize); 928 List <ExtIcon> icons = new ArrayList <ExtIcon>(listSize); 929 for (Iterator it = recentProjectsInfos.iterator(); it.hasNext(); ) { 930 UnloadedProjectInformation prjInfo = (UnloadedProjectInformation) it.next(); 931 names.add(prjInfo.getDisplayName()); 932 ExtIcon extIcon = new ExtIcon(); 933 extIcon.setIcon(prjInfo.getIcon()); 934 icons.add(extIcon); 935 } 936 OpenProjectListSettings.getInstance().setRecentProjectsDisplayNames(names); 937 OpenProjectListSettings.getInstance().setRecentProjectsIcons(icons); 938 } 939 940 private int getIndex( Project p ) { 941 942 URL pURL; 943 try { 944 if ( p == null || p.getProjectDirectory() == null ) { 945 return -1; 946 } 947 pURL = p.getProjectDirectory().getURL(); 948 } 949 catch( FileStateInvalidException e ) { 950 return -1; 951 } 952 953 int i = 0; 954 955 for( Iterator it = recentProjects.iterator(); it.hasNext(); i++) { 956 URL p2URL = ((ProjectReference)it.next()).getURL(); 957 if ( pURL.equals( p2URL ) ) { 958 return i; 959 } 960 } 961 962 return -1; 963 } 964 965 private List getRecentProjectsInfo() { 966 return recentProjectsInfos; 967 } 968 969 private static class ProjectReference { 970 971 private WeakReference <Project> projectReference; 972 private URL projectURL; 973 974 public ProjectReference( URL url ) { 975 this.projectURL = url; 976 } 977 978 public ProjectReference( Project p ) { 979 this.projectReference = new WeakReference <Project>( p ); 980 try { 981 projectURL = p.getProjectDirectory().getURL(); 982 } 983 catch( FileStateInvalidException e ) { 984 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 985 ERR.log("FSIE getting URL for project: " + p.getProjectDirectory()); 986 } 987 } 988 } 989 990 public Project getProject() { 991 992 Project p = null; 993 994 if ( projectReference != null ) { p = projectReference.get(); 996 if ( p != null ) { 997 if ( ProjectManager.getDefault().isValid( p ) ) 999 return p; 1000 else 1001 return null; 1002 } 1003 } 1004 1005 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 1006 ERR.log("no active project reference for " + projectURL); 1007 } 1008 if ( projectURL != null ) { 1009 FileObject dir = URLMapper.findFileObject( projectURL ); 1010 if ( dir != null && dir.isFolder() ) { 1011 try { 1012 p = ProjectManager.getDefault().findProject( dir ); 1013 if ( p != null ) { 1014 projectReference = new WeakReference <Project>( p ); 1015 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 1016 ERR.log("found " + p); 1017 } 1018 return p; 1019 } 1020 } 1021 catch ( IOException e ) { 1022 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 1024 ERR.log("could not load recent project from " + projectURL); 1025 } 1026 } 1027 } 1028 } 1029 1030 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 1031 ERR.log("no recent project in " + projectURL); 1032 } 1033 return null; } 1035 1036 public URL getURL() { 1037 return projectURL; 1038 } 1039 1040 } 1041 1042 } 1043 1044 public static class ProjectByDisplayNameComparator implements Comparator <Project> { 1045 1046 private static Comparator <Object > COLLATOR = Collator.getInstance(); 1047 1048 public int compare(Project p1, Project p2) { 1049 1059 String n1 = ProjectUtils.getInformation(p1).getDisplayName(); 1060 String n2 = ProjectUtils.getInformation(p2).getDisplayName(); 1061 if (n1 != null && n2 != null) { 1062 return COLLATOR.compare(n1, n2); 1063 } else if (n1 == null && n2 != null) { 1064 ERR.log(ErrorManager.WARNING, p1 + ": ProjectInformation.getDisplayName() should not return null!"); 1065 return -1; 1066 } else if (n1 != null && n2 == null) { 1067 ERR.log(ErrorManager.WARNING, p2 + ": ProjectInformation.getDisplayName() should not return null!"); 1068 return 1; 1069 } 1070 return 0; 1072 } 1073 1074 } 1075 1076 1079 private final class ProjectDeletionListener extends FileChangeAdapter { 1080 1081 public ProjectDeletionListener() {} 1082 1083 public void fileDeleted(FileEvent fe) { 1084 synchronized (OpenProjectList.this) { 1085 Project toRemove = null; 1086 for (Project prj : openProjects) { 1087 if (fe.getFile().equals(prj.getProjectDirectory())) { 1088 toRemove = prj; 1089 break; 1090 } 1091 } 1092 if (toRemove != null) { 1093 close(new Project[] {toRemove}, false); 1094 } 1095 } 1096 } 1097 1098 } 1099 1100 1101 private static ModuleInfo findModuleForProject(Project prj) { 1102 Collection <? extends ModuleInfo> instances = Lookup.getDefault().lookupAll(ModuleInfo.class); 1103 ModuleInfo info = null; 1104 for (ModuleInfo cur : instances) { 1105 if (!cur.isEnabled()) { 1106 continue; 1107 } 1108 if (cur.getClassLoader() == prj.getClass().getClassLoader()) { 1109 info = cur; 1110 break; 1111 } 1112 } 1113 return info; 1114 } 1115 1116 private void addModuleInfo(Project prj) { 1117 ModuleInfo info = findModuleForProject(prj); 1118 if (info != null) { 1119 if (!openProjectsModuleInfos.containsKey(info)) { 1121 openProjectsModuleInfos.put(info, new ArrayList <Project>()); 1122 info.addPropertyChangeListener(infoListener); 1123 } 1124 openProjectsModuleInfos.get(info).add(prj); 1125 } 1126 } 1127 1128 private void removeModuleInfo(Project prj) { 1129 ModuleInfo info = findModuleForProject(prj); 1130 removeModuleInfo(prj, info); 1131 } 1132 1133 private void removeModuleInfo(Project prj, ModuleInfo info) { 1134 if (info != null) { 1136 openProjectsModuleInfos.get(info).remove(prj); 1137 if (openProjectsModuleInfos.get(info).size() == 0) { 1138 info.removePropertyChangeListener(infoListener); 1139 openProjectsModuleInfos.remove(info); 1140 } 1141 } 1142 } 1143 1144 private void checkModuleInfo(ModuleInfo info) { 1145 if (info.isEnabled()) { 1146 return; 1147 } 1148 Collection <Project> toRemove = new ArrayList <Project>(openProjectsModuleInfos.get(info)); 1149 if (toRemove != null && toRemove.size() > 0) { 1150 for (Project prj : toRemove) { 1151 removeModuleInfo(prj, info); 1152 } 1153 close(toRemove.toArray(new Project[toRemove.size()]), false); 1154 } 1155 } 1156 1157} | Popular Tags |