1 19 20 package org.netbeans.modules.ruby.railsprojects.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.railsprojects.GenerateAction; 44 import org.netbeans.modules.ruby.railsprojects.RailsActionProvider; 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.ruby.railsprojects.RailsProjectUtil; 51 import org.netbeans.modules.ruby.railsprojects.ui.customizer.RailsProjectProperties; 52 import org.netbeans.modules.ruby.railsprojects.RailsProject; 53 import org.netbeans.modules.ruby.railsprojects.SourceRoots; 54 import org.netbeans.modules.ruby.railsprojects.UpdateHelper; 55 import org.netbeans.spi.project.ActionProvider; 56 import org.netbeans.spi.project.SubprojectProvider; 57 import org.netbeans.modules.ruby.spi.project.support.rake.PropertyEvaluator; 58 import org.netbeans.modules.ruby.spi.project.support.rake.ReferenceHelper; 59 import org.netbeans.spi.project.ui.LogicalViewProvider; 60 import org.netbeans.spi.project.ui.support.CommonProjectActions; 61 import org.netbeans.spi.project.ui.support.NodeFactorySupport; 62 import org.netbeans.spi.project.ui.support.DefaultProjectOperations; 63 import org.netbeans.spi.project.ui.support.ProjectSensitiveActions; 64 import org.openide.ErrorManager; 65 import org.openide.actions.FindAction; 66 import org.openide.actions.ToolsAction; 67 import org.openide.filesystems.FileObject; 68 import org.openide.filesystems.FileStateInvalidException; 69 import org.openide.filesystems.FileStatusEvent; 70 import org.openide.filesystems.FileStatusListener; 71 import org.openide.filesystems.FileSystem; 72 import org.openide.filesystems.FileUtil; 73 import org.openide.filesystems.Repository; 74 import org.openide.loaders.DataFolder; 75 import org.openide.loaders.DataObject; 76 import org.openide.loaders.DataObjectNotFoundException; 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 RailsLogicalViewProvider implements LogicalViewProvider { 96 97 99 private final RailsProject project; 100 private final UpdateHelper helper; 101 private final PropertyEvaluator evaluator; 102 private final SubprojectProvider spp; 103 private final ReferenceHelper resolver; 104 private List changeListeners; 105 106 public RailsLogicalViewProvider(RailsProject 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 RailsLogicalViewRootNode(); 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 = (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 Node[] nodes = root.getChildren().getNodes(true); 148 for (int i = 0; i < nodes.length; i++) { 149 TreeRootNode.PathFinder pf2 = (TreeRootNode.PathFinder) nodes[i].getLookup().lookup(TreeRootNode.PathFinder.class); 150 if (pf2 != null) { 151 Node n = pf2.findPath(nodes[i], target); 152 if (n != null) { 153 return n; 154 } 155 } 156 } 157 } 158 159 return null; 160 } 161 162 163 164 public synchronized void addChangeListener(ChangeListener l) { 165 if (this.changeListeners == null) { 166 this.changeListeners = new ArrayList (); 167 } 168 this.changeListeners.add(l); 169 } 170 171 public synchronized void removeChangeListener(ChangeListener l) { 172 if (this.changeListeners == null) { 173 return; 174 } 175 this.changeListeners.remove(l); 176 } 177 178 183 public void testBroken() { 184 ChangeListener [] _listeners; 185 synchronized (this) { 186 if (this.changeListeners == null) { 187 return; 188 } 189 _listeners = (ChangeListener []) this.changeListeners.toArray( 190 new ChangeListener [this.changeListeners.size()]); 191 } 192 ChangeEvent event = new ChangeEvent (this); 193 for (int i=0; i < _listeners.length; i++) { 194 _listeners[i].stateChanged(event); 195 } 196 } 197 198 204 205 207 private static final String [] BREAKABLE_PROPERTIES = new String [] { 208 RailsProjectProperties.JAVAC_CLASSPATH, 209 RailsProjectProperties.RUN_CLASSPATH, 210 RailsProjectProperties.DEBUG_CLASSPATH, 211 RailsProjectProperties.RUN_TEST_CLASSPATH, 212 RailsProjectProperties.DEBUG_TEST_CLASSPATH, 213 RailsProjectProperties.JAVAC_TEST_CLASSPATH, 214 }; 215 216 private static Image brokenProjectBadge = Utilities.loadImage("org/netbeans/modules/ruby/railsprojects/ui/resources/brokenProjectBadge.gif", true); 217 218 220 private final class RailsLogicalViewRootNode extends AbstractNode implements Runnable , FileStatusListener, ChangeListener , PropertyChangeListener { 221 private Set files; 222 private Map fileSystemListeners; 223 private RequestProcessor.Task task; 224 private final Object privateLock = new Object (); 225 private boolean iconChange; 226 private boolean nameChange; 227 private ChangeListener sourcesListener; 228 private Map groupsListeners; 229 230 public RailsLogicalViewRootNode() { 231 super(NodeFactorySupport.createCompositeChildren(project, "Projects/org-netbeans-modules-ruby-railsprojects/Nodes"), Lookups.singleton(project)); 233 setIconBaseWithExtension("org/netbeans/modules/ruby/railsprojects/ui/resources/rails.png"); super.setName( ProjectUtils.getInformation( project ).getDisplayName() ); 235 setProjectFiles(project); 236 } 237 238 protected final void setProjectFiles(Project project) { 239 Sources sources = ProjectUtils.getSources(project); if (sourcesListener == null) { 241 sourcesListener = WeakListeners.change(this, sources); 242 sources.addChangeListener(sourcesListener); 243 } 244 setGroups(Arrays.asList(sources.getSourceGroups(Sources.TYPE_GENERIC))); 245 } 246 247 248 private final void setGroups(Collection groups) { 249 if (groupsListeners != null) { 250 Iterator it = groupsListeners.keySet().iterator(); 251 while (it.hasNext()) { 252 SourceGroup group = (SourceGroup) it.next(); 253 PropertyChangeListener pcl = (PropertyChangeListener ) groupsListeners.get(group); 254 group.removePropertyChangeListener(pcl); 255 } 256 } 257 groupsListeners = new HashMap (); 258 Set roots = new HashSet (); 259 Iterator it = groups.iterator(); 260 while (it.hasNext()) { 261 SourceGroup group = (SourceGroup) it.next(); 262 PropertyChangeListener pcl = WeakListeners.propertyChange(this, group); 263 groupsListeners.put(group, pcl); 264 group.addPropertyChangeListener(pcl); 265 FileObject fo = group.getRootFolder(); 266 roots.add(fo); 267 } 268 setFiles(roots); 269 } 270 271 protected final void setFiles(Set files) { 272 if (fileSystemListeners != null) { 273 Iterator it = fileSystemListeners.keySet().iterator(); 274 while (it.hasNext()) { 275 FileSystem fs = (FileSystem) it.next(); 276 FileStatusListener fsl = (FileStatusListener) fileSystemListeners.get(fs); 277 fs.removeFileStatusListener(fsl); 278 } 279 } 280 281 fileSystemListeners = new HashMap (); 282 this.files = files; 283 if (files == null) { 284 return; 285 } 286 287 Iterator it = files.iterator(); 288 Set hookedFileSystems = new HashSet (); 289 while (it.hasNext()) { 290 FileObject fo = (FileObject) it.next(); 291 try { 292 FileSystem fs = fo.getFileSystem(); 293 if (hookedFileSystems.contains(fs)) { 294 continue; 295 } 296 hookedFileSystems.add(fs); 297 FileStatusListener fsl = FileUtil.weakFileStatusListener(this, fs); 298 fs.addFileStatusListener(fsl); 299 fileSystemListeners.put(fs, fsl); 300 } catch (FileStateInvalidException e) { 301 ErrorManager err = ErrorManager.getDefault(); 302 err.annotate(e, ErrorManager.UNKNOWN, "Cannot get " + fo + " filesystem, ignoring...", null, null, null); err.notify(ErrorManager.INFORMATIONAL, e); 304 } 305 } 306 } 307 308 320 public Image getIcon(int type) { 321 Image img = getMyIcon(type); 322 323 if (files != null && files.iterator().hasNext()) { 324 try { 325 FileObject fo = (FileObject) files.iterator().next(); 326 img = fo.getFileSystem().getStatus().annotateIcon(img, type, files); 327 } catch (FileStateInvalidException e) { 328 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); 329 } 330 } 331 332 return img; 333 } 334 335 private Image getMyIcon(int type) { 336 Image original = super.getIcon(type); 337 return original; 339 } 340 341 public Image getOpenedIcon(int type) { 342 Image img = getMyOpenedIcon(type); 343 344 if (files != null && files.iterator().hasNext()) { 345 try { 346 FileObject fo = (FileObject) files.iterator().next(); 347 img = fo.getFileSystem().getStatus().annotateIcon(img, type, files); 348 } catch (FileStateInvalidException e) { 349 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); 350 } 351 } 352 353 return img; 354 } 355 356 private Image getMyOpenedIcon(int type) { 357 Image original = super.getOpenedIcon(type); 358 return original; 360 } 361 362 public void run() { 363 boolean fireIcon; 364 boolean fireName; 365 synchronized (privateLock) { 366 fireIcon = iconChange; 367 fireName = nameChange; 368 iconChange = false; 369 nameChange = false; 370 } 371 if (fireIcon) { 372 fireIconChange(); 373 fireOpenedIconChange(); 374 } 375 if (fireName) { 376 fireDisplayNameChange(null, null); 377 } 378 } 379 380 public void annotationChanged(FileStatusEvent event) { 381 if (task == null) { 382 task = RequestProcessor.getDefault().create(this); 383 } 384 385 synchronized (privateLock) { 386 if ((iconChange == false && event.isIconChange()) || (nameChange == false && event.isNameChange())) { 387 Iterator it = files.iterator(); 388 while (it.hasNext()) { 389 FileObject fo = (FileObject) it.next(); 390 if (event.hasChanged(fo)) { 391 iconChange |= event.isIconChange(); 392 nameChange |= event.isNameChange(); 393 } 394 } 395 } 396 } 397 398 task.schedule(50); } 400 401 public void stateChanged(ChangeEvent e) { 403 setProjectFiles(project); 404 } 405 406 public void propertyChange(PropertyChangeEvent evt) { 408 setProjectFiles(project); 409 } 410 411 public Action [] getActions( boolean context ) { 412 return getAdditionalActions(); 413 } 414 415 public boolean canRename() { 416 return true; 417 } 418 419 public void setName(String s) { 420 DefaultProjectOperations.performDefaultRenameOperation(project, s); 421 } 422 423 public HelpCtx getHelpCtx() { 424 return new HelpCtx(RailsLogicalViewRootNode.class); 425 } 426 427 438 439 441 private Action [] getAdditionalActions() { 442 443 ResourceBundle bundle = NbBundle.getBundle(RailsLogicalViewProvider.class); 444 445 List actions = new ArrayList (); 446 447 actions.add(SystemAction.get(GenerateAction.class)); 448 actions.add(null); 449 actions.add(CommonProjectActions.newFileAction()); 450 actions.add(null); 451 actions.add(ProjectSensitiveActions.projectCommandAction(RailsActionProvider.COMMAND_RDOC, bundle.getString("LBL_RDocAction_Name"), null)); actions.add(null); 457 actions.add(ProjectSensitiveActions.projectCommandAction(ActionProvider.COMMAND_RUN, bundle.getString("LBL_RunAction_Name"), null)); actions.add(CommonProjectActions.setProjectConfigurationAction()); 461 actions.add(null); 462 actions.add(CommonProjectActions.setAsMainProjectAction()); 463 actions.add(CommonProjectActions.openSubprojectsAction()); 464 actions.add(CommonProjectActions.closeProjectAction()); 465 actions.add(null); 466 actions.add(CommonProjectActions.renameProjectAction()); 467 actions.add(CommonProjectActions.moveProjectAction()); 468 actions.add(CommonProjectActions.copyProjectAction()); 469 actions.add(CommonProjectActions.deleteProjectAction()); 470 actions.add(null); 471 actions.add(SystemAction.get(FindAction.class)); 472 473 475 try { 476 FileObject fo = Repository.getDefault().getDefaultFileSystem().findResource("Projects/Actions"); if (fo != null) { 478 DataObject dobj = DataObject.find(fo); 479 FolderLookup actionRegistry = new FolderLookup((DataFolder)dobj); 480 Lookup.Template query = new Lookup.Template(Object .class); 481 Lookup lookup = actionRegistry.getLookup(); 482 Iterator it = lookup.lookup(query).allInstances().iterator(); 483 if (it.hasNext()) { 484 actions.add(null); 485 } 486 while (it.hasNext()) { 487 Object next = it.next(); 488 if (next instanceof Action ) { 489 actions.add(next); 490 } else if (next instanceof JSeparator ) { 491 actions.add(null); 492 } 493 } 494 } 495 } catch (DataObjectNotFoundException ex) { 496 ErrorManager.getDefault().notify(ex); 498 } 499 500 actions.add(null); 501 actions.add(SystemAction.get(ToolsAction.class)); 502 actions.add(null); 503 actions.add(CommonProjectActions.customizeProjectAction()); 504 505 return (Action []) actions.toArray(new Action [actions.size()]); 506 507 } 508 } 509 } 510 | Popular Tags |