blob: 36ffc041504cef61545c8b5ee344ab1b624b5c70 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 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.compiler.tool;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipException;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.util.JRTUtil;
public class JrtFileSystem extends Archive {
private static URI JRT_URI = URI.create("jrt:/"); //$NON-NLS-1$
static final String BOOT_MODULE = "jrt-fs.jar"; //$NON-NLS-1$
private static final String MODULES_SUBDIR = "/modules"; //$NON-NLS-1$
private static final String DEFAULT_PACKAGE = ""; //$NON-NLS-1$
private Set<String> typesCache = null;
public JrtFileSystem(File file) throws ZipException, IOException {
this.file = file;
initialize();
}
@SuppressWarnings("resource")
private void initialize() throws IOException {
// initialize packages
this.packagesCache = new Hashtable<>();
this.typesCache = new HashSet<>();
if (!this.file.getName().equals(BOOT_MODULE)) return;
java.nio.file.FileSystem fs = FileSystems.getFileSystem(JRT_URI);
Iterable<java.nio.file.Path> roots = fs.getRootDirectories();
for (java.nio.file.Path path : roots) {
try (DirectoryStream<java.nio.file.Path> stream = Files.newDirectoryStream(path)) {
for (final java.nio.file.Path subdir: stream) {
if (subdir.toString().equals(MODULES_SUBDIR)) {
Files.walkFileTree(subdir, new FileVisitor<java.nio.file.Path>() {
@Override
public FileVisitResult preVisitDirectory(java.nio.file.Path entry, BasicFileAttributes attrs)
throws IOException {
int count = entry.getNameCount();
if (count < 2) return FileVisitResult.CONTINUE;
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(java.nio.file.Path entry, BasicFileAttributes attrs) throws IOException {
int count = entry.getNameCount();
if (entry == subdir || count < 3) return FileVisitResult.CONTINUE;
if (count == 3) {
cacheTypes(DEFAULT_PACKAGE, entry.getName(2).toString(), entry.getName(1).toString());
} else {
cacheTypes(entry.subpath(2, count - 1).toString(),
entry.getName(count - 1).toString(), entry.getName(1).toString());
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(java.nio.file.Path entry, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(java.nio.file.Path entry, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
});
}
}
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
}
@Override
public ArchiveFileObject getArchiveFileObject(String fileName, String module, Charset charset) {
return new JrtFileObject(this.file, fileName, module, charset);
}
public ArchiveFileObject getArchiveFileObject(String fileName, Charset charset) {
return new JrtFileObject(this.file, fileName, null, charset);
}
@Override
public boolean contains(String entryName) {
return this.typesCache.contains(entryName);
}
protected void cacheTypes(String packageName, String typeName, String module) {
int length = packageName.length();
if (length > 0 && packageName.charAt(packageName.length() - 1) != '/') {
packageName = packageName + '/';
}
ArrayList<String[]> types = this.packagesCache.get(packageName);
if (typeName == null) return;
if (types == null) {
types = new ArrayList<>();
types.add(new String[]{typeName, module});
this.packagesCache.put(packageName, types);
} else {
types.add(new String[]{typeName, module});
}
this.typesCache.add(packageName + typeName);
}
@Override
public List<String[]> getTypes(String packageName) {
// package name is expected to ends with '/'
if (this.packagesCache == null) {
try {
this.initialize();
} catch(IOException e) {
return Collections.<String[]>emptyList();
}
}
return this.packagesCache.get(packageName);
}
@Override
public String toString() {
return "JRT: " + (this.file == null ? "UNKNOWN_ARCHIVE" : this.file.getAbsolutePath()); //$NON-NLS-1$ //$NON-NLS-2$
}
class JrtFileObject extends ArchiveFileObject {
String module = null;
private JrtFileObject(File file, String fileName, String module, Charset charset) {
super(file, fileName, charset);
this.module = module;
}
@Override
protected void finalize() throws Throwable {
// Nothing to do here
}
@Override
protected ClassFileReader getClassReader() {
ClassFileReader reader = null;
try {
byte[] content = JRTUtil.getClassfileContent(this.file, this.entryName, this.module);
if (content == null) return null;
return new ClassFileReader(content, this.entryName.toCharArray());
} catch (ClassFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return reader;
}
/* (non-Javadoc)
* @see javax.tools.FileObject#getCharContent(boolean)
*/
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
try {
return Util.getCharContents(this, ignoreEncodingErrors,
org.eclipse.jdt.internal.compiler.util.JRTUtil.getClassfileContent(this.file, this.entryName, this.module),
this.charset.name());
} catch (ClassFormatException e) {
e.printStackTrace();
return null;
}
}
/* (non-Javadoc)
* @see javax.tools.FileObject#getLastModified()
*/
@Override
public long getLastModified() {
return 0;
}
/* (non-Javadoc)
* @see javax.tools.FileObject#getName()
*/
@Override
public String getName() {
return this.entryName;
}
/* (non-Javadoc)
* @see javax.tools.FileObject#openInputStream()
*/
@Override
public InputStream openInputStream() throws IOException {
return org.eclipse.jdt.internal.compiler.util.JRTUtil.getContentFromJrt(this.file, this.entryName, null);
}
/* (non-Javadoc)
* @see javax.tools.FileObject#openOutputStream()
*/
@Override
public OutputStream openOutputStream() throws IOException {
throw new UnsupportedOperationException();
}
/* (non-Javadoc)
* @see javax.tools.FileObject#openReader(boolean)
*/
@Override
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
throw new UnsupportedOperationException();
}
/* (non-Javadoc)
* @see javax.tools.FileObject#openWriter()
*/
@Override
public Writer openWriter() throws IOException {
throw new UnsupportedOperationException();
}
/* (non-Javadoc)
* @see javax.tools.FileObject#toUri()
*/
@Override
public URI toUri() {
try {
return new URI("JRT:" + this.file.toURI().getPath() + "!" + this.entryName); //$NON-NLS-1$//$NON-NLS-2$
} catch (URISyntaxException e) {
return null;
}
}
@Override
public String toString() {
return this.file.getAbsolutePath() + "[" + this.entryName + "]";//$NON-NLS-1$//$NON-NLS-2$
}
}
}