diff --git a/java.api.common/nbproject/project.xml b/java.api.common/nbproject/project.xml
--- a/java.api.common/nbproject/project.xml
+++ b/java.api.common/nbproject/project.xml
@@ -163,7 +163,7 @@
1
- 1.66
+ 1.70
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/classpath/ClassPathModifier.java b/java.api.common/src/org/netbeans/modules/java/api/common/classpath/ClassPathModifier.java
--- a/java.api.common/src/org/netbeans/modules/java/api/common/classpath/ClassPathModifier.java
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/classpath/ClassPathModifier.java
@@ -117,6 +117,7 @@
ClassPathSupport.ENDORSED,
JavaClassPathConstants.PROCESSOR_PATH,
JavaClassPathConstants.MODULE_COMPILE_PATH,
+ JavaClassPathConstants.MODULE_EXECUTE_PATH,
};
}
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/classpath/ClassPathProviderImpl.java b/java.api.common/src/org/netbeans/modules/java/api/common/classpath/ClassPathProviderImpl.java
--- a/java.api.common/src/org/netbeans/modules/java/api/common/classpath/ClassPathProviderImpl.java
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/classpath/ClassPathProviderImpl.java
@@ -1324,40 +1324,38 @@
FileObject[] path = getPrimarySrcPath();
for (int i=0; i moduleNames) throws IOException {
+ assert projectArtifact != null;
+ assert originalPathType != null;
+
+ Project p = FileOwnerQuery.getOwner(projectArtifact);
+ if (p == null) {
+ return false;
+ }
+ FileObject modInfo = findModuleInfo(projectArtifact);
+ if (modInfo == null) {
+ return false;
+ }
+ ProjectModulesModifier delegate = p.getLookup().lookup(ProjectModulesModifier.class);
+ if (delegate != null) {
+ return delegate.addRequiredModules(originalPathType, projectArtifact, moduleNames);
+ }
+
+ switch (originalPathType) {
+ case ClassPath.COMPILE:
+ case ClassPath.EXECUTE:
+ case JavaClassPathConstants.MODULE_COMPILE_PATH:
+ case JavaClassPathConstants.MODULE_EXECUTE_PATH:
+ return extendModuleInfo(modInfo, moduleNames);
+
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public boolean removeRequiredModules(String originalPathType, FileObject projectArtifact, Collection moduleNames) throws IOException {
+ assert projectArtifact != null;
+ assert originalPathType != null;
+
+ Project p = FileOwnerQuery.getOwner(projectArtifact);
+ if (p == null) {
+ return false;
+ }
+ FileObject modInfo = findModuleInfo(projectArtifact);
+ if (modInfo == null) {
+ return false;
+ }
+ ProjectModulesModifier delegate = p.getLookup().lookup(ProjectModulesModifier.class);
+ if (delegate != null) {
+ return delegate.removeRequiredModules(originalPathType, projectArtifact, moduleNames);
+ }
+ switch (originalPathType) {
+ case ClassPath.COMPILE:
+ case ClassPath.EXECUTE:
+ case JavaClassPathConstants.MODULE_COMPILE_PATH:
+ case JavaClassPathConstants.MODULE_EXECUTE_PATH:
+ return removeRequiredModuleArtifacts(modInfo, moduleNames);
+
+ default:
+ return false;
+ }
+ }
+
+ private static final SpecificationVersion JDK9_SPEC = new SpecificationVersion("9");
+
+ @CheckForNull
+ private static FileObject findModuleInfo(@NonNull final FileObject artifact) {
+ if (JDK9_SPEC.compareTo(new SpecificationVersion(SourceLevelQuery.getSourceLevel(artifact))) < 0) {
+ return null;
+ }
+ ClassPath cp = ClassPath.getClassPath(artifact, ClassPath.SOURCE);
+ return cp == null ? null : cp.findResource("module-info.java"); //NOI18N
+ }
+
+ public static boolean extendModuleInfo(
+ @NonNull final FileObject info,
+ @NonNull final Collection modules) throws IOException {
+ if (info == null || modules.isEmpty()) {
+ return false;
+ }
+ final Collection moduleNames = modules.stream()
+ .map((url) -> SourceUtils.getModuleName(url, true))
+ .filter((name) -> name != null)
+ .collect(Collectors.toList());
+ if (moduleNames.isEmpty()) {
+ return false;
+ }
+ final JavaSource js = JavaSource.forFileObject(info);
+ if (js == null) {
+ return false;
+ }
+ boolean[] modified = new boolean[1];
+ js.runModificationTask((wc) -> {
+ wc.toPhase(JavaSource.Phase.RESOLVED);
+ final CompilationUnitTree cu = wc.getCompilationUnit();
+ final Set knownModules = new HashSet<>();
+ final ModuleTree[] module = new ModuleTree[1];
+ final RequiresTree[] lastRequires = new RequiresTree[1];
+ cu.accept(new TreeScanner() {
+ @Override
+ public Void visitModule(ModuleTree m, Void p) {
+ module[0] = m;
+ return super.visitModule(m, p);
+ }
+ @Override
+ public Void visitRequires(RequiresTree r, Void p) {
+ lastRequires[0] = r;
+ knownModules.add(r.getModuleName().toString());
+ return super.visitRequires(r, p);
+ }
+ },
+ null);
+ if (module[0] != null) {
+ moduleNames.removeAll(knownModules);
+ modified[0] = !moduleNames.isEmpty();
+ final TreeMaker tm = wc.getTreeMaker();
+ final List newRequires = moduleNames.stream()
+ .map((name) -> tm.Requires(false, false, tm.QualIdent(name)))
+ .collect(Collectors.toList());
+
+ final List newDirectives = new ArrayList<>(
+ module[0].getDirectives().size() + newRequires.size());
+ if (lastRequires[0] == null) {
+ newDirectives.addAll(newRequires);
+ }
+ for (DirectiveTree dt : module[0].getDirectives()) {
+ newDirectives.add(dt);
+ if (dt == lastRequires[0]) {
+ newDirectives.addAll(newRequires);
+ }
+ }
+ final ModuleTree newModule = tm.Module(
+ tm.Modifiers(0, module[0].getAnnotations()),
+ module[0].getModuleType(),
+ module[0].getName(),
+ newDirectives);
+ wc.rewrite(module[0], newModule);
+ }
+ })
+ .commit();
+ save(info);
+ return modified[0];
+ }
+
+ public static boolean removeRequiredModuleArtifacts(FileObject moduleInfo, Collection modules) {
+ return removeRequiredModules(moduleInfo,
+ modules.stream()
+ .map((url) -> SourceUtils.getModuleName(url, true))
+ .filter((name) -> name != null)
+ .collect(Collectors.toSet()
+ ));
+ }
+
+ public static boolean removeRequiredModules(FileObject moduleInfo, Collection modules) {
+ if (moduleInfo == null || modules.isEmpty()) {
+ return false;
+ }
+ final boolean[] modified = new boolean[1];
+ try {
+ final JavaSource js = JavaSource.forFileObject(moduleInfo);
+ if (js != null) {
+ js.runModificationTask((wc) -> {
+ wc.toPhase(JavaSource.Phase.PARSED);
+ final CompilationUnitTree cu = wc.getCompilationUnit();
+ final Set toRemove = new HashSet<>();
+ final ModuleTree[] module = new ModuleTree[1];
+ cu.accept(
+ new TreeScanner>() {
+ @Override
+ public Void visitModule(final ModuleTree node, Set param) {
+ module[0] = node;
+ return super.visitModule(node, param);
+ }
+ @Override
+ public Void visitRequires(final RequiresTree node, final Set param) {
+ final String fqn = node.getModuleName().toString();
+ if (modules.contains(fqn)) {
+ param.add(node);
+ }
+ return super.visitRequires(node, param);
+ }
+ },
+ toRemove);
+ if (!toRemove.isEmpty()) {
+ modified[0] = true;
+ final List newDirectives = new ArrayList<>(module[0].getDirectives().size());
+ for (DirectiveTree dt : module[0].getDirectives()) {
+ if (!toRemove.contains(dt)) {
+ newDirectives.add(dt);
+ }
+ }
+ final ModuleTree newModule = wc.getTreeMaker().Module(
+ wc.getTreeMaker().Modifiers(0, module[0].getAnnotations()),
+ module[0].getModuleType(),
+ module[0].getName(),
+ newDirectives);
+ wc.rewrite(module[0], newModule);
+ }
+ }).commit();
+ save(moduleInfo);
+ }
+ } catch (IOException ioe) {
+ Exceptions.printStackTrace(ioe);
+ }
+ return modified[0];
+ }
+
+ private static void save(@NonNull final FileObject file) throws IOException {
+ final Savable save = file.getLookup().lookup(Savable.class);
+ if (save != null) {
+ save.save();
+ }
+ }
+}
diff --git a/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/LibrariesNode.java b/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/LibrariesNode.java
--- a/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/LibrariesNode.java
+++ b/java.api.common/src/org/netbeans/modules/java/api/common/project/ui/LibrariesNode.java
@@ -128,6 +128,7 @@
import org.netbeans.modules.java.api.common.classpath.ClassPathModifier;
import org.netbeans.modules.java.api.common.classpath.ClassPathSupport;
import org.netbeans.modules.java.api.common.SourceRoots;
+import org.netbeans.modules.java.api.common.impl.DefaultProjectModulesModifier;
import org.netbeans.modules.java.api.common.util.CommonModuleUtils;
import org.netbeans.modules.java.api.common.project.ui.customizer.AntArtifactItem;
import org.netbeans.modules.java.api.common.project.ui.customizer.EditMediator;
@@ -1187,53 +1188,7 @@
.map((url) -> SourceUtils.getModuleName(url, true))
.filter((name) -> name != null)
.collect(Collectors.toSet());
- if (!modules.isEmpty()) {
- try {
- final JavaSource js = JavaSource.forFileObject(moduleInfo);
- if (js != null) {
- js.runModificationTask((wc) -> {
- wc.toPhase(JavaSource.Phase.PARSED);
- final CompilationUnitTree cu = wc.getCompilationUnit();
- final Set toRemove = new HashSet<>();
- final ModuleTree[] module = new ModuleTree[1];
- cu.accept(
- new TreeScanner>() {
- @Override
- public Void visitModule(final ModuleTree node, Set param) {
- module[0] = node;
- return super.visitModule(node, param);
- }
- @Override
- public Void visitRequires(final RequiresTree node, final Set param) {
- final String fqn = node.getModuleName().toString();
- if (modules.contains(fqn)) {
- param.add(node);
- }
- return super.visitRequires(node, param);
- }
- },
- toRemove);
- if (!toRemove.isEmpty()) {
- final List newDirectives = new ArrayList<>(module[0].getDirectives().size());
- for (DirectiveTree dt : module[0].getDirectives()) {
- if (!toRemove.contains(dt)) {
- newDirectives.add(dt);
- }
- }
- final ModuleTree newModule = wc.getTreeMaker().Module(
- wc.getTreeMaker().Modifiers(0, module[0].getAnnotations()),
- module[0].getModuleType(),
- module[0].getName(),
- newDirectives);
- wc.rewrite(module[0], newModule);
- }
- }).commit();
- save(moduleInfo);
- }
- } catch (IOException ioe) {
- Exceptions.printStackTrace(ioe);
- }
- }
+ DefaultProjectModulesModifier.removeRequiredModules(moduleInfo, modules);
}
}
}
@@ -1483,7 +1438,7 @@
}
}
}
-
+
private static class AddProjectAction extends AbstractAction {
private final Project project;
@@ -1529,9 +1484,7 @@
ClassPath.COMPILE;
ProjectClassPathModifier.addAntArtifacts(artifacts, artifactURIs,
projectSourcesArtifact, cpType);
- if (moduleInfo != null) {
- extendModuleInfo(moduleInfo, toURLs(artifactItems));
- }
+ DefaultProjectModulesModifier.extendModuleInfo(moduleInfo, Arrays.asList(toURLs(artifactItems)));
} catch (IOException ioe) {
Exceptions.printStackTrace(ioe);
} catch (UnsupportedOperationException e) {
@@ -1582,9 +1535,7 @@
ClassPath.COMPILE;
ProjectClassPathModifier.addLibraries(libraries,
projectSourcesArtifact, cpType);
- if (moduleInfo != null) {
- extendModuleInfo(moduleInfo, toURLs(libraries));
- }
+ DefaultProjectModulesModifier.extendModuleInfo(moduleInfo, Arrays.asList(toURLs(libraries)));
} catch (IOException ioe) {
Exceptions.printStackTrace(ioe);
} catch (UnsupportedOperationException e) {
@@ -1731,9 +1682,7 @@
findSourceGroup(projectSourcesArtifact, modifierImpl),
cpType,
ClassPathModifier.ADD_NO_HEURISTICS);
- if (moduleInfo != null) {
- extendModuleInfo(moduleInfo, toAddURLs.toArray(new URL[toAddURLs.size()]));
- }
+ DefaultProjectModulesModifier.extendModuleInfo(moduleInfo, toAddURLs);
}
} catch (IOException ioe) {
Exceptions.printStackTrace(ioe);
@@ -1783,76 +1732,6 @@
return res.toArray(new URL[res.size()]);
}
- private static void extendModuleInfo(
- @NonNull final FileObject info,
- @NonNull final URL... modules) throws IOException {
- final Collection moduleNames = Arrays.stream(modules)
- .map((url) -> SourceUtils.getModuleName(url, true))
- .filter((name) -> name != null)
- .collect(Collectors.toList());
- if (!moduleNames.isEmpty()) {
- final JavaSource js = JavaSource.forFileObject(info);
- if (js != null) {
- js.runModificationTask((wc) -> {
- wc.toPhase(JavaSource.Phase.RESOLVED);
- final CompilationUnitTree cu = wc.getCompilationUnit();
- final Set knownModules = new HashSet<>();
- final ModuleTree[] module = new ModuleTree[1];
- final RequiresTree[] lastRequires = new RequiresTree[1];
- cu.accept(new TreeScanner() {
- @Override
- public Void visitModule(ModuleTree m, Void p) {
- module[0] = m;
- return super.visitModule(m, p);
- }
- @Override
- public Void visitRequires(RequiresTree r, Void p) {
- lastRequires[0] = r;
- knownModules.add(r.getModuleName().toString());
- return super.visitRequires(r, p);
- }
- },
- null);
- if (module[0] != null) {
- moduleNames.removeAll(knownModules);
- final TreeMaker tm = wc.getTreeMaker();
- final List newRequires = moduleNames.stream()
- .map((name) -> tm.Requires(false, false, tm.QualIdent(name)))
- .collect(Collectors.toList());
-
- final List newDirectives = new ArrayList<>(
- module[0].getDirectives().size() + newRequires.size());
- if (lastRequires[0] == null) {
- newDirectives.addAll(newRequires);
- }
- for (DirectiveTree dt : module[0].getDirectives()) {
- newDirectives.add(dt);
- if (dt == lastRequires[0]) {
- newDirectives.addAll(newRequires);
- }
- }
- final ModuleTree newModule = tm.Module(
- tm.Modifiers(0, module[0].getAnnotations()),
- module[0].getModuleType(),
- module[0].getName(),
- newDirectives);
- wc.rewrite(module[0], newModule);
- }
- })
- .commit();
- save(info);
- }
- }
- }
-
- private static void save(@NonNull final FileObject file) throws IOException {
- final DataObject dobj = DataObject.find(file);
- final Savable save = dobj.getLookup().lookup(Savable.class);
- if (save != null) {
- save.save();
- }
- }
-
private static SourceGroup findSourceGroup(FileObject fo, ClassPathModifier modifierImpl) {
SourceGroup[]sgs = modifierImpl.getExtensibleSourceGroups();
for (SourceGroup sg : sgs) {
diff --git a/java.project/apichanges.xml b/java.project/apichanges.xml
--- a/java.project/apichanges.xml
+++ b/java.project/apichanges.xml
@@ -109,6 +109,19 @@
+
+
+ Added a SPI to manipulate with project's module-info.java
declarations
+
+
+
+
+
+ Added a SPI interface, which should be implemented on project to provide backwards compatibility for
+ ProjectClassPathModifier
for modular projects.
+
+
+
Added a LookupMerger
for CompilerOptionsQueryImplementation
s
diff --git a/java.project/manifest.mf b/java.project/manifest.mf
--- a/java.project/manifest.mf
+++ b/java.project/manifest.mf
@@ -2,6 +2,6 @@
OpenIDE-Module: org.netbeans.modules.java.project/1
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/project/Bundle.properties
OpenIDE-Module-Needs: javax.script.ScriptEngine.freemarker
-OpenIDE-Module-Specification-Version: 1.69
+OpenIDE-Module-Specification-Version: 1.70
AutoUpdate-Show-In-Client: false
diff --git a/java.project/src/org/netbeans/api/java/project/classpath/ProjectClassPathModifier.java b/java.project/src/org/netbeans/api/java/project/classpath/ProjectClassPathModifier.java
--- a/java.project/src/org/netbeans/api/java/project/classpath/ProjectClassPathModifier.java
+++ b/java.project/src/org/netbeans/api/java/project/classpath/ProjectClassPathModifier.java
@@ -46,16 +46,26 @@
import java.io.File;
import java.io.IOException;
+import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.project.JavaProjectConstants;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.SourceGroup;
import org.netbeans.api.project.ant.AntArtifact;
+import org.netbeans.api.project.ant.AntArtifactQuery;
import org.netbeans.api.project.libraries.Library;
import org.netbeans.modules.java.project.classpath.ProjectClassPathModifierAccessor;
import org.netbeans.spi.java.project.classpath.ProjectClassPathModifierImplementation;
+import org.netbeans.spi.java.project.classpath.ProjectModulesModifier;
import org.netbeans.spi.java.project.support.LookupMergerSupport;
import org.netbeans.spi.project.libraries.support.LibrariesSupport;
import org.netbeans.spi.project.support.ant.PropertyUtils;
@@ -63,6 +73,7 @@
import org.openide.filesystems.FileUtil;
import org.openide.filesystems.URLMapper;
import org.openide.util.BaseUtilities;
+import org.openide.util.Lookup;
import org.openide.util.Parameters;
/**
@@ -73,6 +84,7 @@
* @since org.netbeans.modules.java.project/1 1.10
*/
public class ProjectClassPathModifier {
+ private static final Logger LOG = Logger.getLogger(ProjectClassPathModifier.class.getName());
private ProjectClassPathModifier() {}
@@ -94,7 +106,10 @@
if (extensible.pcmi != null) {
assert extensible.sg != null;
assert extensible.classPathType != null;
- return ProjectClassPathModifierAccessor.INSTANCE.addLibraries (libraries, extensible.pcmi, extensible.sg, extensible.classPathType);
+ boolean r1 = ProjectClassPathModifierAccessor.INSTANCE.addLibraries (libraries, extensible.pcmi, extensible.sg, extensible.classPathType);
+ boolean r2 = !extensible.classPathType.equals(classPathType) &&
+ addRootsToModinfo(classPathType, projectArtifact, toURLs(libraries));
+ return r1 || r2;
} else {
boolean result = false;
for (Library library : libraries) {
@@ -121,7 +136,10 @@
if (extensible.pcmi != null) {
assert extensible.sg != null;
assert extensible.classPathType != null;
- return ProjectClassPathModifierAccessor.INSTANCE.removeLibraries (libraries, extensible.pcmi, extensible.sg, extensible.classPathType);
+ boolean r1 = ProjectClassPathModifierAccessor.INSTANCE.removeLibraries (libraries, extensible.pcmi, extensible.sg, extensible.classPathType);
+ boolean r2 = !extensible.classPathType.equals(classPathType) &&
+ removeRootsFromModinfo(classPathType, projectArtifact, toURLs(libraries));
+ return r1 || r2;
} else {
throw new UnsupportedOperationException("Cannot remove libraries using " + extensible); // NOI18N
}
@@ -146,7 +164,9 @@
if (extensible.pcmi != null) {
assert extensible.sg != null;
assert extensible.classPathType != null;
- return ProjectClassPathModifierAccessor.INSTANCE.addRoots (classPathRoots, extensible.pcmi, extensible.sg, extensible.classPathType);
+ boolean r1 = ProjectClassPathModifierAccessor.INSTANCE.addRoots (classPathRoots, extensible.pcmi, extensible.sg, extensible.classPathType);
+ boolean r2 = !extensible.classPathType.equals(classPathType) && addRootsToModinfo(classPathType, projectArtifact, Arrays.asList(classPathRoots));
+ return r1 || r2;
} else {
boolean result = false;
for (URL urlToAdd : classPathRoots) {
@@ -183,7 +203,10 @@
if (extensible.pcmi != null) {
assert extensible.sg != null;
assert extensible.classPathType != null;
- return ProjectClassPathModifierAccessor.INSTANCE.addRoots (classPathRoots, extensible.pcmi, extensible.sg, extensible.classPathType);
+ boolean r1 = ProjectClassPathModifierAccessor.INSTANCE.addRoots (classPathRoots, extensible.pcmi, extensible.sg, extensible.classPathType);
+ boolean r2 = !extensible.classPathType.equals(classPathType) &&
+ addRootsToModinfo(classPathType, projectArtifact, toURLs(Arrays.asList(classPathRoots)));
+ return r1 || r2;
} else {
boolean result = false;
final Project project = FileOwnerQuery.getOwner(projectArtifact);
@@ -226,7 +249,10 @@
if (extensible.pcmi != null) {
assert extensible.sg != null;
assert extensible.classPathType != null;
- return ProjectClassPathModifierAccessor.INSTANCE.removeRoots (classPathRoots, extensible.pcmi, extensible.sg, extensible.classPathType);
+ boolean r1 = ProjectClassPathModifierAccessor.INSTANCE.removeRoots (classPathRoots, extensible.pcmi, extensible.sg, extensible.classPathType);
+ boolean r2 = !extensible.classPathType.equals(classPathType) &&
+ removeRootsFromModinfo(classPathType, projectArtifact, Arrays.asList(classPathRoots));
+ return r1 || r2;
} else {
throw new UnsupportedOperationException("Cannot remove roots from " + extensible); // NOI18N
}
@@ -279,7 +305,10 @@
if (extensible.pcmi != null) {
assert extensible.sg != null;
assert extensible.classPathType != null;
- return ProjectClassPathModifierAccessor.INSTANCE.addAntArtifacts (artifacts, artifactElements, extensible.pcmi, extensible.sg, extensible.classPathType);
+ boolean r1 = ProjectClassPathModifierAccessor.INSTANCE.addAntArtifacts (artifacts, artifactElements, extensible.pcmi, extensible.sg, extensible.classPathType);
+ boolean r2 = !extensible.classPathType.equals(classPathType) &&
+ addRootsToModinfo(classPathType, projectArtifact, toURLs(Arrays.asList(artifactElements)));
+ return r1 || r2;
} else {
boolean result = false;
for (int i=0; i< artifacts.length; i++) {
@@ -312,7 +341,10 @@
if (extensible.pcmi != null) {
assert extensible.sg != null;
assert extensible.classPathType != null;
- return ProjectClassPathModifierAccessor.INSTANCE.addProjects (projects, extensible.pcmi, extensible.sg, extensible.classPathType);
+ boolean r1 = ProjectClassPathModifierAccessor.INSTANCE.addProjects (projects, extensible.pcmi, extensible.sg, extensible.classPathType);
+ boolean r2 = !extensible.classPathType.equals(classPathType) &&
+ addRootsToModinfo(classPathType, projectArtifact, toURLs(projects));
+ return r1 || r2;
} else {
throw new UnsupportedOperationException("Cannot add project as dependency. Missing ProjectClassPathModifierImplementation service in project type.");
}
@@ -339,7 +371,10 @@
if (extensible.pcmi != null) {
assert extensible.sg != null;
assert extensible.classPathType != null;
- return ProjectClassPathModifierAccessor.INSTANCE.removeAntArtifacts (artifacts, artifactElements, extensible.pcmi, extensible.sg, extensible.classPathType);
+ boolean r1 = ProjectClassPathModifierAccessor.INSTANCE.removeAntArtifacts (artifacts, artifactElements, extensible.pcmi, extensible.sg, extensible.classPathType);
+ boolean r2 = !extensible.classPathType.equals(classPathType) &&
+ removeRootsFromModinfo(classPathType, projectArtifact, toURLs(Arrays.asList(artifactElements)));
+ return r1 || r2;
} else {
throw new UnsupportedOperationException("Cannot remove artifacts from " + extensible); // NOI18N
}
@@ -368,6 +403,10 @@
throw new UnsupportedOperationException("No project found to correspond to " + FileUtil.getFileDisplayName(projectArtifact)); // NOI18N
}
final ProjectClassPathModifierImplementation pm = project.getLookup().lookup(ProjectClassPathModifierImplementation.class);
+ final ProjectModulesModifier pmm = Lookup.getDefault().lookup(ProjectModulesModifier.class);
+
+ String substModulePath = pmm == null ? null : pmm.provideModularClasspath(projectArtifact, classPathType);
+ final String _classPathType = substModulePath == null ? classPathType : substModulePath;
if (pm != null) {
final SourceGroup[] sgs = ProjectClassPathModifierAccessor.INSTANCE.getExtensibleSourceGroups(pm);
assert sgs != null : "Class: " + pm.getClass() + " returned null as source groups."; //NOI18N
@@ -375,26 +414,33 @@
if ((projectArtifact == sg.getRootFolder() || FileUtil.isParentOf(sg.getRootFolder(),projectArtifact)) && sg.contains(projectArtifact)) {
final String[] types = ProjectClassPathModifierAccessor.INSTANCE.getExtensibleClassPathTypes(pm,sg);
assert types != null : "Class: " + pm.getClass() + " returned null as classpath types."; //NOI18N
+ boolean originalFound = false;
for (String type : types) {
- if (classPathType.equals(type)) {
- String label = "ProjectClassPathModifierImplementation for " + classPathType + " on " + FileUtil.getFileDisplayName(sg.getRootFolder()); // NOI18N
+ if (_classPathType.equals(type)) {
+ String label = "ProjectClassPathModifierImplementation for " + _classPathType + " on " + FileUtil.getFileDisplayName(sg.getRootFolder()); // NOI18N
return new Extensible(pm, sg, type,label);
+ } else if (classPathType.equals(type)) {
+ originalFound = true;
}
}
+ if (originalFound) {
+ String label = "ProjectClassPathModifierImplementation for " + classPathType + " on " + FileUtil.getFileDisplayName(sg.getRootFolder()); // NOI18N
+ return new Extensible(pm, sg, classPathType,label);
+ }
}
}
throw new UnsupportedOperationException("Project in " + FileUtil.getFileDisplayName(project.getProjectDirectory()) + " of " + project.getClass() +
- " has a ProjectClassPathModifierImplementation but it will not handle " + classPathType + " for " + FileUtil.getFileDisplayName(projectArtifact) +
+ " has a ProjectClassPathModifierImplementation but it will not handle " + _classPathType + " for " + FileUtil.getFileDisplayName(projectArtifact) +
" extensible source groups: " + sourceGroupsToString(sgs)); // NOI18N
} else {
final org.netbeans.spi.java.project.classpath.ProjectClassPathExtender pe =
project.getLookup().lookup(org.netbeans.spi.java.project.classpath.ProjectClassPathExtender.class);
if (pe != null) {
- if (classPathType.equals(ClassPath.COMPILE)) {
+ if (_classPathType.equals(ClassPath.COMPILE)) {
return new Extensible(pe, "ProjectClassPathExtender for " + FileUtil.getFileDisplayName(project.getProjectDirectory())); // NOI18N
} else {
throw new UnsupportedOperationException("Project in " + FileUtil.getFileDisplayName(project.getProjectDirectory()) + " of " + project.getClass() +
- " has a ProjectClassPathExtender in its lookup but no ProjectClassPathModifierImplementation to handle " + classPathType); // NOI18N
+ " has a ProjectClassPathExtender in its lookup but no ProjectClassPathModifierImplementation to handle " + _classPathType); // NOI18N
}
} else {
throw new UnsupportedOperationException("Project in " + FileUtil.getFileDisplayName(project.getProjectDirectory()) + " of " + project.getClass() +
@@ -550,5 +596,62 @@
}
};
}
-
+
+ private static Collection toURLs(Collection libs) {
+ URL[] urls = new URL[libs.size()];
+ int index = 0;
+ for (URI u : libs) {
+ try {
+ urls[index++] = u.toURL();
+ } catch (MalformedURLException ex) {
+ return Collections.emptyList();
+ }
+ }
+ return Arrays.asList(urls);
+ }
+
+ private static Collection toURLs(Project[] prjs) {
+ return
+ toURLs(
+ Arrays.asList(prjs).stream().flatMap(
+ (prj) -> Arrays.asList(
+ AntArtifactQuery.findArtifactsByType(prj, JavaProjectConstants.ARTIFACT_TYPE_JAR)
+ ).stream()).
+ flatMap((a) -> Arrays.asList(a.getArtifactLocations()).stream()).
+ collect(Collectors.toList())
+ );
+ }
+
+ private static Collection toURLs(Library[] libraries) {
+ return Arrays.stream(libraries).flatMap((l) -> l.getContent("classpath").stream()).
+ collect(Collectors.toList());
+ }
+
+ private static boolean removeRootsFromModinfo(String originalPathType, FileObject artifact, Collection libs) {
+ ProjectModulesModifier pmm = Lookup.getDefault().lookup(ProjectModulesModifier.class);
+ if (pmm == null) {
+ return false;
+ } else {
+ try {
+ return pmm.removeRequiredModules(originalPathType, artifact, libs);
+ } catch (IOException ex) {
+ LOG.log(Level.WARNING, "Could not remove module requires", ex);
+ return false;
+ }
+ }
+ }
+
+ private static boolean addRootsToModinfo(String originalPathType, FileObject artifact, Collection libs) {
+ ProjectModulesModifier pmm = Lookup.getDefault().lookup(ProjectModulesModifier.class);
+ if (pmm == null) {
+ return false;
+ } else {
+ try {
+ return pmm.addRequiredModules(originalPathType, artifact, libs);
+ } catch (IOException ex) {
+ LOG.log(Level.WARNING, "Could not declare module requires", ex);
+ return false;
+ }
+ }
+ }
}
diff --git a/java.project/src/org/netbeans/modules/java/project/ProjectClassPathProvider.java b/java.project/src/org/netbeans/modules/java/project/ProjectClassPathProvider.java
--- a/java.project/src/org/netbeans/modules/java/project/ProjectClassPathProvider.java
+++ b/java.project/src/org/netbeans/modules/java/project/ProjectClassPathProvider.java
@@ -76,7 +76,7 @@
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "findClassPath({0}, {1}) -> {2} from {3}", new Object[] {file, type, result, cpp});
}
- return result;
+ return result != null ? result : ClassPath.EMPTY;
} else {
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "cpp.findClassPath({0}, {1}) -> null", new Object[] {file, type});
diff --git a/java.project/src/org/netbeans/spi/java/project/classpath/ProjectModulesModifier.java b/java.project/src/org/netbeans/spi/java/project/classpath/ProjectModulesModifier.java
new file mode 100644
--- /dev/null
+++ b/java.project/src/org/netbeans/spi/java/project/classpath/ProjectModulesModifier.java
@@ -0,0 +1,109 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License. When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ */
+package org.netbeans.spi.java.project.classpath;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collection;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.classpath.JavaClassPathConstants;
+import org.openide.filesystems.FileObject;
+
+/**
+ * Allows to update the module declaration in projects which support JDK9. For modular projects,
+ * it is not sufficient just to add artifacts on classpath - modular projects cannot access unnamed module,
+ * so the project would not see such classes/resources during compilation. Even if added to the module path,
+ * a directive needs to be added so that compiler and runtime honour the dependency.
+ *
+ * This SPI allows the project implementation for modular project to diverge additions to a more suitable classpath,
+ * and to update the module information.
+ *
+ * Project-specific implementation should be placed in project lookup. If not present, default implementation
+ * will handle the requests, so that artifacts, libraries and projects will land on {@link JavaClassPathConstants#MODULE_COMPILE_PATH}
+ * or {@link JavaClassPathConstants#MODULE_EXECUTE_PATH} respectively. The default implementation does nothing for projects (sources), which do
+ * not use {@code module-info} declaration, although they target JDK 9.
+ *
+ * @author sdedic
+ * @since 1.70
+ */
+public interface ProjectModulesModifier {
+ /**
+ * Translates the classpath type into a proper modular one, if necessary. If the project
+ * is modular, artifacts added to {@link ClassPath#COMPILE} does not make sense, since it
+ * is inaccessible (without specific startup parameters), so modular project may need to
+ * diverge the request to some other classpath type.
+ * If the project is not modular, or modular project can use artifacts from the original classpath
+ * type, the value of {@code null} should be returned. If {@code null} is returned,
+ * the {@link #addRequiredModules} or {@link #removeRequiredModuels} should not be called.
+ *
+ * @param classPathType original classpath type
+ * @param projectArtifact source, directory which will determine the proper {@code module-info.java}
+ * @return modular replacement for the {@code classPathType}.
+ */
+ public String provideModularClasspath(FileObject projectArtifact, String classPathType);
+
+ /**
+ * Adds "requires" clauses to module-info. "requires" directive will be created
+ * for each of the module names in module-info which applies to the "source" file.
+ * The implementation may ignore requests for classpath types, which do not interact with the
+ * module system.
+ *
+ * @param projectArtifact source, directory which will determine the proper {@code module-info.java}
+ * @param locations module names to add required directives for
+ * @param pathType the original path type
+ * @return true, if some modification has been made
+ * @throws IOException on error
+ */
+ public boolean addRequiredModules(String pathType, FileObject projectArtifact, Collection locations) throws IOException;
+
+ /**
+ * Removes "requires" clauses from module-info. Requires directives will be removed for all
+ * modules in the "moduleNames".
+ * The implementation may ignore requests for classpath types, which do not interact with the
+ * module system.
+ *
+ * @param projectArtifact source, directory which will determine the proper {@code module-info.java}
+ * @param locations module names whose required directives should be removed
+ * @param pathType the original path type
+ * @return true, if some modification has been made
+ * @throws IOException on error
+ */
+ public boolean removeRequiredModules(String pathType, FileObject projectArtifact, Collection locations) throws IOException;
+}
diff --git a/jshell.support/src/org/netbeans/modules/jshell/project/REPLAction2.java b/jshell.support/src/org/netbeans/modules/jshell/project/REPLAction2.java
--- a/jshell.support/src/org/netbeans/modules/jshell/project/REPLAction2.java
+++ b/jshell.support/src/org/netbeans/modules/jshell/project/REPLAction2.java
@@ -46,6 +46,7 @@
import org.netbeans.api.project.Project;
import org.netbeans.modules.jshell.env.JShellEnvironment;
import org.netbeans.modules.jshell.env.ShellRegistry;
+import org.netbeans.modules.projectapi.ProjectFileBuiltQuery;
import org.netbeans.spi.project.ui.support.ProjectActionPerformer;
import org.netbeans.spi.project.ui.support.ProjectSensitiveActions;
import org.openide.awt.ActionID;
@@ -81,6 +82,7 @@
public void perform(Project project) {
JShellEnvironment env;
try {
+
env = ShellRegistry.get().openProjectSession(project);
env.open();
} catch (IOException ex) {
diff --git a/maven/src/org/netbeans/modules/maven/classpath/CPExtender.java b/maven/src/org/netbeans/modules/maven/classpath/CPExtender.java
--- a/maven/src/org/netbeans/modules/maven/classpath/CPExtender.java
+++ b/maven/src/org/netbeans/modules/maven/classpath/CPExtender.java
@@ -254,6 +254,8 @@
ClassPath.COMPILE,
ClassPath.EXECUTE,
JavaClassPathConstants.COMPILE_ONLY,
+ JavaClassPathConstants.MODULE_COMPILE_PATH,
+ JavaClassPathConstants.MODULE_EXECUTE_PATH,
JavaClassPathConstants.PROCESSOR_PATH
};
}
@@ -379,7 +381,7 @@
}
private static String findScope(SourceGroup grp, String type) {
- String scope = ClassPath.EXECUTE.equals(type) ? Artifact.SCOPE_RUNTIME : null; //NOI18N
+ String scope = ClassPath.EXECUTE.equals(type) || JavaClassPathConstants.MODULE_EXECUTE_PATH.equals(type)? Artifact.SCOPE_RUNTIME : null; //NOI18N
//figure if we deal with test or regular sources.
String name = grp.getName();
if (MavenSourcesImpl.NAME_TESTSOURCE.equals(name)) {