PackagesHelper.java revision 1474:f93753325c7b
1/* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.nashorn.tools.jjs; 27 28import java.io.IOException; 29import java.io.File; 30import java.util.ArrayList; 31import java.util.Collections; 32import java.util.EnumSet; 33import java.util.HashSet; 34import java.util.LinkedHashMap; 35import java.util.List; 36import java.util.Map; 37import java.util.Set; 38import java.util.stream.Collectors; 39import java.util.stream.Stream; 40import javax.tools.JavaCompiler; 41import javax.tools.JavaFileManager.Location; 42import javax.tools.JavaFileObject; 43import javax.tools.StandardJavaFileManager; 44import javax.tools.StandardLocation; 45import javax.tools.ToolProvider; 46 47/** 48 * A helper class to compute properties of a Java package object. Properties of 49 * package object are (simple) top level class names in that java package and 50 * immediate subpackages of that package. 51 */ 52final class PackagesHelper { 53 // JavaCompiler may be null on certain platforms (eg. JRE) 54 private static final JavaCompiler compiler; 55 static { 56 // Use javac only if security manager is not around! 57 compiler = System.getSecurityManager() == null? ToolProvider.getSystemJavaCompiler() : null; 58 } 59 60 /** 61 * Is Java package properties helper available? 62 * 63 * @return true if package properties support is available 64 */ 65 static boolean isAvailable() { 66 return compiler != null; 67 } 68 69 private final StandardJavaFileManager fm; 70 private final Set<JavaFileObject.Kind> fileKinds; 71 72 /** 73 * Construct a new PackagesHelper. 74 * 75 * @param classPath Class path to compute properties of java package objects 76 */ 77 PackagesHelper(final String classPath) throws IOException { 78 assert isAvailable() : "no java compiler found!"; 79 80 fm = compiler.getStandardFileManager(null, null, null); 81 fileKinds = EnumSet.of(JavaFileObject.Kind.CLASS); 82 83 if (classPath != null && !classPath.isEmpty()) { 84 fm.setLocation(StandardLocation.CLASS_PATH, getFiles(classPath)); 85 } else { 86 // no classpath set. Make sure that it is empty and not any default like "." 87 fm.setLocation(StandardLocation.CLASS_PATH, Collections.<File>emptyList()); 88 } 89 } 90 91 // LRU cache for java package properties lists 92 private final LinkedHashMap<String, List<String>> propsCache = 93 new LinkedHashMap<String, List<String>>(32, 0.75f, true) { 94 private static final int CACHE_SIZE = 100; 95 private static final long serialVersionUID = 1; 96 97 @Override 98 protected boolean removeEldestEntry(final Map.Entry<String, List<String>> eldest) { 99 return size() > CACHE_SIZE; 100 } 101 }; 102 103 /** 104 * Return the list of properties of the given Java package or package prefix 105 * 106 * @param pkg Java package name or package prefix name 107 * @return the list of properties of the given Java package or package prefix 108 */ 109 List<String> getPackageProperties(final String pkg) { 110 // check the cache first 111 if (propsCache.containsKey(pkg)) { 112 return propsCache.get(pkg); 113 } 114 115 try { 116 // make sorted list of properties 117 final List<String> props = new ArrayList<>(listPackage(pkg)); 118 Collections.sort(props); 119 propsCache.put(pkg, props); 120 return props; 121 } catch (final IOException exp) { 122 if (Main.DEBUG) { 123 exp.printStackTrace(); 124 } 125 return Collections.<String>emptyList(); 126 } 127 } 128 129 public void close() throws IOException { 130 fm.close(); 131 } 132 133 private Set<String> listPackage(final String pkg) throws IOException { 134 final Set<String> props = new HashSet<>(); 135 listPackage(StandardLocation.PLATFORM_CLASS_PATH, pkg, props); 136 listPackage(StandardLocation.CLASS_PATH, pkg, props); 137 return props; 138 } 139 140 private void listPackage(final Location loc, final String pkg, final Set<String> props) 141 throws IOException { 142 for (JavaFileObject file : fm.list(loc, pkg, fileKinds, true)) { 143 final String binaryName = fm.inferBinaryName(loc, file); 144 // does not start with the given package prefix 145 if (!binaryName.startsWith(pkg + ".")) { 146 continue; 147 } 148 149 final int nextDot = binaryName.indexOf('.', pkg.length() + 1); 150 final int start = pkg.length() + 1; 151 152 if (nextDot != -1) { 153 // subpackage - eg. "regex" for "java.util" 154 props.add(binaryName.substring(start, nextDot)); 155 } else { 156 // class - filter out nested, inner, anonymous, local classes. 157 // Dynalink supported public nested classes as properties of 158 // StaticClass object anyway. We don't want to expose those 159 // "$" internal names as properties of package object. 160 161 final String clsName = binaryName.substring(start); 162 if (clsName.indexOf('$') == -1) { 163 props.add(clsName); 164 } 165 } 166 } 167 } 168 169 // return list of File objects for the given class path 170 private static List<File> getFiles(final String classPath) { 171 return Stream.of(classPath.split(File.pathSeparator)) 172 .map(File::new) 173 .collect(Collectors.toList()); 174 } 175} 176