8063145: ToolBox should support extracting classes from a JavaFileManager/Location
authorjjg
Fri, 07 Nov 2014 14:51:35 -0800
changeset 27547 d07b3dcbc707
parent 27546 79b6b60ff60a
child 27548 d3f506b108e5
8063145: ToolBox should support extracting classes from a JavaFileManager/Location Reviewed-by: ksrini
langtools/test/tools/javac/6508981/TestInferBinaryName.java
langtools/test/tools/lib/ToolBox.java
--- a/langtools/test/tools/javac/6508981/TestInferBinaryName.java	Fri Nov 07 18:22:36 2014 +0100
+++ b/langtools/test/tools/javac/6508981/TestInferBinaryName.java	Fri Nov 07 14:51:35 2014 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 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
@@ -26,7 +26,8 @@
  * @bug 6508981
  * @summary cleanup file separator handling in JavacFileManager
  * (This test is specifically to test the new impl of inferBinaryName)
- * @build p.A
+ * @library /tools/lib
+ * @build ToolBox p.A
  * @run main TestInferBinaryName
  */
 
@@ -61,51 +62,75 @@
         //System.err.println(System.getProperties());
         testDirectory();
         testSymbolArchive();
-        testZipArchive();
-        testZipFileIndexArchive();
-        testZipFileIndexArchive2();
+
+        File testJar = createJar();
+
+        testZipArchive(testJar);
+        testZipFileIndexArchive(testJar);
+        testZipFileIndexArchive2(testJar);
         if (errors > 0)
             throw new Exception(errors + " error found");
     }
 
+    File createJar() throws IOException {
+        File f = new File("test.jar");
+        try (JavaFileManager fm = new JavacFileManager(new Context(), false, null)) {
+            ToolBox tb = new ToolBox();
+            tb.new JarTask(f.getPath())
+                .files(fm, StandardLocation.PLATFORM_CLASS_PATH, "java.lang.*")
+                .run();
+        }
+        return f;
+    }
+
     void testDirectory() throws IOException {
         String testClassName = "p.A";
-        JavaFileManager fm =
-            getFileManager("test.classes", USE_SYMBOL_FILE, USE_ZIP_FILE_INDEX);
-        test("testDirectory",
-             fm, testClassName, "com.sun.tools.javac.file.RegularFileObject");
+        List<File> testClasses = Arrays.asList(new File(System.getProperty("test.classes")));
+        try (JavaFileManager fm =
+                getFileManager(testClasses, USE_SYMBOL_FILE, USE_ZIP_FILE_INDEX)) {
+            test("testDirectory",
+                fm, testClassName, "com.sun.tools.javac.file.RegularFileObject");
+        }
     }
 
     void testSymbolArchive() throws IOException {
         String testClassName = "java.lang.String";
-        JavaFileManager fm =
-            getFileManager("sun.boot.class.path", USE_SYMBOL_FILE, DONT_USE_ZIP_FILE_INDEX);
-        test("testSymbolArchive",
-             fm, testClassName, "com.sun.tools.javac.file.SymbolArchive$SymbolFileObject");
+        List<File> path = getPath(System.getProperty("sun.boot.class.path"));
+        try (JavaFileManager fm =
+                getFileManager(path, USE_SYMBOL_FILE, DONT_USE_ZIP_FILE_INDEX)) {
+            test("testSymbolArchive",
+                    fm, testClassName, "com.sun.tools.javac.file.SymbolArchive$SymbolFileObject");
+        }
     }
 
