com.redhat.ceylon.eclipse.core.model.JDTModuleManager.java Source code

Java tutorial

Introduction

Here is the source code for com.redhat.ceylon.eclipse.core.model.JDTModuleManager.java

Source

/*
 * Copyright Red Hat Inc. and/or its affiliates and other contributors
 * as indicated by the authors tag. All rights reserved.
 *
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU General Public License version 2.
 * 
 * This particular file is subject to the "Classpath" exception as provided in the 
 * LICENSE file that accompanied this code.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License,
 * along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 */

package com.redhat.ceylon.eclipse.core.model;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import org.antlr.runtime.CommonTokenStream;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.JarPackageFragmentRoot;

import com.redhat.ceylon.cmr.api.ArtifactContext;
import com.redhat.ceylon.cmr.api.ArtifactResult;
import com.redhat.ceylon.cmr.api.JDKUtils;
import com.redhat.ceylon.cmr.api.RepositoryManager;
import com.redhat.ceylon.compiler.java.util.Util;
import com.redhat.ceylon.compiler.loader.AbstractModelLoader;
import com.redhat.ceylon.compiler.loader.model.LazyModuleManager;
import com.redhat.ceylon.compiler.typechecker.TypeChecker;
import com.redhat.ceylon.compiler.typechecker.analyzer.ModuleHelper;
import com.redhat.ceylon.compiler.typechecker.analyzer.ModuleManager;
import com.redhat.ceylon.compiler.typechecker.context.Context;
import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit;
import com.redhat.ceylon.compiler.typechecker.context.PhasedUnits;
import com.redhat.ceylon.compiler.typechecker.io.VirtualFile;
import com.redhat.ceylon.compiler.typechecker.io.impl.ZipFileVirtualFile;
import com.redhat.ceylon.compiler.typechecker.model.Module;
import com.redhat.ceylon.compiler.typechecker.model.ModuleImport;
import com.redhat.ceylon.compiler.typechecker.model.Modules;
import com.redhat.ceylon.compiler.typechecker.model.Package;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.CompilationUnit;
import com.redhat.ceylon.compiler.typechecker.util.ModuleManagerFactory;
import com.redhat.ceylon.eclipse.core.builder.CeylonBuilder;
import com.redhat.ceylon.eclipse.core.typechecker.CrossProjectPhasedUnit;
import com.redhat.ceylon.eclipse.core.typechecker.ExternalPhasedUnit;
import com.redhat.ceylon.eclipse.ui.CeylonPlugin;
import com.redhat.ceylon.eclipse.util.CeylonSourceParser;

/**
 * @author david
 *
 */
public class JDTModuleManager extends LazyModuleManager {

    private JDTModelLoader modelLoader;
    private IJavaProject javaProject;
    private Set<String> sourceModules;
    private TypeChecker typeChecker;
    private boolean loadDependenciesFromModelLoaderFirst;

    public Set<String> getSourceModules() {
        return sourceModules;
    }

    public IJavaProject getJavaProject() {
        return javaProject;
    }

    public JDTModuleManager(Context context, IJavaProject javaProject) {
        super(context);
        this.javaProject = javaProject;
        if (javaProject == null) {
            loadDependenciesFromModelLoaderFirst = false;
        } else {
            loadDependenciesFromModelLoaderFirst = CeylonBuilder
                    .loadDependenciesFromModelLoaderFirst(javaProject.getProject());
        }
        sourceModules = new HashSet<String>();
        if (!loadDependenciesFromModelLoaderFirst) {
            sourceModules.add(Module.LANGUAGE_MODULE_NAME);
        }
    }
    /*
     * TODO : Remove when the package creation (and module binding) in ModuleManager will be done with a method 
     * that can be overriden (createPackage, as suggested here - a "" name parameter correspond to the empty package)
     * Then we can only override this new createPackage method with our already-existing one
     */

