1 19 20 package org.netbeans.modules.ruby.spi.project.support.rake; 21 22 import java.beans.PropertyChangeEvent ; 23 import java.beans.PropertyChangeListener ; 24 import java.io.File ; 25 import java.io.IOException ; 26 import java.net.URL ; 27 import java.util.ArrayList ; 28 import java.util.HashMap ; 29 import java.util.HashSet ; 30 import java.util.Iterator ; 31 import java.util.LinkedHashMap ; 32 import java.util.List ; 33 import java.util.Map ; 34 import java.util.Set ; 35 import javax.swing.Icon ; 36 import javax.swing.event.ChangeEvent ; 37 import javax.swing.event.ChangeListener ; 38 import org.netbeans.api.project.FileOwnerQuery; 39 import org.netbeans.api.project.Project; 40 import org.netbeans.api.project.ProjectManager; 41 import org.netbeans.api.project.ProjectUtils; 42 import org.netbeans.api.project.SourceGroup; 43 import org.netbeans.api.project.Sources; 44 import org.netbeans.modules.ruby.modules.project.rake.RakeBasedProjectFactorySingleton; 45 import org.netbeans.modules.ruby.modules.project.rake.FileChangeSupport; 46 import org.netbeans.modules.ruby.modules.project.rake.FileChangeSupportEvent; 47 import org.netbeans.modules.ruby.modules.project.rake.FileChangeSupportListener; 48 import org.netbeans.spi.project.support.GenericSources; 49 import org.openide.filesystems.FileObject; 50 import org.openide.filesystems.FileStateInvalidException; 51 import org.openide.filesystems.FileUtil; 52 import org.openide.util.WeakListeners; 53 54 58 60 64 public final class SourcesHelper { 65 66 private class Root { 67 protected final String location; 68 public Root(String location) { 69 this.location = location; 70 } 71 public final File getActualLocation() { 72 String val = evaluator.evaluate(location); 73 if (val == null) { 74 return null; 75 } 76 return project.resolveFile(val); 77 } 78 } 79 80 private class SourceRoot extends Root { 81 private final String displayName; 82 private final Icon icon; 83 private final Icon openedIcon; 84 public SourceRoot(String location, String displayName, Icon icon, Icon openedIcon) { 85 super(location); 86 this.displayName = displayName; 87 this.icon = icon; 88 this.openedIcon = openedIcon; 89 } 90 public final SourceGroup toGroup(FileObject loc) { 91 assert loc != null; 92 return GenericSources.group(getProject(), loc, location.length() > 0 ? location : "generic", displayName, icon, openedIcon); 94 } 95 } 96 97 private final class TypedSourceRoot extends SourceRoot { 98 private final String type; 99 public TypedSourceRoot(String type, String location, String displayName, Icon icon, Icon openedIcon) { 100 super(location, displayName, icon, openedIcon); 101 this.type = type; 102 } 103 public final String getType() { 104 return type; 105 } 106 } 107 108 private final RakeProjectHelper project; 109 private final PropertyEvaluator evaluator; 110 private final List <SourceRoot> principalSourceRoots = new ArrayList <SourceRoot>(); 111 private final List <Root> nonSourceRoots = new ArrayList <Root>(); 112 private final List <TypedSourceRoot> typedSourceRoots = new ArrayList <TypedSourceRoot>(); 113 private int registeredRootAlgorithm; 114 120 private Set <FileObject> lastRegisteredRoots; 121 private PropertyChangeListener propChangeL; 122 123 129 public SourcesHelper(RakeProjectHelper project, PropertyEvaluator evaluator) { 130 this.project = project; 131 this.evaluator = evaluator; 132 } 133 134 154 public void addPrincipalSourceRoot(String location, String displayName, Icon icon, Icon openedIcon) throws IllegalStateException { 155 if (lastRegisteredRoots != null) { 156 throw new IllegalStateException ("registerExternalRoots was already called"); } 158 principalSourceRoots.add(new SourceRoot(location, displayName, icon, openedIcon)); 159 } 160 161 175 public void addNonSourceRoot(String location) throws IllegalStateException { 176 if (lastRegisteredRoots != null) { 177 throw new IllegalStateException ("registerExternalRoots was already called"); } 179 nonSourceRoots.add(new Root(location)); 180 } 181 182 194 public void addTypedSourceRoot(String location, String type, String displayName, Icon icon, Icon openedIcon) throws IllegalStateException { 195 if (lastRegisteredRoots != null) { 196 throw new IllegalStateException ("registerExternalRoots was already called"); } 198 typedSourceRoots.add(new TypedSourceRoot(type, location, displayName, icon, openedIcon)); 199 } 200 201 private Project getProject() { 202 return RakeBasedProjectFactorySingleton.getProjectFor(project); 203 } 204 205 242 public void registerExternalRoots(int algorithm) throws IllegalArgumentException , IllegalStateException { 243 if (lastRegisteredRoots != null) { 244 throw new IllegalStateException ("registerExternalRoots was already called before"); } 246 registeredRootAlgorithm = algorithm; 247 remarkExternalRoots(); 248 } 249 250 private void remarkExternalRoots() throws IllegalArgumentException { 251 List <Root> allRoots = new ArrayList <Root>(principalSourceRoots); 252 allRoots.addAll(nonSourceRoots); 253 Project p = getProject(); 254 FileObject pdir = project.getProjectDirectory(); 255 Set <FileObject> newRootsToRegister; 258 if (lastRegisteredRoots == null) { 259 newRootsToRegister = null; 261 lastRegisteredRoots = new HashSet <FileObject>(); 262 propChangeL = new PropChangeL(); evaluator.addPropertyChangeListener(WeakListeners.propertyChange(propChangeL, evaluator)); 264 } else { 265 newRootsToRegister = new HashSet <FileObject>(); 266 } 267 for (Root r : allRoots) { 271 File locF = r.getActualLocation(); 272 FileObject loc = locF != null ? FileUtil.toFileObject(locF) : null; 273 if (loc == null) { 274 continue; 276 } 277 if (!loc.isFolder()) { 278 continue; 280 } 281 if (FileUtil.getRelativePath(pdir, loc) != null) { 282 continue; 284 } 285 try { 286 Project other = ProjectManager.getDefault().findProject(loc); 287 if (other != null) { 288 continue; 290 } 291 } catch (IOException e) { 292 continue; 294 } 295 if (newRootsToRegister != null) { 297 newRootsToRegister.add(loc); 298 } else { 299 lastRegisteredRoots.add(loc); 300 FileOwnerQuery.markExternalOwner(loc, p, registeredRootAlgorithm); 301 } 302 } 303 if (newRootsToRegister != null) { 304 Set <FileObject> toUnregister = new HashSet <FileObject>(lastRegisteredRoots); 306 toUnregister.removeAll(newRootsToRegister); 307 for (FileObject loc : toUnregister) { 308 FileOwnerQuery.markExternalOwner(loc, null, registeredRootAlgorithm); 309 } 310 newRootsToRegister.removeAll(lastRegisteredRoots); 311 for (FileObject loc : newRootsToRegister) { 312 FileOwnerQuery.markExternalOwner(loc, p, registeredRootAlgorithm); 313 } 314 } 315 } 316 317 356 public Sources createSources() { 357 return new SourcesImpl(); 358 } 359 360 private final class SourcesImpl implements Sources, PropertyChangeListener , FileChangeSupportListener { 361 362 private final List <ChangeListener > listeners = new ArrayList <ChangeListener >(); 363 private boolean haveAttachedListeners; 364 private final Set <File > rootsListenedTo = new HashSet <File >(); 365 368 private final Map <String ,List <URL >> lastComputedRoots = new HashMap <String ,List <URL >>(); 369 370 public SourcesImpl() { 371 evaluator.addPropertyChangeListener(WeakListeners.propertyChange(this, evaluator)); 372 } 373 374 public SourceGroup[] getSourceGroups(String type) { 375 List <SourceGroup> groups = new ArrayList <SourceGroup>(); 376 if (type.equals(Sources.TYPE_GENERIC)) { 377 List <SourceRoot> roots = new ArrayList <SourceRoot>(principalSourceRoots); 378 roots.add(new SourceRoot("", ProjectUtils.getInformation(getProject()).getDisplayName(), null, null)); Map <FileObject,SourceRoot> rootsByDir = new LinkedHashMap <FileObject,SourceRoot>(); 381 for (SourceRoot r : roots) { 383 File locF = r.getActualLocation(); 384 if (locF == null) { 385 continue; 386 } 387 listen(locF); 388 FileObject loc = FileUtil.toFileObject(locF); 389 if (loc == null) { 390 continue; 391 } 392 if (rootsByDir.containsKey(loc)) { 393 continue; 394 } 395 rootsByDir.put(loc, r); 396 } 397 Iterator <FileObject> it = rootsByDir.keySet().iterator(); 399 while (it.hasNext()) { 400 FileObject loc = it.next(); 401 FileObject parent = loc.getParent(); 402 while (parent != null) { 403 if (rootsByDir.containsKey(parent)) { 404 it.remove(); 406 break; 407 } 408 parent = parent.getParent(); 409 } 410 } 411 for (Map.Entry <FileObject,SourceRoot> entry : rootsByDir.entrySet()) { 413 groups.add(entry.getValue().toGroup(entry.getKey())); 414 } 415 } else { 416 Set <FileObject> dirs = new HashSet <FileObject>(); 417 for (TypedSourceRoot r : typedSourceRoots) { 418 if (!r.getType().equals(type)) { 419 continue; 420 } 421 File locF = r.getActualLocation(); 422 if (locF == null) { 423 continue; 424 } 425 listen(locF); 426 FileObject loc = FileUtil.toFileObject(locF); 427 if (loc == null) { 428 continue; 429 } 430 if (!dirs.add(loc)) { 431 continue; 433 } 434 groups.add(r.toGroup(loc)); 435 } 436 } 437 List <URL > rootURLs = new ArrayList <URL >(groups.size()); 439 for (SourceGroup g : groups) { 440 try { 441 rootURLs.add(g.getRootFolder().getURL()); 442 } catch (FileStateInvalidException e) { 443 assert false : e; } 445 } 446 lastComputedRoots.put(type, rootURLs); 447 return groups.toArray(new SourceGroup[groups.size()]); 448 } 449 450 private void listen(File rootLocation) { 451 if (rootsListenedTo.add(rootLocation) && haveAttachedListeners) { 453 FileChangeSupport.DEFAULT.addListener(this, rootLocation); 454 } 455 } 456 457 public void addChangeListener(ChangeListener listener) { 458 if (!haveAttachedListeners) { 459 haveAttachedListeners = true; 460 for (File rootLocation : rootsListenedTo) { 461 FileChangeSupport.DEFAULT.addListener(this, rootLocation); 462 } 463 } 464 synchronized (listeners) { 465 listeners.add(listener); 466 } 467 } 468 469 public void removeChangeListener(ChangeListener listener) { 470 synchronized (listeners) { 471 listeners.remove(listener); 472 } 473 } 474 475 private void fireChange() { 476 ChangeListener [] _listeners; 477 synchronized (listeners) { 478 if (listeners.isEmpty()) { 479 return; 480 } 481 _listeners = listeners.toArray(new ChangeListener [listeners.size()]); 482 } 483 ChangeEvent ev = new ChangeEvent (this); 484 for (ChangeListener l : _listeners) { 485 l.stateChanged(ev); 486 } 487 } 488 489 private void maybeFireChange() { 490 boolean change = false; 492 for (String type : new HashSet <String >(lastComputedRoots.keySet())) { 494 List <URL > previous = new ArrayList <URL >(lastComputedRoots.get(type)); 495 getSourceGroups(type); 496 List <URL > nue = lastComputedRoots.get(type); 497 if (!nue.equals(previous)) { 498 change = true; 499 break; 500 } 501 } 502 if (change) { 503 fireChange(); 504 } 505 } 506 507 public void fileCreated(FileChangeSupportEvent event) { 508 maybeFireChange(); 510 } 511 512 public void fileDeleted(FileChangeSupportEvent event) { 513 maybeFireChange(); 515 } 516 517 public void fileModified(FileChangeSupportEvent event) { 518 } 520 521 public void propertyChange(PropertyChangeEvent propertyChangeEvent) { 522 maybeFireChange(); 524 } 525 526 } 527 528 private final class PropChangeL implements PropertyChangeListener { 529 530 public PropChangeL() {} 531 532 public void propertyChange(PropertyChangeEvent evt) { 533 remarkExternalRoots(); 535 } 536 537 } 538 539 } 540 | Popular Tags |