package com.redhat.ceylon.tools.moduleloading; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; import com.redhat.ceylon.cmr.api.ArtifactContext; import com.redhat.ceylon.cmr.api.ModuleQuery; import com.redhat.ceylon.cmr.api.RepositoryManager; import com.redhat.ceylon.cmr.api.VersionComparator; import com.redhat.ceylon.cmr.ceylon.RepoUsingTool; import com.redhat.ceylon.common.Messages; import com.redhat.ceylon.common.ModuleUtil; import com.redhat.ceylon.common.Versions; import com.redhat.ceylon.common.tool.ToolUsageError; import com.redhat.ceylon.model.cmr.ArtifactResult; import com.redhat.ceylon.model.cmr.ImportType; import com.redhat.ceylon.model.cmr.JDKUtils; import com.redhat.ceylon.model.typechecker.model.Module; public abstract class ModuleLoadingTool extends RepoUsingTool { protected Map<String, ArtifactResult> loadedModules = new HashMap<>(); protected Map<String, SortedSet<String>> loadedModuleVersions = new HashMap<>(); public ModuleLoadingTool() { super(ModuleLoadingMessages.RESOURCE_BUNDLE); } protected String moduleVersion(String moduleNameOptVersion) throws IOException { return checkModuleVersionsOrShowSuggestions( getRepositoryManager(), ModuleUtil.moduleName(moduleNameOptVersion), ModuleUtil.moduleVersion(moduleNameOptVersion), ModuleQuery.Type.JVM, Versions.JVM_BINARY_MAJOR_VERSION, Versions.JVM_BINARY_MINOR_VERSION); } protected boolean loadModule(String moduleName, String moduleVersion) throws IOException { return loadModule(moduleName, moduleVersion, false); } protected boolean loadModule(String moduleName, String moduleVersion, boolean optional) throws IOException { boolean success = false; if (moduleVersion != null) { success = true; success &= internalLoadModule(Module.LANGUAGE_MODULE_NAME, Versions.CEYLON_VERSION_NUMBER, false); success &= internalLoadModule("com.redhat.ceylon.compiler.java", Versions.CEYLON_VERSION_NUMBER, false); success &= internalLoadModule("com.redhat.ceylon.common", Versions.CEYLON_VERSION_NUMBER, false); success &= internalLoadModule("com.redhat.ceylon.model", Versions.CEYLON_VERSION_NUMBER, false); success &= internalLoadModule("com.redhat.ceylon.module-resolver", Versions.CEYLON_VERSION_NUMBER, false); success &= internalLoadModule("com.redhat.ceylon.typechecker", Versions.CEYLON_VERSION_NUMBER, false); success &= internalLoadModule(moduleName, moduleVersion, false); } return success; } protected boolean shouldExclude(String moduleName) { return JDKUtils.isJDKModule(moduleName) || JDKUtils.isOracleJDKModule(moduleName); } private boolean internalLoadModule(String name, String version, boolean optional) throws IOException { String key = name + "/" + version; if(loadedModules.containsKey(key)) return true; if(shouldExclude(name)) { // let's not check the version and assume it's provided // treat it as a missing optional for the purpose of classpath loadedModules.put(key, null); return true; } // remember which version we loaded SortedSet<String> loadedVersions = loadedModuleVersions.get(name); if(loadedVersions == null){ loadedVersions = new TreeSet<>(VersionComparator.INSTANCE); loadedModuleVersions.put(name, loadedVersions); } loadedVersions.add(version); RepositoryManager repositoryManager = getRepositoryManager(); ArtifactContext artifactContext = new ArtifactContext(name, version, ArtifactContext.CAR, ArtifactContext.JAR); ArtifactResult result = repositoryManager.getArtifactResult(artifactContext); if(!optional && (result == null || result.artifact() == null || !result.artifact().exists())){ String err = getModuleNotFoundErrorMessage(repositoryManager, name, version); errorAppend(err); errorNewline(); return false; } // save even missing optional modules as nulls to not re-resolve them loadedModules.put(key, result); if(result != null){ for(ArtifactResult dep : result.dependencies()){ internalLoadModule(dep.name(), dep.version(), dep.importType() == ImportType.OPTIONAL); } } return true; } protected void errorOnConflictingModule(String module, String version) throws IOException{ boolean duplicate = false; for(Map.Entry<String, SortedSet<String>> entry : loadedModuleVersions.entrySet()){ if(entry.getValue().size() > 1){ duplicate = true; printDuplicateModuleErrorMessage(entry.getKey(), entry.getValue()); } } if(duplicate) throw new ToolUsageError(Messages.msg(bundle, "module.conflict.error", module, version)); } private void printDuplicateModuleErrorMessage(String name, SortedSet<String> versions) throws IOException { StringBuilder err = new StringBuilder(); boolean first = true; for(String version : versions){ if(first) first = false; else err.append(", "); err.append(version); } errorMsg("module.duplicate.error", name, err, versions.last()); } }