1 19 20 package org.netbeans.modules.ruby.rubyproject.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.Iterator ; 34 import java.util.List ; 35 import java.util.Map ; 36 import java.util.ResourceBundle ; 37 import java.util.Set ; 38 import javax.swing.AbstractAction ; 39 import javax.swing.Action ; 40 import javax.swing.JSeparator ; 41 import javax.swing.event.ChangeEvent ; 42 import javax.swing.event.ChangeListener ; 43 import org.netbeans.modules.ruby.rubyproject.RubyActionProvider; 44 import org.netbeans.api.project.FileOwnerQuery; 45 import org.netbeans.api.project.Project; 46 import org.netbeans.api.project.ProjectUtils; 47 import org.netbeans.api.project.SourceGroup; 48 import org.netbeans.api.project.Sources; 49 import org.netbeans.modules.ruby.rubyproject.RubyProjectUtil; 50 import org.netbeans.modules.ruby.rubyproject.ui.customizer.RubyProjectProperties; 51 import org.netbeans.modules.ruby.rubyproject.RubyProject; 52 import org.netbeans.modules.ruby.rubyproject.SourceRoots; 53 import org.netbeans.modules.ruby.rubyproject.UpdateHelper; 54 import org.netbeans.spi.project.ActionProvider; 55 import org.netbeans.spi.project.SubprojectProvider; 56 import org.netbeans.modules.ruby.spi.project.support.rake.PropertyEvaluator; 57 import org.netbeans.modules.ruby.spi.project.support.rake.ReferenceHelper; 58 import org.netbeans.spi.project.ui.LogicalViewProvider; 59 import org.netbeans.spi.project.ui.support.CommonProjectActions; 60 import org.netbeans.spi.project.ui.support.NodeFactorySupport; 61 import org.netbeans.spi.project.ui.support.DefaultProjectOperations; 62 import org.netbeans.spi.project.ui.support.ProjectSensitiveActions; 63 import org.openide.ErrorManager; 64 import org.openide.actions.FindAction; 65 import org.openide.actions.ToolsAction; 66 import org.openide.filesystems.FileObject; 67 import org.openide.filesystems.FileStateInvalidException; 68 import org.openide.filesystems.FileStatusEvent; 69 import org.openide.filesystems.FileStatusListener; 70 import org.openide.filesystems.FileSystem; 71 import org.openide.filesystems.FileUtil; 72 import org.openide.filesystems.Repository; 73 import org.openide.loaders.DataFolder; 74 import org.openide.loaders.DataObject; 75 import org.openide.loaders.DataObjectNotFoundException; 76 import org.openide.loaders.FolderLookup; 77 import org.openide.modules.SpecificationVersion; 78 import org.openide.nodes.AbstractNode; 79 import org.openide.nodes.Node; 80 import org.openide.util.HelpCtx; 81 import org.openide.util.Lookup; 82 import org.openide.util.NbBundle; 83 import org.openide.util.RequestProcessor; 84 import org.openide.util.Utilities; 85 import org.openide.util.WeakListeners; 86 import org.openide.util.actions.SystemAction; 87 import org.openide.util.lookup.Lookups; 88 import org.openide.xml.XMLUtil; 89 90 94 public class RubyLogicalViewProvider implements LogicalViewProvider { 95 96 98 private final RubyProject project; 99 private final UpdateHelper helper; 100 private final PropertyEvaluator evaluator; 101 private final SubprojectProvider spp; 102 private final ReferenceHelper resolver; 103 private List changeListeners; 104 105 public RubyLogicalViewProvider(RubyProject project, UpdateHelper helper, PropertyEvaluator evaluator, SubprojectProvider spp, ReferenceHelper resolver) { 106 this.project = project; 107 assert project != null; 108 this.helper = helper; 109 assert helper != null; 110 this.evaluator = evaluator; 111 assert evaluator != null; 112 this.spp = spp; 113 assert spp != null; 114 this.resolver = resolver; 115 } 116 117 public Node createLogicalView() { 118 return new RubyLogicalViewRootNode(); 119 } 120 121 public PropertyEvaluator getEvaluator() { 122 return evaluator; 123 } 124 125 public ReferenceHelper getRefHelper() { 126 return resolver; 127 } 128 129 public UpdateHelper getUpdateHelper() { 130 return helper; 131 } 132 133 public Node findPath(Node root, Object target) { 134 Project project = (Project) root.getLookup().lookup(Project.class); 135 if (project == null) { 136 return null; 137 } 138 139 if (target instanceof FileObject) { 140 FileObject fo = (FileObject) target; 141 Project owner = FileOwnerQuery.getOwner(fo); 142 if (!project.equals(owner)) { 143 return null; } 145 146 Node[] nodes = root.getChildren().getNodes(true); 147 for (int i = 0; i < nodes.length; i++) { 148 TreeRootNode.PathFinder pf2 = (TreeRootNode.PathFinder) nodes[i].getLookup().lookup(TreeRootNode.PathFinder.class); 149 if (pf2 != null) { 150 Node n = pf2.findPath(nodes[i], target); 151 if (n != null) { 152 return n; 153 } 154 } 155 } 156 } 157 158 return null; 159 } 160 161 162 163 public synchronized void addChangeListener(ChangeListener l) { 164 if (this.changeListeners == null) { 165 this.changeListeners = new ArrayList (); 166 } 167 this.changeListeners.add(l); 168 } 169 170 public synchronized void removeChangeListener(ChangeListener l) { 171 if (this.changeListeners == null) { 172 return; 173 } 174 this.changeListeners.remove(l); 175 } 176 177 182 public void testBroken() { 183 ChangeListener [] _listeners; 184 synchronized (this) { 185 if (this.changeListeners == null) { 186 return; 187 } 188 _listeners = (ChangeListener []) this.changeListeners.toArray( 189 new ChangeListener [this.changeListeners.size()]); 190 } 191 ChangeEvent event = new ChangeEvent (this); 192 for (int i=0; i < _listeners.length; i++) { 193 _listeners[i].stateChanged(event); 194 } 195 } 196 197 203 204 206 private static final String [] BREAKABLE_PROPERTIES = new String [] { 207 RubyProjectProperties.JAVAC_CLASSPATH, 208 RubyProjectProperties.RUN_CLASSPATH, 209 RubyProjectProperties.DEBUG_CLASSPATH, 210 RubyProjectProperties.RUN_TEST_CLASSPATH, 211 RubyProjectProperties.DEBUG_TEST_CLASSPATH, 212 RubyProjectProperties.JAVAC_TEST_CLASSPATH, 213 }; 214 215 public boolean hasBrokenLinks () { 216 return false; 219 } 220 221 public boolean hasInvalidJdkVersion () { 222 return false; 244 } 245 246 258 private static Image brokenProjectBadge = Utilities.loadImage("org/netbeans/modules/ruby/rubyproject/ui/resources/brokenProjectBadge.gif", true); 259 260 262 private final class RubyLogicalViewRootNode extends AbstractNode implements Runnable , FileStatusListener, ChangeListener , PropertyChangeListener { 263 264 private Image icon; 265 private Lookup lookup; 266 private boolean broken; private boolean illegalState; 270 private Set files; 272 private Map fileSystemListeners; 273 private RequestProcessor.Task task; 274 private final Object privateLock = new Object (); 275 private boolean iconChange; 276 private boolean nameChange; 277 private ChangeListener sourcesListener; 278 private Map groupsListeners; 279 282 public RubyLogicalViewRootNode() { 283 super(NodeFactorySupport.createCompositeChildren(project, "Projects/org-netbeans-modules-ruby-rubyproject/Nodes"), 284 Lookups.singleton(project)); 285 setIconBaseWithExtension("org/netbeans/modules/ruby/rubyproject/ui/resources/jruby.png"); 286 super.setName( ProjectUtils.getInformation( project ).getDisplayName() ); 287 if (hasBrokenLinks()) { 288 broken = true; 289 } 290 else if (hasInvalidJdkVersion ()) { 291 illegalState = true; 292 } 293 setProjectFiles(project); 295 } 296 297 298 protected final void setProjectFiles(Project project) { 299 Sources sources = ProjectUtils.getSources(project); if (sourcesListener == null) { 301 sourcesListener = WeakListeners.change(this, sources); 302 sources.addChangeListener(sourcesListener); 303 } 304 setGroups(Arrays.asList(sources.getSourceGroups(Sources.TYPE_GENERIC))); 305 } 306 307 308 private final void setGroups(Collection groups) { 309 if (groupsListeners != null) { 310 Iterator it = groupsListeners.keySet().iterator(); 311 while (it.hasNext()) { 312 SourceGroup group = (SourceGroup) it.next(); 313 PropertyChangeListener pcl = (PropertyChangeListener ) groupsListeners.get(group); 314 group.removePropertyChangeListener(pcl); 315 } 316 } 317 groupsListeners = new HashMap (); 318 Set roots = new HashSet (); 319 Iterator it = groups.iterator(); 320 while (it.hasNext()) { 321 SourceGroup group = (SourceGroup) it.next(); 322 PropertyChangeListener pcl = WeakListeners.propertyChange(this, group); 323 groupsListeners.put(group, pcl); 324 group.addPropertyChangeListener(pcl); 325 FileObject fo = group.getRootFolder(); 326 roots.add(fo); 327 } 328 setFiles(roots); 329 } 330 331 protected final void setFiles(Set files) { 332 if (fileSystemListeners != null) { 333 Iterator it = fileSystemListeners.keySet().iterator(); 334 while (it.hasNext()) { 335 FileSystem fs = (FileSystem) it.next(); 336 FileStatusListener fsl = (FileStatusListener) fileSystemListeners.get(fs); 337 fs.removeFileStatusListener(fsl); 338 } 339 } 340 341 fileSystemListeners = new HashMap (); 342 this.files = files; 343 if (files == null) { 344 return; 345 } 346 347 Iterator it = files.iterator(); 348 Set hookedFileSystems = new HashSet (); 349 while (it.hasNext()) { 350 FileObject fo = (FileObject) it.next(); 351 try { 352 FileSystem fs = fo.getFileSystem(); 353 if (hookedFileSystems.contains(fs)) { 354 continue; 355 } 356 hookedFileSystems.add(fs); 357 FileStatusListener fsl = FileUtil.weakFileStatusListener(this, fs); 358 fs.addFileStatusListener(fsl); 359 fileSystemListeners.put(fs, fsl); 360 } catch (FileStateInvalidException e) { 361 ErrorManager err = ErrorManager.getDefault(); 362 err.annotate(e, ErrorManager.UNKNOWN, "Cannot get " + fo + " filesystem, ignoring...", null, null, null); err.notify(ErrorManager.INFORMATIONAL, e); 364 } 365 } 366 } 367 368 public String getHtmlDisplayName() { 369 String dispName = super.getDisplayName(); 370 try { 371 dispName = XMLUtil.toElementContent(dispName); 372 } catch (CharConversionException ex) { 373 return dispName; 374 } 375 return broken || illegalState ? "<font color=\"#A40000\">" + dispName + "</font>" : null; } 378 379 public Image getIcon(int type) { 380 Image img = getMyIcon(type); 381 382 if (files != null && files.iterator().hasNext()) { 383 try { 384 FileObject fo = (FileObject) files.iterator().next(); 385 img = fo.getFileSystem().getStatus().annotateIcon(img, type, files); 386 } catch (FileStateInvalidException e) { 387 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); 388 } 389 } 390 391 return img; 392 } 393 394 private Image getMyIcon(int type) { 395 Image original = super.getIcon(type); 396 return broken || illegalState ? Utilities.mergeImages(original, brokenProjectBadge, 8, 0) : original; 397 } 398 399 public Image getOpenedIcon(int type) { 400 Image img = getMyOpenedIcon(type); 401 402 if (files != null && files.iterator().hasNext()) { 403 try { 404 FileObject fo = (FileObject) files.iterator().next(); 405 img = fo.getFileSystem().getStatus().annotateIcon(img, type, files); 406 } catch (FileStateInvalidException e) { 407 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); 408 } 409 } 410 411 return img; 412 } 413 414 private Image getMyOpenedIcon(int type) { 415 Image original = super.getOpenedIcon(type); 416 return broken || illegalState ? Utilities.mergeImages(original, brokenProjectBadge, 8, 0) : original; 417 } 418 419 public void run() { 420 boolean fireIcon; 421 boolean fireName; 422 synchronized (privateLock) { 423 fireIcon = iconChange; 424 fireName = nameChange; 425 iconChange = false; 426 nameChange = false; 427 } 428 if (fireIcon) { 429 fireIconChange(); 430 fireOpenedIconChange(); 431 } 432 if (fireName) { 433 fireDisplayNameChange(null, null); 434 } 435 } 436 437 public void annotationChanged(FileStatusEvent event) { 438 if (task == null) { 439 task = RequestProcessor.getDefault().create(this); 440 } 441 442 synchronized (privateLock) { 443 if ((iconChange == false && event.isIconChange()) || (nameChange == false && event.isNameChange())) { 444 Iterator it = files.iterator(); 445 while (it.hasNext()) { 446 FileObject fo = (FileObject) it.next(); 447 if (event.hasChanged(fo)) { 448 iconChange |= event.isIconChange(); 449 nameChange |= event.isNameChange(); 450 } 451 } 452 } 453 } 454 455 task.schedule(50); } 457 458 public void stateChanged(ChangeEvent e) { 460 setProjectFiles(project); 461 } 462 463 public void propertyChange(PropertyChangeEvent evt) { 465 setProjectFiles(project); 466 } 467 468 public Action [] getActions( boolean context ) { 469 return getAdditionalActions(); 470 } 471 472 public boolean canRename() { 473 return true; 474 } 475 476 public void setName(String s) { 477 DefaultProjectOperations.performDefaultRenameOperation(project, s); 478 } 479 480 public HelpCtx getHelpCtx() { 481 return new HelpCtx(RubyLogicalViewRootNode.class); 482 } 483 484 495 496 498 private Action [] getAdditionalActions() { 499 500 ResourceBundle bundle = NbBundle.getBundle(RubyLogicalViewProvider.class); 501 502 List actions = new ArrayList (); 503 504 actions.add(CommonProjectActions.newFileAction()); 505 actions.add(null); 506 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(null); 510 actions.add(ProjectSensitiveActions.projectCommandAction(RubyActionProvider.COMMAND_RDOC, bundle.getString("LBL_RDocAction_Name"), null)); actions.add(null); 512 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()); 516 actions.add(null); 517 actions.add(CommonProjectActions.setAsMainProjectAction()); 518 actions.add(CommonProjectActions.openSubprojectsAction()); 519 actions.add(CommonProjectActions.closeProjectAction()); 520 actions.add(null); 521 actions.add(CommonProjectActions.renameProjectAction()); 522 actions.add(CommonProjectActions.moveProjectAction()); 523 actions.add(CommonProjectActions.copyProjectAction()); 524 actions.add(CommonProjectActions.deleteProjectAction()); 525 actions.add(null); 526 actions.add(SystemAction.get(FindAction.class)); 527 528 530 try { 531 FileObject fo = Repository.getDefault().getDefaultFileSystem().findResource("Projects/Actions"); if (fo != null) { 533 DataObject dobj = DataObject.find(fo); 534 FolderLookup actionRegistry = new FolderLookup((DataFolder)dobj); 535 Lookup.Template query = new Lookup.Template(Object .class); 536 Lookup lookup = actionRegistry.getLookup(); 537 Iterator it = lookup.lookup(query).allInstances().iterator(); 538 if (it.hasNext()) { 539 actions.add(null); 540 } 541 while (it.hasNext()) { 542 Object next = it.next(); 543 if (next instanceof Action ) { 544 actions.add(next); 545 } else if (next instanceof JSeparator ) { 546 actions.add(null); 547 } 548 } 549 } 550 } catch (DataObjectNotFoundException ex) { 551 ErrorManager.getDefault().notify(ex); 553 } 554 555 actions.add(null); 556 actions.add(SystemAction.get(ToolsAction.class)); 557 actions.add(null); 558 actions.add(CommonProjectActions.customizeProjectAction()); 562 563 return (Action []) actions.toArray(new Action [actions.size()]); 564 565 } 566 567 } 653 654 } 655 | Popular Tags |