1 19 20 package org.netbeans.modules.java.j2seproject.ui; 21 22 import java.awt.Image ; 23 import java.awt.event.ActionEvent ; 24 import java.beans.PropertyChangeEvent ; 25 import java.beans.PropertyChangeListener ; 26 import java.io.CharConversionException ; 27 import java.io.IOException ; 28 import java.util.ArrayList ; 29 import java.util.Arrays ; 30 import java.util.Collection ; 31 import java.util.HashMap ; 32 import java.util.HashSet ; 33 import java.util.List ; 34 import java.util.Map ; 35 import java.util.ResourceBundle ; 36 import java.util.Set ; 37 import javax.swing.AbstractAction ; 38 import javax.swing.Action ; 39 import javax.swing.JSeparator ; 40 import javax.swing.event.ChangeEvent ; 41 import javax.swing.event.ChangeListener ; 42 import org.netbeans.api.java.platform.JavaPlatform; 43 import org.netbeans.api.java.platform.JavaPlatformManager; 44 import org.netbeans.api.java.project.JavaProjectConstants; 45 import org.netbeans.api.project.FileOwnerQuery; 46 import org.netbeans.api.project.Project; 47 import org.netbeans.api.project.ProjectUtils; 48 import org.netbeans.api.project.SourceGroup; 49 import org.netbeans.api.project.Sources; 50 import org.netbeans.modules.java.j2seproject.J2SEProjectUtil; 51 import org.netbeans.modules.java.j2seproject.ui.customizer.J2SEProjectProperties; 52 import org.netbeans.modules.java.j2seproject.J2SEProject; 53 import org.netbeans.modules.java.j2seproject.SourceRoots; 54 import org.netbeans.modules.java.j2seproject.UpdateHelper; 55 import org.netbeans.spi.java.project.support.ui.BrokenReferencesSupport; 56 import org.netbeans.spi.java.project.support.ui.PackageView; 57 import org.netbeans.spi.project.ActionProvider; 58 import org.netbeans.spi.project.SubprojectProvider; 59 import org.netbeans.spi.project.support.ant.PropertyEvaluator; 60 import org.netbeans.spi.project.support.ant.ReferenceHelper; 61 import org.netbeans.spi.project.ui.LogicalViewProvider; 62 import org.netbeans.spi.project.ui.support.CommonProjectActions; 63 import org.netbeans.spi.project.ui.support.NodeFactorySupport; 64 import org.netbeans.spi.project.ui.support.DefaultProjectOperations; 65 import org.netbeans.spi.project.ui.support.ProjectSensitiveActions; 66 import org.openide.ErrorManager; 67 import org.openide.actions.FindAction; 68 import org.openide.actions.ToolsAction; 69 import org.openide.filesystems.FileObject; 70 import org.openide.filesystems.FileStateInvalidException; 71 import org.openide.filesystems.FileStatusEvent; 72 import org.openide.filesystems.FileStatusListener; 73 import org.openide.filesystems.FileSystem; 74 import org.openide.filesystems.FileUtil; 75 import org.openide.filesystems.Repository; 76 import org.openide.loaders.DataFolder; 77 import org.openide.loaders.FolderLookup; 78 import org.openide.modules.SpecificationVersion; 79 import org.openide.nodes.AbstractNode; 80 import org.openide.nodes.Node; 81 import org.openide.util.HelpCtx; 82 import org.openide.util.Lookup; 83 import org.openide.util.NbBundle; 84 import org.openide.util.RequestProcessor; 85 import org.openide.util.Utilities; 86 import org.openide.util.WeakListeners; 87 import org.openide.util.actions.SystemAction; 88 import org.openide.util.lookup.Lookups; 89 import org.openide.xml.XMLUtil; 90 91 95 public class J2SELogicalViewProvider implements LogicalViewProvider { 96 97 private static final RequestProcessor BROKEN_LINKS_RP = new RequestProcessor("J2SEPhysicalViewProvider.BROKEN_LINKS_RP"); 99 private final J2SEProject project; 100 private final UpdateHelper helper; 101 private final PropertyEvaluator evaluator; 102 private final SubprojectProvider spp; 103 private final ReferenceHelper resolver; 104 private List <ChangeListener > changeListeners; 105 106 public J2SELogicalViewProvider(J2SEProject project, UpdateHelper helper, PropertyEvaluator evaluator, SubprojectProvider spp, ReferenceHelper resolver) { 107 this.project = project; 108 assert project != null; 109 this.helper = helper; 110 assert helper != null; 111 this.evaluator = evaluator; 112 assert evaluator != null; 113 this.spp = spp; 114 assert spp != null; 115 this.resolver = resolver; 116 } 117 118 public Node createLogicalView() { 119 return new J2SELogicalViewRootNode(); 120 } 121 122 public PropertyEvaluator getEvaluator() { 123 return evaluator; 124 } 125 126 public ReferenceHelper getRefHelper() { 127 return resolver; 128 } 129 130 public UpdateHelper getUpdateHelper() { 131 return helper; 132 } 133 134 public Node findPath(Node root, Object target) { 135 Project project = root.getLookup().lookup(Project.class); 136 if (project == null) { 137 return null; 138 } 139 140 if (target instanceof FileObject) { 141 FileObject fo = (FileObject) target; 142 Project owner = FileOwnerQuery.getOwner(fo); 143 if (!project.equals(owner)) { 144 return null; } 146 147 for (Node n : root.getChildren().getNodes(true)) { 148 Node result = PackageView.findPath(n, target); 149 if (result != null) { 150 return result; 151 } 152 } 153 } 154 155 return null; 156 } 157 158 159 160 public synchronized void addChangeListener(ChangeListener l) { 161 if (this.changeListeners == null) { 162 this.changeListeners = new ArrayList <ChangeListener >(); 163 } 164 this.changeListeners.add(l); 165 } 166 167 public synchronized void removeChangeListener(ChangeListener l) { 168 if (this.changeListeners == null) { 169 return; 170 } 171 this.changeListeners.remove(l); 172 } 173 174 179 public void testBroken() { 180 ChangeListener [] _listeners; 181 synchronized (this) { 182 if (this.changeListeners == null) { 183 return; 184 } 185 _listeners = changeListeners.toArray(new ChangeListener [changeListeners.size()]); 186 } 187 ChangeEvent event = new ChangeEvent (this); 188 for (ChangeListener l : _listeners) { 189 l.stateChanged(event); 190 } 191 } 192 193 private static Lookup createLookup( Project project ) { 194 DataFolder rootFolder = DataFolder.findFolder(project.getProjectDirectory()); 195 return Lookups.fixed(project, rootFolder); 197 } 198 199 200 202 private static final String [] BREAKABLE_PROPERTIES = { 203 J2SEProjectProperties.JAVAC_CLASSPATH, 204 J2SEProjectProperties.RUN_CLASSPATH, 205 J2SEProjectProperties.DEBUG_CLASSPATH, 206 J2SEProjectProperties.RUN_TEST_CLASSPATH, 207 J2SEProjectProperties.DEBUG_TEST_CLASSPATH, 208 J2SEProjectProperties.JAVAC_TEST_CLASSPATH, 209 }; 210 211 public boolean hasBrokenLinks () { 212 return BrokenReferencesSupport.isBroken(helper.getAntProjectHelper(), resolver, getBreakableProperties(), 213 new String [] {J2SEProjectProperties.JAVA_PLATFORM}); 214 } 215 216 public boolean hasInvalidJdkVersion () { 217 String javaSource = this.evaluator.getProperty("javac.source"); String javaTarget = this.evaluator.getProperty("javac.target"); if (javaSource == null && javaTarget == null) { 220 return false; 222 } 223 224 final String platformId = this.evaluator.getProperty("platform.active"); final JavaPlatform activePlatform = J2SEProjectUtil.getActivePlatform (platformId); 226 if (activePlatform == null) { 227 return true; 228 } 229 SpecificationVersion platformVersion = activePlatform.getSpecification().getVersion(); 230 try { 231 return (javaSource != null && new SpecificationVersion (javaSource).compareTo(platformVersion)>0) 232 || (javaTarget != null && new SpecificationVersion (javaTarget).compareTo(platformVersion)>0); 233 } catch (NumberFormatException nfe) { 234 ErrorManager.getDefault().log("Invalid javac.source: "+javaSource+" or javac.target: "+javaTarget+" of project:" 235 +this.project.getProjectDirectory().getPath()); 236 return true; 237 } 238 } 239 240 private String [] getBreakableProperties() { 241 SourceRoots roots = this.project.getSourceRoots(); 242 String [] srcRootProps = roots.getRootProperties(); 243 roots = this.project.getTestSourceRoots(); 244 String [] testRootProps = roots.getRootProperties(); 245 String [] result = new String [BREAKABLE_PROPERTIES.length + srcRootProps.length + testRootProps.length]; 246 System.arraycopy(BREAKABLE_PROPERTIES, 0, result, 0, BREAKABLE_PROPERTIES.length); 247 System.arraycopy(srcRootProps, 0, result, BREAKABLE_PROPERTIES.length, srcRootProps.length); 248 System.arraycopy(testRootProps, 0, result, BREAKABLE_PROPERTIES.length + srcRootProps.length, testRootProps.length); 249 return result; 250 } 251 252 private static Image brokenProjectBadge = Utilities.loadImage("org/netbeans/modules/java/j2seproject/ui/resources/brokenProjectBadge.gif", true); 253 254 256 private final class J2SELogicalViewRootNode extends AbstractNode implements Runnable , FileStatusListener, ChangeListener , PropertyChangeListener { 257 258 private Image icon; 259 private Lookup lookup; 260 private Action brokenLinksAction; 261 private boolean broken; private boolean illegalState; 264 private Set <FileObject> files; 266 private Map <FileSystem,FileStatusListener> fileSystemListeners; 267 private RequestProcessor.Task task; 268 private final Object privateLock = new Object (); 269 private boolean iconChange; 270 private boolean nameChange; 271 private ChangeListener sourcesListener; 272 private Map <SourceGroup,PropertyChangeListener > groupsListeners; 273 276 public J2SELogicalViewRootNode() { 277 super(NodeFactorySupport.createCompositeChildren(project, "Projects/org-netbeans-modules-java-j2seproject/Nodes"), 278 Lookups.singleton(project)); 279 setIconBaseWithExtension("org/netbeans/modules/java/j2seproject/ui/resources/j2seProject.gif"); 280 super.setName( ProjectUtils.getInformation( project ).getDisplayName() ); 281 if (hasBrokenLinks()) { 282 broken = true; 283 } 284 else if (hasInvalidJdkVersion ()) { 285 illegalState = true; 286 } 287 brokenLinksAction = new BrokenLinksAction(); 288 setProjectFiles(project); 289 } 290 291 292 protected final void setProjectFiles(Project project) { 293 Sources sources = ProjectUtils.getSources(project); if (sourcesListener == null) { 295 sourcesListener = WeakListeners.change(this, sources); 296 sources.addChangeListener(sourcesListener); 297 } 298 setGroups(Arrays.asList(sources.getSourceGroups(Sources.TYPE_GENERIC))); 299 } 300 301 302 private final void setGroups(Collection <SourceGroup> groups) { 303 if (groupsListeners != null) { 304 for (Map.Entry <SourceGroup,PropertyChangeListener > e : groupsListeners.entrySet()) { 305 e.getKey().removePropertyChangeListener(e.getValue()); 306 } 307 } 308 groupsListeners = new HashMap <SourceGroup,PropertyChangeListener >(); 309 Set <FileObject> roots = new HashSet <FileObject>(); 310 for (SourceGroup group : groups) { 311 PropertyChangeListener pcl = WeakListeners.propertyChange(this, group); 312 groupsListeners.put(group, pcl); 313 group.addPropertyChangeListener(pcl); 314 FileObject fo = group.getRootFolder(); 315 roots.add(fo); 316 } 317 setFiles(roots); 318 } 319 320 protected final void setFiles(Set <FileObject> files) { 321 if (fileSystemListeners != null) { 322 for (Map.Entry <FileSystem,FileStatusListener> e : fileSystemListeners.entrySet()) { 323 e.getKey().removeFileStatusListener(e.getValue()); 324 } 325 } 326 327 fileSystemListeners = new HashMap <FileSystem,FileStatusListener>(); 328 this.files = files; 329 if (files == null) { 330 return; 331 } 332 333 Set <FileSystem> hookedFileSystems = new HashSet <FileSystem>(); 334 for (FileObject fo : files) { 335 try { 336 FileSystem fs = fo.getFileSystem(); 337 if (hookedFileSystems.contains(fs)) { 338 continue; 339 } 340 hookedFileSystems.add(fs); 341 FileStatusListener fsl = FileUtil.weakFileStatusListener(this, fs); 342 fs.addFileStatusListener(fsl); 343 fileSystemListeners.put(fs, fsl); 344 } catch (FileStateInvalidException e) { 345 ErrorManager err = ErrorManager.getDefault(); 346 err.annotate(e, ErrorManager.UNKNOWN, "Cannot get " + fo + " filesystem, ignoring...", null, null, null); err.notify(ErrorManager.INFORMATIONAL, e); 348 } 349 } 350 } 351 352 public String getHtmlDisplayName() { 353 String dispName = super.getDisplayName(); 354 try { 355 dispName = XMLUtil.toElementContent(dispName); 356 } catch (CharConversionException ex) { 357 return dispName; 358 } 359 return broken || illegalState ? "<font color=\"#A40000\">" + dispName + "</font>" : null; } 362 363 public Image getIcon(int type) { 364 Image img = getMyIcon(type); 365 366 if (files != null && !files.isEmpty()) { 367 try { 368 FileObject fo = files.iterator().next(); 369 img = fo.getFileSystem().getStatus().annotateIcon(img, type, files); 370 } catch (FileStateInvalidException e) { 371 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); 372 } 373 } 374 375 return img; 376 } 377 378 private Image getMyIcon(int type) { 379 Image original = super.getIcon(type); 380 return broken || illegalState ? Utilities.mergeImages(original, brokenProjectBadge, 8, 0) : original; 381 } 382 383 public Image getOpenedIcon(int type) { 384 Image img = getMyOpenedIcon(type); 385 386 if (files != null && !files.isEmpty()) { 387 try { 388 FileObject fo = files.iterator().next(); 389 img = fo.getFileSystem().getStatus().annotateIcon(img, type, files); 390 } catch (FileStateInvalidException e) { 391 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); 392 } 393 } 394 395 return img; 396 } 397 398 private Image getMyOpenedIcon(int type) { 399 Image original = super.getOpenedIcon(type); 400 return broken || illegalState ? Utilities.mergeImages(original, brokenProjectBadge, 8, 0) : original; 401 } 402 403 public void run() { 404 boolean fireIcon; 405 boolean fireName; 406 synchronized (privateLock) { 407 fireIcon = iconChange; 408 fireName = nameChange; 409 iconChange = false; 410 nameChange = false; 411 } 412 if (fireIcon) { 413 fireIconChange(); 414 fireOpenedIconChange(); 415 } 416 if (fireName) { 417 fireDisplayNameChange(null, null); 418 } 419 } 420 421 public void annotationChanged(FileStatusEvent event) { 422 if (task == null) { 423 task = RequestProcessor.getDefault().create(this); 424 } 425 426 synchronized (privateLock) { 427 if ((iconChange == false && event.isIconChange()) || (nameChange == false && event.isNameChange())) { 428 for (FileObject fo : files) { 429 if (event.hasChanged(fo)) { 430 iconChange |= event.isIconChange(); 431 nameChange |= event.isNameChange(); 432 } 433 } 434 } 435 } 436 437 task.schedule(50); } 439 440 public void stateChanged(ChangeEvent e) { 442 setProjectFiles(project); 443 } 444 445 public void propertyChange(PropertyChangeEvent evt) { 447 setProjectFiles(project); 448 } 449 450 public Action [] getActions( boolean context ) { 451 return getAdditionalActions(); 452 } 453 454 public boolean canRename() { 455 return true; 456 } 457 458 public void setName(String s) { 459 DefaultProjectOperations.performDefaultRenameOperation(project, s); 460 } 461 462 public HelpCtx getHelpCtx() { 463 return new HelpCtx(J2SELogicalViewRootNode.class); 464 } 465 466 477 478 480 private Action [] getAdditionalActions() { 481 482 ResourceBundle bundle = NbBundle.getBundle(J2SELogicalViewProvider.class); 483 484 List <Action > actions = new ArrayList <Action >(); 485 486 actions.add(CommonProjectActions.newFileAction()); 487 actions.add(null); 488 actions.add(ProjectSensitiveActions.projectCommandAction(ActionProvider.COMMAND_BUILD, bundle.getString("LBL_BuildAction_Name"), null)); actions.add(ProjectSensitiveActions.projectCommandAction(ActionProvider.COMMAND_REBUILD, bundle.getString("LBL_RebuildAction_Name"), null)); actions.add(ProjectSensitiveActions.projectCommandAction(ActionProvider.COMMAND_CLEAN, bundle.getString("LBL_CleanAction_Name"), null)); actions.add(ProjectSensitiveActions.projectCommandAction(JavaProjectConstants.COMMAND_JAVADOC, bundle.getString("LBL_JavadocAction_Name"), null)); actions.add(null); 493 actions.add(ProjectSensitiveActions.projectCommandAction(ActionProvider.COMMAND_RUN, bundle.getString("LBL_RunAction_Name"), null)); actions.add(ProjectSensitiveActions.projectCommandAction(ActionProvider.COMMAND_DEBUG, bundle.getString("LBL_DebugAction_Name"), null)); actions.add(ProjectSensitiveActions.projectCommandAction(ActionProvider.COMMAND_TEST, bundle.getString("LBL_TestAction_Name"), null)); actions.add(CommonProjectActions.setProjectConfigurationAction()); 497 actions.add(null); 498 actions.add(CommonProjectActions.setAsMainProjectAction()); 499 actions.add(CommonProjectActions.openSubprojectsAction()); 500 actions.add(CommonProjectActions.closeProjectAction()); 501 actions.add(null); 502 actions.add(CommonProjectActions.renameProjectAction()); 503 actions.add(CommonProjectActions.moveProjectAction()); 504 actions.add(CommonProjectActions.copyProjectAction()); 505 actions.add(CommonProjectActions.deleteProjectAction()); 506 actions.add(null); 507 actions.add(SystemAction.get(FindAction.class)); 508 actions.add(null); 509 510 512 FileObject fo = Repository.getDefault().getDefaultFileSystem().findResource("Projects/Actions"); if (fo != null) { 514 for (Object next : new FolderLookup(DataFolder.findFolder(fo)).getLookup().lookupAll(Object .class)) { 515 if (next instanceof Action ) { 516 actions.add((Action ) next); 517 } else if (next instanceof JSeparator ) { 518 actions.add(null); 519 } 520 } 521 } 522 523 actions.add(null); 524 actions.add(SystemAction.get(ToolsAction.class)); 525 actions.add(null); 526 if (broken) { 527 actions.add(brokenLinksAction); 528 } 529 actions.add(CommonProjectActions.customizeProjectAction()); 530 531 return actions.toArray(new Action [actions.size()]); 532 533 } 534 535 private boolean isBroken() { 536 return this.broken; 537 } 538 539 private void setBroken(boolean broken) { 540 this.broken = broken; 541 brokenLinksAction.setEnabled(broken); 542 fireIconChange(); 543 fireOpenedIconChange(); 544 fireDisplayNameChange(null, null); 545 } 546 547 private void setIllegalState (boolean illegalState) { 548 this.illegalState = illegalState; 549 fireIconChange(); 550 fireOpenedIconChange(); 551 fireDisplayNameChange(null, null); 552 } 553 554 557 private class BrokenLinksAction extends AbstractAction implements PropertyChangeListener , ChangeListener , Runnable { 558 559 private RequestProcessor.Task task = null; 560 561 private PropertyChangeListener weakPCL; 562 563 public BrokenLinksAction() { 564 putValue(Action.NAME, NbBundle.getMessage(J2SELogicalViewProvider.class, "LBL_Fix_Broken_Links_Action")); 565 setEnabled(broken); 566 evaluator.addPropertyChangeListener(this); 567 weakPCL = WeakListeners.propertyChange(this, JavaPlatformManager.getDefault()); 571 JavaPlatformManager.getDefault().addPropertyChangeListener(weakPCL); 572 J2SELogicalViewProvider.this.addChangeListener(WeakListeners.change(this, J2SELogicalViewProvider.this)); 573 } 574 575 public void actionPerformed(ActionEvent e) { 576 try { 577 helper.requestSave(); 578 BrokenReferencesSupport.showCustomizer(helper.getAntProjectHelper(), resolver, getBreakableProperties(), new String [] {J2SEProjectProperties.JAVA_PLATFORM}); 579 run(); 580 } catch (IOException ioe) { 581 ErrorManager.getDefault().notify(ioe); 582 } 583 } 584 585 public void propertyChange(PropertyChangeEvent evt) { 586 refsMayChanged(); 587 } 588 589 590 public void stateChanged(ChangeEvent evt) { 591 refsMayChanged(); 592 } 593 594 public synchronized void run() { 595 boolean old = J2SELogicalViewRootNode.this.broken; 596 boolean broken = hasBrokenLinks(); 597 if (old != broken) { 598 setBroken(broken); 599 } 600 601 old = J2SELogicalViewRootNode.this.illegalState; 602 broken = hasInvalidJdkVersion (); 603 if (old != broken) { 604 setIllegalState(broken); 605 } 606 } 607 608 private void refsMayChanged() { 609 if (task == null) { 613 task = BROKEN_LINKS_RP.create(this); 614 } 615 task.schedule(100); 616 } 617 618 } 619 620 } 621 622 } 623 | Popular Tags |