Package com.redhat.ceylon.compiler.typechecker.analyzer

Source Code of com.redhat.ceylon.compiler.typechecker.analyzer.ModuleValidator$ProgressListener

package com.redhat.ceylon.compiler.typechecker.analyzer;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.redhat.ceylon.cmr.api.ArtifactContext;
import com.redhat.ceylon.cmr.api.ArtifactResult;
import com.redhat.ceylon.cmr.api.RepositoryManager;
import com.redhat.ceylon.cmr.api.VersionComparator;
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.model.Module;
import com.redhat.ceylon.compiler.typechecker.model.ModuleImport;

/**
* Validate module dependency:
*  - make sure all modules are available
*  - get modules from local or remote repos if necessary
*  - parse and process external modules
*
* @author Emmanuel Bernard <emmanuel@hibernate.org>
*/
public class ModuleValidator {
    private final Context context;
    private List<PhasedUnits> phasedUnitsOfDependencies;
    private final ModuleManager moduleManager;
    private Map<Module, ArtifactResult> searchedArtifacts = new HashMap<Module, ArtifactResult>();

    public static interface ProgressListener {
        void retrievingModuleArtifact(Module module, ArtifactContext artifactContext);
        void resolvingModuleArtifact(Module module, ArtifactResult artifactResult);
    }
    private ProgressListener listener = new ProgressListener() {
        @Override
        public void resolvingModuleArtifact(Module module,
                ArtifactResult artifactResult) {
        }

        @Override
        public void retrievingModuleArtifact(Module module,
                ArtifactContext artifactContext) {
        }
    };
   
    public ModuleValidator(Context context, PhasedUnits phasedUnits) {
        this.context = context;
        this.moduleManager = phasedUnits.getModuleManager();
    }

    public void setListener (ProgressListener listener) {
        this.listener = listener;
    }

    public List<PhasedUnits> getPhasedUnitsOfDependencies() {
        return phasedUnitsOfDependencies;
    }

    /**
     *  At this stage we need to
     *  - resolve all non local modules (recursively)
     *  - build the object model of these compiled modules
     *  - declare a missing module as an error
     *  - detect circular dependencies
     *  - detect module version conflicts
     */
    public void verifyModuleDependencyTree() {
        phasedUnitsOfDependencies = new ArrayList<PhasedUnits>();
        LinkedList<Module> dependencyTree = new LinkedList<Module>();
        // only verify modules we compile (and default/language), as that makes us traverse their dependencies anyways
        Set<Module> compiledModules = moduleManager.getCompiledModules();
        List<Module> modules = new ArrayList<Module>(compiledModules.size()+2);
        // we must resolve the language module first because it contains definitions that must be in the classpath
        // before any other JVM class is loaded, including the module descriptor annotations themselves
        modules.add(context.getModules().getLanguageModule());
        modules.add(context.getModules().getDefaultModule());
        modules.addAll(compiledModules);
        for (Module module : modules) {
            dependencyTree.addLast(module);
            //we don't care about propagated dependency here as top modules are independent from one another
            verifyModuleDependencyTree(module.getImports(), dependencyTree, new ArrayList<Module>(), ImportDepth.First, searchedArtifacts);
            dependencyTree.pollLast();
        }
        moduleManager.addImplicitImports();
        executeExternalModulePhases();
    }

    public final long numberOfModulesNotAlreadySearched() {
        long result = 0;
        for (Module m : context.getModules().getListOfModules()) {
            if (! m.isAvailable() && !searchedArtifacts.containsKey(m)) {
                result ++;
            }
        }
        return result;
    }
   
    public final long numberOfModulesAlreadySearched() {
        return searchedArtifacts.size();
    }

    /**
     * Used to represent import links with compiled modules
     */
    private enum ImportDepth {
        /**
         * Represents a module directly imported by a compiled module
         */
        First {
            @Override
            public ImportDepth forModuleImport(ModuleImport moduleImport) {
                return Required;
            }

            @Override
            public boolean isVisibleToCompiledModules() {
                return true;
            }
        },
        /**
         * Represents a module indirectly imported by a compiled module, through a chain of exported/shared
         * modules imported by a module imported directly by a compiled module
         */
        Required {
            @Override
            public ImportDepth forModuleImport(ModuleImport moduleImport) {
                return moduleImport.isExport() ? Required : Transitive;
            }

            @Override
            public boolean isVisibleToCompiledModules() {
                return true;
            }
        },
        /**
         * Represents a module indirectly imported by a compiled module, but not visible to it because
         * it was not shared/exported to it
         */
        Transitive {
            @Override
            public ImportDepth forModuleImport(ModuleImport moduleImport) {
                return Transitive;
            }

            @Override
            public boolean isVisibleToCompiledModules() {
                return false;
            }
        };

        /**
         * Returns a new ImportDepth for the given module import
         */
        public abstract ImportDepth forModuleImport(ModuleImport moduleImport);

        /**
         * Returns true if this import is visible to the compiled modules
         */
        public abstract boolean isVisibleToCompiledModules();
    }
   
