103 this.log = log; |
108 this.log = log; |
104 warn = lint.isEnabled(Lint.LintCategory.PATH); |
109 warn = lint.isEnabled(Lint.LintCategory.PATH); |
105 this.fsInfo = fsInfo; |
110 this.fsInfo = fsInfo; |
106 } |
111 } |
107 |
112 |
108 public Collection<File> bootClassPath() { |
113 public Collection<Path> bootClassPath() { |
109 return getLocation(PLATFORM_CLASS_PATH); |
114 return getLocation(PLATFORM_CLASS_PATH); |
110 } |
115 } |
111 |
116 |
112 public boolean isDefaultBootClassPath() { |
117 public boolean isDefaultBootClassPath() { |
113 BootClassPathLocationHandler h |
118 BootClassPathLocationHandler h |
114 = (BootClassPathLocationHandler) getHandler(PLATFORM_CLASS_PATH); |
119 = (BootClassPathLocationHandler) getHandler(PLATFORM_CLASS_PATH); |
115 return h.isDefault(); |
120 return h.isDefault(); |
116 } |
121 } |
117 |
122 |
118 boolean isDefaultBootClassPathRtJar(File file) { |
123 boolean isDefaultBootClassPathRtJar(Path file) { |
119 BootClassPathLocationHandler h |
124 BootClassPathLocationHandler h |
120 = (BootClassPathLocationHandler) getHandler(PLATFORM_CLASS_PATH); |
125 = (BootClassPathLocationHandler) getHandler(PLATFORM_CLASS_PATH); |
121 return h.isDefaultRtJar(file); |
126 return h.isDefaultRtJar(file); |
122 } |
127 } |
123 |
128 |
124 public Collection<File> userClassPath() { |
129 public Collection<Path> userClassPath() { |
125 return getLocation(CLASS_PATH); |
130 return getLocation(CLASS_PATH); |
126 } |
131 } |
127 |
132 |
128 public Collection<File> sourcePath() { |
133 public Collection<Path> sourcePath() { |
129 Collection<File> p = getLocation(SOURCE_PATH); |
134 Collection<Path> p = getLocation(SOURCE_PATH); |
130 // TODO: this should be handled by the LocationHandler |
135 // TODO: this should be handled by the LocationHandler |
131 return p == null || p.isEmpty() ? null : p; |
136 return p == null || p.isEmpty() ? null : p; |
132 } |
137 } |
133 |
138 |
134 /** |
139 /** |
135 * Split a path into its elements. Empty path elements will be ignored. |
140 * Split a search path into its elements. Empty path elements will be ignored. |
136 * |
141 * |
137 * @param path The path to be split |
142 * @param searchPath The search path to be split |
138 * @return The elements of the path |
143 * @return The elements of the path |
139 */ |
144 */ |
140 private static Iterable<File> getPathEntries(String path) { |
145 private static Iterable<Path> getPathEntries(String searchPath) { |
141 return getPathEntries(path, null); |
146 return getPathEntries(searchPath, null); |
142 } |
147 } |
143 |
148 |
144 /** |
149 /** |
145 * Split a path into its elements. If emptyPathDefault is not null, all empty elements in the |
150 * Split a search path into its elements. If emptyPathDefault is not null, all empty elements in the |
146 * path, including empty elements at either end of the path, will be replaced with the value of |
151 * path, including empty elements at either end of the path, will be replaced with the value of |
147 * emptyPathDefault. |
152 * emptyPathDefault. |
148 * |
153 * |
149 * @param path The path to be split |
154 * @param searchPath The search path to be split |
150 * @param emptyPathDefault The value to substitute for empty path elements, or null, to ignore |
155 * @param emptyPathDefault The value to substitute for empty path elements, or null, to ignore |
151 * empty path elements |
156 * empty path elements |
152 * @return The elements of the path |
157 * @return The elements of the path |
153 */ |
158 */ |
154 private static Iterable<File> getPathEntries(String path, File emptyPathDefault) { |
159 private static Iterable<Path> getPathEntries(String searchPath, Path emptyPathDefault) { |
155 ListBuffer<File> entries = new ListBuffer<>(); |
160 ListBuffer<Path> entries = new ListBuffer<>(); |
156 int start = 0; |
161 for (String s: searchPath.split(Pattern.quote(File.pathSeparator), -1)) { |
157 while (start <= path.length()) { |
162 if (s.isEmpty()) { |
158 int sep = path.indexOf(File.pathSeparatorChar, start); |
163 if (emptyPathDefault != null) { |
159 if (sep == -1) { |
164 entries.add(emptyPathDefault); |
160 sep = path.length(); |
165 } |
161 } |
166 } else { |
162 if (start < sep) { |
167 entries.add(Paths.get(s)); |
163 entries.add(new File(path.substring(start, sep))); |
168 } |
164 } else if (emptyPathDefault != null) { |
|
165 entries.add(emptyPathDefault); |
|
166 } |
|
167 start = sep + 1; |
|
168 } |
169 } |
169 return entries; |
170 return entries; |
170 } |
171 } |
171 |
172 |
172 /** |
173 /** |
173 * Utility class to help evaluate a path option. Duplicate entries are ignored, jar class paths |
174 * Utility class to help evaluate a path option. Duplicate entries are ignored, jar class paths |
174 * can be expanded. |
175 * can be expanded. |
175 */ |
176 */ |
176 private class SearchPath extends LinkedHashSet<File> { |
177 private class SearchPath extends LinkedHashSet<Path> { |
177 |
178 |
178 private static final long serialVersionUID = 0; |
179 private static final long serialVersionUID = 0; |
179 |
180 |
180 private boolean expandJarClassPaths = false; |
181 private boolean expandJarClassPaths = false; |
181 private final Set<File> canonicalValues = new HashSet<>(); |
182 private final Set<Path> canonicalValues = new HashSet<>(); |
182 |
183 |
183 public SearchPath expandJarClassPaths(boolean x) { |
184 public SearchPath expandJarClassPaths(boolean x) { |
184 expandJarClassPaths = x; |
185 expandJarClassPaths = x; |
185 return this; |
186 return this; |
186 } |
187 } |
187 |
188 |
188 /** |
189 /** |
189 * What to use when path element is the empty string |
190 * What to use when path element is the empty string |
190 */ |
191 */ |
191 private File emptyPathDefault = null; |
192 private Path emptyPathDefault = null; |
192 |
193 |
193 public SearchPath emptyPathDefault(File x) { |
194 public SearchPath emptyPathDefault(Path x) { |
194 emptyPathDefault = x; |
195 emptyPathDefault = x; |
195 return this; |
196 return this; |
196 } |
197 } |
197 |
198 |
198 public SearchPath addDirectories(String dirs, boolean warn) { |
199 public SearchPath addDirectories(String dirs, boolean warn) { |
199 boolean prev = expandJarClassPaths; |
200 boolean prev = expandJarClassPaths; |
200 expandJarClassPaths = true; |
201 expandJarClassPaths = true; |
201 try { |
202 try { |
202 if (dirs != null) { |
203 if (dirs != null) { |
203 for (File dir : getPathEntries(dirs)) { |
204 for (Path dir : getPathEntries(dirs)) { |
204 addDirectory(dir, warn); |
205 addDirectory(dir, warn); |
205 } |
206 } |
206 } |
207 } |
207 return this; |
208 return this; |
208 } finally { |
209 } finally { |
212 |
213 |
213 public SearchPath addDirectories(String dirs) { |
214 public SearchPath addDirectories(String dirs) { |
214 return addDirectories(dirs, warn); |
215 return addDirectories(dirs, warn); |
215 } |
216 } |
216 |
217 |
217 private void addDirectory(File dir, boolean warn) { |
218 private void addDirectory(Path dir, boolean warn) { |
218 if (!dir.isDirectory()) { |
219 if (!Files.isDirectory(dir)) { |
219 if (warn) { |
220 if (warn) { |
220 log.warning(Lint.LintCategory.PATH, |
221 log.warning(Lint.LintCategory.PATH, |
221 "dir.path.element.not.found", dir); |
222 "dir.path.element.not.found", dir); |
222 } |
223 } |
223 return; |
224 return; |
224 } |
225 } |
225 |
226 |
226 File[] files = dir.listFiles(); |
227 try (Stream<Path> s = Files.list(dir)) { |
227 if (files == null) { |
228 s.filter(dirEntry -> isArchive(dirEntry)) |
228 return; |
229 .forEach(dirEntry -> addFile(dirEntry, warn)); |
229 } |
230 } catch (IOException ignore) { |
230 |
|
231 for (File direntry : files) { |
|
232 if (isArchive(direntry)) { |
|
233 addFile(direntry, warn); |
|
234 } |
|
235 } |
231 } |
236 } |
232 } |
237 |
233 |
238 public SearchPath addFiles(String files, boolean warn) { |
234 public SearchPath addFiles(String files, boolean warn) { |
239 if (files != null) { |
235 if (files != null) { |
363 abstract boolean handleOption(Option option, String value); |
359 abstract boolean handleOption(Option option, String value); |
364 |
360 |
365 /** |
361 /** |
366 * @see StandardJavaFileManager#getLocation |
362 * @see StandardJavaFileManager#getLocation |
367 */ |
363 */ |
368 abstract Collection<File> getLocation(); |
364 abstract Collection<Path> getLocation(); |
369 |
365 |
370 /** |
366 /** |
371 * @see StandardJavaFileManager#setLocation |
367 * @see StandardJavaFileManager#setLocation |
372 */ |
368 */ |
373 abstract void setLocation(Iterable<? extends File> files) throws IOException; |
369 abstract void setLocation(Iterable<? extends Path> files) throws IOException; |
374 } |
370 } |
375 |
371 |
376 /** |
372 /** |
377 * General purpose implementation for output locations, such as -d/CLASS_OUTPUT and |
373 * General purpose implementation for output locations, such as -d/CLASS_OUTPUT and |
378 * -s/SOURCE_OUTPUT. All options are treated as equivalent (i.e. aliases.) The value is a single |
374 * -s/SOURCE_OUTPUT. All options are treated as equivalent (i.e. aliases.) The value is a single |
379 * file, possibly null. |
375 * file, possibly null. |
380 */ |
376 */ |
381 private class OutputLocationHandler extends LocationHandler { |
377 private class OutputLocationHandler extends LocationHandler { |
382 |
378 |
383 private File outputDir; |
379 private Path outputDir; |
384 |
380 |
385 OutputLocationHandler(Location location, Option... options) { |
381 OutputLocationHandler(Location location, Option... options) { |
386 super(location, options); |
382 super(location, options); |
387 } |
383 } |
388 |
384 |
394 |
390 |
395 // TODO: could/should validate outputDir exists and is a directory |
391 // TODO: could/should validate outputDir exists and is a directory |
396 // need to decide how best to report issue for benefit of |
392 // need to decide how best to report issue for benefit of |
397 // direct API call on JavaFileManager.handleOption(specifies IAE) |
393 // direct API call on JavaFileManager.handleOption(specifies IAE) |
398 // vs. command line decoding. |
394 // vs. command line decoding. |
399 outputDir = (value == null) ? null : new File(value); |
395 outputDir = (value == null) ? null : Paths.get(value); |
400 return true; |
396 return true; |
401 } |
397 } |
402 |
398 |
403 @Override |
399 @Override |
404 Collection<File> getLocation() { |
400 Collection<Path> getLocation() { |
405 return (outputDir == null) ? null : Collections.singleton(outputDir); |
401 return (outputDir == null) ? null : Collections.singleton(outputDir); |
406 } |
402 } |
407 |
403 |
408 @Override |
404 @Override |
409 void setLocation(Iterable<? extends File> files) throws IOException { |
405 void setLocation(Iterable<? extends Path> files) throws IOException { |
410 if (files == null) { |
406 if (files == null) { |
411 outputDir = null; |
407 outputDir = null; |
412 } else { |
408 } else { |
413 Iterator<? extends File> pathIter = files.iterator(); |
409 Iterator<? extends Path> pathIter = files.iterator(); |
414 if (!pathIter.hasNext()) { |
410 if (!pathIter.hasNext()) { |
415 throw new IllegalArgumentException("empty path for directory"); |
411 throw new IllegalArgumentException("empty path for directory"); |
416 } |
412 } |
417 File dir = pathIter.next(); |
413 Path dir = pathIter.next(); |
418 if (pathIter.hasNext()) { |
414 if (pathIter.hasNext()) { |
419 throw new IllegalArgumentException("path too long for directory"); |
415 throw new IllegalArgumentException("path too long for directory"); |
420 } |
416 } |
421 if (!dir.exists()) { |
417 if (!Files.exists(dir)) { |
422 throw new FileNotFoundException(dir + ": does not exist"); |
418 throw new FileNotFoundException(dir + ": does not exist"); |
423 } else if (!dir.isDirectory()) { |
419 } else if (!Files.isDirectory(dir)) { |
424 throw new IOException(dir + ": not a directory"); |
420 throw new IOException(dir + ": not a directory"); |
425 } |
421 } |
426 outputDir = dir; |
422 outputDir = dir; |
427 } |
423 } |
428 } |
424 } |
707 public boolean handleOption(Option option, String value) { |
703 public boolean handleOption(Option option, String value) { |
708 LocationHandler h = handlersForOption.get(option); |
704 LocationHandler h = handlersForOption.get(option); |
709 return (h == null ? false : h.handleOption(option, value)); |
705 return (h == null ? false : h.handleOption(option, value)); |
710 } |
706 } |
711 |
707 |
712 Collection<File> getLocation(Location location) { |
708 Collection<Path> getLocation(Location location) { |
713 LocationHandler h = getHandler(location); |
709 LocationHandler h = getHandler(location); |
714 return (h == null ? null : h.getLocation()); |
710 return (h == null ? null : h.getLocation()); |
715 } |
711 } |
716 |
712 |
717 File getOutputLocation(Location location) { |
713 Path getOutputLocation(Location location) { |
718 if (!location.isOutputLocation()) { |
714 if (!location.isOutputLocation()) { |
719 throw new IllegalArgumentException(); |
715 throw new IllegalArgumentException(); |
720 } |
716 } |
721 LocationHandler h = getHandler(location); |
717 LocationHandler h = getHandler(location); |
722 return ((OutputLocationHandler) h).outputDir; |
718 return ((OutputLocationHandler) h).outputDir; |
723 } |
719 } |
724 |
720 |
725 void setLocation(Location location, Iterable<? extends File> files) throws IOException { |
721 void setLocation(Location location, Iterable<? extends Path> files) throws IOException { |
726 LocationHandler h = getHandler(location); |
722 LocationHandler h = getHandler(location); |
727 if (h == null) { |
723 if (h == null) { |
728 if (location.isOutputLocation()) { |
724 if (location.isOutputLocation()) { |
729 h = new OutputLocationHandler(location); |
725 h = new OutputLocationHandler(location); |
730 } else { |
726 } else { |
741 } |
737 } |
742 |
738 |
743 /** |
739 /** |
744 * Is this the name of an archive file? |
740 * Is this the name of an archive file? |
745 */ |
741 */ |
746 private boolean isArchive(File file) { |
742 private boolean isArchive(Path file) { |
747 String n = StringUtils.toLowerCase(file.getName()); |
743 String n = StringUtils.toLowerCase(file.getFileName().toString()); |
748 return fsInfo.isFile(file) |
744 return fsInfo.isFile(file) |
749 && (n.endsWith(".jar") || n.endsWith(".zip")); |
745 && (n.endsWith(".jar") || n.endsWith(".zip")); |
750 } |
746 } |
751 |
747 |
752 /** |
748 /** |
753 * Utility method for converting a search path string to an array of directory and JAR file |
749 * Utility method for converting a search path string to an array of directory and JAR file |
754 * URLs. |
750 * URLs. |
755 * |
751 * |
756 * Note that this method is called by apt and the DocletInvoker. |
752 * Note that this method is called by the DocletInvoker. |
757 * |
753 * |
758 * @param path the search path string |
754 * @param path the search path string |
759 * @return the resulting array of directory and JAR file URLs |
755 * @return the resulting array of directory and JAR file URLs |
760 */ |
756 */ |
761 public static URL[] pathToURLs(String path) { |
757 public static URL[] pathToURLs(String path) { |
762 StringTokenizer st = new StringTokenizer(path, File.pathSeparator); |
758 java.util.List<URL> urls = new ArrayList<>(); |
763 URL[] urls = new URL[st.countTokens()]; |
759 for (String s: path.split(Pattern.quote(File.pathSeparator))) { |
764 int count = 0; |
760 if (!s.isEmpty()) { |
765 while (st.hasMoreTokens()) { |
761 URL url = fileToURL(Paths.get(s)); |
766 URL url = fileToURL(new File(st.nextToken())); |
762 if (url != null) { |
767 if (url != null) { |
763 urls.add(url); |
768 urls[count++] = url; |
764 } |
769 } |
765 } |
770 } |
766 } |
771 urls = Arrays.copyOf(urls, count); |
767 return urls.toArray(new URL[urls.size()]); |
772 return urls; |
|
773 } |
768 } |
774 |
769 |
775 /** |
770 /** |
776 * Returns the directory or JAR file URL corresponding to the specified local file name. |
771 * Returns the directory or JAR file URL corresponding to the specified local file name. |
777 * |
772 * |
778 * @param file the File object |
773 * @param file the Path object |
779 * @return the resulting directory or JAR file URL, or null if unknown |
774 * @return the resulting directory or JAR file URL, or null if unknown |
780 */ |
775 */ |
781 private static URL fileToURL(File file) { |
776 private static URL fileToURL(Path file) { |
782 String name; |
777 Path p; |
783 try { |
778 try { |
784 name = file.getCanonicalPath(); |
779 p = file.toRealPath(); |
785 } catch (IOException e) { |
780 } catch (IOException e) { |
786 name = file.getAbsolutePath(); |
781 p = file.toAbsolutePath(); |
787 } |
|
788 name = name.replace(File.separatorChar, '/'); |
|
789 if (!name.startsWith("/")) { |
|
790 name = "/" + name; |
|
791 } |
|
792 // If the file does not exist, then assume that it's a directory |
|
793 if (!file.isFile()) { |
|
794 name = name + "/"; |
|
795 } |
782 } |
796 try { |
783 try { |
797 return new URL("file", "", name); |
784 return p.normalize().toUri().toURL(); |
798 } catch (MalformedURLException e) { |
785 } catch (MalformedURLException e) { |
799 throw new IllegalArgumentException(file.toString()); |
786 return null; |
800 } |
787 } |
801 } |
788 } |
802 } |
789 } |