Lines 57-62
Link Here
|
57 |
import java.util.List; |
57 |
import java.util.List; |
58 |
import java.util.Map; |
58 |
import java.util.Map; |
59 |
import java.util.Set; |
59 |
import java.util.Set; |
|
|
60 |
import java.util.logging.Level; |
61 |
import java.util.logging.Logger; |
60 |
import javax.swing.Icon; |
62 |
import javax.swing.Icon; |
61 |
import javax.swing.event.ChangeListener; |
63 |
import javax.swing.event.ChangeListener; |
62 |
import org.netbeans.api.project.FileOwnerQuery; |
64 |
import org.netbeans.api.project.FileOwnerQuery; |
Lines 64-72
Link Here
|
64 |
import org.netbeans.api.project.ProjectManager; |
66 |
import org.netbeans.api.project.ProjectManager; |
65 |
import org.netbeans.api.project.ProjectUtils; |
67 |
import org.netbeans.api.project.ProjectUtils; |
66 |
import org.netbeans.api.project.SourceGroup; |
68 |
import org.netbeans.api.project.SourceGroup; |
|
|
69 |
import org.netbeans.api.project.SourceGroupModifier; |
67 |
import org.netbeans.api.project.Sources; |
70 |
import org.netbeans.api.project.Sources; |
68 |
import org.netbeans.api.queries.SharabilityQuery; |
71 |
import org.netbeans.api.queries.SharabilityQuery; |
69 |
import org.netbeans.modules.project.ant.AntBasedProjectFactorySingleton; |
72 |
import org.netbeans.modules.project.ant.AntBasedProjectFactorySingleton; |
|
|
73 |
import org.netbeans.spi.project.SourceGroupModifierImplementation; |
70 |
import org.openide.filesystems.FileAttributeEvent; |
74 |
import org.openide.filesystems.FileAttributeEvent; |
71 |
import org.openide.filesystems.FileChangeListener; |
75 |
import org.openide.filesystems.FileChangeListener; |
72 |
import org.openide.filesystems.FileEvent; |
76 |
import org.openide.filesystems.FileEvent; |
Lines 75-80
Link Here
|
75 |
import org.openide.filesystems.FileStateInvalidException; |
79 |
import org.openide.filesystems.FileStateInvalidException; |
76 |
import org.openide.filesystems.FileUtil; |
80 |
import org.openide.filesystems.FileUtil; |
77 |
import org.openide.util.ChangeSupport; |
81 |
import org.openide.util.ChangeSupport; |
|
|
82 |
import org.openide.util.Exceptions; |
78 |
import org.openide.util.Parameters; |
83 |
import org.openide.util.Parameters; |
79 |
import org.openide.util.WeakListeners; |
84 |
import org.openide.util.WeakListeners; |
80 |
|
85 |
|
Lines 125-143
Link Here
|
125 |
private final Icon openedIcon; |
130 |
private final Icon openedIcon; |
126 |
private final String includes; |
131 |
private final String includes; |
127 |
private final String excludes; |
132 |
private final String excludes; |
|
|
133 |
private final String hint; |
134 |
private boolean removed; // just for sanity checking |
128 |
|
135 |
|
129 |
public SourceRoot(String location, String includes, String excludes, String displayName, Icon icon, Icon openedIcon) { |
136 |
public SourceRoot(String location, String includes, String excludes, String hint, String displayName, Icon icon, Icon openedIcon) { |
130 |
super(location); |
137 |
super(location); |
131 |
this.displayName = displayName; |
138 |
this.displayName = displayName; |
132 |
this.icon = icon; |
139 |
this.icon = icon; |
133 |
this.openedIcon = openedIcon; |
140 |
this.openedIcon = openedIcon; |
134 |
this.includes = includes; |
141 |
this.includes = includes; |
135 |
this.excludes = excludes; |
142 |
this.excludes = excludes; |
|
|
143 |
this.hint = hint; |
144 |
removed = false; |
136 |
} |
145 |
} |
137 |
|
146 |
|
138 |
public final SourceGroup toGroup(FileObject loc) { |
147 |
public final SourceGroup toGroup(FileObject loc) { |
139 |
assert loc != null; |
148 |
assert loc != null; |
140 |
return new Group(loc); |
149 |
return new Group(loc); |
|
|
150 |
} |
151 |
|
152 |
public String getHint() { |
153 |
return hint; |
154 |
} |
155 |
|
156 |
public boolean isRemoved() { |
157 |
return removed; |
141 |
} |
158 |
} |
142 |
|
159 |
|
143 |
@Override |
160 |
@Override |
Lines 291-298
Link Here
|
291 |
|
308 |
|
292 |
private final class TypedSourceRoot extends SourceRoot { |
309 |
private final class TypedSourceRoot extends SourceRoot { |
293 |
private final String type; |
310 |
private final String type; |
294 |
public TypedSourceRoot(String type, String location, String includes, String excludes, String displayName, Icon icon, Icon openedIcon) { |
311 |
public TypedSourceRoot(String type, String hint, String location, String includes, String excludes, String displayName, Icon icon, Icon openedIcon) { |
295 |
super(location, includes, excludes, displayName, icon, openedIcon); |
312 |
super(location, includes, excludes, hint, displayName, icon, openedIcon); |
296 |
this.type = type; |
313 |
this.type = type; |
297 |
} |
314 |
} |
298 |
public final String getType() { |
315 |
public final String getType() { |
Lines 371-376
Link Here
|
371 |
addPrincipalSourceRoot(location, null, null, displayName, icon, openedIcon); |
388 |
addPrincipalSourceRoot(location, null, null, displayName, icon, openedIcon); |
372 |
} |
389 |
} |
373 |
|
390 |
|
|
|
391 |
// no code hint for source root, group cannot be created from it via SourceGroupModifier |
392 |
private static final String HINT_NONE = "none"; |
393 |
|
374 |
/** |
394 |
/** |
375 |
* Add a possible principal source root, or top-level folder which may |
395 |
* Add a possible principal source root, or top-level folder which may |
376 |
* contain sources that should be considered part of the project, with |
396 |
* contain sources that should be considered part of the project, with |
Lines 409-417
Link Here
|
409 |
if (lastRegisteredRoots != null) { |
429 |
if (lastRegisteredRoots != null) { |
410 |
throw new IllegalStateException("registerExternalRoots was already called"); // NOI18N |
430 |
throw new IllegalStateException("registerExternalRoots was already called"); // NOI18N |
411 |
} |
431 |
} |
412 |
principalSourceRoots.add(new SourceRoot(location, includes, excludes, displayName, icon, openedIcon)); |
432 |
principalSourceRoots.add(new SourceRoot(location, includes, excludes, HINT_NONE, displayName, icon, openedIcon)); |
413 |
} |
433 |
} |
414 |
|
434 |
|
|
|
435 |
/** |
436 |
* Add a possible principal source root, or top-level folder which may |
437 |
* contain sources that should be considered part of the project. |
438 |
* <p> |
439 |
* If the actual value of the location is inside the project directory, |
440 |
* this is simply ignored; so it safe to configure principal source roots |
441 |
* for any source directory which might be set to use an external path, even |
442 |
* if the common location is internal. |
443 |
* </p><p> |
444 |
* Location need not to exist physically, when <tt>hint</tt> is specified |
445 |
* and {@link SourceGroupModifier} created by this helper is added to project |
446 |
* lookup, source root can be created on demand. |
447 |
* </p> |
448 |
* @param location a project-relative or absolute path giving the location |
449 |
* of a source tree; may contain Ant property substitutions |
450 |
* @param hint Hint for {@link SourceGroupModifier} allowing creation of this |
451 |
* source root on demand |
452 |
* @param displayName a display name (for {@link SourceGroup#getDisplayName}) |
453 |
* @param icon a regular icon for the source root, or null |
454 |
* @param openedIcon an opened variant icon for the source root, or null |
455 |
* @throws IllegalStateException if this method is called after either |
456 |
* {@link #createSources} or {@link #registerExternalRoots} |
457 |
* was called |
458 |
* @see #registerExternalRoots |
459 |
* @see Sources#TYPE_GENERIC |
460 |
* @see #createSourceGroupModifierImplementation() |
461 |
* @since org.netbeans.modules.project.ant/1 1.33 |
462 |
*/ |
463 |
public void addPrincipalSourceRoot(String location, String hint, String displayName, Icon icon, Icon openedIcon) throws IllegalStateException { |
464 |
addPrincipalSourceRoot(location, null, null, hint, displayName, icon, openedIcon); |
465 |
} |
466 |
|
467 |
/** |
468 |
* Add a possible principal source root, or top-level folder which may |
469 |
* contain sources that should be considered part of the project, with |
470 |
* optional include and exclude lists. |
471 |
* <p> |
472 |
* If an include or exclude string is given as null, then it is skipped. A non-null value is |
473 |
* evaluated and then treated as a comma- or space-separated pattern list, |
474 |
* as detailed in the Javadoc for {@link PathMatcher}. |
475 |
* (As a special convenience, a value consisting solely of an Ant property reference |
476 |
* which cannot be evaluated, e.g. <samp>${undefined}</samp>, is treated like null.) |
477 |
* {@link SourceGroup#contains} will then reflect the includes and excludes for files, but note that the |
478 |
* semantics of that method requires that a folder be "contained" in case any folder or file |
479 |
* beneath it is contained, and in particular the root folder is always contained. |
480 |
* </p><p> |
481 |
* Location need not to exist physically, when <tt>hint</tt> is specified |
482 |
* and {@link SourceGroupModifier} created by this helper is added to project |
483 |
* lookup, source root can be created on demand. |
484 |
* </p> |
485 |
* @param location a project-relative or absolute path giving the location |
486 |
* of a source tree; may contain Ant property substitutions |
487 |
* @param includes Ant-style includes; may contain Ant property substitutions; |
488 |
* if not null, only files and folders |
489 |
* matching the pattern (or patterns), and not specified in the excludes list, |
490 |
* will be {@link SourceGroup#contains included} |
491 |
* @param excludes Ant-style excludes; may contain Ant property substitutions; |
492 |
* if not null, files and folders |
493 |
* matching the pattern (or patterns) will not be {@link SourceGroup#contains included}, |
494 |
* even if specified in the includes list |
495 |
* @param hint Hint for {@link SourceGroupModifier} allowing creation of this |
496 |
* source root on demand |
497 |
* @param displayName a display name (for {@link SourceGroup#getDisplayName}) |
498 |
* @param icon a regular icon for the source root, or null |
499 |
* @param openedIcon an opened variant icon for the source root, or null |
500 |
* @throws IllegalStateException if this method is called after either |
501 |
* {@link #createSources} or {@link #registerExternalRoots} |
502 |
* was called |
503 |
* @see #registerExternalRoots |
504 |
* @see Sources#TYPE_GENERIC |
505 |
* @see #createSourceGroupModifierImplementation() |
506 |
* @since org.netbeans.modules.project.ant/1 1.33 |
507 |
*/ |
508 |
public void addPrincipalSourceRoot(String location, String includes, String excludes, String hint, String displayName, Icon icon, Icon openedIcon) throws IllegalStateException { |
509 |
if (lastRegisteredRoots != null) { |
510 |
throw new IllegalStateException("registerExternalRoots was already called"); // NOI18N |
511 |
} |
512 |
principalSourceRoots.add(new SourceRoot(location, includes, excludes, hint, displayName, icon, openedIcon)); |
513 |
} |
514 |
|
415 |
/** |
515 |
/** |
416 |
* Similar to {@link #addPrincipalSourceRoot} but affects only |
516 |
* Similar to {@link #addPrincipalSourceRoot} but affects only |
417 |
* {@link #registerExternalRoots} and not {@link #createSources}. |
517 |
* {@link #registerExternalRoots} and not {@link #createSources}. |
Lines 493-499
Link Here
|
493 |
if (lastRegisteredRoots != null) { |
593 |
if (lastRegisteredRoots != null) { |
494 |
throw new IllegalStateException("registerExternalRoots was already called"); // NOI18N |
594 |
throw new IllegalStateException("registerExternalRoots was already called"); // NOI18N |
495 |
} |
595 |
} |
496 |
typedSourceRoots.add(new TypedSourceRoot(type, location, includes, excludes, displayName, icon, openedIcon)); |
596 |
typedSourceRoots.add(new TypedSourceRoot(type, HINT_NONE, location, includes, excludes, displayName, icon, openedIcon)); |
|
|
597 |
} |
598 |
|
599 |
/** |
600 |
* Add a typed source root which will be considered only in certain contexts. |
601 |
* <p> |
602 |
* Location need not to exist physically, when <tt>hint</tt> is specified |
603 |
* and {@link SourceGroupModifier} created by this helper is added to project |
604 |
* lookup, source root can be created on demand. |
605 |
* </p> |
606 |
* @param location a project-relative or absolute path giving the location |
607 |
* of a source tree; may contain Ant property substitutions |
608 |
* @param type a source root type such as <a href="@JAVA/PROJECT@/org/netbeans/api/java/project/JavaProjectConstants.html#SOURCES_TYPE_JAVA"><code>JavaProjectConstants.SOURCES_TYPE_JAVA</code></a> |
609 |
* @param hint Hint for {@link SourceGroupModifier} allowing creation of this |
610 |
* source root on demand |
611 |
* @param displayName a display name (for {@link SourceGroup#getDisplayName}) |
612 |
* @param icon a regular icon for the source root, or null |
613 |
* @param openedIcon an opened variant icon for the source root, or null |
614 |
* @throws IllegalStateException if this method is called after either |
615 |
* {@link #createSources} or {@link #registerExternalRoots} |
616 |
* was called |
617 |
* @see #createSourceGroupModifierImplementation() |
618 |
* @since org.netbeans.modules.project.ant/1 1.33 |
619 |
*/ |
620 |
public void addTypedSourceRoot(String location, String type, String hint, String displayName, Icon icon, Icon openedIcon) throws IllegalStateException { |
621 |
addTypedSourceRoot(location, null, null, type, hint, displayName, icon, openedIcon); |
622 |
} |
623 |
|
624 |
/** |
625 |
* Add a typed source root with optional include and exclude lists. |
626 |
* See {@link #addPrincipalSourceRoot(String,String,String,String,String,Icon,Icon)} |
627 |
* for details on semantics of includes and excludes. |
628 |
* <p> |
629 |
* Location need not to exist physically, when <tt>hint</tt> is specified |
630 |
* and {@link SourceGroupModifier} created by this helper is added to project |
631 |
* lookup, source root can be created on demand. |
632 |
* </p> |
633 |
* @param location a project-relative or absolute path giving the location |
634 |
* of a source tree; may contain Ant property substitutions |
635 |
* @param includes an optional list of Ant-style includes |
636 |
* @param excludes an optional list of Ant-style excludes |
637 |
* @param type a source root type such as <a href="@JAVA/PROJECT@/org/netbeans/api/java/project/JavaProjectConstants.html#SOURCES_TYPE_JAVA"><code>JavaProjectConstants.SOURCES_TYPE_JAVA</code></a> |
638 |
* @param hint Hint for {@link SourceGroupModifier} allowing creation of this |
639 |
* source root on demand |
640 |
* @param displayName a display name (for {@link SourceGroup#getDisplayName}) |
641 |
* @param icon a regular icon for the source root, or null |
642 |
* @param openedIcon an opened variant icon for the source root, or null |
643 |
* @throws IllegalStateException if this method is called after either |
644 |
* {@link #createSources} or {@link #registerExternalRoots} |
645 |
* was called |
646 |
* @see #createSourceGroupModifierImplementation() |
647 |
* @since org.netbeans.modules.project.ant/1 1.33 |
648 |
*/ |
649 |
public void addTypedSourceRoot(String location, String includes, String excludes, String type, String hint, String displayName, Icon icon, Icon openedIcon) throws IllegalStateException { |
650 |
if (lastRegisteredRoots != null) { |
651 |
throw new IllegalStateException("registerExternalRoots was already called"); // NOI18N |
652 |
} |
653 |
typedSourceRoots.add(new TypedSourceRoot(type, hint, location, includes, excludes, displayName, icon, openedIcon)); |
497 |
} |
654 |
} |
498 |
|
655 |
|
499 |
private Project getProject() { |
656 |
private Project getProject() { |
Lines 725-731
Link Here
|
725 |
if (type.equals(Sources.TYPE_GENERIC)) { |
882 |
if (type.equals(Sources.TYPE_GENERIC)) { |
726 |
List<SourceRoot> roots = new ArrayList<SourceRoot>(principalSourceRoots); |
883 |
List<SourceRoot> roots = new ArrayList<SourceRoot>(principalSourceRoots); |
727 |
// Always include the project directory itself as a default: |
884 |
// Always include the project directory itself as a default: |
728 |
roots.add(new SourceRoot("", null, null, ProjectUtils.getInformation(getProject()).getDisplayName(), null, null)); // NOI18N |
885 |
roots.add(new SourceRoot("", null, null, HINT_NONE, ProjectUtils.getInformation(getProject()).getDisplayName(), null, null)); // NOI18N |
729 |
Map<FileObject,SourceRoot> rootsByDir = new LinkedHashMap<FileObject,SourceRoot>(); |
886 |
Map<FileObject,SourceRoot> rootsByDir = new LinkedHashMap<FileObject,SourceRoot>(); |
730 |
// First collect all non-redundant existing roots. |
887 |
// First collect all non-redundant existing roots. |
731 |
for (SourceRoot r : roots) { |
888 |
for (SourceRoot r : roots) { |
Lines 751-756
Link Here
|
751 |
while (parent != null) { |
908 |
while (parent != null) { |
752 |
if (rootsByDir.containsKey(parent)) { |
909 |
if (rootsByDir.containsKey(parent)) { |
753 |
// This is a subroot of something, so skip it. |
910 |
// This is a subroot of something, so skip it. |
|
|
911 |
rootsByDir.get(loc).removed = true; |
754 |
it.remove(); |
912 |
it.remove(); |
755 |
break; |
913 |
break; |
756 |
} |
914 |
} |
Lines 866-872
Link Here
|
866 |
maybeFireChange(); |
1024 |
maybeFireChange(); |
867 |
} |
1025 |
} |
868 |
} |
1026 |
} |
869 |
|
1027 |
|
|
|
1028 |
/** |
1029 |
* Creates new {@link SourceGroupModifierImplementation} that can be put into project lookup. |
1030 |
* <p> |
1031 |
* Only source roots added with "<tt>hint</tt>-ed" variant of <tt>addPrincipalSourceRoot</tt> |
1032 |
* or <tt>AddTypedSourceRoot</tt> can be created with this <tt>SourceGroupModifierImplementation</tt>. |
1033 |
* </p> |
1034 |
* @param sources Sources object to which creatable source roots has been added. Must be created |
1035 |
* by {@link #createSources()}. |
1036 |
* @return <tt>SourceGroupModifierImplementation</tt> implementation for given sources. |
1037 |
* @throws java.lang.IllegalArgumentException When passed sources were not created with {@link #createSources()}. |
1038 |
* @see #addPrincipalSourceRoot(java.lang.String, java.lang.String, java.lang.String, javax.swing.Icon, javax.swing.Icon) |
1039 |
* @see #addPrincipalSourceRoot(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, javax.swing.Icon, javax.swing.Icon) |
1040 |
* @see #addTypedSourceRoot(java.lang.String, java.lang.String, java.lang.String, java.lang.String, javax.swing.Icon, javax.swing.Icon) |
1041 |
* @see #addTypedSourceRoot(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, javax.swing.Icon, javax.swing.Icon) |
1042 |
* @since org.netbeans.modules.project.ant/1 1.33 |
1043 |
*/ |
1044 |
public SourceGroupModifierImplementation createSourceGroupModifierImplementation(Sources sources) throws IllegalArgumentException { |
1045 |
if (!(sources instanceof SourcesImpl)) |
1046 |
throw new IllegalArgumentException("Sources object must be created by " |
1047 |
+ "org.netbeans.spi.project.support.ant.SourcesHelper#createSources(), got " |
1048 |
+ sources.getClass().getName()); |
1049 |
return new SourceGroupModifierImpl((SourcesImpl) sources); |
1050 |
} |
1051 |
|
1052 |
private class SourceGroupModifierImpl implements SourceGroupModifierImplementation { |
1053 |
private Logger logger = Logger.getLogger(SourcesHelper.class.getName()); |
1054 |
private SourcesImpl sources; |
1055 |
|
1056 |
private SourceGroupModifierImpl(SourcesImpl sources) { |
1057 |
this.sources = sources; |
1058 |
} |
1059 |
|
1060 |
public SourceGroup createSourceGroup(String type, String hint) { |
1061 |
SourceRoot root = findRoot(type, hint); |
1062 |
if (root == null) |
1063 |
return null; |
1064 |
if (root.isRemoved()) |
1065 |
return null; // getSourceGroups wouldn't return it, neither will we |
1066 |
|
1067 |
File loc = root.getActualLocation(); |
1068 |
FileObject foloc; |
1069 |
if (! loc.exists()) { |
1070 |
try { |
1071 |
foloc = FileUtil.createFolder(loc); |
1072 |
} catch (IOException ex) { |
1073 |
logger.log(Level.WARNING, "Failed to create folder " + loc, ex); |
1074 |
return null; |
1075 |
} |
1076 |
} else { |
1077 |
foloc = FileUtil.toFileObject(loc); |
1078 |
} |
1079 |
SourceGroup sg = root.toGroup(foloc); |
1080 |
assert sg != null; |
1081 |
sources.maybeFireChange(); |
1082 |
return sg; |
1083 |
} |
1084 |
|
1085 |
public boolean canCreateSourceGroup(String type, String hint) { |
1086 |
return findRoot(type, hint) != null; |
1087 |
} |
1088 |
private SourceRoot findRoot(String type, String hint) { |
1089 |
if (Sources.TYPE_GENERIC.equals(type)) { |
1090 |
for (SourceRoot root : principalSourceRoots) { |
1091 |
if (root.getHint().equals(hint) |
1092 |
&& ! root.getHint().equals(HINT_NONE) |
1093 |
&& ! root.isRemoved()) |
1094 |
return root; |
1095 |
} |
1096 |
} else { |
1097 |
for (TypedSourceRoot root : typedSourceRoots) { |
1098 |
if (root.getType().equals(type) |
1099 |
&& root.getHint().equals(hint) |
1100 |
&& ! root.getHint().equals(HINT_NONE)) |
1101 |
return root; |
1102 |
} |
1103 |
} |
1104 |
return null; |
1105 |
} |
1106 |
} |
1107 |
|
870 |
private final class PropChangeL implements PropertyChangeListener { |
1108 |
private final class PropChangeL implements PropertyChangeListener { |
871 |
|
1109 |
|
872 |
public PropChangeL() {} |
1110 |
public PropChangeL() {} |