1 /*
2 * Copyright (c) 2014, 2021, 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;
59 public static synchronized 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 * 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
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
146 CtSym(boolean hidden, boolean proprietary, String minProfile) {
147 this.hidden = hidden;
148 this.proprietary = proprietary;
149 this.minProfile = minProfile;
150 }
151
152 @Override
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 */
179 private JRTIndex() throws IOException {
180 jrtfs = FileSystems.getFileSystem(URI.create("jrt:/"));
181 entries = new HashMap<>();
182 }
183
184 public CtSym getCtSym(CharSequence packageName) throws IOException {
185 return getEntry(RelativeDirectory.forPackage(packageName)).ctSym;
186 }
187
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
231 public boolean isInJRT(FileObject fo) {
232 if (fo instanceof PathFileObject pathFileObject) {
233 Path path = pathFileObject.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 }