    private void verifyModuleDependencyTree(
            Collection<ModuleImport> moduleImports,
            LinkedList<Module> dependencyTree,
            List<Module> propagatedDependencies,
            ImportDepth importDepth,
            Map<Module, ArtifactResult> alreadySearchedArtifacts) {
        List<Module> visibleDependencies = new ArrayList<Module>();
        visibleDependencies.add(dependencyTree.getLast()); //first addition => no possible conflict
        for (ModuleImport moduleImport : moduleImports) {
            Module module = moduleImport.getModule();
            if (moduleManager.findModule(module, dependencyTree, true) != null) {
                //circular dependency: stop right here
                return;
            }
            Iterable<String> searchedArtifactExtensions = moduleManager.getSearchedArtifactExtensions();
            ImportDepth newImportDepth = importDepth.forModuleImport(moduleImport);
           
            boolean forCompiledModule = newImportDepth.isVisibleToCompiledModules();
            if ( ! module.isAvailable()) {
                ArtifactResult artifact = null;
                if (alreadySearchedArtifacts.containsKey(module)) {
                    artifact = alreadySearchedArtifacts.get(module);
                } else {
                    //try and load the module from the repository
                    RepositoryManager repositoryManager = context.getRepositoryManager();
                    Exception exceptionOnGetArtifact = null;
                    ArtifactContext artifactContext = new ArtifactContext(module.getNameAsString(), module.getVersion(), getArtifactSuffixes(searchedArtifactExtensions));
                    listener.retrievingModuleArtifact(module, artifactContext);
                    try {
                        artifact = repositoryManager.getArtifactResult(artifactContext);
                    } catch (Exception e) {
                        exceptionOnGetArtifact = catchIfPossible(e);
                    }
                    if (artifact == null) {
                        //not there => error
                        ModuleHelper.buildErrorOnMissingArtifact(artifactContext, module, moduleImport, dependencyTree, exceptionOnGetArtifact, moduleManager);
                    }
                    alreadySearchedArtifacts.put(module, artifact);
                }
               
                if (artifact != null) {
                    //parse module units and build module dependency and carry on
                    listener.resolvingModuleArtifact(module, artifact);
                    moduleManager.resolveModule(artifact, module, moduleImport, dependencyTree, phasedUnitsOfDependencies, forCompiledModule);
                }
            }
            moduleManager.visitedModule(module, forCompiledModule);
            dependencyTree.addLast(module);
            List<Module> subModulePropagatedDependencies = new ArrayList<Module>();
            verifyModuleDependencyTree( module.getImports(), dependencyTree, subModulePropagatedDependencies, newImportDepth, alreadySearchedArtifacts);
            //visible dependency += subModule + subModulePropagatedDependencies
            checkAndAddDependency(visibleDependencies, module, dependencyTree);
            for (Module submodule : subModulePropagatedDependencies) {
                checkAndAddDependency(visibleDependencies, submodule, dependencyTree);
            }
            //propagated dependency += if subModule.export then subModule + subModulePropagatedDependencies
            if (moduleImport.isExport()) {
                checkAndAddDependency(propagatedDependencies, module, dependencyTree);
                for (Module submodule : subModulePropagatedDependencies) {
                    checkAndAddDependency(propagatedDependencies, submodule, dependencyTree);
                }
            }
            dependencyTree.pollLast();
        }
    }

    protected Exception catchIfPossible(Exception e) {
        return e;
    }

    private String[] getArtifactSuffixes(Iterable<String> extensions) {
        ArrayList<String> suffixes = new ArrayList<String>();
        for (String ext : extensions) {
            suffixes.add("." + ext);
        }
        return suffixes.toArray(new String[suffixes.size()]);
    }
   
    private void checkAndAddDependency(List<Module> dependencies, Module module, LinkedList<Module> dependencyTree) {
        Module dupe = moduleManager.findModule(module, dependencies, false);
        if (dupe != null && !isSameVersion(module, dupe)) {
            //TODO improve by giving the dependency string leading to these two conflicting modules
            StringBuilder error = new StringBuilder("module (transitively) imports conflicting versions of dependency '");
            error.append(module.getNameAsString()).append("': ");
            String[] versions = VersionComparator.orderVersions(module.getVersion(), dupe.getVersion());
            error.append("version '").append(versions[0]).append("' and version '").append(versions[1]).append("'");
            moduleManager.addErrorToModule(dependencyTree.getFirst(), error.toString());
        }
        else {
            dependencies.add(module);
        }
    }

    private boolean isSameVersion(Module module, Module dupe) {
        if (module == null || dupe == null) return false;
        if (dupe.getVersion() == null) {
            System.err.println("TypeChecker assertion failure: version should not be null in " +
                    "ModuleValidator.isSameVersion. Please report the issue with a test case");
            return false;
        }
        return dupe.getVersion().equals(module.getVersion());
    }

    protected void executeExternalModulePhases() {
        //moduleimport phase already done
        //Already called from within verifyModuleDependencyTree
        for (PhasedUnits units : phasedUnitsOfDependencies) {
            for (PhasedUnit pu : units.getPhasedUnits()) {
                pu.scanDeclarations();
            }
        }
        for (PhasedUnits units : phasedUnitsOfDependencies) {
            for (PhasedUnit pu : units.getPhasedUnits()) {
                pu.scanTypeDeclarations();
            }
        }
        for (PhasedUnits units : phasedUnitsOfDependencies) {
            for (PhasedUnit pu : units.getPhasedUnits()) {
                pu.validateRefinement();
            }
        }
    }
}
TOP

Related Classes of com.redhat.ceylon.compiler.typechecker.analyzer.ModuleValidator$ProgressListener

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.