| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.netbeans.modules.maven.classpath; |
| |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.beans.PropertyChangeSupport; |
| import java.io.File; |
| import java.net.URI; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.function.Function; |
| import java.util.function.Supplier; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import org.apache.maven.project.MavenProject; |
| import org.netbeans.api.annotations.common.NonNull; |
| import org.netbeans.modules.maven.api.FileUtilities; |
| import org.netbeans.modules.maven.NbMavenProjectImpl; |
| import org.netbeans.modules.maven.api.classpath.ProjectSourcesClassPathProvider; |
| import org.netbeans.modules.maven.api.execute.ActiveJ2SEPlatformProvider; |
| import org.netbeans.api.java.classpath.ClassPath; |
| import org.netbeans.api.java.classpath.JavaClassPathConstants; |
| import org.netbeans.api.java.platform.JavaPlatform; |
| import org.netbeans.api.java.queries.SourceLevelQuery; |
| import org.netbeans.api.project.Project; |
| import org.netbeans.api.project.ProjectManager; |
| import org.netbeans.modules.java.api.common.classpath.ClassPathSupport; |
| import org.netbeans.modules.java.api.common.classpath.ClassPathSupportFactory; |
| import org.netbeans.modules.maven.api.NbMavenProject; |
| import org.netbeans.spi.java.classpath.ClassPathFactory; |
| import org.netbeans.spi.java.classpath.ClassPathProvider; |
| import static org.netbeans.spi.java.classpath.support.ClassPathSupport.Selector.PROP_ACTIVE_CLASS_PATH; |
| import org.netbeans.spi.project.ProjectServiceProvider; |
| import org.openide.filesystems.FileObject; |
| import org.openide.filesystems.FileUtil; |
| import org.openide.util.Mutex; |
| import org.openide.util.Utilities; |
| import org.openide.util.WeakListeners; |
| |
| /** |
| * Defines class path for maven2 projects.. |
| * |
| * @author Milos Kleint |
| */ |
| @ProjectServiceProvider(service={ClassPathProvider.class, ActiveJ2SEPlatformProvider.class, ProjectSourcesClassPathProvider.class}, projectType="org-netbeans-modules-maven") |
| public final class ClassPathProviderImpl implements ClassPathProvider, ActiveJ2SEPlatformProvider, ProjectSourcesClassPathProvider { |
| |
| private static final Logger LOGGER = Logger.getLogger(ClassPathProviderImpl.class.getName()); |
| |
| public static final String MODULE_INFO_JAVA = "module-info.java"; // NOI18N |
| |
| private static final int TYPE_SRC = 0; |
| private static final int TYPE_TESTSRC = 1; |
| private static final int TYPE_WEB = 5; |
| private static final int TYPE_UNKNOWN = -1; |
| |
| private final @NonNull Project proj; |
| |
| private static final int SOURCE_PATH = 0; // TEST_SOURCE_PATH = 1 |
| private static final int COMPILE_TIME_PATH = 2; // TEST_COMPILE_TIME_PATH = 3 |
| private static final int RUNTIME_PATH = 4; // TEST_RUNTIME_PATH = 5 |
| private static final int BOOT_PATH = 6; // TEST_BOOT_PATH = 7 |
| private static final int ENDORSED_PATH = 8; |
| private static final int MODULE_BOOT_PATH = 9; |
| private static final int MODULE_COMPILE_PATH = 10; // TEST_MODULE_COMPILE_PATH = 11 |
| private static final int MODULE_LEGACY_PATH = 12; // TEST_MODULE_LEGACY_PATH = 13 |
| |
| private static final int MODULE_EXECUTE_PATH = 14; // TEST_MODULE_EXECUTE_PATH = 15 |
| private static final int MODULE_EXECUTE_CLASS_PATH = 16; // TEST_MODULE_EXECUTE_CLASS_PATH = 17 |
| |
| private static final int JAVA8_COMPILE_PATH = 18; |
| private static final int JAVA8_TEST_COMPILE_PATH = 19; |
| private static final int JAVA8_TEST_SCOPED_COMPILE_PATH = 20; |
| private static final int JAVA8_RUNTIME_PATH = 21; // JAVA8_TEST_RUNTIME_PATH = 22 |
| private static final int JAVA8_TEST_SCOPED_RUNTIME_PATH = 23; |
| |
| private static final int ANNOTATION_PROC_PATH = 24; // TEST_ANNOTATION_PROC_PATH = 25 |
| |
| private final ClassPath[] cache = new ClassPath[26]; |
| |
| private BootClassPathImpl bcpImpl; |
| private EndorsedClassPathImpl ecpImpl; |
| |
| public ClassPathProviderImpl(@NonNull Project proj) { |
| this.proj = proj; |
| } |
| |
| /** |
| * Returns array of all classpaths of the given type in the project. |
| * The result is used for example for GlobalPathRegistry registrations. |
| */ |
| @Override public ClassPath[] getProjectClassPaths(String type) { |
| return ProjectManager.mutex().readAccess((Mutex.Action<ClassPath[]>) () -> { |
| if (ClassPath.BOOT.equals(type)) { |
| //TODO |
| return new ClassPath[]{ getBootClassPath(TYPE_SRC), getBootClassPath(TYPE_TESTSRC) }; |
| } |
| if (ClassPathSupport.ENDORSED.equals(type)) { |
| return new ClassPath[]{ getEndorsedClassPath() }; |
| } |
| if (ClassPath.COMPILE.equals(type)) { |
| List<ClassPath> l = new ArrayList<>(2); |
| l.add(getCompileTimeClasspath(TYPE_SRC)); |
| l.add(getCompileTimeClasspath(TYPE_TESTSRC)); |
| return l.toArray(new ClassPath[l.size()]); |
| } |
| if (ClassPath.EXECUTE.equals(type)) { |
| List<ClassPath> l = new ArrayList<>(2); |
| l.add(getRuntimeClasspath(TYPE_SRC)); |
| l.add(getRuntimeClasspath(TYPE_TESTSRC)); |
| return l.toArray(new ClassPath[l.size()]); |
| } |
| |
| if (ClassPath.SOURCE.equals(type)) { |
| List<ClassPath> l = new ArrayList<>(2); |
| l.add(getSourcepath(TYPE_SRC)); |
| l.add(getSourcepath(TYPE_TESTSRC)); |
| return l.toArray(new ClassPath[l.size()]); |
| } |
| if (JavaClassPathConstants.MODULE_BOOT_PATH.equals(type)) { |
| return new ClassPath[] {getModuleBootPath()}; |
| } |
| if (JavaClassPathConstants.MODULE_COMPILE_PATH.equals(type)) { |
| ClassPath[] l = new ClassPath[2]; |
| l[0] = getModuleCompilePath(TYPE_SRC); |
| l[1] = getModuleCompilePath(TYPE_TESTSRC); |
| return l; |
| } |
| if (JavaClassPathConstants.MODULE_CLASS_PATH.equals(type)) { |
| ClassPath[] l = new ClassPath[2]; |
| l[0] = getModuleLegacyClassPath(TYPE_SRC); |
| l[1] = getModuleLegacyClassPath(TYPE_TESTSRC); |
| return l; |
| } |
| return new ClassPath[0]; |
| }); |
| } |
| |
| /** |
| * Returns the given type of the classpath for the project sources |
| * (i.e., excluding tests roots). |
| */ |
| @Override public ClassPath getProjectSourcesClassPath(String type) { |
| if (ClassPath.BOOT.equals(type)) { |
| return getBootClassPath(TYPE_SRC); |
| } |
| if (ClassPathSupport.ENDORSED.equals(type)) { |
| return getEndorsedClassPath(); |
| } |
| if (ClassPath.COMPILE.equals(type)) { |
| return getCompileTimeClasspath(TYPE_SRC); |
| } |
| if (ClassPath.SOURCE.equals(type)) { |
| return getSourcepath(TYPE_SRC); |
| } |
| if (ClassPath.EXECUTE.equals(type)) { |
| return getRuntimeClasspath(TYPE_SRC); |
| } |
| if (type.equals(JavaClassPathConstants.MODULE_BOOT_PATH)) { |
| return getModuleBootPath(); |
| } |
| if (type.equals(JavaClassPathConstants.MODULE_COMPILE_PATH)) { |
| return getModuleCompilePath(TYPE_SRC); |
| } |
| if (type.equals(JavaClassPathConstants.MODULE_CLASS_PATH)) { |
| return getModuleLegacyClassPath(TYPE_SRC); |
| } |
| if (JavaClassPathConstants.MODULE_EXECUTE_PATH.equals(type)) { |
| return getModuleExecutePath(TYPE_SRC); |
| } |
| if (JavaClassPathConstants.MODULE_EXECUTE_CLASS_PATH.equals(type)) { |
| return getModuleLegacyRuntimeClassPath(TYPE_SRC); |
| } |
| assert false; |
| return null; |
| } |
| |
| @Override public ClassPath findClassPath(FileObject file, String type) { |
| assert file != null; |
| if(file == null) { |
| LOGGER.log(Level.WARNING, " passed null fileobject fo ClassPathProviderImpl.findClassPath."); //NOI18N |
| return null; |
| } |
| int fileType = getType(file); |
| if (fileType != TYPE_SRC && fileType != TYPE_TESTSRC && fileType != TYPE_WEB) { |
| LOGGER.log(Level.FINEST, " bad type={0} for {1}", new Object[] {type, file}); //NOI18N |
| return null; |
| } |
| if (type.equals(ClassPath.COMPILE)) { |
| return getCompileTimeClasspath(fileType); |
| } else if (type.equals(ClassPath.EXECUTE)) { |
| return getRuntimeClasspath(fileType); |
| } else if (ClassPath.SOURCE.equals(type)) { |
| return getSourcepath(fileType); |
| } else if (type.equals(ClassPath.BOOT)) { |
| return getBootClassPath(fileType); |
| } else if (type.equals(ClassPathSupport.ENDORSED)) { |
| return getEndorsedClassPath(); |
| } else if (type.equals(JavaClassPathConstants.PROCESSOR_PATH)) { |
| return getAnnotationProcClassPath(fileType); |
| } else if (type.equals(JavaClassPathConstants.MODULE_BOOT_PATH)) { |
| return getModuleBootPath(); |
| } else if (type.equals(JavaClassPathConstants.MODULE_COMPILE_PATH)) { |
| return getModuleCompilePath(fileType); |
| } else if (type.equals(JavaClassPathConstants.MODULE_CLASS_PATH)) { |
| return getModuleLegacyClassPath(fileType); |
| } else if (type.equals(JavaClassPathConstants.MODULE_EXECUTE_PATH)) { |
| return getModuleExecutePath(fileType); |
| } else if (type.equals(JavaClassPathConstants.MODULE_EXECUTE_CLASS_PATH)) { |
| return getModuleLegacyRuntimeClassPath(fileType); |
| } else { |
| return null; |
| } |
| } |
| |
| @Override public @NonNull JavaPlatform getJavaPlatform() { |
| return getBootClassPathImpl().findActivePlatform(); |
| } |
| |
| private boolean isChildOf(FileObject child, URI[] uris) { |
| for (int i = 0; i < uris.length; i++) { |
| FileObject fo = FileUtilities.convertURItoFileObject(uris[i]); |
| if (fo != null && fo.isFolder() && (fo.equals(child) || FileUtil.isParentOf(fo, child))) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public static FileObject[] convertStringsToFileObjects(List<String> strings) { |
| FileObject[] fos = new FileObject[strings.size()]; |
| int index = 0; |
| Iterator<String> it = strings.iterator(); |
| while (it.hasNext()) { |
| String str = it.next(); |
| fos[index] = FileUtilities.convertStringToFileObject(str); |
| index++; |
| } |
| return fos; |
| } |
| |
| |
| private int getType(FileObject file) { |
| if(file == null) { |
| return TYPE_UNKNOWN; |
| } |
| NbMavenProjectImpl project = getNBMavenProject(); |
| if (isChildOf(file, project.getSourceRoots(false)) || |
| isChildOf(file, project.getGeneratedSourceRoots(false))) { |
| return TYPE_SRC; |
| } |
| if (isChildOf(file, project.getSourceRoots(true)) || |
| isChildOf(file, project.getGeneratedSourceRoots(true))) { |
| return TYPE_TESTSRC; |
| } |
| |
| URI web = project.getWebAppDirectory(); |
| FileObject fo = FileUtil.toFileObject(Utilities.toFile(web)); |
| if (fo != null && (fo.equals(file) || FileUtil.isParentOf(fo, file))) { |
| return TYPE_WEB; |
| } |
| |
| //MEVENIDE-613, #125603 need to check later than the actual java sources.. |
| // sometimes the root of resources is the basedir for example that screws up |
| // test sources. |
| if (isChildOf(file, project.getResources(false))) { |
| return TYPE_SRC; |
| } |
| if (isChildOf(file, project.getResources(true))) { |
| return TYPE_TESTSRC; |
| } |
| return TYPE_UNKNOWN; |
| } |
| |
| private ClassPath getSourcepath(int type) { |
| final int ftype = type == TYPE_WEB ? TYPE_SRC : type; |
| return computeIfAbsent( |
| SOURCE_PATH + ftype, |
| () -> ClassPathFactory.createClassPath(ftype == TYPE_SRC ? new SourceClassPathImpl(getNBMavenProject()) : new TestSourceClassPathImpl(getNBMavenProject()))); |
| } |
| |
| private ClassPath getAnnotationProcClassPath(int type) { |
| final int ftype; |
| switch (type) { |
| case TYPE_WEB: |
| default: |
| ftype = TYPE_SRC; |
| break; |
| case TYPE_SRC: |
| case TYPE_TESTSRC: |
| ftype = type; |
| break; |
| } |
| int index = ANNOTATION_PROC_PATH + ftype; |
| return computeIfAbsent( |
| index, |
| () -> { |
| ClassPath anno = ClassPathFactory.createClassPath(new AnnotationProcClassPathImpl(getNBMavenProject(), ftype == TYPE_SRC)); |
| return createMultiplexClassPath( |
| new AnnotationPathSelector( |
| getNBMavenProject(), anno, |
| () -> getCompileTimeClasspath(type) |
| ) |
| ); |
| } |
| |
| ); |
| } |
| |
| private ClassPath getCompileTimeClasspath(int type) { |
| final int ftype = type == TYPE_WEB ? TYPE_SRC : type; |
| return computeIfAbsent( |
| COMPILE_TIME_PATH + ftype, |
| () -> createModuleInfoSelector( |
| // if there is a main module-info |
| () -> createModuleInfoBasedPath( |
| getModuleCompilePath(ftype), // base |
| getSourcepath(ftype), // source |
| getModuleBootPath(), // system modules |
| getModuleCompilePath(ftype), // usermodules |
| ftype == TYPE_SRC ? // legacy |
| getJava8CompileClasspath() : |
| createTestClassPathSelector(() -> getJava8TestCompileClasspath(), () -> getTestScopedCompileClasspath(), "TestsCompileTimeLegacyClasspath"), |
| null), |
| // if there is no module-info: |
| // note that the maven compile plugin (3.5.1) does not allow |
| // a test module-info while there is no main module info |
| () -> ftype == TYPE_SRC ? getJava8CompileClasspath() : getJava8TestCompileClasspath(), |
| "CompileTimeClasspath")); // NOI18N |
| } |
| |
| private ClassPath getRuntimeClasspath(int type) { |
| final int ftype = type == TYPE_WEB ? TYPE_SRC : type; |
| return computeIfAbsent(RUNTIME_PATH + ftype, |
| () -> createModuleInfoSelector( |
| // if there is a main module-info |
| () -> createModuleInfoBasedPath( |
| getJava8RunTimeClassPath(ftype), // base |
| getSourcepath(ftype), // source |
| getModuleBootPath(), // system modules |
| getJava8RunTimeClassPath(ftype), // user modules |
| ftype == TYPE_SRC ? // legacy |
| getJava8RunTimeClassPath(TYPE_SRC) : |
| createTestClassPathSelector(() -> getJava8RunTimeClassPath(TYPE_TESTSRC), () -> getTestScopedRuntimeClasspath(), "TestsRuntimeLegacyClasspath"), |
| null), |
| // if there is no module-info |
| () -> getJava8RunTimeClassPath(ftype), |
| "RuntimeClasspath")); |
| } |
| |
| private ClassPath getJava8RunTimeClassPath(int type) { |
| final int ftype = type == TYPE_WEB ? TYPE_SRC : type; |
| return computeIfAbsent(JAVA8_RUNTIME_PATH + ftype, () -> ClassPathFactory.createClassPath(ftype == TYPE_SRC ? new RuntimeClassPathImpl(getNBMavenProject()) : new TestRuntimeClassPathImpl(getNBMavenProject(), false))); |
| } |
| |
| private ClassPath getTestScopedRuntimeClasspath() { |
| return computeIfAbsent(JAVA8_TEST_SCOPED_RUNTIME_PATH, () -> ClassPathFactory.createClassPath(new TestRuntimeClassPathImpl(getNBMavenProject(), true))); |
| } |
| |
| private ClassPath getBootClassPath(int type) { |
| final int ftype = type == TYPE_WEB ? TYPE_SRC : type; |
| return computeIfAbsent(BOOT_PATH + ftype, |
| () -> createModuleInfoSelector( |
| () -> createModuleInfoBasedPath( |
| getModuleBootPath(), // base |
| getSourcepath(ftype), // source |
| getModuleBootPath(), // system modules |
| getModuleCompilePath(ftype), // user modules |
| null, // legacy |
| null), |
| () -> ClassPathFactory.createClassPath(getBootClassPathImpl()), |
| "BootClasspath")); // NOI18N |
| } |
| |
| private BootClassPathImpl getBootClassPathImpl() { |
| if (bcpImpl == null) { |
| bcpImpl = new BootClassPathImpl(getNBMavenProject(), getEndorsedClassPathImpl()); |
| } |
| return bcpImpl; |
| } |
| |
| /* |
| WARNING: getCompileClasspath, getTestCompileClasspath, getTestScopedCompileClasspath |
| |
| the classpathElements in maven-compiler-plugin always were: |
| - all artifacts from the project with the scope - COMPILE, PROFILE and SYSTEM |
| - the path given by project.build.getOutputDirectory |
| |
| until jdk9 jigsaw: |
| CompileClassPathImpl provided only the artifacts with the respective scope, |
| but NOT the project.build.getOutputDirectory. |
| since jdk9 jigsaw (and therefore maven-compiler-plugin 2.6): |
| it is necessary to provide also project.build.getOutputDirectory |
| (as that is the dir where maven copies the dependand jar/modules?) |
| |
| The question at this point is if we now should do so for all compiler versions |
| (and also for < 2.6) and jdk-s < 9 or if we should differ between m-c-p < 2.6 and >=2.6 |
| and jdk version respectively. |
| */ |
| |
| /* |
| * see WARNING above. |
| */ |
| private ClassPath getJava8CompileClasspath() { |
| return computeIfAbsent(JAVA8_COMPILE_PATH, () -> ClassPathFactory.createClassPath(new CompileClassPathImpl(getNBMavenProject(), true))); |
| } |
| |
| /* |
| * see WARNING above. |
| */ |
| private ClassPath getJava8TestCompileClasspath() { |
| return computeIfAbsent(JAVA8_TEST_COMPILE_PATH, () -> ClassPathFactory.createClassPath(new TestCompileClassPathImpl(getNBMavenProject(), true))); |
| } |
| |
| /* |
| * see WARNING above. |
| */ |
| private ClassPath getTestScopedCompileClasspath() { |
| return computeIfAbsent(JAVA8_TEST_SCOPED_COMPILE_PATH, () -> ClassPathFactory.createClassPath(new TestCompileClassPathImpl(getNBMavenProject(), true, true))); |
| } |
| |
| private EndorsedClassPathImpl getEndorsedClassPathImpl() { |
| if (ecpImpl == null) { |
| ecpImpl = new EndorsedClassPathImpl(getNBMavenProject()); |
| } |
| return ecpImpl; |
| } |
| |
| private ClassPath getEndorsedClassPath() { |
| return computeIfAbsent(ENDORSED_PATH, () -> { |
| getBootClassPathImpl(); |
| return ClassPathFactory.createClassPath(getEndorsedClassPathImpl()); |
| }); |
| } |
| |
| private ClassPath getModuleBootPath() { |
| return computeIfAbsent(MODULE_BOOT_PATH, () -> createModuleInfoSelector(() -> createPlatformModulesPath(), () -> createPlatformModulesPath(), "ModuleBootPath")); // NOI18N |
| } |
| |
| private ClassPath getModuleCompilePath(int type) { |
| final int ftype = type == TYPE_WEB ? TYPE_SRC : type; |
| return computeIfAbsent( |
| MODULE_COMPILE_PATH + ftype, |
| () -> ftype == TYPE_SRC ? |
| // XXX <= jdk8 |
| // jdk9 has module-info |
| // jdk9 has no module-info but is SL9 |
| createModuleInfoSelector(() -> getJava8CompileClasspath(), () -> ClassPath.EMPTY, "ModuleCompilePath") : // NOI18N |
| createTestModulePathSelector(() -> getJava8CompileClasspath(), () -> getJava8TestCompileClasspath(), "TestModuleCompilePath")); // NOI18N |
| } |
| |
| private ClassPath getModuleExecutePath(int type) { |
| final int ftype = type == TYPE_WEB ? TYPE_SRC : type; |
| return computeIfAbsent( |
| MODULE_EXECUTE_PATH + ftype, |
| () -> ftype == TYPE_SRC ? |
| // XXX <= jdk8 |
| // jdk9 has module-info |
| // jdk9 has no module-info but is SL9 |
| createModuleInfoSelector(() -> getJava8RunTimeClassPath(TYPE_SRC), () -> ClassPath.EMPTY, "ModuleExecutePath") : // NOI18N |
| createTestModulePathSelector(() -> getJava8RunTimeClassPath(TYPE_SRC), () -> getJava8RunTimeClassPath(TYPE_TESTSRC), "TestModuleExecutePath")); |
| } |
| |
| @NonNull |
| private ClassPath getModuleLegacyClassPath(int type) { |
| final int ftype = type == TYPE_WEB ? TYPE_SRC : type; |
| assert ftype >=0 && ftype <=1; |
| return computeIfAbsent( |
| MODULE_LEGACY_PATH + ftype, |
| () -> ftype == TYPE_SRC ? |
| createModuleInfoSelector(() -> ClassPath.EMPTY, () -> getJava8CompileClasspath(), "ModuleLegacyClassPath") : // NOI18N |
| createModuleInfoSelector(() -> getTestScopedCompileClasspath(), () -> getJava8TestCompileClasspath(), "TestModuleLegacyClassPath")); // NOI18N |
| } |
| |
| @NonNull |
| private ClassPath getModuleLegacyRuntimeClassPath(int type) { |
| final int ftype = type == TYPE_WEB ? TYPE_SRC : type; |
| assert ftype >=0 && ftype <=1; |
| return computeIfAbsent( |
| MODULE_EXECUTE_CLASS_PATH + ftype, |
| () -> ftype == TYPE_SRC ? |
| createModuleInfoSelector(() -> ClassPath.EMPTY, () -> getJava8RunTimeClassPath(TYPE_SRC), "ModuleLegacyRuntimeClassPath") : // NOI18N |
| createModuleInfoSelector(() -> getTestScopedRuntimeClasspath(), () -> getJava8RunTimeClassPath(TYPE_TESTSRC), "TestModuleLegacyRuntimeClassPath")); // NOI18N |
| } |
| |
| private ClassPath computeIfAbsent(final int cacheIndex, final Supplier<ClassPath> provider) { |
| synchronized (this) { |
| ClassPath cp = cache[cacheIndex]; |
| if (cp != null) { |
| return cp; |
| } |
| } |
| return ProjectManager.mutex().readAccess(()-> { |
| synchronized(this) { |
| ClassPath cp = cache[cacheIndex]; |
| if (cp == null) { |
| cp = provider.get(); |
| cache[cacheIndex] = cp; |
| } |
| return cp; |
| } |
| }); |
| } |
| |
| private ClassPath createPlatformModulesPath() { |
| return ClassPathFactory.createClassPath(new PlatformModulesPathImpl(getNBMavenProject())); |
| } |
| |
| private ClassPath createModuleInfoBasedPath(ClassPath base, ClassPath sourceRoots, ClassPath systemModules, ClassPath userModules, ClassPath legacyClassPath, Function<URL,Boolean> filter) { |
| return ClassPathFactory.createClassPath(ClassPathSupportFactory.createModuleInfoBasedPath(base, sourceRoots, systemModules, userModules, legacyClassPath, filter)); |
| } |
| |
| private ClassPath createModuleInfoSelector(Supplier<ClassPath> hasModuleInfoClassPath, Supplier<ClassPath> noModuleInfoClassPath, String logDesc) { |
| return createMultiplexClassPath(new ModuleInfoSelector(getNBMavenProject(), hasModuleInfoClassPath, noModuleInfoClassPath, logDesc)); |
| } |
| |
| private ClassPath createTestClassPathSelector(Supplier<ClassPath> testPath, Supplier<ClassPath> testScopedPath, String logDesc) { |
| return createMultiplexClassPath(new TestClassPathSelector(getNBMavenProject(), testPath, testScopedPath, logDesc)); |
| } |
| |
| private ClassPath createTestModulePathSelector(Supplier<ClassPath> path, Supplier<ClassPath> testPath, String logDesc) { |
| return createMultiplexClassPath(new TestModulePathSelector(getNBMavenProject(), path, testPath, logDesc)); |
| } |
| |
| private ClassPath createMultiplexClassPath(ClassPathSelector selector) { |
| return org.netbeans.spi.java.classpath.support.ClassPathSupport.createMultiplexClassPath(selector); |
| } |
| |
| private NbMavenProjectImpl getNBMavenProject() { |
| return proj.getLookup().lookup(NbMavenProjectImpl.class); |
| } |
| |
| private static class ModuleInfoSelector extends ClassPathSelector { |
| |
| private final Supplier<ClassPath> noModuleInfoCP; |
| private final Supplier<ClassPath> hasModuleInfoCP; |
| private final String logDesc; |
| |
| public ModuleInfoSelector(NbMavenProjectImpl proj, Supplier<ClassPath> hasModuleInfoClassPath, Supplier<ClassPath> noModuleInfoClassPath, String logDesc) { |
| super(proj); |
| this.hasModuleInfoCP = hasModuleInfoClassPath; |
| this.noModuleInfoCP = noModuleInfoClassPath; |
| this.logDesc = logDesc; |
| } |
| |
| @Override |
| public ClassPath getActiveClassPath() { |
| ClassPath ret = active; |
| if (ret == null) { |
| // see org.apache.maven.plugin.compiler.CompilerMojo.classpathElements |
| for (String sourceRoot : proj.getOriginalMavenProject().getCompileSourceRoots()) { |
| final File moduleInfoFile = new File(sourceRoot, MODULE_INFO_JAVA); |
| if(moduleInfoFile.exists()) { |
| FileObject moduleInfo = FileUtil.toFileObject(moduleInfoFile); |
| String sourceLevel = SourceLevelQuery.getSourceLevel2(moduleInfo).getSourceLevel(); |
| String ide_jdkvers = System.getProperty("java.version"); //NOI18N |
| if(!sourceLevel.startsWith("1.") && !ide_jdkvers.startsWith("1.")) { //NOI18N |
| // both sourceLevel and ideJDK are 9+ |
| ret = hasModuleInfoCP.get(); |
| } |
| final Object retObject = ret; |
| LOGGER.log(Level.FINER, () -> String.format("ModuleInfoSelector %s for project %s: has module-info.java %s", logDesc, proj.getProjectDirectory().getPath(), retObject == null ? "IGNORED" : "")); // NOI18N |
| break; |
| } |
| } |
| if(ret == null) { |
| ret = noModuleInfoCP.get(); |
| } |
| active = ret; |
| } |
| LOGGER.log(Level.FINE, "ModuleInfoSelector {0} for project {1} active class path: {2}", new Object[]{logDesc, proj.getProjectDirectory().getPath(), ret}); // NOI18N |
| return ret; |
| } |
| |
| @Override |
| protected boolean isReset(PropertyChangeEvent evt) { |
| boolean reset = false; |
| if( (NbMavenProject.PROP_RESOURCE.equals(evt.getPropertyName()) && evt.getNewValue() instanceof URI)) { |
| File file = Utilities.toFile((URI) evt.getNewValue()); |
| for (String sourceRoot : proj.getOriginalMavenProject().getCompileSourceRoots()) { |
| if(file.equals(new File(sourceRoot, MODULE_INFO_JAVA))) { |
| reset = true; |
| break; |
| } |
| } |
| if(reset) { |
| LOGGER.log(Level.FINER, "ModuleInfoSelector {0} for project {1} resource changed: {2}", new Object[]{logDesc, proj.getProjectDirectory().getPath(), evt}); |
| } |
| } |
| return reset; |
| } |
| } |
| |
| private static class TestModulePathSelector extends TestPathSelector { |
| protected final Supplier<ClassPath> path; |
| protected final Supplier<ClassPath> testPath; |
| TestModulePathSelector(NbMavenProjectImpl proj, Supplier<ClassPath> path, Supplier<ClassPath> testPath, String logDesc) { |
| super(proj, logDesc); |
| this.path = path; |
| this.testPath = testPath; |
| } |
| @Override |
| protected ClassPath getActiveClassPath(boolean hasTestModuleDescriptor, boolean hasMainModuleDescriptor) { |
| // see how modulepathElements are set in org.apache.maven.plugin.compiler.TestCompilerMojo |
| // XXX at the moment the asumption is made that exec-maven-plugin (runtime) will follow symetric logic like maven-compiler-plugin |
| if ( hasTestModuleDescriptor ) { |
| return testPath.get(); |
| } else { |
| if(hasMainModuleDescriptor) { |
| return path.get(); |
| } else { |
| return ClassPath.EMPTY; |
| } |
| } |
| } |
| } |
| |
| private static class TestClassPathSelector extends TestPathSelector { |
| private final Supplier<ClassPath> testPath; |
| private final Supplier<ClassPath> testScopedPath; |
| TestClassPathSelector(NbMavenProjectImpl proj, Supplier<ClassPath> testPath, Supplier<ClassPath> testScopedPath, String logDesc) { |
| super(proj, logDesc); // NOI18N |
| this.testPath = testPath; |
| this.testScopedPath = testScopedPath; |
| } |
| @Override |
| protected ClassPath getActiveClassPath(boolean hasTestModuleDescriptor, boolean hasMainModuleDescriptor) { |
| // see how classpathElements are set in org.apache.maven.plugin.compiler.TestCompilerMojo |
| // XXX at the moment the asumption is made that exec-maven-plugin (runtime) will follow symetric logic like maven-compiler-plugin |
| if ( hasTestModuleDescriptor ) { |
| return ClassPath.EMPTY; |
| } else { |
| if(hasMainModuleDescriptor) { |
| return testScopedPath.get(); |
| } else { |
| return testPath.get(); |
| } |
| } |
| } |
| } |
| |
| private abstract static class TestPathSelector extends ClassPathSelector { |
| private final String logDesc; |
| |
| TestPathSelector(NbMavenProjectImpl proj, String logDesc) { |
| super(proj); |
| this.logDesc = logDesc; |
| } |
| |
| @Override |
| public ClassPath getActiveClassPath() { |
| ClassPath ret = active; |
| if (ret == null) { |
| MavenProject mp = proj.getOriginalMavenProject(); |
| boolean hasMainModuleDescriptor = mp.getCompileSourceRoots().stream().anyMatch((sourceRoot) -> (new File(sourceRoot, MODULE_INFO_JAVA).exists())); |
| if(hasMainModuleDescriptor) { |
| LOGGER.log(Level.FINER, "TestPathSelector {0} for project {1}: has main module-info.java", new Object [] {logDesc, proj.getProjectDirectory().getPath()}); // NOI18N |
| } |
| |
| boolean hasTestModuleDescriptor = mp.getTestCompileSourceRoots().stream().anyMatch((testSourceRoot) -> (new File(testSourceRoot, MODULE_INFO_JAVA).exists())); |
| if(hasTestModuleDescriptor) { |
| LOGGER.log(Level.FINER, "TestPathSelector {0} for project {1}: has test module-info.java", new Object [] {logDesc, proj.getProjectDirectory().getPath()}); // NOI18N |
| } |
| |
| ret = getActiveClassPath(hasTestModuleDescriptor, hasMainModuleDescriptor); |
| |
| active = ret; |
| } |
| LOGGER.log(Level.FINE, "TestPathSelector {0} for project {1} active class path: {2}", new Object[]{logDesc, proj.getProjectDirectory().getPath(), ret}); // NOI18N |
| return ret; |
| } |
| |
| protected abstract ClassPath getActiveClassPath(boolean hasTestModuleDescriptor, boolean hasMainModuleDescriptor); |
| |
| @Override |
| protected boolean isReset(PropertyChangeEvent evt) { |
| boolean reset = false; |
| if( (NbMavenProject.PROP_RESOURCE.equals(evt.getPropertyName()) && evt.getNewValue() instanceof URI)) { |
| File file = Utilities.toFile((URI) evt.getNewValue()); |
| MavenProject mp = proj.getOriginalMavenProject(); |
| reset = mp.getCompileSourceRoots().stream().anyMatch((sourceRoot) -> (file.equals(new File(sourceRoot, MODULE_INFO_JAVA)))) || |
| mp.getTestCompileSourceRoots().stream().anyMatch((sourceRoot) -> (file.equals(new File(sourceRoot, MODULE_INFO_JAVA)))); |
| if(reset) { |
| LOGGER.log(Level.FINER, "TestPathSelector {0} for project {1} resource changed: {2}", new Object[]{logDesc, proj.getProjectDirectory().getPath(), evt}); |
| } |
| } |
| return reset; |
| } |
| } |
| |
| /** |
| * This selector chooses the annotation classpath, if it is not empty (has items, or is broken), or the regular |
| * compile classpath if annotation path is empty. The selector reacts |
| */ |
| private static class AnnotationPathSelector extends ClassPathSelector { |
| private final ClassPath annotationCP; |
| private final Supplier<ClassPath> compileClassPath; |
| |
| public AnnotationPathSelector(NbMavenProjectImpl proj, ClassPath anno, Supplier<ClassPath> compile) { |
| super(proj); |
| this.annotationCP = anno; |
| this.compileClassPath = compile; |
| |
| anno.addPropertyChangeListener(WeakListeners.propertyChange( |
| e -> { |
| active = null; |
| support.firePropertyChange(PROP_ACTIVE_CLASS_PATH, null, null); |
| }, ClassPath.PROP_ROOTS, anno |
| )); |
| // proj.getProjectWatcher().addPropertyChangeListener((e) -> { |
| // if (NbMavenProject.PROP_PROJECT.equals(e.getPropertyName())) { |
| // active = null; |
| // support.firePropertyChange(PROP_ACTIVE_CLASS_PATH, null, null); |
| // } |
| // }); |
| } |
| |
| @Override |
| protected boolean isReset(PropertyChangeEvent evt) { |
| return NbMavenProject.PROP_PROJECT.equals(evt.getPropertyName()); |
| } |
| |
| @Override |
| public ClassPath getActiveClassPath() { |
| if (active != null) { |
| return active; |
| } |
| if (annotationCP.getFlags().contains(ClassPath.Flag.INCOMPLETE) || |
| !annotationCP.entries().isEmpty()) { |
| return active = annotationCP; |
| } else { |
| return active = compileClassPath.get(); |
| } |
| } |
| } |
| |
| private abstract static class ClassPathSelector implements org.netbeans.spi.java.classpath.support.ClassPathSupport.Selector { |
| protected final PropertyChangeSupport support = new PropertyChangeSupport(this); |
| |
| protected final NbMavenProjectImpl proj; |
| protected ClassPath active = null; |
| |
| public ClassPathSelector(NbMavenProjectImpl proj) { |
| this.proj = proj; |
| // see the usage of org.apache.maven.plugin.compiler.CompilerMojo.preparePaths |
| // maven checks recursively all source roots for module-info, |
| // for performace reasons we will be checking and listening only on the root of a source root |
| NbMavenProject.addPropertyChangeListener(proj, (evt) -> { |
| if (isReset(evt)) { |
| active = null; |
| support.firePropertyChange(PROP_ACTIVE_CLASS_PATH, null, null); |
| } |
| }); |
| } |
| |
| protected abstract boolean isReset(PropertyChangeEvent evt); |
| |
| @Override |
| public void addPropertyChangeListener(PropertyChangeListener listener) { |
| support.addPropertyChangeListener(listener); |
| } |
| |
| @Override |
| public void removePropertyChangeListener(PropertyChangeListener listener) { |
| support.removePropertyChangeListener(listener); |
| } |
| |
| } |
| } |
| |