langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/JRTIndex.java
changeset 27579 d1a63c99cdd5
child 27859 f5bd37460a09
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/JRTIndex.java	Wed Dec 03 14:25:46 2014 +0000
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2014, 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.Files;
+import java.nio.file.Path;
+import java.nio.file.spi.FileSystemProvider;
+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.nio.PathFileObject;
+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() {
+        for (FileSystemProvider p: FileSystemProvider.installedProviders()) {
+            if (p.getScheme().equals("jrt"))
+                return true;
+        }
+        return false;
+    }
+
+
+    /**
+     * The jrt: file system.
+     */
+    private final FileSystem jrtfs;
+
+    /**
+     * The set of module directories within the jrt: file system.
+     */
+    private final Set<Path> jrtModules;
+
+    /**
+     * 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:/"));
+        jrtModules = new LinkedHashSet<>();
+        Path root = jrtfs.getPath("/");
+        try (DirectoryStream<Path> stream = Files.newDirectoryStream(root)) {
+            for (Path entry: stream) {
+                if (Files.isDirectory(entry))
+                    jrtModules.add(entry);
+            }
+        }
+        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<>();
+            for (Path module: jrtModules) {
+                Path p = rd.getFile(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;
+}