-    void testZipArchive() throws IOException {
+    void testZipArchive(File testJar) throws IOException {
         String testClassName = "java.lang.String";
-        JavaFileManager fm =
-            getFileManager("sun.boot.class.path", IGNORE_SYMBOL_FILE, DONT_USE_ZIP_FILE_INDEX);
-        test("testZipArchive",
-             fm, testClassName, "com.sun.tools.javac.file.ZipArchive$ZipFileObject");
+        List<File> path = Arrays.asList(testJar);
+        try (JavaFileManager fm =
+                getFileManager(path, IGNORE_SYMBOL_FILE, DONT_USE_ZIP_FILE_INDEX)) {
+            test("testZipArchive",
+                 fm, testClassName, "com.sun.tools.javac.file.ZipArchive$ZipFileObject");
+        }
     }
 
-    void testZipFileIndexArchive() throws IOException {
+    void testZipFileIndexArchive(File testJar) throws IOException {
         String testClassName = "java.lang.String";
-        JavaFileManager fm =
-            getFileManager("sun.boot.class.path", USE_SYMBOL_FILE, USE_ZIP_FILE_INDEX);
-        test("testZipFileIndexArchive",
-             fm, testClassName, "com.sun.tools.javac.file.ZipFileIndexArchive$ZipFileIndexFileObject");
+        List<File> path = Arrays.asList(testJar);
+        try (JavaFileManager fm =
+                getFileManager(path, USE_SYMBOL_FILE, USE_ZIP_FILE_INDEX)) {
+            test("testZipFileIndexArchive",
+                 fm, testClassName, "com.sun.tools.javac.file.ZipFileIndexArchive$ZipFileIndexFileObject");
+        }
     }
 
-    void testZipFileIndexArchive2() throws IOException {
+    void testZipFileIndexArchive2(File testJar) throws IOException {
         String testClassName = "java.lang.String";
-        JavaFileManager fm =
-            getFileManager("sun.boot.class.path", IGNORE_SYMBOL_FILE, USE_ZIP_FILE_INDEX);
-        test("testZipFileIndexArchive2",
-             fm, testClassName, "com.sun.tools.javac.file.ZipFileIndexArchive$ZipFileIndexFileObject");
+        List<File> path = Arrays.asList(testJar);
+        try (JavaFileManager fm =
+                getFileManager(path, IGNORE_SYMBOL_FILE, USE_ZIP_FILE_INDEX)) {
+            test("testZipFileIndexArchive2",
+                 fm, testClassName, "com.sun.tools.javac.file.ZipFileIndexArchive$ZipFileIndexFileObject");
+        }
     }
 
     /**
@@ -133,7 +158,7 @@
         System.err.println("OK");
     }
 
-    JavaFileManager getFileManager(String classpathProperty,
+    JavaFileManager getFileManager(List<File> path,
                                    boolean symFileKind,
                                    boolean zipFileIndexKind)
             throws IOException {
@@ -145,7 +170,6 @@
         if (symFileKind == IGNORE_SYMBOL_FILE)
             options.put("ignore.symbol.file", "true");
         JavacFileManager fm = new JavacFileManager(ctx, false, null);
-        List<File> path = getPath(System.getProperty(classpathProperty));
         fm.setLocation(CLASS_PATH, path);
         return fm;
     }
--- a/langtools/test/tools/lib/ToolBox.java	Fri Nov 07 18:22:36 2014 +0100
+++ b/langtools/test/tools/lib/ToolBox.java	Fri Nov 07 14:51:35 2014 -0800
@@ -21,6 +21,7 @@
  * questions.
  */
 
+import java.io.BufferedInputStream;
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.ByteArrayInputStream;
@@ -28,6 +29,7 @@
 import java.io.File;
 import java.io.FilterOutputStream;
 import java.io.FilterWriter;
+import java.io.IOError;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -49,12 +51,15 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumMap;
+import java.util.EnumSet;
 import java.util.HashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.jar.Attributes;
 import java.util.jar.JarEntry;
 import java.util.jar.JarOutputStream;
@@ -64,18 +69,20 @@
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
+
 import javax.tools.FileObject;
 import javax.tools.ForwardingJavaFileManager;
 import javax.tools.JavaCompiler;
 import javax.tools.JavaFileManager;
 import javax.tools.JavaFileObject;
+import javax.tools.JavaFileObject.Kind;
+import javax.tools.JavaFileManager.Location;
 import javax.tools.SimpleJavaFileObject;
 import javax.tools.StandardJavaFileManager;
 import javax.tools.StandardLocation;
 
 import com.sun.tools.javac.api.JavacTaskImpl;
 import com.sun.tools.javac.api.JavacTool;
-import java.io.IOError;
 
 /**
  * Utility methods and classes for writing jtreg tests for
@@ -233,7 +240,7 @@
     public void createDirectories(String... paths) throws IOException {
         if (paths.length == 0)
             throw new IllegalArgumentException("no directories specified");
-        for (String p: paths)
+        for (String p : paths)
             Files.createDirectories(Paths.get(p));
     }
 
@@ -248,7 +255,7 @@
     public void createDirectories(Path... paths) throws IOException {
         if (paths.length == 0)
             throw new IllegalArgumentException("no directories specified");
-        for (Path p: paths)
+        for (Path p : paths)
             Files.createDirectories(p);
     }
 
@@ -262,7 +269,7 @@
     public void deleteFiles(String... files) throws IOException {
         if (files.length == 0)
             throw new IllegalArgumentException("no files specified");
-        for (String file: files)
+        for (String file : files)
             Files.delete(Paths.get(file));
     }
 
@@ -392,7 +399,7 @@
     public void writeJavaFiles(Path dir, String... contents) throws IOException {
         if (contents.length == 0)
             throw new IllegalArgumentException("no content specified for any files");
-        for (String c: contents) {
+        for (String c : contents) {
             new JavaSource(c).write(dir);
         }
     }
@@ -1090,7 +1097,7 @@
 
         private List<File> toFiles(String path) {
             List<File> result = new ArrayList<>();
-            for (String s: path.split(File.pathSeparator)) {
+            for (String s : path.split(File.pathSeparator)) {
                 if (!s.isEmpty())
                     result.add(new File(s));
             }
@@ -1108,7 +1115,7 @@
             if (fileObjects == null)
                 return filesAsFileObjects;
             List<JavaFileObject> combinedList = new ArrayList<>();
-            for (JavaFileObject o: filesAsFileObjects)
+            for (JavaFileObject o : filesAsFileObjects)
                 combinedList.add(o);
             combinedList.addAll(fileObjects);
             return combinedList;
@@ -1308,6 +1315,7 @@
         private String mainClass;
         private Path baseDir;
         private List<Path> paths;
+        private Set<FileObject> fileObjects;
 
         /**
          * Creates a task to write jar files, using API mode.
@@ -1315,6 +1323,7 @@
         public JarTask() {
             super(Mode.API);
             paths = Collections.emptyList();
+            fileObjects = new LinkedHashSet<>();
         }
 
         /**
@@ -1392,6 +1401,53 @@
         }
 
         /**
+         * Adds a set of file objects to be written into the jar file, by copying them
+         * from a Location in a JavaFileManager.
+         * The file objects to be written are specified by a series of paths;
+         * each path can be in one of the following forms:
+         * <ul>
+         * <li>The name of a class. For example, java.lang.Object.
+         * In this case, the corresponding .class file will be written to the jar file.
+         * <li>the name of a package followed by {@code .*}. For example, {@code java.lang.*}.
+         * In this case, all the class files in the specified package will be written to
+         * the jar file.
+         * <li>the name of a package followed by {@code .**}. For example, {@code java.lang.**}.
+         * In this case, all the class files in the specified package, and any subpackages
+         * will be written to the jar file.
+         * </ul>
+         *
+         * @param fm the file manager in which to find the file objects
+         * @param l  the location in which to find the file objects
+         * @param paths the paths specifying the file objects to be copied
+         * @return this task object
+         * @throws IOException if errors occur while determining the set of file objects
+         */
+        public JarTask files(JavaFileManager fm, Location l, String... paths)
+                throws IOException {
+            for (String p : paths) {
+                if (p.endsWith(".**"))
+                    addPackage(fm, l, p.substring(0, p.length() - 3), true);
+                else if (p.endsWith(".*"))
+                    addPackage(fm, l, p.substring(0, p.length() - 2), false);
+                else
+                    addFile(fm, l, p);
+            }
+            return this;
+        }
+
+        private void addPackage(JavaFileManager fm, Location l, String pkg, boolean recurse)
+                throws IOException {
+            for (JavaFileObject fo : fm.list(l, pkg, EnumSet.allOf(JavaFileObject.Kind.class), recurse)) {
+                fileObjects.add(fo);
+            }
+        }
+
+        private void addFile(JavaFileManager fm, Location l, String path) throws IOException {
+            JavaFileObject fo = fm.getJavaFileForInput(l, path, Kind.CLASS);
+            fileObjects.add(fo);
+        }
+
+        /**
          * Provides limited jar command-like functionality.
          * The supported commands are:
          * <ul>
@@ -1464,42 +1520,19 @@
             StreamOutput sysOut = new StreamOutput(System.out, System::setOut);
             StreamOutput sysErr = new StreamOutput(System.err, System::setErr);
 
-            int rc;
             Map<OutputKind, String> outputMap = new HashMap<>();
 
             try (OutputStream os = Files.newOutputStream(jar);
                     JarOutputStream jos = openJar(os, m)) {
-                Path base = (baseDir == null) ? currDir : baseDir;
-                for (Path path: paths) {
-                    Files.walkFileTree(base.resolve(path), new SimpleFileVisitor<Path>() {
-                        @Override
-                        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
-                            try {
-                                String p = base.relativize(file)
-                                        .normalize()
-                                        .toString()
-                                        .replace(File.separatorChar, '/');
-                                JarEntry e = new JarEntry(p);
-                                jos.putNextEntry(e);
-                                jos.write(Files.readAllBytes(file));
-                                jos.closeEntry();
-                                return FileVisitResult.CONTINUE;
-                            } catch (IOException e) {
-                                System.err.println("Error adding " + file + " to jar file: " + e);
-                                return FileVisitResult.TERMINATE;
-                            }
-                        }
-                    });
-                }
-                rc = 0;
+                writeFiles(jos);
+                writeFileObjects(jos);
             } catch (IOException e) {
-                System.err.println("Error opening " + jar + ": " + e);
-                rc = 1;
+                error("Exception while opening " + jar, e);
             } finally {
                 outputMap.put(OutputKind.STDOUT, sysOut.close());
                 outputMap.put(OutputKind.STDERR, sysErr.close());
             }
-            return checkExit(new Result(this, rc, outputMap));
+            return checkExit(new Result(this, (errors == 0) ? 0 : 1, outputMap));
         }
 
         private JarOutputStream openJar(OutputStream os, Manifest m) throws IOException {
@@ -1512,6 +1545,79 @@
             }
         }
 
+        private void writeFiles(JarOutputStream jos) throws IOException {
+            Path base = (baseDir == null) ? currDir : baseDir;
+            for (Path path : paths) {
+                Files.walkFileTree(base.resolve(path), new SimpleFileVisitor<Path>() {
+                    @Override
+                    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+                        try {
+                            String p = base.relativize(file)
+                                    .normalize()
+                                    .toString()
+                                    .replace(File.separatorChar, '/');
+                            JarEntry e = new JarEntry(p);
+                            jos.putNextEntry(e);
+                            try {
+                                jos.write(Files.readAllBytes(file));
+                            } finally {
+                                jos.closeEntry();
+                            }
+                            return FileVisitResult.CONTINUE;
+                        } catch (IOException e) {
+                            error("Exception while adding " + file + " to jar file", e);
+                            return FileVisitResult.TERMINATE;
+                        }
+                    }
+                });
+            }
+        }
+
+        private void writeFileObjects(JarOutputStream jos) throws IOException {
+            for (FileObject fo : fileObjects) {
+                String p = guessPath(fo);
+                JarEntry e = new JarEntry(p);
+                jos.putNextEntry(e);
+                try {
+                    byte[] buf = new byte[1024];
+                    try (BufferedInputStream in = new BufferedInputStream(fo.openInputStream())) {
+                        int n;
+                        while ((n = in.read(buf)) > 0)
+                            jos.write(buf, 0, n);
+                    } catch (IOException ex) {
+                        error("Exception while adding " + fo.getName() + " to jar file", ex);
+                    }
+                } finally {
+                    jos.closeEntry();
+                }
+            }
+        }
+
+        /*
+         * A jar: URL is of the form  jar:URL!/entry  where URL is a URL for the .jar file itself.
+         * In Symbol files (i.e. ct.sym) the underlying entry is prefixed META-INF/sym/<base>.
+         */
+        private final Pattern jarEntry = Pattern.compile(".*!/(?:META-INF/sym/[^/]+/)?(.*)");
+
+        private String guessPath(FileObject fo) {
+            URI u = fo.toUri();
+            switch (u.getScheme()) {
+                case "jar":
+                    Matcher m = jarEntry.matcher(u.getSchemeSpecificPart());
+                    if (m.matches()) {
+                        return m.group(1);
+                    }
+                    break;
+            }
+            throw new IllegalArgumentException(fo.getName());
+        }
+
+        private void error(String message, Throwable t) {
+            out.println("Error: " + message + ": " + t);
+            errors++;
+        }
+
+        private int errors;
     }
 
     /**