1 19 20 package org.netbeans.spi.project.support.ant; 21 22 import java.beans.PropertyChangeEvent ; 23 import java.beans.PropertyChangeListener ; 24 import java.beans.PropertyChangeSupport ; 25 import java.io.File ; 26 import java.io.IOException ; 27 import java.net.URL ; 28 import java.util.ArrayList ; 29 import java.util.Collection ; 30 import java.util.Collections ; 31 import java.util.HashMap ; 32 import java.util.HashSet ; 33 import java.util.Iterator ; 34 import java.util.LinkedHashMap ; 35 import java.util.List ; 36 import java.util.Map ; 37 import java.util.Set ; 38 import javax.swing.Icon ; 39 import javax.swing.event.ChangeEvent ; 40 import javax.swing.event.ChangeListener ; 41 import org.netbeans.api.project.FileOwnerQuery; 42 import org.netbeans.api.project.Project; 43 import org.netbeans.api.project.ProjectManager; 44 import org.netbeans.api.project.ProjectUtils; 45 import org.netbeans.api.project.SourceGroup; 46 import org.netbeans.api.project.Sources; 47 import org.netbeans.api.queries.SharabilityQuery; 48 import org.netbeans.modules.project.ant.AntBasedProjectFactorySingleton; 49 import org.netbeans.modules.project.ant.FileChangeSupport; 50 import org.netbeans.modules.project.ant.FileChangeSupportEvent; 51 import org.netbeans.modules.project.ant.FileChangeSupportListener; 52 import org.openide.filesystems.FileObject; 53 import org.openide.filesystems.FileStateInvalidException; 54 import org.openide.filesystems.FileUtil; 55 import org.openide.util.WeakListeners; 56 57 61 63 67 public final class SourcesHelper { 68 69 private class Root { 70 protected final String location; 71 public Root(String location) { 72 this.location = location; 73 } 74 public final File getActualLocation() { 75 String val = evaluator.evaluate(location); 76 if (val == null) { 77 return null; 78 } 79 return project.resolveFile(val); 80 } 81 public Collection <FileObject> getIncludeRoots() { 82 File loc = getActualLocation(); 83 if (loc != null) { 84 FileObject fo = FileUtil.toFileObject(loc); 85 if (fo != null) { 86 return Collections.singleton(fo); 87 } 88 } 89 return Collections.emptySet(); 90 } 91 } 92 93 private class SourceRoot extends Root { 94 95 private final String displayName; 96 private final Icon icon; 97 private final Icon openedIcon; 98 private final String includes; 99 private final String excludes; 100 private PathMatcher matcher; 101 102 public SourceRoot(String location, String includes, String excludes, String displayName, Icon icon, Icon openedIcon) { 103 super(location); 104 this.displayName = displayName; 105 this.icon = icon; 106 this.openedIcon = openedIcon; 107 this.includes = includes; 108 this.excludes = excludes; 109 } 110 111 public final SourceGroup toGroup(FileObject loc) { 112 assert loc != null; 113 return new Group(loc); 114 } 115 116 @Override 117 public String toString() { 118 return "SourceRoot[" + location + "]"; } 120 121 private final class Group implements SourceGroup, PropertyChangeListener { 123 124 private final FileObject loc; 125 private final PropertyChangeSupport pcs = new PropertyChangeSupport (this); 126 127 Group(FileObject loc) { 128 this.loc = loc; 129 evaluator.addPropertyChangeListener(WeakListeners.propertyChange(this, evaluator)); 130 } 131 132 public FileObject getRootFolder() { 133 return loc; 134 } 135 136 public String getName() { 137 return location.length() > 0 ? location : "generic"; } 139 140 public String getDisplayName() { 141 return displayName; 142 } 143 144 public Icon getIcon(boolean opened) { 145 return opened ? icon : openedIcon; 146 } 147 148 public boolean contains(FileObject file) throws IllegalArgumentException { 149 if (file == loc) { 150 return true; 151 } 152 String path = FileUtil.getRelativePath(loc, file); 153 if (path == null) { 154 throw new IllegalArgumentException (); 155 } 156 if (file.isFolder()) { 157 path += "/"; } 159 computeIncludeExcludePatterns(); 160 if (!matcher.matches(path, true)) { 161 return false; 162 } 163 Project p = getProject(); 164 if (file.isFolder() && file != p.getProjectDirectory() && ProjectManager.getDefault().isProject(file)) { 165 return false; 167 } 168 Project owner = FileOwnerQuery.getOwner(file); 169 if (owner != null && owner != p) { 170 return false; 171 } 172 File f = FileUtil.toFile(file); 173 if (f != null && SharabilityQuery.getSharability(f) == SharabilityQuery.NOT_SHARABLE) { 174 return false; 175 } return true; 177 } 178 179 public void addPropertyChangeListener(PropertyChangeListener l) { 180 pcs.addPropertyChangeListener(l); 181 } 182 183 public void removePropertyChangeListener(PropertyChangeListener l) { 184 pcs.removePropertyChangeListener(l); 185 } 186 187 @Override 188 public String toString() { 189 return "SourcesHelper.Group[name=" + getName() + ",rootFolder=" + getRootFolder() + "]"; } 191 192 public void propertyChange(PropertyChangeEvent ev) { 193 assert ev.getSource() == evaluator : ev; 194 String prop = ev.getPropertyName(); 195 if (prop == null || 196 (includes != null && includes.contains("${" + prop + "}")) || (excludes != null && excludes.contains("${" + prop + "}"))) { matcher = null; 199 pcs.firePropertyChange(PROP_CONTAINERSHIP, null, null); 200 } 201 } 203 204 } 205 206 private String evalForMatcher(String raw) { 207 if (raw == null) { 208 return null; 209 } 210 String patterns = evaluator.evaluate(raw); 211 if (patterns == null) { 212 return null; 213 } 214 if (patterns.matches("\\$\\{[^}]+\\}")) { return null; 217 } 218 return patterns; 219 } 220 221 private void computeIncludeExcludePatterns() { 222 if (matcher != null) { 223 return; 224 } 225 String includesPattern = evalForMatcher(includes); 226 String excludesPattern = evalForMatcher(excludes); 227 matcher = new PathMatcher(includesPattern, excludesPattern, getActualLocation()); 228 } 229 230 231 @Override 232 public Collection <FileObject> getIncludeRoots() { 233 Collection <FileObject> supe = super.getIncludeRoots(); 234 computeIncludeExcludePatterns(); 235 if (supe.size() == 1) { 236 Set <FileObject> roots = new HashSet <FileObject>(); 237 for (File r : matcher.findIncludedRoots()) { 238 FileObject subroot = FileUtil.toFileObject(r); 239 if (subroot != null) { 240 roots.add(subroot); 241 } 242 } 243 return roots; 244 } else { 245 assert supe.isEmpty(); 246 return supe; 247 } 248 } 249 250 } 251 252 private final class TypedSourceRoot extends SourceRoot { 253 private final String type; 254 public TypedSourceRoot(String type, String location, String includes, String excludes, String displayName, Icon icon, Icon openedIcon) { 255 super(location, includes, excludes, displayName, icon, openedIcon); 256 this.type = type; 257 } 258 public final String getType() { 259 return type; 260 } 261 } 262 263 private final AntProjectHelper project; 264 private final PropertyEvaluator evaluator; 265 private final List <SourceRoot> principalSourceRoots = new ArrayList <SourceRoot>(); 266 private final List <Root> nonSourceRoots = new ArrayList <Root>(); 267 private final List <TypedSourceRoot> typedSourceRoots = new ArrayList <TypedSourceRoot>(); 268 private int registeredRootAlgorithm; 269 275 private Set <FileObject> lastRegisteredRoots; 276 private PropertyChangeListener propChangeL; 277 278 284 public SourcesHelper(AntProjectHelper project, PropertyEvaluator evaluator) { 285 this.project = project; 286 this.evaluator = evaluator; 287 } 288 289 309 public void addPrincipalSourceRoot(String location, String displayName, Icon icon, Icon openedIcon) throws IllegalStateException { 310 addPrincipalSourceRoot(location, null, null, displayName, icon, openedIcon); 311 } 312 313 347 public void addPrincipalSourceRoot(String location, String includes, String excludes, String displayName, Icon icon, Icon openedIcon) throws IllegalStateException { 348 if (lastRegisteredRoots != null) { 349 throw new IllegalStateException ("registerExternalRoots was already called"); } 351 principalSourceRoots.add(new SourceRoot(location, includes, excludes, displayName, icon, openedIcon)); 352 } 353 354 368 public void addNonSourceRoot(String location) throws IllegalStateException { 369 if (lastRegisteredRoots != null) { 370 throw new IllegalStateException ("registerExternalRoots was already called"); } 372 nonSourceRoots.add(new Root(location)); 373 } 374 375 387 public void addTypedSourceRoot(String location, String type, String displayName, Icon icon, Icon openedIcon) throws IllegalStateException { 388 addTypedSourceRoot(location, null, null, type, displayName, icon, openedIcon); 389 } 390 391 408 public void addTypedSourceRoot(String location, String includes, String excludes, String type, String displayName, Icon icon, Icon openedIcon) throws IllegalStateException { 409 if (lastRegisteredRoots != null) { 410 throw new IllegalStateException ("registerExternalRoots was already called"); } 412 typedSourceRoots.add(new TypedSourceRoot(type, location, includes, excludes, displayName, icon, openedIcon)); 413 } 414 415 private Project getProject() { 416 return AntBasedProjectFactorySingleton.getProjectFor(project); 417 } 418 419 462 public void registerExternalRoots(int algorithm) throws IllegalArgumentException , IllegalStateException { 463 if (lastRegisteredRoots != null) { 464 throw new IllegalStateException ("registerExternalRoots was already called before"); } 466 registeredRootAlgorithm = algorithm; 467 remarkExternalRoots(); 468 } 469 470 private void remarkExternalRoots() throws IllegalArgumentException { 471 List <Root> allRoots = new ArrayList <Root>(principalSourceRoots); 472 allRoots.addAll(nonSourceRoots); 473 Project p = getProject(); 474 FileObject pdir = project.getProjectDirectory(); 475 if (lastRegisteredRoots == null) { 478 lastRegisteredRoots = Collections.emptySet(); 480 propChangeL = new PropChangeL(); evaluator.addPropertyChangeListener(WeakListeners.propertyChange(propChangeL, evaluator)); 482 } 483 Set <FileObject> newRegisteredRoots = new HashSet <FileObject>(); 484 for (Root r : allRoots) { 488 for (FileObject loc : r.getIncludeRoots()) { 489 if (!loc.isFolder()) { 490 continue; 491 } 492 if (FileUtil.getRelativePath(pdir, loc) != null) { 493 continue; 495 } 496 try { 497 Project other = ProjectManager.getDefault().findProject(loc); 498 if (other != null) { 499 continue; 501 } 502 } catch (IOException e) { 503 continue; 505 } 506 newRegisteredRoots.add(loc); 508 } 509 } 510 Set <FileObject> toUnregister = new HashSet <FileObject>(lastRegisteredRoots); 512 toUnregister.removeAll(newRegisteredRoots); 513 for (FileObject loc : toUnregister) { 514 FileOwnerQuery.markExternalOwner(loc, null, registeredRootAlgorithm); 515 } 516 Set <FileObject> toRegister = new HashSet <FileObject>(newRegisteredRoots); 517 toRegister.removeAll(lastRegisteredRoots); 518 for (FileObject loc : toRegister) { 519 FileOwnerQuery.markExternalOwner(loc, p, registeredRootAlgorithm); 520 } 521 lastRegisteredRoots = newRegisteredRoots; 522 } 523 524 563 public Sources createSources() { 564 return new SourcesImpl(); 565 } 566 567 private final class SourcesImpl implements Sources, PropertyChangeListener , FileChangeSupportListener { 568 569 private final List <ChangeListener > listeners = new ArrayList <ChangeListener >(); 570 private boolean haveAttachedListeners; 571 private final Set <File > rootsListenedTo = new HashSet <File >(); 572 575 private final Map <String ,List <URL >> lastComputedRoots = new HashMap <String ,List <URL >>(); 576 577 public SourcesImpl() { 578 evaluator.addPropertyChangeListener(WeakListeners.propertyChange(this, evaluator)); 579 } 580 581 public SourceGroup[] getSourceGroups(String type) { 582 List <SourceGroup> groups = new ArrayList <SourceGroup>(); 583 if (type.equals(Sources.TYPE_GENERIC)) { 584 List <SourceRoot> roots = new ArrayList <SourceRoot>(principalSourceRoots); 585 roots.add(new SourceRoot("", null, null, ProjectUtils.getInformation(getProject()).getDisplayName(), null, null)); Map <FileObject,SourceRoot> rootsByDir = new LinkedHashMap <FileObject,SourceRoot>(); 588 for (SourceRoot r : roots) { 590 File locF = r.getActualLocation(); 591 if (locF == null) { 592 continue; 593 } 594 listen(locF); 595 FileObject loc = FileUtil.toFileObject(locF); 596 if (loc == null) { 597 continue; 598 } 599 if (rootsByDir.containsKey(loc)) { 600 continue; 601 } 602 rootsByDir.put(loc, r); 603 } 604 Iterator <FileObject> it = rootsByDir.keySet().iterator(); 606 while (it.hasNext()) { 607 FileObject loc = it.next(); 608 FileObject parent = loc.getParent(); 609 while (parent != null) { 610 if (rootsByDir.containsKey(parent)) { 611 it.remove(); 613 break; 614 } 615 parent = parent.getParent(); 616 } 617 } 618 for (Map.Entry <FileObject,SourceRoot> entry : rootsByDir.entrySet()) { 620 groups.add(entry.getValue().toGroup(entry.getKey())); 621 } 622 } else { 623 Set <FileObject> dirs = new HashSet <FileObject>(); 624 for (TypedSourceRoot r : typedSourceRoots) { 625 if (!r.getType().equals(type)) { 626 continue; 627 } 628 File locF = r.getActualLocation(); 629 if (locF == null) { 630 continue; 631 } 632 listen(locF); 633 FileObject loc = FileUtil.toFileObject(locF); 634 if (loc == null) { 635 continue; 636 } 637 if (!dirs.add(loc)) { 638 continue; 640 } 641 groups.add(r.toGroup(loc)); 642 } 643 } 644 List <URL > rootURLs = new ArrayList <URL >(groups.size()); 646 for (SourceGroup g : groups) { 647 try { 648 rootURLs.add(g.getRootFolder().getURL()); 649 } catch (FileStateInvalidException e) { 650 assert false : e; } 652 } 653 lastComputedRoots.put(type, rootURLs); 654 return groups.toArray(new SourceGroup[groups.size()]); 655 } 656 657 private void listen(File rootLocation) { 658 if (rootsListenedTo.add(rootLocation) && haveAttachedListeners) { 660 FileChangeSupport.DEFAULT.addListener(this, rootLocation); 661 } 662 } 663 664 public void addChangeListener(ChangeListener listener) { 665 if (!haveAttachedListeners) { 666 haveAttachedListeners = true; 667 for (File rootLocation : rootsListenedTo) { 668 FileChangeSupport.DEFAULT.addListener(this, rootLocation); 669 } 670 } 671 synchronized (listeners) { 672 listeners.add(listener); 673 } 674 } 675 676 public void removeChangeListener(ChangeListener listener) { 677 synchronized (listeners) { 678 listeners.remove(listener); 679 } 680 } 681 682 private void fireChange() { 683 ChangeListener [] _listeners; 684 synchronized (listeners) { 685 if (listeners.isEmpty()) { 686 return; 687 } 688 _listeners = listeners.toArray(new ChangeListener [listeners.size()]); 689 } 690 ChangeEvent ev = new ChangeEvent (this); 691 for (ChangeListener l : _listeners) { 692 l.stateChanged(ev); 693 } 694 } 695 696 private void maybeFireChange() { 697 boolean change = false; 699 for (String type : new HashSet <String >(lastComputedRoots.keySet())) { 701 List <URL > previous = new ArrayList <URL >(lastComputedRoots.get(type)); 702 getSourceGroups(type); 703 List <URL > nue = lastComputedRoots.get(type); 704 if (!nue.equals(previous)) { 705 change = true; 706 break; 707 } 708 } 709 if (change) { 710 fireChange(); 711 } 712 } 713 714 public void fileCreated(FileChangeSupportEvent event) { 715 maybeFireChange(); 717 } 718 719 public void fileDeleted(FileChangeSupportEvent event) { 720 maybeFireChange(); 722 } 723 724 public void fileModified(FileChangeSupportEvent event) { 725 } 727 728 public void propertyChange(PropertyChangeEvent propertyChangeEvent) { 729 maybeFireChange(); 731 } 732 733 } 734 735 private final class PropChangeL implements PropertyChangeListener { 736 737 public PropChangeL() {} 738 739 public void propertyChange(PropertyChangeEvent evt) { 740 for (SourceRoot r : principalSourceRoots) { 742 r.matcher = null; 743 } 744 remarkExternalRoots(); 745 } 746 747 } 748 749 } 750 | Popular Tags |