langtools/src/jdk.dev/share/classes/com/sun/tools/jdeps/PlatformClassPath.java
changeset 25874 83c19f00452c
parent 25442 755ff386d1ac
child 26276 0dca2378aa34
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.dev/share/classes/com/sun/tools/jdeps/PlatformClassPath.java	Sun Aug 17 15:52:32 2014 +0100
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2012, 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.jdeps;
+
+import com.sun.tools.classfile.Annotation;
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.ConstantPool;
+import com.sun.tools.classfile.ConstantPoolException;
+import com.sun.tools.classfile.RuntimeAnnotations_attribute;
+import com.sun.tools.classfile.Dependencies.ClassFileError;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.*;
+import java.util.jar.*;
+
+import static com.sun.tools.classfile.Attribute.*;
+import static com.sun.tools.jdeps.ClassFileReader.*;
+
+/**
+ * ClassPath for Java SE and JDK
+ */
+class PlatformClassPath {
+    private static List<Archive> modules;
+    static synchronized List<Archive> getArchives(Path mpath) throws IOException {
+        if (modules == null) {
+            initPlatformArchives(mpath);
+        }
+        return modules;
+    }
+
+    /**
+     * Finds the module with the given name. Returns null
+     * if such module doesn't exist.
+     *
+     * @param mn module name
+     */
+    static Module findModule(String mn) {
+        for (Archive a : modules) {
+            if (Module.class.isInstance(a)) {
+                Module m = (Module)a;
+                if (mn.equals(m.name())) {
+                    return m;
+                }
+            }
+        }
+        return null;
+    }
+
+    private static List<Archive> initPlatformArchives(Path mpath) throws IOException {
+        Path home = Paths.get(System.getProperty("java.home"));
+        if (mpath == null && !home.endsWith("jre")) {
+            // jdk build
+            Path p = home.resolve("modules");
+            if (Files.isDirectory(p)) {
+                mpath = p;
+            }
+        }
+        modules = mpath != null ? initModules(mpath) : initLegacyImage(home);
+        if (findModule("java.base") != null) {
+            Profile.initProfiles();
+        }
+        return modules;
+    }
+
+    private static List<Archive> initModules(Path mpath) throws IOException {
+        try (InputStream in = PlatformClassPath.class
+                .getResourceAsStream("resources/modules.xml")) {
+            return new ArrayList<Archive>(ModulesXmlReader.load(mpath, in));
+        }
+    }
+
+    private static List<Archive> initLegacyImage(Path home) throws IOException {
+        LegacyImageHelper cfr = new LegacyImageHelper(home);
+        List<Archive> archives = new ArrayList<>(cfr.nonPlatformArchives);
+        try (InputStream in = PlatformClassPath.class
+                .getResourceAsStream("resources/modules.xml")) {
+            archives.addAll(ModulesXmlReader.loadFromImage(cfr, in));
+            return archives;
+        }
+    }
+
+    static class LegacyImageHelper {
+        private static final List<String> NON_PLATFORM_JARFILES =
+                Arrays.asList("alt-rt.jar", "jfxrt.jar", "ant-javafx.jar", "javafx-mx.jar");
+        final List<Archive> nonPlatformArchives = new ArrayList<>();
+        final List<JarFile> jarfiles = new ArrayList<>();
+        final Path home;
+
+        LegacyImageHelper(Path home) {
+            this.home = home;
+            try {
+                if (home.endsWith("jre")) {
+                    // jar files in <javahome>/jre/lib
+                    addJarFiles(home.resolve("lib"));
+                    if (home.getParent() != null) {
+                        // add tools.jar and other JDK jar files
+                        Path lib = home.getParent().resolve("lib");
+                        if (Files.exists(lib)) {
+                            addJarFiles(lib);
+                        }
+                    }
+                } else if (Files.exists(home.resolve("lib"))) {
+                    // add other JAR files
+                    addJarFiles(home.resolve("lib"));
+                } else {
+                    throw new RuntimeException("\"" + home + "\" not a JDK home");
+                }
+            } catch (IOException e) {
+                throw new Error(e);
+            }
+        }
+
+        /**
+         * Returns a ClassFileReader that only reads classes for the given modulename.
+         */
+        ClassFileReader getClassReader(String modulename, Set<String> packages) throws IOException {
+            return new ModuleClassReader(modulename, packages);
+        }
+
+        private void addJarFiles(final Path root) throws IOException {
+            final Path ext = root.resolve("ext");
+            Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
+                @Override
+                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
+                    throws IOException
+                {
+                    if (dir.equals(root) || dir.equals(ext)) {
+                        return FileVisitResult.CONTINUE;
+                    } else {
+                        // skip other cobundled JAR files
+                        return FileVisitResult.SKIP_SUBTREE;
+                    }
+                }
+
+                @Override
+                public FileVisitResult visitFile(Path p, BasicFileAttributes attrs)
+                    throws IOException
+                {
+                    String fn = p.getFileName().toString();
+                    if (fn.endsWith(".jar")) {
+                        // JDK may cobundle with JavaFX that doesn't belong to any profile
+                        // Treat jfxrt.jar as regular Archive
+                        if (NON_PLATFORM_JARFILES.contains(fn)) {
+                            nonPlatformArchives.add(Archive.getInstance(p));
+                        } else {
+                            jarfiles.add(new JarFile(p.toFile()));
+                        }
+                    }
+                    return FileVisitResult.CONTINUE;
+                }
+            });
+        }
+
+        /**
+         * ModuleClassFile reads classes for the specified module from the legacy image.
+         *
+         */
+        class ModuleClassReader extends JarFileReader {
+            private JarFile cachedJarFile = getJarFile(0);
+            private final Set<String> packages;
+            private final String module;
+            ModuleClassReader(String module, Set<String> packages) throws IOException {
+                super(home, null);
+                this.module = module;
+                this.packages = packages;
+            }
+
+            private boolean includes(String name) {
+                String cn = name.replace('/', '.');
+                int i = cn.lastIndexOf('.');
+                String pn = i > 0 ? cn.substring(0, i) : "";
+                return packages.contains(pn);
+            }
+
+            private JarEntry findJarEntry(JarFile jarfile, String entryName1, String entryName2) {
+                JarEntry e = jarfile.getJarEntry(entryName1);
+                if (e == null) {
+                    e = jarfile.getJarEntry(entryName2);
+                }
+                return e;
+            }
+
+            public String toString() {
+                return module + " " + packages.size() + " " + packages;
+            }
+
+            @Override
+            public ClassFile getClassFile(String name) throws IOException {
+                if (jarfiles.isEmpty() || !includes(name)) {
+                    return null;
+                }
+
+                if (name.indexOf('.') > 0) {
+                    int i = name.lastIndexOf('.');
+                    String entryName = name.replace('.', '/') + ".class";
+                    String innerClassName = entryName.substring(0, i) + "$"
+                            + entryName.substring(i + 1, entryName.length());
+                    JarEntry e = findJarEntry(cachedJarFile, entryName, innerClassName);
+                    if (e != null) {
+                        return readClassFile(cachedJarFile, e);
+                    }
+                    for (JarFile jf : jarfiles) {
+                        if (jf == cachedJarFile) {
+                            continue;
+                        }
+                        System.err.format("find jar entry %s at %s%n", entryName, jf);
+                        e = findJarEntry(jf, entryName, innerClassName);
+                        if (e != null) {
+                            cachedJarFile = jf;
+                            return readClassFile(jf, e);
+                        }
+                    }
+                } else {
+                    String entryName = name + ".class";
+                    JarEntry e = cachedJarFile.getJarEntry(entryName);
+                    if (e != null) {
+                        return readClassFile(cachedJarFile, e);
+                    }
+                    for (JarFile jf : jarfiles) {
+                        if (jf == cachedJarFile) {
+                            continue;
+                        }
+                        e = jf.getJarEntry(entryName);
+                        if (e != null) {
+                            cachedJarFile = jf;
+                            return readClassFile(jf, e);
+                        }
+                    }
+                }
+                return null;
+            }
+
+            @Override
+            public Iterable<ClassFile> getClassFiles() throws IOException {
+                final Iterator<ClassFile> iter = new ModuleClassIterator(this);
+                return new Iterable<ClassFile>() {
+                    public Iterator<ClassFile> iterator() {
+                        return iter;
+                    }
+                };
+            }
+
+            private JarFile getJarFile(int index) {
+                return index < jarfiles.size() ? jarfiles.get(index) : null;
+            }
+
+            class ModuleClassIterator extends JarFileIterator {
+                private int index;
+                ModuleClassIterator(ModuleClassReader reader) {
+                    super(reader);
+                    this.index = 0;
+                    this.jf = getJarFile(0);
+                    this.entries = jf != null ? jf.entries() : null;
+                    this.nextEntry = nextEntry();
+                }
+
+                @Override
+                protected JarEntry nextEntry() {
+                    while (jf != null) {
+                        while (entries.hasMoreElements()) {
+                            JarEntry e = entries.nextElement();
+                            String name = e.getName();
+                            if (name.endsWith(".class") && includes(name)) {
+                                return e;
+                            }
+                        }
+                        jf = getJarFile(++index);
+                        entries = jf != null ? jf.entries() : null;
+                    }
+                    return null;
+                }
+            }
+        }
+    }
+}