    @Override
    public void initCoreModules() {
        Modules modules = getContext().getModules();
        if (modules == null) {
            modules = new Modules();

            //build default module (module in which packages belong to when not explicitly under a module
            final List<String> defaultModuleName = Collections.singletonList(Module.DEFAULT_MODULE_NAME);
            final JDTModule defaultModule = createModule(defaultModuleName, "unversioned");
            defaultModule.setDefault(true);
            defaultModule.setAvailable(true);
            defaultModule.setProjectModule();
            modules.getListOfModules().add(defaultModule);
            modules.setDefaultModule(defaultModule);

            //create language module and add it as a dependency of defaultModule
            //since packages outside a module cannot declare dependencies
            final List<String> languageName = Arrays.asList("ceylon", "language");
            Module languageModule = createModule(languageName, TypeChecker.LANGUAGE_MODULE_VERSION);
            languageModule.setLanguageModule(languageModule);
            languageModule.setAvailable(false); //not available yet
            modules.setLanguageModule(languageModule);
            modules.getListOfModules().add(languageModule);
            defaultModule.addImport(new ModuleImport(languageModule, false, false));
            defaultModule.setLanguageModule(languageModule);
            getContext().setModules(modules);

            //build empty package
            createPackage("", defaultModule);
        }
        super.initCoreModules();
    }

    @Override
    protected Package createPackage(String pkgName, Module module) {
        return getModelLoader().findOrCreatePackage(module, pkgName);
    }

    @Override
    public synchronized JDTModelLoader getModelLoader() {
        if (modelLoader == null) {
            Modules modules = getContext().getModules();
            modelLoader = new JDTModelLoader(this, modules);
        }
        return modelLoader;
    }

    public boolean isExternalModuleLoadedFromSource(String moduleName) {
        return sourceModules.contains(moduleName);
    }

    /**
     * Return true if this module should be loaded from source we are compiling
     * and not from its compiled artifact at all. Returns false by default, so
     * modules will be loaded from their compiled artifact.
     */
    @Override
    protected boolean isModuleLoadedFromSource(String moduleName) {
        if (isExternalModuleLoadedFromSource(moduleName)) {
            return true;
        }
        if (isModuleLoadedFromCompiledSource(moduleName)) {
            return true;
        }
        return false;
    }

