blob: cea35c706397c1c9f1de7222758d8ee2ecf4b69a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 IBM Corporation and others.
* 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.builder;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.zip.ZipFile;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.env.IModule;
import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
import org.eclipse.jdt.internal.compiler.lookup.ModuleEnvironment;
import org.eclipse.jdt.internal.compiler.util.JRTUtil;
import org.eclipse.jdt.internal.compiler.util.SimpleSet;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
public class ClasspathJrt extends ClasspathLocation {
private HashMap<String, SimpleSet> packagesInModule = null;
private static HashMap<String, HashMap<String, SimpleSet>> PackageCache = new HashMap<>();
private static HashMap<String, Set<IModule>> ModulesCache = new HashMap<>();
INameEnvironment env = null;
private String externalAnnotationPath;
private ZipFile annotationZipFile;
String zipFilename; // keep for equals
public ClasspathJrt(String zipFilename, IPath externalAnnotationPath, INameEnvironment env) {
this.zipFilename = zipFilename;
this.env = env;
if (externalAnnotationPath != null)
this.externalAnnotationPath = externalAnnotationPath.toString();
loadModules(this);
}
/**
* Calculate and cache the package list available in the zipFile.
* @param jrt The ClasspathJar to use
* @return A SimpleSet with the all the package names in the zipFile.
*/
static HashMap<String, SimpleSet> findPackagesInModules(final ClasspathJrt jrt) {
String zipFileName = jrt.zipFilename;
HashMap<String, SimpleSet> cache = PackageCache.get(zipFileName);
if (cache != null) {
return cache;
}
final HashMap<String, SimpleSet> packagesInModule = new HashMap<>();
PackageCache.put(zipFileName, packagesInModule);
try {
final File imageFile = new File(zipFileName);
org.eclipse.jdt.internal.compiler.util.JRTUtil.walkModuleImage(imageFile,
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 mod) throws IOException {
String name = mod.toString();
try {
jrt.acceptModule(JRTUtil.getClassfileContent(imageFile, MODULE_INFO_CLASS, name));
} catch (ClassFormatException e) {
e.printStackTrace();
}
this.packageSet = new SimpleSet(41);
this.packageSet.add(""); //$NON-NLS-1$
packagesInModule.put(name, this.packageSet);
return FileVisitResult.CONTINUE;
}
}, JRTUtil.NOTIFY_PACKAGES | JRTUtil.NOTIFY_MODULES);
} catch (IOException e) {
// TODO: BETA_JAVA9 Should report better
}
return packagesInModule;
}
public static void loadModules(final ClasspathJrt jrt) {
String zipFileName = jrt.zipFilename;
Set<IModule> cache = ModulesCache.get(zipFileName);
if (cache == null) {
try {
final File imageFile = new File(zipFileName);
org.eclipse.jdt.internal.compiler.util.JRTUtil.walkModuleImage(imageFile,
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 mod) throws IOException {
try {
jrt.acceptModule(JRTUtil.getClassfileContent(imageFile, MODULE_INFO_CLASS, mod.toString()));
} catch (ClassFormatException e) {
e.printStackTrace();
}
return FileVisitResult.SKIP_SUBTREE;
}
}, JRTUtil.NOTIFY_MODULES);
} catch (IOException e) {
// TODO: BETA_JAVA9 Should report better
}
} else {
// for (IModule iModule : cache) {
// jimage.env.acceptModule(iModule, jimage);
// }
}
}
void acceptModule(byte[] content) {
if (content == null)
return;
ClassFileReader reader = null;
try {
reader = new ClassFileReader(content, MODULE_INFO_CLASS.toCharArray(), null);
} catch (ClassFormatException e) {
e.printStackTrace();
}
if (reader != null) {
IModule moduleDecl = reader.getModuleDeclaration();
if (moduleDecl != null) {
Set<IModule> cache = ModulesCache.get(this.zipFilename);
if (cache == null) {
ModulesCache.put(this.zipFilename, cache = new HashSet<IModule>());
}
cache.add(moduleDecl);
//this.env.acceptModule(moduleDecl, this);
}
}
}
public void cleanup() {
if (this.annotationZipFile != null) {
try {
this.annotationZipFile.close();
} catch(IOException e) { // ignore it
}
this.annotationZipFile = null;
}
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ClasspathJrt)) return false;
ClasspathJrt jar = (ClasspathJrt) o;
return this.zipFilename.endsWith(jar.zipFilename);
}
public NameEnvironmentAnswer findClass(String binaryFileName, String qualifiedPackageName, String qualifiedBinaryFileName, boolean asBinaryOnly, IModule mod) {
return findClass(binaryFileName, qualifiedPackageName, qualifiedBinaryFileName, mod);
}
public NameEnvironmentAnswer findClass(String binaryFileName, String qualifiedPackageName, String qualifiedBinaryFileName, IModule mod) {
if (!isPackage(qualifiedPackageName)) return null; // most common case
try {
ClassFileReader reader = ClassFileReader.readFromJrt(new File(this.zipFilename), qualifiedBinaryFileName, mod);
if (reader != null) {
if (this.externalAnnotationPath != null) {
String fileNameWithoutExtension = qualifiedBinaryFileName.substring(0, qualifiedBinaryFileName.length() - SuffixConstants.SUFFIX_CLASS.length);
try {
this.annotationZipFile = reader.setExternalAnnotationProvider(this.externalAnnotationPath, fileNameWithoutExtension, this.annotationZipFile, null);
} catch (IOException e) {
// don't let error on annotations fail class reading
}
}
return new NameEnvironmentAnswer(reader, null);
}
} catch (IOException e) { // treat as if class file is missing
} catch (ClassFormatException e) { // treat as if class file is missing
}
return null;
}
public IPath getProjectRelativePath() {
return null;
}
public int hashCode() {
return this.zipFilename == null ? super.hashCode() : this.zipFilename.hashCode();
}
public boolean isPackage(String qualifiedPackageName) {
try {
synchronized (this) {
if (this.packagesInModule == null) {
this.packagesInModule = findPackagesInModules(this);
}
}
} catch(Exception e) {
// TODO BETA_JAVA9
}
Set<String> keySet = this.packagesInModule.keySet();
for (String string : keySet) {
SimpleSet set = this.packagesInModule.get(string);
if (set.includes(qualifiedPackageName)) return true;
}
return false;
}
public String toString() {
String start = "Classpath jrt file " + this.zipFilename; //$NON-NLS-1$
return start;
}
public String debugPathString() {
return this.zipFilename;
}
@Override
public boolean servesModule(IModule mod) {
if (mod == null)
return false;
synchronized (this) {
if (this.packagesInModule == null) {
this.packagesInModule = findPackagesInModules(this);
}
}
if (mod == ModuleEnvironment.UNNAMED_MODULE)
return true;
if (this.packagesInModule.containsKey(new String(mod.name()))) {
return true;
}
return false;
}
@Override
public IModule getModule(char[] moduleName) {
//
Set<IModule> modules = ModulesCache.get(this.zipFilename);
if (modules != null) {
for (IModule mod : modules) {
if (CharOperation.equals(mod.name(), moduleName))
return mod;
}
}
return null;
}
}