package org.eclipse.jdt.internal.core.builder;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.env.AccessRuleSet;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.env.IModule;
import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
import org.eclipse.jdt.internal.compiler.util.JRTUtil;
import org.eclipse.jdt.internal.compiler.util.SimpleSet;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
import org.eclipse.jdt.internal.core.util.Util;
public class ClasspathJrtWithReleaseOption extends ClasspathJrt {
static String MODULE_INFO = "module-info.sig";
final String release;
String releaseInHex;
private String[] subReleases;
private java.nio.file.FileSystem fs;
protected Path releasePath;
protected Path modulePath;
private String modPathString;
private boolean isJRE12Plus;
public ClasspathJrtWithReleaseOption(String zipFilename, AccessRuleSet accessRuleSet, IPath externalAnnotationPath,
String release) throws CoreException {
super();
if (release == null || release.equals("")) {
throw new IllegalArgumentException("--release argument can not be null");
}
this.zipFilename = zipFilename;
this.accessRuleSet = accessRuleSet;
if (externalAnnotationPath != null)
this.externalAnnotationPath = externalAnnotationPath.toString();
this.release = getReleaseOptionFromCompliance(release);
initialize();
loadModules(this);
}
private String getReleaseOptionFromCompliance(String comp) {
if (JavaCore.compareJavaVersions(comp, JavaCore.VERSION_1_5) <= 0) {
return "6";
}
int index = comp.indexOf("1.");
if (index != -1) {
return comp.substring(index + 2, comp.length());
} else {
return comp;
}
}
private boolean isJRE12Plus(Path path) {
try (DirectoryStream<java.nio.file.Path> stream = Files.newDirectoryStream(path)) {
for (final java.nio.file.Path subdir : stream) {
String rel = JRTUtil.sanitizedFileName(subdir);
if (Files.exists(this.fs.getPath(rel, "system-modules"))) {
int parseInt = Integer.parseInt(rel, 16);
return (parseInt > 11);
}
}
} catch (IOException e) {
this.fs = null;
}
return false;
}
protected void initialize() throws CoreException {
this.releaseInHex = Integer.toHexString(Integer.parseInt(this.release)).toUpperCase();
Path lib = Paths.get(this.zipFilename).getParent();
Path filePath = Paths.get(lib.toString(), "ct.sym");
URI t = filePath.toUri();
if (!Files.exists(filePath)) {
return;
}
URI uri = URI.create("jar:file:" + t.getRawPath());
try {
this.fs = FileSystems.getFileSystem(uri);
} catch (FileSystemNotFoundException fne) {
}
if (this.fs == null) {
HashMap<String, ?> env = new HashMap<>();
try {
this.fs = FileSystems.newFileSystem(uri, env);
} catch (IOException e) {
return;
}
}
this.releasePath = this.fs.getPath("/");
this.isJRE12Plus = isJRE12Plus(this.releasePath);
Path modPath = this.fs.getPath(this.releaseInHex + (this.isJRE12Plus ? "" : "-modules"));
if (Files.exists(modPath)) {
this.modulePath = modPath;
this.modPathString = this.zipFilename + "|"+ modPath.toString();
}
if (!Files.exists(this.releasePath.resolve(this.releaseInHex))) {
Exception e = new IllegalArgumentException("release " + this.release + " is not found in the system");
throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, e.getMessage(), e));
}
if (Files.exists(this.fs.getPath(this.releaseInHex, "system-modules"))) {
this.fs = null;
return;
}
if (this.release != null) {
List<String> sub = new ArrayList<>();
try (DirectoryStream<java.nio.file.Path> stream = Files.newDirectoryStream(this.releasePath)) {
for (final java.nio.file.Path subdir : stream) {
String rel = JRTUtil.sanitizedFileName(subdir);
if (rel.contains(this.releaseInHex)) {
sub.add(rel);
} else {
continue;
}
}
} catch (IOException e) {
this.fs = null;
}
this.subReleases = sub.toArray(new String[sub.size()]);
}
}
static HashMap<String, SimpleSet> findPackagesInModules(final ClasspathJrtWithReleaseOption jrt) {
if (jrt.fs == null || !jrt.isJRE12Plus) {
return ClasspathJrt.findPackagesInModules(jrt);
}
String zipFileName = jrt.zipFilename;
HashMap<String, SimpleSet> cache = PackageCache.get(jrt.modPathString);
if (cache != null) {
return cache;
}
final HashMap<String, SimpleSet> packagesInModule = new HashMap<>();
PackageCache.put(jrt.modPathString, packagesInModule);
try {
final File imageFile = new File(zipFileName);
org.eclipse.jdt.internal.compiler.util.JRTUtil.walkModuleImage(imageFile, jrt.release,
new org.eclipse.jdt.internal.compiler.util.JRTUtil.JrtFileVisitor<Path>() {
SimpleSet packageSet = null;
@Override
public FileVisitResult visitPackage(Path dir, Path mod, BasicFileAttributes attrs)
throws IOException {
ClasspathJar.addToPackageSet(this.packageSet, dir.toString(), true);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, Path mod, BasicFileAttributes attrs)
throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitModule(Path path, String name) throws IOException {
this.packageSet = new SimpleSet(41);
this.packageSet.add("");
if (name.endsWith("/")) {
name = name.substring(0, name.length() - 1);
}
packagesInModule.put(name, this.packageSet);
return FileVisitResult.CONTINUE;
}
}, JRTUtil.NOTIFY_PACKAGES | JRTUtil.NOTIFY_MODULES);
} catch (IOException e) {
}
return packagesInModule;
}
public static void loadModules(final ClasspathJrtWithReleaseOption jrt) {
if (jrt.fs == null || !jrt.isJRE12Plus) {
ClasspathJrt.loadModules(jrt);
return;
}
if (jrt.modPathString == null)
return;
Set<IModule> cache = ModulesCache.get(jrt.modPathString);
if (cache == null) {
try (DirectoryStream<java.nio.file.Path> stream = Files.newDirectoryStream(jrt.releasePath)) {
for (final java.nio.file.Path subdir : stream) {
if (!subdir.getFileName().toString().contains(jrt.releaseInHex)) {
continue;
}
Files.walkFileTree(subdir, Collections.EMPTY_SET, 2, new FileVisitor<java.nio.file.Path>() {
@Override
public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs)
throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(java.nio.file.Path f, BasicFileAttributes attrs)
throws IOException {
if (attrs.isDirectory() || f.getNameCount() < 3) {
return FileVisitResult.CONTINUE;
}
if (f.getFileName().toString().equals(MODULE_INFO)) {
byte[] content = JRTUtil.safeReadBytes(f);
if (content == null)
return FileVisitResult.CONTINUE;
jrt.acceptModule(content);
}
return FileVisitResult.SKIP_SIBLINGS;
}
@Override
public FileVisitResult visitFileFailed(java.nio.file.Path f, IOException exc)
throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(java.nio.file.Path dir, IOException exc)
throws IOException {
return FileVisitResult.CONTINUE;
}
});
}
} catch (IOException e) {
}
}
}
@Override
public NameEnvironmentAnswer findClass(String binaryFileName, String qualifiedPackageName, String moduleName,
String qualifiedBinaryFileName, boolean asBinaryOnly, Predicate<String> moduleNameFilter) {
if (this.fs == null) {
return super.findClass(binaryFileName, qualifiedPackageName, moduleName, qualifiedBinaryFileName,
asBinaryOnly, moduleNameFilter);
}
if (!isPackage(qualifiedPackageName, moduleName))
return null;
try {
IBinaryType reader = null;
byte[] content = null;
String fileNameWithoutExtension = qualifiedBinaryFileName.substring(0,
qualifiedBinaryFileName.length() - SuffixConstants.SUFFIX_CLASS.length);
if (this.subReleases != null && this.subReleases.length > 0) {
qualifiedBinaryFileName = qualifiedBinaryFileName.replace(".class", ".sig");
outer: for (String rel : this.subReleases) {
Path p = null;
inner: if (this.isJRE12Plus) {
if (moduleName != null) {
p = this.fs.getPath(rel, moduleName, qualifiedBinaryFileName);
}
else {
try (DirectoryStream<java.nio.file.Path> stream = Files
.newDirectoryStream(this.fs.getPath(rel))) {
for (final java.nio.file.Path subdir : stream) {
p = subdir.resolve(qualifiedBinaryFileName);
if (Files.exists(p)) {
if (subdir.getNameCount() == 2 ) {
moduleName = subdir.getName(1).toString();
}
break inner;
}
}
}
}
} else {
p = this.fs.getPath(rel, qualifiedBinaryFileName);
}
if (Files.exists(p)) {
content = JRTUtil.safeReadBytes(p);
if (content != null) {
reader = new ClassFileReader(content, qualifiedBinaryFileName.toCharArray());
if (moduleName != null)
((ClassFileReader) reader).moduleName = moduleName.toCharArray();
break outer;
}
}
}
} else {
reader = ClassFileReader.readFromModule(new File(this.zipFilename), moduleName, qualifiedBinaryFileName,
moduleNameFilter);
}
return createAnswer(fileNameWithoutExtension, reader);
} catch (ClassFormatException | IOException e) {
}
return null;
}
@Override
public Collection<String> getModuleNames(Collection<String> limitModules) {
HashMap<String, SimpleSet> cache = findPackagesInModules(this);
if (cache != null)
return selectModules(cache.keySet(), limitModules);
return Collections.emptyList();
}
@Override
public void cleanup() {
try {
super.reset();
} finally {
this.fs = null;
}
}
@Override
public boolean hasModule() {
return this.fs == null ? super.hasModule() : this.modPathString != null;
}
@Override
protected String getKey() {
return this.fs == null ? super.getKey() : this.modPathString;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof ClasspathJrtWithReleaseOption))
return false;
ClasspathJrtWithReleaseOption jar = (ClasspathJrtWithReleaseOption) o;
if (!Util.equalOrNull(this.release, jar.release)) {
return false;
}
return super.equals(o);
}
@Override
public int hashCode() {
int hash = this.zipFilename == null ? super.hashCode() : this.zipFilename.hashCode();
return Util.combineHashCodes(hash, this.release.hashCode());
}
@Override
public String toString() {
String start = "Classpath jrt file " + this.zipFilename + " with --release option " + this.release;
return start;
}
}