    public boolean isModuleLoadedFromCompiledSource(String moduleName) {
        if (javaProject == null) {
            return false;
        }

        if (moduleFileInProject(moduleName, javaProject)) {
            return true;
        }

        if (!loadDependenciesFromModelLoaderFirst) {
            try {
                IProject project = javaProject.getProject();
                for (IProject p : project.getReferencedProjects()) {
                    if (p.isAccessible() && moduleFileInProject(moduleName, JavaCore.create(p))) {
                        return true;
                    }
                }
            } catch (CoreException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    private static boolean moduleFileInProject(String moduleName, IJavaProject p) {
        if (p == null) {
            return false;
        }
        try {
            for (IPackageFragmentRoot sourceFolder : p.getPackageFragmentRoots()) {
                if (!sourceFolder.isArchive() && sourceFolder.exists()
                        && sourceFolder.getKind() == IPackageFragmentRoot.K_SOURCE
                        && sourceFolder.getPackageFragment(moduleName).exists()) {
                    return true;
                }
                /*IPath moduleFile = sourceFolder.append(moduleName.replace('.', '/') + 
                    "/module.ceylon").makeRelativeTo(p.getFullPath());
                if (p.getFile(moduleFile).exists()) {
                return true;
                }*/
            }
        } catch (JavaModelException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    protected JDTModule createModule(List<String> moduleName, String version) {
        JDTModule module = null;
        String moduleNameString = Util.getName(moduleName);
        List<IPackageFragmentRoot> roots = new ArrayList<IPackageFragmentRoot>();
        if (javaProject != null) {
            try {
                if (moduleNameString.equals(Module.DEFAULT_MODULE_NAME)) {
                    // Add the list of source package fragment roots
                    for (IPackageFragmentRoot root : javaProject.getPackageFragmentRoots()) {
                        if (root.exists() && javaProject.isOnClasspath(root)) {
                            IClasspathEntry entry = root.getResolvedClasspathEntry();
                            if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE && !root.isExternal()) {
                                roots.add(root);
                            }
                        }
                    }
                } else {
                    for (IPackageFragmentRoot root : javaProject.getPackageFragmentRoots()) {
                        if (root.exists() && javaProject.isOnClasspath(root)) {
                            if (JDKUtils.isJDKModule(moduleNameString)) {
                                // find the first package that exists in this root
                                for (String pkg : JDKUtils.getJDKPackagesByModule(moduleNameString)) {
                                    if (root.getPackageFragment(pkg).exists()) {
                                        roots.add(root);
                                        break;
                                    }
                                }
                            } else if (JDKUtils.isOracleJDKModule(moduleNameString)) {
                                // find the first package that exists in this root
                                for (String pkg : JDKUtils.getOracleJDKPackagesByModule(moduleNameString)) {
                                    if (root.getPackageFragment(pkg).exists()) {
                                        roots.add(root);
                                        break;
                                    }
                                }
                            } else if (!(root instanceof JarPackageFragmentRoot)
                                    && !CeylonBuilder.isInCeylonClassesOutputFolder(root.getPath())) {
                                String packageToSearch = moduleNameString;
                                if (root.getPackageFragment(packageToSearch).exists()) {
                                    roots.add(root);
                                }
                            }
                        }
                    }
                }
            } catch (JavaModelException e) {
                e.printStackTrace();
            }
        }

        module = new JDTModule(this, roots);
        module.setName(moduleName);
        module.setVersion(version);
        setupIfJDKModule(module);
        return module;
    }

    @Override
    public void resolveModule(ArtifactResult artifact, Module module, ModuleImport moduleImport,
            LinkedList<Module> dependencyTree, List<PhasedUnits> phasedUnitsOfDependencies,
            boolean forCompiledModule) {
        File artifactFile = artifact.artifact();
        if (isModuleLoadedFromSource(module.getNameAsString())
                && artifactFile.getName().endsWith(ArtifactContext.CAR)) {
            ArtifactContext artifactContext = new ArtifactContext(module.getNameAsString(), module.getVersion(),
                    ArtifactContext.SRC);
            RepositoryManager repositoryManager = getContext().getRepositoryManager();
            Exception exceptionOnGetArtifact = null;
            ArtifactResult sourceArtifact = null;
            try {
                sourceArtifact = repositoryManager.getArtifactResult(artifactContext);
            } catch (Exception e) {
                exceptionOnGetArtifact = e;
            }
            if (sourceArtifact == null) {
                ModuleHelper.buildErrorOnMissingArtifact(artifactContext, module, moduleImport, dependencyTree,
                        exceptionOnGetArtifact, this);
            } else {
                artifact = sourceArtifact;
            }

        }
        if (module instanceof JDTModule) {
            ((JDTModule) module).setArtifact(artifact);
        }
        if (!isModuleLoadedFromCompiledSource(module.getNameAsString())) {
            File file = artifact.artifact();
            if (artifact.artifact().getName().endsWith(".src")) {
                sourceModules.add(module.getNameAsString());
                file = new File(file.getAbsolutePath().replaceAll("\\.src$", ".car"));
            }
        }
        try {
            super.resolveModule(artifact, module, moduleImport, dependencyTree, phasedUnitsOfDependencies,
                    forCompiledModule);
        } catch (Exception e) {
            if (module instanceof JDTModule) {
                CeylonPlugin.getInstance().getLog().log(new Status(IStatus.ERROR, CeylonPlugin.PLUGIN_ID,
                        "Failed resolving module " + module.getSignature(), e));
                ((JDTModule) module).setResolutionException(e);
            }
        }
    }

    @Override
    public void prepareForTypeChecking() {
        getModelLoader().loadStandardModules();
    }

    @Override
    public Iterable<String> getSearchedArtifactExtensions() {
        if (loadDependenciesFromModelLoaderFirst) {
            return Arrays.asList("car", "jar", "src");
        } else {
            return Arrays.asList("jar", "src", "car");
        }
    }

    public void visitModuleFile() {
        Package currentPkg = getCurrentPackage();
        sourceModules.add(currentPkg.getNameAsString());
        super.visitModuleFile();
    }

    // Todo : to push into the base ModelManager class
    public void addTopLevelModuleError() {
        addErrorToModule(new ArrayList<String>(), "A module cannot be defined at the top level of the hierarchy");
    }

    public class ExternalModulePhasedUnits extends PhasedUnits {
        private IProject referencedProject = null;
        //        private VirtualFile sourceDirectory = null;

        public ExternalModulePhasedUnits(Context context, ModuleManagerFactory moduleManagerFactory) {
            super(context, moduleManagerFactory);
        }

        @Override
        protected void parseFile(final VirtualFile file, final VirtualFile srcDir) throws Exception {
            if (file.getName().endsWith(".ceylon")) {
                parseFile(file, srcDir, getModuleManager().getCurrentPackage());
            }
        }

        /*
         *  This additional method is when we have to parse a new file, into a specific package of an existing archive  
         */
        public void parseFile(final VirtualFile file, final VirtualFile srcDir, final Package pkg) {
            PhasedUnit phasedUnit = new CeylonSourceParser<PhasedUnit>() {
                @Override
                protected String getCharset() {
                    try {
                        //TODO: is this correct? does this file actually
                        //      live in the project, or is it external?
                        //       should VirtualFile have a getCharset()?
                        return javaProject != null ? javaProject.getProject().getDefaultCharset()
                                : ResourcesPlugin.getWorkspace().getRoot().getDefaultCharset();
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }

                @Override
                protected PhasedUnit createPhasedUnit(CompilationUnit cu, Package pkg,
                        CommonTokenStream tokenStream) {
                    if (referencedProject == null) {
                        return new ExternalPhasedUnit(file, srcDir, cu, pkg, getModuleManager(), getTypeChecker(),
                                tokenStream.getTokens());
                    } else {
                        return new CrossProjectPhasedUnit(file, srcDir, cu, pkg, getModuleManager(),
                                getTypeChecker(), tokenStream.getTokens(), referencedProject);
                    }
                }
            }.parseFileToPhasedUnit(getModuleManager(), getTypeChecker(), file, srcDir, pkg);

            addPhasedUnit(file, phasedUnit);
        }

        @Override
        public void parseUnit(VirtualFile srcDir) {
            //            sourceDirectory = srcDir;
            if (srcDir instanceof ZipFileVirtualFile && javaProject != null) {
                ZipFileVirtualFile zipFileVirtualFile = (ZipFileVirtualFile) srcDir;
                String archiveName = zipFileVirtualFile.getPath();
                try {
                    for (IProject refProject : javaProject.getProject().getReferencedProjects()) {
                        if (archiveName.contains(
                                CeylonBuilder.getCeylonModulesOutputDirectory(refProject).getAbsolutePath())) {
                            referencedProject = refProject;
                            break;
                        }
                    }
                } catch (CoreException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            super.parseUnit(srcDir);
        }
    }

    @Override
    protected PhasedUnits createPhasedUnits() {
        ModuleManagerFactory moduleManagerFactory = new ModuleManagerFactory() {
            @Override
            public ModuleManager createModuleManager(Context context) {
                return JDTModuleManager.this;
            }
        };

        return new ExternalModulePhasedUnits(getContext(), moduleManagerFactory);
    }

    public TypeChecker getTypeChecker() {
        return typeChecker;
    }

    public void setTypeChecker(TypeChecker typeChecker) {
        this.typeChecker = typeChecker;
    }

    public boolean isLoadDependenciesFromModelLoaderFirst() {
        return loadDependenciesFromModelLoaderFirst;
    }

    public JDTModule getArchiveModuleFromSourcePath(String sourceUnitPath) {
        for (Module m : typeChecker.getContext().getModules().getListOfModules()) {
            if (m instanceof JDTModule) {
                JDTModule module = (JDTModule) m;
                if (module.isCeylonArchive()) {
                    if (sourceUnitPath.startsWith(module.getSourceArchivePath() + "!")) {
                        return module;
                    }
                }
            }
        }
        return null;
    }

    public JDTModule getArchiveModuleFromSourcePath(IPath sourceUnitPath) {
        return getArchiveModuleFromSourcePath(sourceUnitPath.toOSString());
    }

    @Override
    protected void addToPhasedUnitsOfDependencies(PhasedUnits modulePhasedUnits,
            List<PhasedUnits> phasedUnitsOfDependencies, Module module) {
        super.addToPhasedUnitsOfDependencies(modulePhasedUnits, phasedUnitsOfDependencies, module);
        if (module instanceof JDTModule) {
            ((JDTModule) module).setSourcePhasedUnits((ExternalModulePhasedUnits) modulePhasedUnits);
        }
    }

    @Override
    public void visitedModule(Module module, boolean forCompiledModule) {
        if (forCompiledModule && AbstractModelLoader.isJDKModule(module.getNameAsString()))
            modelLoader.addJDKModuleToClassPath(module);
    }
}