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.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; 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. */ 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 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 * The set of module directories within the jrt: file system. 99 */ 100 private final Set<Path> jrtModules; 101 102 /** 103 * A lazily evaluated set of entries about the contents of the jrt: file system. 104 */ 105 private final Map<RelativeDirectory, SoftReference<Entry>> entries; 106 107 /** 108 * An entry provides cached info about a specific package directory within jrt:. 109 */ 110 class Entry { 111 /** 112 * The regular files for this package. 113 * For now, assume just one instance of each file across all modules. 114 */ 115 final Map<String, Path> files; 116 117 /** 118 * The set of subdirectories in jrt: for this package. 119 */ 120 final Set<RelativeDirectory> subdirs; 121 122 /** 123 * The info that used to be in ct.sym for classes in this package. 124 */ 125 final CtSym ctSym; 126 127 private Entry(Map<String, Path> files, Set<RelativeDirectory> subdirs, CtSym ctSym) { 128 this.files = files; 129 this.subdirs = subdirs; 130 this.ctSym = ctSym; 131 } 132 } 133 134 /** 135 * The info that used to be in ct.sym for classes in a package. 136 */ 137 public static class CtSym { 138 /** 139 * The classes in this package are internal and not visible. 140 */ 141 public final boolean hidden; 142 /** 143 * The classes in this package are proprietary and will generate a warning. 144 */ 145 public final boolean proprietary; 146 /** 147 * The minimum profile in which classes in this package are available. 148 */ 149 public final String minProfile; 150 151 CtSym(boolean hidden, boolean proprietary, String minProfile) { 152 this.hidden = hidden; 153 this.proprietary = proprietary; 154 this.minProfile = minProfile; 155 } 156 157 @Override 158 public String toString() { 159 StringBuilder sb = new StringBuilder("CtSym["); 160 boolean needSep = false; 161 if (hidden) { 162 sb.append("hidden"); 163 needSep = true; 164 } 165 if (proprietary) { 166 if (needSep) sb.append(","); 167 sb.append("proprietary"); 168 needSep = true; 169 } 170 if (minProfile != null) { 171 if (needSep) sb.append(","); 172 sb.append(minProfile); 173 } 174 sb.append("]"); 175 return sb.toString(); 176 } 177 178 static final CtSym EMPTY = new CtSym(false, false, null); 179 } 180 181 /** 182 * Create and initialize the index. 183 */ 184 private JRTIndex() throws IOException { 185 jrtfs = FileSystems.getFileSystem(URI.create("jrt:/")); 186 jrtModules = new LinkedHashSet<>(); 187 Path root = jrtfs.getPath("/"); 188 try (DirectoryStream<Path> stream = Files.newDirectoryStream(root)) { 189 for (Path entry: stream) { 190 if (Files.isDirectory(entry)) 191 jrtModules.add(entry); 192 } 193 } 194 entries = new HashMap<>(); 195 } 196 197 public CtSym getCtSym(CharSequence packageName) throws IOException { 198 return getEntry(RelativeDirectory.forPackage(packageName)).ctSym; 199 } 200 201 synchronized Entry getEntry(RelativeDirectory rd) throws IOException { 202 SoftReference<Entry> ref = entries.get(rd); 203 Entry e = (ref == null) ? null : ref.get(); 204 if (e == null) { 205 Map<String, Path> files = new LinkedHashMap<>(); 206 Set<RelativeDirectory> subdirs = new LinkedHashSet<>(); 207 for (Path module: jrtModules) { 208 Path p = rd.getFile(module); 209 if (!Files.exists(p)) 210 continue; 211 try (DirectoryStream<Path> stream = Files.newDirectoryStream(p)) { 212 for (Path entry: stream) { 213 String name = entry.getFileName().toString(); 214 if (Files.isRegularFile(entry)) { 215 // TODO: consider issue of files with same name in different modules 216 files.put(name, entry); 217 } else if (Files.isDirectory(entry)) { 218 subdirs.add(new RelativeDirectory(rd, name)); 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 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 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 }