blob: 20f7a4847120148925fb6bd7028585cc31888fec [file] [log] [blame]
/*
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.javac.file;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.ref.SoftReference;
import java.net.URI;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.ProviderNotFoundException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;
import javax.tools.FileObject;
import com.sun.tools.javac.file.RelativePath.RelativeDirectory;
import com.sun.tools.javac.util.Context;
/**
* A package-oriented index into the jrt: filesystem.
*/
public class JRTIndex {
/** Get a shared instance of the cache. */
private static JRTIndex sharedInstance;
public synchronized static JRTIndex getSharedInstance() {
if (sharedInstance == null) {
try {
sharedInstance = new JRTIndex();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
return sharedInstance;
}
/** Get a context-specific instance of a cache. */
public static JRTIndex instance(Context context) {
try {
JRTIndex instance = context.get(JRTIndex.class);
if (instance == null)
context.put(JRTIndex.class, instance = new JRTIndex());
return instance;
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public static boolean isAvailable() {
try {
FileSystems.getFileSystem(URI.create("jrt:/"));
return true;
} catch (ProviderNotFoundException | FileSystemNotFoundException e) {
return false;
}
}
/**
* The jrt: file system.
*/
private final FileSystem jrtfs;
/**
* A lazily evaluated set of entries about the contents of the jrt: file system.
*/
private final Map<RelativeDirectory, SoftReference<Entry>> entries;
/**
* An entry provides cached info about a specific package directory within jrt:.
*/
class Entry {
/**
* The regular files for this package.
* For now, assume just one instance of each file across all modules.
*/
final Map<String, Path> files;
/**
* The set of subdirectories in jrt: for this package.
*/
final Set<RelativeDirectory> subdirs;
/**
* The info that used to be in ct.sym for classes in this package.
*/
final CtSym ctSym;
private Entry(Map<String, Path> files, Set<RelativeDirectory> subdirs, CtSym ctSym) {
this.files = files;
this.subdirs = subdirs;
this.ctSym = ctSym;
}
}
/**
* The info that used to be in ct.sym for classes in a package.
*/
public static class CtSym {
/**
* The classes in this package are internal and not visible.
*/
public final boolean hidden;
/**
* The classes in this package are proprietary and will generate a warning.
*/
public final boolean proprietary;
/**
* The minimum profile in which classes in this package are available.
*/
public final String minProfile;
CtSym(boolean hidden, boolean proprietary, String minProfile) {
this.hidden = hidden;
this.proprietary = proprietary;
this.minProfile = minProfile;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("CtSym[");
boolean needSep = false;
if (hidden) {
sb.append("hidden");
needSep = true;
}
if (proprietary) {
if (needSep) sb.append(",");
sb.append("proprietary");
needSep = true;
}
if (minProfile != null) {
if (needSep) sb.append(",");
sb.append(minProfile);
}
sb.append("]");
return sb.toString();
}
static final CtSym EMPTY = new CtSym(false, false, null);
}
/**
* Create and initialize the index.
*/
private JRTIndex() throws IOException {
jrtfs = FileSystems.getFileSystem(URI.create("jrt:/"));
entries = new HashMap<>();
}
public CtSym getCtSym(CharSequence packageName) throws IOException {
return getEntry(RelativeDirectory.forPackage(packageName)).ctSym;
}
synchronized Entry getEntry(RelativeDirectory rd) throws IOException {
SoftReference<Entry> ref = entries.get(rd);
Entry e = (ref == null) ? null : ref.get();
if (e == null) {
Map<String, Path> files = new LinkedHashMap<>();
Set<RelativeDirectory> subdirs = new LinkedHashSet<>();
Path dir;
if (rd.path.isEmpty()) {
dir = jrtfs.getPath("/modules");
} else {
Path pkgs = jrtfs.getPath("/packages");
dir = pkgs.resolve(rd.getPath().replaceAll("/$", "").replace("/", "."));
}
if (Files.exists(dir)) {
try (DirectoryStream<Path> modules = Files.newDirectoryStream(dir)) {
for (Path module: modules) {
if (Files.isSymbolicLink(module))
module = Files.readSymbolicLink(module);
Path p = rd.resolveAgainst(module);
if (!Files.exists(p))
continue;
try (DirectoryStream<Path> stream = Files.newDirectoryStream(p)) {
for (Path entry: stream) {
String name = entry.getFileName().toString();
if (Files.isRegularFile(entry)) {
// TODO: consider issue of files with same name in different modules
files.put(name, entry);
} else if (Files.isDirectory(entry)) {
subdirs.add(new RelativeDirectory(rd, name));
}
}
}
}
}
}
e = new Entry(Collections.unmodifiableMap(files),
Collections.unmodifiableSet(subdirs),
getCtInfo(rd));
entries.put(rd, new SoftReference<>(e));
}
return e;
}
public boolean isInJRT(FileObject fo) {
if (fo instanceof PathFileObject) {
Path path = ((PathFileObject) fo).getPath();
return (path.getFileSystem() == jrtfs);
} else {
return false;
}
}
private CtSym getCtInfo(RelativeDirectory dir) {
if (dir.path.isEmpty())
return CtSym.EMPTY;
// It's a side-effect of the default build rules that ct.properties
// ends up as a resource bundle.
if (ctBundle == null) {
final String bundleName = "com.sun.tools.javac.resources.ct";
ctBundle = ResourceBundle.getBundle(bundleName);
}
try {
String attrs = ctBundle.getString(dir.path.replace('/', '.') + '*');
boolean hidden = false;
boolean proprietary = false;
String minProfile = null;
for (String attr: attrs.split(" +", 0)) {
switch (attr) {
case "hidden":
hidden = true;
break;
case "proprietary":
proprietary = true;
break;
default:
minProfile = attr;
}
}
return new CtSym(hidden, proprietary, minProfile);
} catch (MissingResourceException e) {
return CtSym.EMPTY;
}
}
private ResourceBundle ctBundle;
}