Java tutorial
/* * 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.loader; import static com.redhat.ceylon.compiler.typechecker.model.Util.formatPath; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; 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.ANTLRInputStream; import org.antlr.runtime.CommonToken; import org.antlr.runtime.CommonTokenStream; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; 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.ArtifactResult; import com.redhat.ceylon.cmr.api.JDKUtils; 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.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.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.parser.CeylonLexer; import com.redhat.ceylon.compiler.typechecker.parser.CeylonParser; import com.redhat.ceylon.compiler.typechecker.parser.LexError; import com.redhat.ceylon.compiler.typechecker.parser.ParseError; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.compiler.typechecker.util.ModuleManagerFactory; import com.redhat.ceylon.eclipse.core.model.CeylonSourceFile; /** * @author david * */ public class JDTModuleManager extends LazyModuleManager { private AbstractModelLoader modelLoader; private IJavaProject javaProject; private Set<String> sourceModules; private Set<File> classpath; private TypeChecker typeChecker; public Set<File> getClasspath() { return classpath; } public Set<String> getSourceModules() { return sourceModules; } public IJavaProject getJavaProject() { return javaProject; } public JDTModuleManager(Context context, IJavaProject javaProject) { super(context); this.javaProject = javaProject; sourceModules = new HashSet<String>(); sourceModules.add("ceylon.language"); classpath = new HashSet<File>(); } /* * 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 Module defaultModule = createModule(defaultModuleName, "unversioned"); defaultModule.setDefault(true); defaultModule.setAvailable(true); 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.getImports().add(new ModuleImport(languageModule, false, false)); defaultModule.setLanguageModule(languageModule); getContext().setModules(modules); //build empty package final Package emptyPackage = createPackage("", defaultModule); } super.initCoreModules(); } @Override protected Package createPackage(String pkgName, Module module) { return getModelLoader().findOrCreatePackage(module, pkgName); } @Override public AbstractModelLoader getModelLoader() { if (modelLoader == null) { Modules modules = getContext().getModules(); modelLoader = new JDTModelLoader(this, modules); } return modelLoader; } /** * 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 laoded from their compiled artifact. */ @Override protected boolean isModuleLoadedFromSource(String moduleName) { if (sourceModules.contains(moduleName)) { return true; } if (isModuleLoadedFromCompiledSource(moduleName)) { return true; } return false; } public boolean isModuleLoadedFromCompiledSource(String moduleName) { if (moduleFileInProject(moduleName, javaProject)) { return true; } 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 boolean moduleFileInProject(String moduleName, IJavaProject p) { try { for (IPackageFragmentRoot sourceFolder : p.getPackageFragmentRoots()) { if (sourceFolder.getKind() == IPackageFragmentRoot.K_SOURCE && !sourceFolder.isArchive() && 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 Module createModule(List<String> moduleName, String version) { JDTModule module = null; String moduleNameString = Util.getName(moduleName); List<IPackageFragmentRoot> roots = new ArrayList<IPackageFragmentRoot>(); try { if (moduleNameString.equals(Module.DEFAULT_MODULE_NAME)) { // Add the list of source package fragment roots for (IPackageFragmentRoot root : javaProject.getPackageFragmentRoots()) { IClasspathEntry entry = root.getResolvedClasspathEntry(); if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE && !root.isExternal()) { roots.add(root); } } } else { for (IPackageFragmentRoot root : javaProject.getPackageFragmentRoots()) { 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)) { 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) { 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")); } classpath.add(file); } super.resolveModule(artifact, module, moduleImport, dependencyTree, phasedUnitsOfDependencies, forCompiledModule); } @Override public void prepareForTypeChecking() { getModelLoader().loadStandardModules(); } @Override public Iterable<String> getSearchedArtifactExtensions() { return Arrays.asList("src", "car", "jar"); } public void visitModuleFile() { Package currentPkg = getCurrentPackage(); sourceModules.add(currentPkg.getNameAsString()); super.visitModuleFile(); } private Method addErrorToModuleMethod = null; // Todo : to be suppressed when the base one will be done protected. private void addErrorToModule(List<String> moduleName, String error) { if (addErrorToModuleMethod == null) { try { addErrorToModuleMethod = ModuleManager.class.getDeclaredMethod("addErrorToModule", new Class[] { List.class, String.class }); addErrorToModuleMethod.setAccessible(true); addErrorToModuleMethod.invoke(this, new Object[] { moduleName, error }); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } // 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 void addTwoModulesInHierarchyError(List<String> existingModuleName, List<String> newModulePackageName) { StringBuilder error = new StringBuilder("Found two modules within the same hierarchy: '"); error.append(formatPath(existingModuleName)).append("' and '").append(formatPath(newModulePackageName)) .append("'"); addErrorToModule(existingModuleName, error.toString()); addErrorToModule(newModulePackageName, error.toString()); } @Override protected PhasedUnits createPhasedUnits() { ModuleManagerFactory moduleManagerFactory = new ModuleManagerFactory() { @Override public ModuleManager createModuleManager(Context context) { return JDTModuleManager.this; } }; return new PhasedUnits(getContext(), moduleManagerFactory) { @Override protected void parseFile(VirtualFile file, VirtualFile srcDir) throws Exception { if (file.getName().endsWith(".ceylon")) { CeylonLexer lexer = new CeylonLexer(new ANTLRInputStream(file.getInputStream(), //TODO: is this correct? does this file actually // live in the project, or is it external? // should VirtualFile have a getCharset()? javaProject.getProject().getDefaultCharset())); CommonTokenStream tokenStream = new CommonTokenStream(lexer); CeylonParser parser = new CeylonParser(tokenStream); Tree.CompilationUnit cu = parser.compilationUnit(); List<CommonToken> tokens = new ArrayList<CommonToken>(tokenStream.getTokens().size()); tokens.addAll(tokenStream.getTokens()); PhasedUnit phasedUnit = new CeylonSourceFile(file, srcDir, cu, getModuleManager().getCurrentPackage(), getModuleManager(), getTypeChecker(), tokens); addPhasedUnit(file, phasedUnit); List<LexError> lexerErrors = lexer.getErrors(); for (LexError le : lexerErrors) { cu.addLexError(le); } lexerErrors.clear(); List<ParseError> parserErrors = parser.getErrors(); for (ParseError pe : parserErrors) { cu.addParseError(pe); } parserErrors.clear(); } } }; } public TypeChecker getTypeChecker() { return typeChecker; } public void setTypeChecker(TypeChecker typeChecker) { this.typeChecker = typeChecker; } }