| /******************************************************************************* |
| * Copyright (c) 2016 IBM Corporation. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * This is an implementation of an early-draft specification developed under the Java |
| * Community Process (JCP) and is made available for testing and evaluation purposes |
| * only. The code is not compatible with any specification of the JCP. |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.core.util; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.ProjectScope; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.preferences.IScopeContext; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IModuleDescription; |
| import org.eclipse.jdt.core.IPackageFragment; |
| import org.eclipse.jdt.core.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.ISourceRange; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.CompilationResult; |
| import org.eclipse.jdt.internal.compiler.Compiler; |
| import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; |
| import org.eclipse.jdt.internal.compiler.ICompilerRequestor; |
| import org.eclipse.jdt.internal.compiler.env.IModuleContext; |
| import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.eclipse.jdt.internal.core.BasicCompilationUnit; |
| import org.eclipse.jdt.internal.core.ModuleRequirement; |
| import org.eclipse.jdt.internal.core.NamedMember; |
| import org.eclipse.jdt.internal.core.OpenableElementInfo; |
| import org.eclipse.jdt.internal.core.PackageExport; |
| import org.eclipse.jdt.internal.core.PackageFragmentRoot; |
| import org.eclipse.jdt.internal.core.SourceRefElement; |
| import org.eclipse.jdt.internal.core.builder.NameEnvironment; |
| import org.eclipse.jdt.internal.core.builder.ProblemFactory; |
| |
| public class ModuleUtil { |
| |
| public static String createModuleFromPackageRoot(String moduleName, IPackageFragmentRoot root) throws CoreException { |
| IJavaProject project = root.getJavaProject(); |
| String lineDelimiter = null; |
| if (project != null) { |
| IScopeContext[] scopeContext; |
| // project preference |
| scopeContext = new IScopeContext[] { new ProjectScope(project.getProject()) }; |
| lineDelimiter = Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null, scopeContext); |
| } |
| if (lineDelimiter == null) { |
| lineDelimiter = System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| LocalModuleImpl module = (LocalModuleImpl) createModuleFromPackageFragmentRoot(moduleName, project); |
| return module.toString(lineDelimiter); |
| } |
| |
| public static IModuleDescription createModuleFromPackageRoot(String moduleName, IJavaProject root) throws CoreException { |
| return createModuleFromPackageFragmentRoot(moduleName, root.getJavaProject()); |
| } |
| |
| static class ModuleAccumulatorEnvironment extends NameEnvironment { |
| public ModuleAccumulatorEnvironment(IJavaProject javaProject) { |
| super(javaProject); |
| } |
| |
| Set<String> modules = new HashSet<>(); |
| public String[] getModules() { |
| String[] mods = new String[this.modules.size()]; |
| return this.modules.toArray(mods); |
| } |
| @Override |
| public org.eclipse.jdt.internal.compiler.env.IModule getModule(char[] name) { |
| return null; |
| } |
| |
| @Override |
| public void cleanup() { |
| this.modules.clear(); |
| } |
| |
| @Override |
| public NameEnvironmentAnswer findType(char[][] compoundTypeName, IModuleContext context) { |
| NameEnvironmentAnswer answer = super.findType(compoundTypeName, context); |
| if (answer.moduleName() != null) { |
| this.modules.add(new String(answer.moduleName())); |
| } |
| return answer; |
| } |
| |
| @Override |
| public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName, IModuleContext context) { |
| NameEnvironmentAnswer answer = super.findType(typeName, packageName, context); |
| if (answer != null && answer.moduleName() != null) { |
| this.modules.add(new String(answer.moduleName())); |
| } |
| return answer; |
| } |
| |
| @Override |
| public boolean isPackage(char[][] parentPackageName, char[] packageName, IModuleContext context) { |
| return super.isPackage(parentPackageName, packageName, context); |
| } |
| } |
| private static Compiler newCompiler(ModuleAccumulatorEnvironment environment, IJavaProject javaProject) { |
| Map<String, String> projectOptions = javaProject.getOptions(true); |
| CompilerOptions compilerOptions = new CompilerOptions(projectOptions); |
| compilerOptions.performMethodsFullRecovery = true; |
| compilerOptions.performStatementsRecovery = true; |
| ICompilerRequestor requestor = new ICompilerRequestor() { |
| @Override |
| public void acceptResult(CompilationResult result) { |
| // Nothing to do here |
| } |
| }; |
| Compiler newCompiler = new Compiler( |
| environment, |
| DefaultErrorHandlingPolicies.proceedWithAllProblems(), |
| compilerOptions, |
| requestor, |
| ProblemFactory.getProblemFactory(Locale.getDefault())); |
| |
| return newCompiler; |
| } |
| private static IModuleDescription createModuleFromPackageFragmentRoot(String moduleName, IJavaProject project) throws CoreException { |
| |
| ModuleAccumulatorEnvironment environment = new ModuleAccumulatorEnvironment(project); |
| Compiler compiler = newCompiler(environment, project); |
| LocalModuleImpl module = new LocalModuleImpl(moduleName == null ? project.getElementName() : moduleName); |
| List<IModuleDescription.IPackageExport> exports = new ArrayList<>(); |
| // First go over the binary roots and see if any of them are modules |
| List<IModuleDescription.IModuleReference> required = new ArrayList<>(); |
| Set<org.eclipse.jdt.internal.compiler.env.ICompilationUnit> toCompile = new HashSet<>(); |
| IPackageFragmentRoot[] roots = project.getPackageFragmentRoots(); |
| for (IPackageFragmentRoot root : roots) { |
| if (root.isArchive()) { |
| PackageFragmentRoot lib = (PackageFragmentRoot) root; |
| IModuleDescription mod = ((OpenableElementInfo) lib.getElementInfo()).getModule(); |
| if (mod != null) { |
| ModuleRequirement ref = new ModuleRequirement(module, mod.getElementName()); |
| required.add(ref); |
| } |
| } |
| if (root.getKind() == IPackageFragmentRoot.K_SOURCE) { |
| IJavaElement[] children = root.getChildren(); |
| for (IJavaElement child : children) { |
| if (child instanceof IPackageFragment) { |
| IPackageFragment fragment = (IPackageFragment) child; |
| if (fragment.isDefaultPackage()) continue; |
| ICompilationUnit[] units = fragment.getCompilationUnits(); |
| if (units.length != 0) { |
| String pack = fragment.getElementName(); |
| exports.add(new PackageExport(module, fragment.getElementName())); |
| for (ICompilationUnit iUnit : units) { |
| org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceFile = |
| new BasicCompilationUnit(iUnit.getSource().toCharArray(), CharOperation.splitOn('.', pack.toCharArray()), iUnit.getPath().toOSString()); |
| toCompile.add(sourceFile); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| org.eclipse.jdt.internal.compiler.env.ICompilationUnit[] sources = new org.eclipse.jdt.internal.compiler.env.ICompilationUnit[toCompile.size()]; |
| toCompile.toArray(sources); |
| compiler.compile(sources); |
| Collections.sort(exports, new Comparator<IModuleDescription.IPackageExport>() { |
| @Override |
| public int compare(IModuleDescription.IPackageExport o1, IModuleDescription.IPackageExport o2) { |
| return o1.getPackageName().compareTo( |
| o2.getPackageName()); |
| } |
| }); |
| IModuleDescription.IPackageExport[] packs = new IModuleDescription.IPackageExport[exports.size()]; |
| packs = exports.toArray(packs); |
| module.setExports(packs); |
| String[] mods = environment.getModules(); |
| for (String string : mods) { |
| required.add(new ModuleRequirement(module, string)); |
| } |
| Collections.sort(required, new Comparator<IModuleDescription.IModuleReference>() { |
| @Override |
| public int compare(IModuleDescription.IModuleReference o1, IModuleDescription.IModuleReference o2) { |
| return new String(o1.getModuleName()).compareTo(new String(o2.getModuleName())); |
| } |
| }); |
| IModuleDescription.IModuleReference[] refs = new IModuleDescription.IModuleReference[required.size()]; |
| refs = required.toArray(refs); |
| module.setRequiredModules(refs); |
| return module; |
| } |
| } |
| class LocalModuleImpl extends NamedMember implements IModuleDescription { |
| IModuleDescription.IPackageExport[] exports = null; |
| IModuleDescription.IModuleReference[] requires = null; |
| IModuleDescription.IProvidedService[] services = null; |
| String[] used = null; |
| LocalModuleImpl(String name) { |
| super(null, name); |
| } |
| @Override |
| public IModuleReference[] getRequiredModules() throws JavaModelException { |
| return this.requires; |
| } |
| public void setRequiredModules(IModuleDescription.IModuleReference[] requires) { |
| this.requires = requires; |
| } |
| @Override |
| public IPackageExport[] getExportedPackages() { |
| return this.exports; |
| } |
| public void setExports(IPackageExport[] exports) { |
| this.exports = exports; |
| } |
| @Override |
| public IProvidedService[] getProvidedServices() { |
| return this.services; |
| } |
| @Override |
| public String[] getUsedServices() { |
| return this.used; |
| } |
| |
| public String toString(String lineDelimiter) { |
| StringBuffer buffer = new StringBuffer(); |
| toStringContent(buffer, lineDelimiter); |
| return buffer.toString(); |
| } |
| protected void toStringContent(StringBuffer buffer, String lineDelimiter) { |
| buffer.append("module "); //$NON-NLS-1$ |
| buffer.append(this.name).append(' '); |
| buffer.append('{').append(lineDelimiter); |
| if (this.exports != null) { |
| for(int i = 0; i < this.exports.length; i++) { |
| buffer.append("\texports "); //$NON-NLS-1$ |
| buffer.append(this.exports[i].toString()); |
| buffer.append(lineDelimiter); |
| } |
| } |
| buffer.append(lineDelimiter); |
| if (this.requires != null) { |
| for(int i = 0; i < this.requires.length; i++) { |
| buffer.append("\trequires "); //$NON-NLS-1$ |
| try { |
| if (this.requires[i].isPublic()) { |
| buffer.append(" public "); //$NON-NLS-1$ |
| } |
| } catch (JavaModelException e) { |
| // Ignore as it is unlikely to get a JME |
| } |
| buffer.append(this.requires[i].getModuleName()); |
| buffer.append(';').append(lineDelimiter); |
| } |
| } |
| buffer.append(lineDelimiter).append('}').toString(); |
| } |
| @Override |
| public int getElementType() { |
| return JAVA_MODULE; |
| } |
| } |
| class LocalModuleReferenceImpl extends SourceRefElement implements IModuleDescription.IModuleReference { |
| String name; |
| boolean isPublic = false; |
| LocalModuleReferenceImpl(char[] name, boolean isPublic) { |
| super(null); |
| this.name = new String(name); |
| this.isPublic = isPublic; |
| } |
| @Override |
| public boolean isPublic() { |
| return this.isPublic; |
| } |
| @Override |
| public int getElementType() { |
| return MODULE_REFERENCE; |
| } |
| @Override |
| public String getModuleName() { |
| return this.name; |
| } |
| @Override |
| public ISourceRange getNameRange() throws JavaModelException { |
| return null; |
| } |
| @Override |
| protected char getHandleMementoDelimiter() { |
| return 0; |
| } |
| |
| } |
| class LocalPackageExportImpl extends SourceRefElement implements IModuleDescription.IPackageExport { |
| private String pkgName; |
| private String[] targets; |
| LocalPackageExportImpl(String pkgName, String[] targets) { |
| super(null); |
| this.pkgName = pkgName; |
| this.targets = targets; |
| } |
| @Override |
| public String getPackageName() { |
| return this.pkgName; |
| } |
| @Override |
| public String[] getTargetModules() { |
| return this.targets; |
| } |
| public String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| buffer.append(this.pkgName); |
| buffer.append(';'); |
| return buffer.toString(); |
| } |
| @Override |
| public int getElementType() { |
| return PACKAGE_EXPORT; |
| } |
| @Override |
| public ISourceRange getNameRange() throws JavaModelException { |
| return null; |
| } |
| @Override |
| protected char getHandleMementoDelimiter() { |
| return 0; |
| } |
| } |