package org.netbeans.gradle.project.lookups; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import java.util.logging.Logger; import org.jtrim.utils.ExceptionHelper; import org.netbeans.api.java.classpath.ClassPath; import org.netbeans.api.project.ProjectInformation; import org.netbeans.gradle.project.NbGradleProject; import org.netbeans.gradle.project.api.config.ProjectSettingsProvider; import org.netbeans.gradle.project.java.JavaExtension; import org.netbeans.gradle.project.java.query.GradleClassPathProvider; import org.netbeans.gradle.project.model.ModelRefreshListener; import org.netbeans.gradle.project.model.ProjectModelChangeListener; import org.netbeans.gradle.project.properties.NbGradleCommonProperties; import org.netbeans.gradle.project.properties.NbGradleSingleProjectConfigProvider; import org.netbeans.spi.java.classpath.ClassPathProvider; import org.netbeans.spi.project.ActionProvider; import org.netbeans.spi.project.AuxiliaryConfiguration; import org.netbeans.spi.project.AuxiliaryProperties; import org.netbeans.spi.project.ProjectConfigurationProvider; import org.netbeans.spi.project.SubprojectProvider; import org.netbeans.spi.project.ui.CustomizerProvider; import org.netbeans.spi.queries.FileEncodingQueryImplementation; import org.netbeans.spi.queries.SharabilityQueryImplementation2; import org.openide.filesystems.FileObject; import org.openide.util.Lookup; import org.openide.util.lookup.Lookups; import org.openide.util.lookup.ProxyLookup; public final class ProjectLookupHack extends ProxyLookup { private static final Logger LOGGER = Logger.getLogger(ProjectLookupHack.class.getName()); private static final AtomicReference<Collection<Class<?>>> NOT_IMPLEMENTED_SERVICES = new AtomicReference<>(); public interface LookupContainer { public NbGradleProject getProject(); public Lookup getLookup(); public Lookup getLookupAndActivate(); } private final AtomicBoolean activated; private final LookupContainer lookupContainer; public ProjectLookupHack(LookupContainer lookupContainer) { ExceptionHelper.checkNotNullArgument(lookupContainer, "lookupContainer"); this.lookupContainer = lookupContainer; this.activated = new AtomicBoolean(false); setLookups(new AccessPreventerLookup()); } private Lookup activate(Object reason) { Lookup result = lookupContainer.getLookupAndActivate(); if (activated.compareAndSet(false, true)) { FileObject projectDir = lookupContainer.getProject().getProjectDirectory(); LOGGER.log(Level.INFO, "Activating project ({0}) lookup due to requesting: {1}", new Object[]{projectDir, reason}); setLookups(result); } return result; } private static void tryAddClass(String className, Collection<Class<?>> result) { try { result.add(Class.forName(className)); } catch (ClassNotFoundException ex) { } } private static Collection<Class<?>> getNotImplementedServices() { Collection<Class<?>> result = NOT_IMPLEMENTED_SERVICES.get(); if (result == null) { result = new ArrayList<>(); // We could implement these interfaces but do not want to // because for this information we need to parse the build script // which is too slow to be useful in the project open dialog. result.add(SubprojectProvider.class); tryAddClass("org.netbeans.spi.project.ProjectContainerProvider", result); tryAddClass("org.netbeans.spi.project.DependencyProjectProvider", result); NOT_IMPLEMENTED_SERVICES.set(Collections.unmodifiableCollection(result)); result = NOT_IMPLEMENTED_SERVICES.get(); } return result; } private class AccessPreventerLookup extends Lookup { private final Map<String, Lookup> typeActions; public AccessPreventerLookup() { this.typeActions = new HashMap<>(); for (Class<?> type: getNotImplementedServices()) { typeActions.put(type.getName(), Lookup.EMPTY); } typeActions.put(ClassPathProvider.class.getName(), Lookups.singleton(new UnimportantRootClassPathProvider())); Lookup wrappedLookup = lookupContainer.getLookup(); typeActions.put(AuxiliaryProperties.class.getName(), wrappedLookup); typeActions.put(AuxiliaryConfiguration.class.getName(), wrappedLookup); typeActions.put(GradleClassPathProvider.class.getName(), wrappedLookup); typeActions.put(NbGradleProject.class.getName(), wrappedLookup); typeActions.put(FileEncodingQueryImplementation.class.getName(), wrappedLookup); typeActions.put(ProjectInformation.class.getName(), wrappedLookup); typeActions.put(ActionProvider.class.getName(), wrappedLookup); typeActions.put(CustomizerProvider.class.getName(), wrappedLookup); typeActions.put(NbGradleSingleProjectConfigProvider.class.getName(), wrappedLookup); typeActions.put(NbGradleCommonProperties.class.getName(), wrappedLookup); typeActions.put(ProjectConfigurationProvider.class.getName(), wrappedLookup); typeActions.put(JavaExtension.class.getName(), wrappedLookup); typeActions.put(SharabilityQueryImplementation2.class.getName(), wrappedLookup); typeActions.put(ProjectSettingsProvider.class.getName(), wrappedLookup); typeActions.put(ModelRefreshListener.class.getName(), wrappedLookup); typeActions.put(ProjectModelChangeListener.class.getName(), wrappedLookup); typeActions.put("org.netbeans.modules.maven.NbMavenProjectImpl", wrappedLookup); typeActions.put("org.netbeans.modules.web.browser.spi.ProjectBrowserProvider", wrappedLookup); typeActions.put("org.netbeans.spi.project.ui.ProjectProblemsProvider", wrappedLookup); } private Lookup lookupForType(Class<?> type) { Lookup action = typeActions.get(type.getName()); if (action != null) { LOGGER.log(Level.INFO, "Using custom lookup for type {0}", type.getName()); return action; } else { return activate(type.getName()); } } @Override public <T> T lookup(Class<T> clazz) { return lookupForType(clazz).lookup(clazz); } @Override public <T> Result<T> lookup(Template<T> template) { return lookupForType(template.getType()).lookup(template); } @Override public <T> Collection<? extends T> lookupAll(Class<T> clazz) { return lookupForType(clazz).lookupAll(clazz); } @Override public <T> Result<T> lookupResult(Class<T> clazz) { return lookupForType(clazz).lookupResult(clazz); } @Override public <T> Item<T> lookupItem(Template<T> template) { return lookupForType(template.getType()).lookupItem(template); } } private class UnimportantRootClassPathProvider implements ClassPathProvider { @Override public ClassPath findClassPath(FileObject file, String type) { Lookup lookup; if (lookupContainer.getProject().getProjectDirectory().equals(file)) { lookup = lookupContainer.getLookup(); } else { lookup = activate("ClassPathProvider.findClassPath"); } for (ClassPathProvider otherProvider: lookup.lookupAll(ClassPathProvider.class)) { ClassPath result = otherProvider.findClassPath(file, type); if (result != null) { return result; } } return null; } } }