Package org.netbeans.gradle.project.java

Source Code of org.netbeans.gradle.project.java.JavaExtension$GlobalPathReg

package org.netbeans.gradle.project.java;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeListener;
import org.jtrim.utils.ExceptionHelper;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.classpath.GlobalPathRegistry;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.gradle.model.java.JavaSourceSet;
import org.netbeans.gradle.project.NbGradleProject;
import org.netbeans.gradle.project.ProjectInfo;
import org.netbeans.gradle.project.ProjectInfoManager;
import org.netbeans.gradle.project.ProjectInfoRef;
import org.netbeans.gradle.project.ProjectInitListener;
import org.netbeans.gradle.project.api.entry.GradleProjectExtension2;
import org.netbeans.gradle.project.java.model.JavaSourceDirHandler;
import org.netbeans.gradle.project.java.model.NbJavaModel;
import org.netbeans.gradle.project.java.model.NbJavaModule;
import org.netbeans.gradle.project.java.model.idea.IdeaJavaModelUtils;
import org.netbeans.gradle.project.java.nodes.JavaExtensionNodes;
import org.netbeans.gradle.project.java.nodes.JavaProjectContextActions;
import org.netbeans.gradle.project.java.query.GradleAnnotationProcessingQuery;
import org.netbeans.gradle.project.java.query.GradleBinaryForSourceQuery;
import org.netbeans.gradle.project.java.query.GradleClassPathProvider;
import org.netbeans.gradle.project.java.query.GradleProjectSources;
import org.netbeans.gradle.project.java.query.GradleProjectTemplates;
import org.netbeans.gradle.project.java.query.GradleSourceForBinaryQuery;
import org.netbeans.gradle.project.java.query.GradleSourceLevelQueryImplementation;
import org.netbeans.gradle.project.java.query.GradleUnitTestFinder;
import org.netbeans.gradle.project.java.query.J2SEPlatformFromScriptQueryImpl;
import org.netbeans.gradle.project.java.query.JavaInitScriptQuery;
import org.netbeans.gradle.project.java.tasks.GradleJavaBuiltInCommands;
import org.netbeans.gradle.project.java.tasks.JavaGradleTaskVariableQuery;
import org.netbeans.gradle.project.model.issue.DependencyResolutionIssue;
import org.netbeans.gradle.project.model.issue.ModelLoadIssueReporter;
import org.netbeans.spi.project.support.LookupProviderSupport;
import org.netbeans.spi.project.ui.ProjectOpenedHook;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.ChangeSupport;
import org.openide.util.Lookup;
import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.ProxyLookup;

public final class JavaExtension implements GradleProjectExtension2<NbJavaModel> {
    private static final Logger LOGGER = Logger.getLogger(JavaExtension.class.getName());

    private final Project project;
    private final File projectDirectoryAsFile;
    private volatile NbJavaModel currentModel;
    private volatile boolean hasEverBeenLoaded;

    private final GradleClassPathProvider cpProvider;
    private final AtomicReference<JavaSourceDirHandler> sourceDirsHandlerRef;
    private final ProjectInfoRef dependencyResolutionFailureRef;

    private final AtomicReference<Lookup> projectLookupRef;
    private final AtomicReference<Lookup> permanentLookupRef;
    private final AtomicReference<Lookup> extensionLookupRef;
    private final AtomicReference<Lookup> combinedLookupRef;

    private final ChangeSupport modelChanges;

    private JavaExtension(Project project) throws IOException {
        ExceptionHelper.checkNotNullArgument(project, "project");

        this.projectDirectoryAsFile = FileUtil.toFile(project.getProjectDirectory());
        if (projectDirectoryAsFile == null) {
            throw new IOException("Project directory does not exist: " + project.getProjectDirectory());
        }

        this.project = project;
        this.currentModel = IdeaJavaModelUtils.createEmptyModel(project.getProjectDirectory());
        this.cpProvider = new GradleClassPathProvider(this);
        this.projectLookupRef = new AtomicReference<>(null);
        this.permanentLookupRef = new AtomicReference<>(null);
        this.extensionLookupRef = new AtomicReference<>(null);
        this.combinedLookupRef = new AtomicReference<>(null);
        this.hasEverBeenLoaded = false;
        this.sourceDirsHandlerRef = new AtomicReference<>(null);
        this.dependencyResolutionFailureRef = getProjectInfoManager(project).createInfoRef();
        this.modelChanges = new ChangeSupport(this);
    }

