1 /* 2 * Copyright (c) 2014, 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 26 package com.sun.tools.javac.file; 27 28 import java.io.IOException; 29 import java.io.UncheckedIOException; 30 import java.lang.ref.SoftReference; 31 import java.net.URI; 32 import java.nio.file.DirectoryStream; 33 import java.nio.file.FileSystem; 34 import java.nio.file.FileSystems; 35 import java.nio.file.FileSystemNotFoundException; 36 import java.nio.file.Files; 37 import java.nio.file.Path; 38 import java.nio.file.ProviderNotFoundException; 39 import java.util.Collections; 40 import java.util.HashMap; 41 import java.util.LinkedHashMap; 42 import java.util.LinkedHashSet; 43 import java.util.Map; 44 import java.util.MissingResourceException; 45 import java.util.ResourceBundle; 46 import java.util.Set; 47 48 import javax.tools.FileObject; 49 50 import com.sun.tools.javac.file.RelativePath.RelativeDirectory; 51 import com.sun.tools.javac.util.Context; 52 53 /** 54 * A package-oriented index into the jrt: filesystem. 55 */ 56 public class JRTIndex { 57 /** Get a shared instance of the cache. */ 58 private static JRTIndex sharedInstance; getSharedInstance()59 public synchronized static JRTIndex getSharedInstance() { 60 if (sharedInstance == null) { 61 try { 62 sharedInstance = new JRTIndex(); 63 } catch (IOException e) { 64 throw new UncheckedIOException(e); 65 } 66 } 67 return sharedInstance; 68 } 69 70 /** Get a context-specific instance of a cache. */ instance(Context context)71 public static JRTIndex instance(Context context) { 72 try { 73 JRTIndex instance = context.get(JRTIndex.class); 74 if (instance == null) 75 context.put(JRTIndex.class, instance = new JRTIndex()); 76 return instance; 77 } catch (IOException e) { 78 throw new UncheckedIOException(e); 79 } 80 } 81 isAvailable()82 public static boolean isAvailable() { 83 try { 84 FileSystems.getFileSystem(URI.create("jrt:/")); 85 return true; 86 } catch (ProviderNotFoundException | FileSystemNotFoundException e) { 87 return false; 88 } 89 } 90 91 92 /** 93 * The jrt: file system. 94 */ 95 private final FileSystem jrtfs; 96 97 /** 98 * A lazily evaluated set of entries about the contents of the jrt: file system. 99 */ 100 private final Map<RelativeDirectory, SoftReference<Entry>> entries; 101 102 /** 103 * An entry provides cached info about a specific package directory within jrt:. 104 */ 105 class Entry { 106 /** 107 * The regular files for this package. 108 * For now, assume just one instance of each file across all modules. 109 */ 110 final Map<String, Path> files; 111 112 /** 113 * The set of subdirectories in jrt: for this package. 114 */ 115 final Set<RelativeDirectory> subdirs; 116 117 /** 118 * The info that used to be in ct.sym for classes in this package. 119 */ 120 final CtSym ctSym; 121 Entry(Map<String, Path> files, Set<RelativeDirectory> subdirs, CtSym ctSym)122 private Entry(Map<String, Path> files, Set<RelativeDirectory> subdirs, CtSym ctSym) { 123 this.files = files; 124 this.subdirs = subdirs; 125 this.ctSym = ctSym; 126 } 127 } 128 129 /** 130 * The info that used to be in ct.sym for classes in a package. 131 */ 132 public static class CtSym { 133 /** 134 * The classes in this package are internal and not visible. 135 */ 136 public final boolean hidden; 137 /** 138 * The classes in this package are proprietary and will generate a warning. 139 */ 140 public final boolean proprietary; 141 /** 142 * The minimum profile in which classes in this package are available. 143 */ 144 public final String minProfile; 145 CtSym(boolean hidden, boolean proprietary, String minProfile)146 CtSym(boolean hidden, boolean proprietary, String minProfile) { 147 this.hidden = hidden; 148 this.proprietary = proprietary; 149 this.minProfile = minProfile; 150 } 151 152 @Override toString()153 public String toString() { 154 StringBuilder sb = new StringBuilder("CtSym["); 155 boolean needSep = false; 156 if (hidden) { 157 sb.append("hidden"); 158 needSep = true; 159 } 160 if (proprietary) { 161 if (needSep) sb.append(","); 162 sb.append("proprietary"); 163 needSep = true; 164 } 165 if (minProfile != null) { 166 if (needSep) sb.append(","); 167 sb.append(minProfile); 168 } 169 sb.append("]"); 170 return sb.toString(); 171 } 172 173 static final CtSym EMPTY = new CtSym(false, false, null); 174 } 175 176 /** 177 * Create and initialize the index. 178 */ JRTIndex()179 private JRTIndex() throws IOException { 180 jrtfs = FileSystems.getFileSystem(URI.create("jrt:/")); 181 entries = new HashMap<>(); 182 } 183 getCtSym(CharSequence packageName)184 public CtSym getCtSym(CharSequence packageName) throws IOException { 185 return getEntry(RelativeDirectory.forPackage(packageName)).ctSym; 186 } 187 getEntry(RelativeDirectory rd)188 synchronized Entry getEntry(RelativeDirectory rd) throws IOException { 189 SoftReference<Entry> ref = entries.get(rd); 190 Entry e = (ref == null) ? null : ref.get(); 191 if (e == null) { 192 Map<String, Path> files = new LinkedHashMap<>(); 193 Set<RelativeDirectory> subdirs = new LinkedHashSet<>(); 194 Path dir; 195 if (rd.path.isEmpty()) { 196 dir = jrtfs.getPath("/modules"); 197 } else { 198 Path pkgs = jrtfs.getPath("/packages"); 199 dir = pkgs.resolve(rd.getPath().replaceAll("/$", "").replace("/", ".")); 200 } 201 if (Files.exists(dir)) { 202 try (DirectoryStream<Path> modules = Files.newDirectoryStream(dir)) { 203 for (Path module: modules) { 204 if (Files.isSymbolicLink(module)) 205 module = Files.readSymbolicLink(module); 206 Path p = rd.resolveAgainst(module); 207 if (!Files.exists(p)) 208 continue; 209 try (DirectoryStream<Path> stream = Files.newDirectoryStream(p)) { 210 for (Path entry: stream) { 211 String name = entry.getFileName().toString(); 212 if (Files.isRegularFile(entry)) { 213 // TODO: consider issue of files with same name in different modules 214 files.put(name, entry); 215 } else if (Files.isDirectory(entry)) { 216 subdirs.add(new RelativeDirectory(rd, name)); 217 } 218 } 219 } 220 } 221 } 222 } 223 e = new Entry(Collections.unmodifiableMap(files), 224 Collections.unmodifiableSet(subdirs), 225 getCtInfo(rd)); 226 entries.put(rd, new SoftReference<>(e)); 227 } 228 return e; 229 } 230 isInJRT(FileObject fo)231 public boolean isInJRT(FileObject fo) { 232 if (fo instanceof PathFileObject) { 233 Path path = ((PathFileObject) fo).getPath(); 234 return (path.getFileSystem() == jrtfs); 235 } else { 236 return false; 237 } 238 } 239 getCtInfo(RelativeDirectory dir)240 private CtSym getCtInfo(RelativeDirectory dir) { 241 if (dir.path.isEmpty()) 242 return CtSym.EMPTY; 243 // It's a side-effect of the default build rules that ct.properties 244 // ends up as a resource bundle. 245 if (ctBundle == null) { 246 final String bundleName = "com.sun.tools.javac.resources.ct"; 247 ctBundle = ResourceBundle.getBundle(bundleName); 248 } 249 try { 250 String attrs = ctBundle.getString(dir.path.replace('/', '.') + '*'); 251 boolean hidden = false; 252 boolean proprietary = false; 253 String minProfile = null; 254 for (String attr: attrs.split(" +", 0)) { 255 switch (attr) { 256 case "hidden": 257 hidden = true; 258 break; 259 case "proprietary": 260 proprietary = true; 261 break; 262 default: 263 minProfile = attr; 264 } 265 } 266 return new CtSym(hidden, proprietary, minProfile); 267 } catch (MissingResourceException e) { 268 return CtSym.EMPTY; 269 } 270 271 } 272 273 private ResourceBundle ctBundle; 274 } 275