blob: b86abbd3e865fc39cb21ad6ee5c6c65d9f77c8e6 [file] [log] [blame]
package org.eclipse.jdt.internal.core.builder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.function.Predicate;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationDecorator;
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.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding.ExternalAnnotationStatus;
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 ClasspathMultiReleaseJar extends ClasspathJar {
private static final String META_INF_VERSIONS = "META-INF/versions/"; //$NON-NLS-1$
private static final int META_INF_LENGTH = META_INF_VERSIONS.length();
String[] supportedVersions;
ClasspathMultiReleaseJar(IFile resource, AccessRuleSet accessRuleSet, IPath externalAnnotationPath,
boolean isOnModulePath, String compliance) {
super(resource, accessRuleSet, externalAnnotationPath, isOnModulePath);
this.compliance = compliance;
initializeVersions(this);
}
ClasspathMultiReleaseJar(String zipFilename, long lastModified, AccessRuleSet accessRuleSet,
IPath externalAnnotationPath, boolean isOnModulePath, String compliance) {
super(zipFilename, lastModified, accessRuleSet, externalAnnotationPath, isOnModulePath);
this.compliance = compliance;
initializeVersions(this);
}
public ClasspathMultiReleaseJar(ZipFile zipFile, AccessRuleSet accessRuleSet, IPath externalAnnotationPath,
boolean isOnModulePath, String compliance) {
this(zipFile.getName(), accessRuleSet, externalAnnotationPath, isOnModulePath, compliance);
this.zipFile = zipFile;
this.closeZipFileAtEnd = true;
}
public ClasspathMultiReleaseJar(String fileName, AccessRuleSet accessRuleSet, IPath externalAnnotationPath,
boolean isOnModulePath, String compliance) {
this(fileName, 0, accessRuleSet, externalAnnotationPath, isOnModulePath, compliance);
if (externalAnnotationPath != null) {
this.externalAnnotationPath = externalAnnotationPath.toString();
}
}
@Override
IModule initializeModule() {
IModule mod = null;
try (ZipFile file = new ZipFile(this.zipFilename)){
ClassFileReader classfile = null;
try {
for (String path : this.supportedVersions) {
classfile = ClassFileReader.read(file, path.toString() + '/' + IModule.MODULE_INFO_CLASS);
if (classfile != null) {
break;
}
}
} catch (Exception e) {
Util.log(e, "Failed to initialize module for: " + this); //$NON-NLS-1$
// move on to the default
}
if (classfile == null) {
classfile = ClassFileReader.read(file, IModule.MODULE_INFO_CLASS); // FIXME: use jar cache
}
if (classfile != null) {
mod = classfile.getModuleDeclaration();
}
} catch (ClassFormatException | IOException e) {
Util.log(e, "Failed to initialize module for: " + this); //$NON-NLS-1$
}
return mod;
}
private static synchronized void initializeVersions(ClasspathMultiReleaseJar jar) {
if (jar.zipFile == null) {
if (org.eclipse.jdt.internal.core.JavaModelManager.ZIP_ACCESS_VERBOSE) {
System.out.println("(" + Thread.currentThread() + ") [ClasspathMultiReleaseJar.initializeVersions(String)] Creating ZipFile on " + jar.zipFilename); //$NON-NLS-1$ //$NON-NLS-2$
}
try {
jar.zipFile = new ZipFile(jar.zipFilename);
} catch (IOException e) {
return;
}
jar.closeZipFileAtEnd = true;
}
int earliestJavaVersion = ClassFileConstants.MAJOR_VERSION_9;
long latestJDK = CompilerOptions.versionToJdkLevel(jar.compliance);
int latestJavaVer = (int) (latestJDK >> 16);
List<String> versions = new ArrayList<>();
for (int i = latestJavaVer; i >= earliestJavaVersion; i--) {
String name = META_INF_VERSIONS+ (i - 44);
ZipEntry entry = jar.zipFile.getEntry(name);
if (entry != null) {
versions.add(name);
}
}
jar.supportedVersions = versions.toArray(new String[versions.size()]);
}
@Override
protected String readJarContent(final SimpleSet packageSet) {
String modInfo = null;
for (Enumeration<? extends ZipEntry> e = this.zipFile.entries(); e.hasMoreElements(); ) {
String fileName = ((ZipEntry) e.nextElement()).getName();
if (fileName.startsWith(META_INF_VERSIONS) && fileName.length() > META_INF_LENGTH) {
int i = fileName.indexOf('/', META_INF_LENGTH);
fileName = fileName.substring(i + 1);
} else if (fileName.startsWith("META-INF/")) //$NON-NLS-1$
continue;
if (modInfo == null) {
int folderEnd = fileName.lastIndexOf('/');
folderEnd += 1;
String className = fileName.substring(folderEnd, fileName.length());
if (className.equalsIgnoreCase(IModule.MODULE_INFO_CLASS)) {
modInfo = fileName;
}
}
addToPackageSet(packageSet, fileName, false);
}
return modInfo;
}
@Override
public NameEnvironmentAnswer findClass(String binaryFileName, String qualifiedPackageName, String moduleName,
String qualifiedBinaryFileName, boolean asBinaryOnly, Predicate<String> moduleNameFilter) {
if (!isPackage(qualifiedPackageName, moduleName)) {
return null; // most common case
}
for (String path : this.supportedVersions) {
String s = null;
try {
s = META_INF_VERSIONS + path + "/" + binaryFileName; //$NON-NLS-1$
ZipEntry entry = this.zipFile.getEntry(s);
if (entry == null)
continue;
IBinaryType reader = ClassFileReader.read(this.zipFile, s);
if (reader != null) {
char[] modName = this.module == null ? null : this.module.name();
if (reader instanceof ClassFileReader) {
ClassFileReader classReader = (ClassFileReader) reader;
if (classReader.moduleName == null) {
classReader.moduleName = modName;
} else {
modName = classReader.moduleName;
}
}
String fileNameWithoutExtension = qualifiedBinaryFileName.substring(0,
qualifiedBinaryFileName.length() - SuffixConstants.SUFFIX_CLASS.length);
if (this.externalAnnotationPath != null) {
try {
if (this.annotationZipFile == null) {
this.annotationZipFile = ExternalAnnotationDecorator
.getAnnotationZipFile(this.externalAnnotationPath, null);
}
reader = ExternalAnnotationDecorator.create(reader, this.externalAnnotationPath,
fileNameWithoutExtension, this.annotationZipFile);
} catch (IOException e) {
// don't let error on annotations fail class reading
}
if (reader.getExternalAnnotationStatus() == ExternalAnnotationStatus.NOT_EEA_CONFIGURED) {
// ensure a reader that answers NO_EEA_FILE
reader = new ExternalAnnotationDecorator(reader, null);
}
}
if (this.accessRuleSet == null) {
return new NameEnvironmentAnswer(reader, null, modName);
}
return new NameEnvironmentAnswer(reader,
this.accessRuleSet.getViolatedRestriction(fileNameWithoutExtension.toCharArray()), modName);
}
} catch (IOException | ClassFormatException e) {
Util.log(e, "Failed to find class for: " + s + " in: " + this); //$NON-NLS-1$ //$NON-NLS-2$
// treat as if class file is missing
}
}
return super.findClass(binaryFileName, qualifiedPackageName, moduleName, qualifiedBinaryFileName, asBinaryOnly,
moduleNameFilter);
}
}