    public static JavaExtension getJavaExtensionOfProject(Project project) {
        JavaExtension result = project.getLookup().lookup(JavaExtension.class);
        if (result != null) {
            return result;
        }
        else {
            LOGGER.log(Level.WARNING,
                    "JavaExtension cannot be found the project''s lookup: {0}",
                    project.getProjectDirectory());
            try {
                return create(project);
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        }
    }

    public void addModelChangeListener(ChangeListener listener) {
        modelChanges.addChangeListener(listener);
    }

    public void removeModelChangeListener(ChangeListener listener) {
        modelChanges.removeChangeListener(listener);
    }

    public JavaSourceDirHandler getSourceDirsHandler() {
        JavaSourceDirHandler result = sourceDirsHandlerRef.get();
        if (result == null) {
            sourceDirsHandlerRef.compareAndSet(null, new JavaSourceDirHandler(this));
            result = sourceDirsHandlerRef.get();
        }
        return result;
    }

    public static JavaExtension create(Project project) throws IOException {
        return new JavaExtension(project);
    }

    public boolean isOwnerProject(File file) {
        FileObject fileObj;

        File currentFile = file;
        fileObj = FileUtil.toFileObject(currentFile);
        while (fileObj == null) {
            currentFile = currentFile.getParentFile();
            if (currentFile == null) {
                return false;
            }

            fileObj = FileUtil.toFileObject(currentFile);
        }

        return isOwnerProject(fileObj);
    }

    public boolean isOwnerProject(FileObject file) {
        Project owner = FileOwnerQuery.getOwner(file);
        if (owner == null) {
            return false;
        }

        return project.getProjectDirectory().equals(owner.getProjectDirectory());
    }

    public NbJavaModel getCurrentModel() {
        return currentModel;
    }

    private void initLookup(Lookup lookup) {
        for (ProjectInitListener listener: lookup.lookupAll(ProjectInitListener.class)) {
            listener.onInitProject();
        }
    }

    // These classes are on the lookup always.
    @Override
    public Lookup getPermanentProjectLookup() {
        Lookup lookup = permanentLookupRef.get();
        if (lookup == null) {
            lookup = Lookups.fixed(this, new OpenHook());

            if (permanentLookupRef.compareAndSet(null, lookup)) {
                initLookup(lookup);
            }
            lookup = permanentLookupRef.get();
        }
        return lookup;
    }

    @Override
    public Lookup getProjectLookup() {
        Lookup lookup = projectLookupRef.get();
        if (lookup == null) {
            lookup = Lookups.fixed(
                    LookupProviderSupport.createSourcesMerger(),
                    new GradleProjectSources(this),
                    cpProvider,
                    new GradleSourceLevelQueryImplementation(this),
                    new GradleUnitTestFinder(this),
                    new GradleAnnotationProcessingQuery(),
                    new GradleSourceForBinaryQuery(this),
                    new GradleBinaryForSourceQuery(this),
                    new GradleProjectTemplates(),
                    new JavaGradleTaskVariableQuery(this),
                    new J2SEPlatformFromScriptQueryImpl(this) // internal use only
                    );

            if (projectLookupRef.compareAndSet(null, lookup)) {
                initLookup(lookup);
            }
            lookup = projectLookupRef.get();
        }
        return lookup;
    }

    @Override
    public Lookup getExtensionLookup() {
        Lookup lookup = extensionLookupRef.get();
        if (lookup == null) {
            lookup = Lookups.fixed(new JavaExtensionNodes(this),
                    new JavaProjectContextActions(this),
                    new GradleJavaBuiltInCommands(this),
                    new JavaInitScriptQuery());

            if (extensionLookupRef.compareAndSet(null, lookup)) {
                initLookup(lookup);
            }
            lookup = extensionLookupRef.get();
        }
        return lookup;
    }

    public Project getProject() {
        return project;
    }

    public Lookup getOwnerProjectLookup() {
        return project.getLookup();
    }

    public FileObject getProjectDirectory() {
        return project.getProjectDirectory();
    }

    public File getProjectDirectoryAsFile() {
        return projectDirectoryAsFile;
    }

    public String getName() {
        return currentModel.getMainModule().getShortName();
    }

    public boolean hasEverBeenLoaded() {
        return hasEverBeenLoaded;
    }

    private Lookup getCombinedLookup() {
        Lookup lookup = combinedLookupRef.get();
        if (lookup == null) {
            lookup = new ProxyLookup(
                    getPermanentProjectLookup(),
                    getProjectLookup(),
                    getExtensionLookup());
            combinedLookupRef.compareAndSet(null, lookup);
            lookup = combinedLookupRef.get();
        }
        return lookup;
    }

    private void fireModelChange() {
        for (JavaModelChangeListener listener: getCombinedLookup().lookupAll(JavaModelChangeListener.class)) {
            listener.onModelChange();
        }
        modelChanges.fireChange();
    }

    private static ProjectInfoManager getProjectInfoManager(Project project) {
        // TODO: In the future this should be a public API.
        NbGradleProject gradleProject = project.getLookup().lookup(NbGradleProject.class);
        if (gradleProject == null) {
            throw new IllegalStateException("project is not an " + NbGradleProject.class.getSimpleName() + ": " + project.getProjectDirectory());
        }

        return gradleProject.getProjectInfoManager();
    }

    private void checkDependencyResolveProblems(NbJavaModule module) {
        String projectName = module.getProperties().getProjectName();

        List<DependencyResolutionIssue> issues = new LinkedList<>();
        for (JavaSourceSet sourceSet: module.getSources()) {
            String sourceSetName = sourceSet.getName();

            Throwable compileProblems = sourceSet.getCompileClassPathProblem();
            if (compileProblems != null) {
                issues.add(DependencyResolutionIssue.compileIssue(projectName, sourceSetName, compileProblems));
            }

            Throwable runtimeProblems = sourceSet.getRuntimeClassPathProblem();
            if (runtimeProblems != null) {
                issues.add(DependencyResolutionIssue.runtimeIssue(projectName, sourceSetName, runtimeProblems));
            }
        }

        if (!issues.isEmpty()) {
            List<ProjectInfo.Entry> entries = new ArrayList<>(issues.size());
            for (DependencyResolutionIssue issue: issues) {
                entries.add(new ProjectInfo.Entry(ProjectInfo.Kind.ERROR, issue.getMessage()));
            }

            dependencyResolutionFailureRef.setInfo(new ProjectInfo(entries));
            ModelLoadIssueReporter.reportDependencyResolutionFailures(issues);
        }
        else {
            dependencyResolutionFailureRef.setInfo(null);
        }
    }

    @Override
    public void activateExtension(NbJavaModel parsedModel) {
        ExceptionHelper.checkNotNullArgument(parsedModel, "parsedModel");

        currentModel = parsedModel;
        hasEverBeenLoaded = true;

        checkDependencyResolveProblems(parsedModel.getMainModule());

        fireModelChange();
    }

    @Override
    public void deactivateExtension() {
    }

    // OpenHook is important for debugging because the debugger relies on the
    // globally registered source class paths for source stepping.

    // SwingUtilities.invokeLater is used only to guarantee the order of events.
    // Actually any executor which executes tasks in the order they were
    // submitted to it is good (using SwingUtilities.invokeLater was only
    // convenient to use because registering paths is cheap enough).
    private class OpenHook extends ProjectOpenedHook implements PropertyChangeListener {
        private final List<GlobalPathReg> paths;
        private boolean opened;

        public OpenHook() {
            this.opened = false;

            this.paths = new LinkedList<>();
            this.paths.add(new GlobalPathReg(ClassPath.SOURCE));
            this.paths.add(new GlobalPathReg(ClassPath.BOOT));
            this.paths.add(new GlobalPathReg(ClassPath.COMPILE));
            this.paths.add(new GlobalPathReg(ClassPath.EXECUTE));
        }

        @Override
        protected void projectOpened() {
            cpProvider.addPropertyChangeListener(this);

            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    opened = true;
                    doRegisterClassPaths();
                }
            });
        }

        @Override
        protected void projectClosed() {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    opened = false;
                    doUnregisterPaths();
                }
            });

            cpProvider.removePropertyChangeListener(this);
        }

        private void doUnregisterPaths() {
            assert SwingUtilities.isEventDispatchThread();

            for (GlobalPathReg pathReg: paths) {
                pathReg.unregister();
            }
        }

        private void doRegisterClassPaths() {
            assert SwingUtilities.isEventDispatchThread();

            for (GlobalPathReg pathReg: paths) {
                pathReg.register();
            }
        }

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    if (opened) {
                        doRegisterClassPaths();
                    }
                }
            });
        }
    }

    private class GlobalPathReg {
        private final String type;
        // Note that using AtomicReference does not really make the methods
        // thread-safe but is only convenient to use.
        private final AtomicReference<ClassPath[]> paths;

        public GlobalPathReg(String type) {
            this.type = type;
            this.paths = new AtomicReference<>(null);
        }

        private void replaceRegistration(ClassPath[] newPaths) {
            GlobalPathRegistry registry = GlobalPathRegistry.getDefault();

            ClassPath[] oldPaths = paths.getAndSet(newPaths);
            if (oldPaths != null) {
                registry.unregister(type, oldPaths);
            }
            if (newPaths != null) {
                registry.register(type, newPaths);
            }
        }

        public void register() {
            ClassPath[] newPaths = new ClassPath[]{cpProvider.getClassPaths(type)};
            replaceRegistration(newPaths);
        }

        public void unregister() {
            replaceRegistration(null);
        }
    }
}
TOP

Related Classes of org.netbeans.gradle.project.java.JavaExtension$GlobalPathReg

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.