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