blob: e9ce9935949f988fd8aa26e8bf0219604a008b9e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2017 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.compiler.batch;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipFile;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.env.IModule;
import org.eclipse.jdt.internal.compiler.env.PackageExportImpl;
import org.eclipse.jdt.internal.compiler.parser.Parser;
import org.eclipse.jdt.internal.compiler.util.Util;
public class ModuleFinder {
protected static List<FileSystem.Classpath> findModules(File f, String destinationPath, Parser parser, Map<String, String> options, boolean isModulepath) {
List<FileSystem.Classpath> collector = new ArrayList<>();
if (f.isDirectory()) {
File[] files = f.listFiles();
if (files == null)
return Collections.EMPTY_LIST;
for (final File file : files) {
Classpath modulePath = findModule(file, destinationPath, parser, options, isModulepath);
if (modulePath != null)
collector.add(modulePath);
}
}
return collector;
}
protected static FileSystem.Classpath findModule(final File file, String destinationPath, Parser parser, Map<String, String> options, boolean isModulepath) {
FileSystem.Classpath modulePath = FileSystem.getClasspath(
file.getAbsolutePath(),
null,
!isModulepath,
null,
destinationPath == null ? null : (destinationPath + File.separator + file.getName()),
options);
if (modulePath != null) {
scanForModule(modulePath, file, parser, isModulepath);
}
return modulePath;
}
protected static IModule scanForModule(FileSystem.Classpath modulePath, final File file, Parser parser, boolean isModulepath) {
IModule module = null;
if (file.isDirectory()) {
String[] list = file.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
if (dir == file && (name.equalsIgnoreCase(IModule.MODULE_INFO_CLASS)
|| name.equalsIgnoreCase(IModule.MODULE_INFO_JAVA))) {
return true;
}
return false;
}
});
if (list.length > 0) {
String fileName = list[0];
switch (fileName) {
case IModule.MODULE_INFO_CLASS:
module = ModuleFinder.extractModuleFromClass(new File(file, fileName), modulePath);
break;
case IModule.MODULE_INFO_JAVA:
module = ModuleFinder.extractModuleFromSource(new File(file, fileName), parser, modulePath);
String modName = new String(module.name());
if (!modName.equals(file.getName())) {
throw new IllegalArgumentException("module name " + modName + " does not match expected name " + file.getName()); //$NON-NLS-1$ //$NON-NLS-2$
}
break;
}
}
} else if (isJar(file)) {
module = extractModuleFromJar(file, modulePath);
}
if (isModulepath && module == null && !(modulePath instanceof ClasspathJrt)) {
module = IModule.createAutomatic(getFileName(file), file.isFile(), getManifest(file));
}
if (module != null)
modulePath.acceptModule(module);
return module;
}
private static Manifest getManifest(File file) {
if (!isJar(file))
return null;
try (JarFile jar = new JarFile(file)) {
return jar.getManifest();
} catch (IOException e) {
return null;
}
}
private static String getFileName(File file) {
String name = file.getName();
int index = name.lastIndexOf('.');
if (index == -1)
return name;
return name.substring(0, index);
}
/**
* Extracts the single reads clause from the given
* command line option (--add-reads). The result is a String[] with two
* element, first being the source module and second being the target module.
* The expected format is:
* --add-reads <source-module>=<target-module>
* @param option
* @return a String[] with source and target module of the "reads" clause.
*/
protected static String[] extractAddonRead(String option) {
StringTokenizer tokenizer = new StringTokenizer(option, "="); //$NON-NLS-1$
String source = null;
String target = null;
if (tokenizer.hasMoreTokens()) {
source = tokenizer.nextToken();
} else {
// Handle error
return null;
}
if (tokenizer.hasMoreTokens()) {
target = tokenizer.nextToken();
} else {
// Handle error
return null;
}
return new String[]{source, target};
}
/**
* Parses the --add-exports command line option and returns the package export definitions
* in the form of an IModule. Note the IModule returned only holds this specific exports-to
* clause and can't by itself be used as a module description.
*
* The expected format is:
* --add-exports <source-module>/<package>=<target-module>(,<target-module>)*
* @param option
* @return a dummy module object with package exports
*/
protected static IModule extractAddonExport(String option) {
StringTokenizer tokenizer = new StringTokenizer(option, "/"); //$NON-NLS-1$
String source = null;
String pack = null;
List<String> targets = new ArrayList<>();
if (tokenizer.hasMoreTokens()) {
source = tokenizer.nextToken("/"); //$NON-NLS-1$
} else {
// Handle error
return null;
}
if (tokenizer.hasMoreTokens()) {
pack = tokenizer.nextToken("/="); //$NON-NLS-1$
} else {
// Handle error
return null;
}
while (tokenizer.hasMoreTokens()) {
targets.add(tokenizer.nextToken("=,")); //$NON-NLS-1$
}
PackageExportImpl export = new PackageExportImpl();
export.pack = pack.toCharArray();
export.exportedTo = new char[targets.size()][];
for(int i = 0; i < export.exportedTo.length; i++) {
export.exportedTo[i] = targets.get(i).toCharArray();
}
BasicModule module = new BasicModule(source.toCharArray(), false);
module.exports = new IModule.IPackageExport[]{export};
return module;
}
private static boolean isJar(File file) {
int format = Util.archiveFormat(file.getAbsolutePath());
return format >= Util.ZIP_FILE;
}
private static IModule extractModuleFromJar(File file, Classpath pathEntry) {
ZipFile zipFile = null;
try {
zipFile = new ZipFile(file);
ClassFileReader reader = ClassFileReader.read(zipFile, IModule.MODULE_INFO_CLASS);
IModule module = getModule(reader);
if (module != null) {
return reader.getModuleDeclaration();
}
return null;
} catch (ClassFormatException | IOException e) {
e.printStackTrace();
} finally {
if (zipFile != null) {
try {
zipFile.close();
} catch (IOException e) {
// Nothing much to do here
}
}
}
return null;
}
private static IModule extractModuleFromClass(File classfilePath, Classpath pathEntry) {
ClassFileReader reader;
try {
reader = ClassFileReader.read(classfilePath);
IModule module = getModule(reader);
if (module != null) {
return reader.getModuleDeclaration();
}
return null;
} catch (ClassFormatException | IOException e) {
e.printStackTrace();
}
return null;
}
private static IModule getModule(ClassFileReader classfile) {
if (classfile != null) {
return classfile.getModuleDeclaration();
}
return null;
}
private static IModule extractModuleFromSource(File file, Parser parser, Classpath pathEntry) {
ICompilationUnit cu = new CompilationUnit(null, file.getAbsolutePath(), null);
CompilationResult compilationResult = new CompilationResult(cu, 0, 1, 10);
CompilationUnitDeclaration unit = parser.parse(cu, compilationResult);
if (unit.isModuleInfo() && unit.moduleDeclaration != null) {
return new BasicModule(unit.moduleDeclaration, pathEntry);
}
return null;
}
}