1 /******************************************************************************* 2 * Copyright (c) 2006, 2014 IBM Corporation and others. 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/epl-v10.html 7 * 8 * Contributors: 9 * IBM Corporation - initial API and implementation 10 *******************************************************************************/ 11 package org.eclipse.jdt.internal.compiler.apt.util; 12 13 import java.io.File; 14 import java.io.IOException; 15 import java.net.MalformedURLException; 16 import java.net.URI; 17 import java.net.URISyntaxException; 18 import java.net.URL; 19 import java.net.URLClassLoader; 20 import java.nio.charset.Charset; 21 import java.text.MessageFormat; 22 import java.util.ArrayList; 23 import java.util.Arrays; 24 import java.util.HashMap; 25 import java.util.Iterator; 26 import java.util.Locale; 27 import java.util.Map; 28 import java.util.MissingResourceException; 29 import java.util.ResourceBundle; 30 import java.util.Set; 31 import java.util.StringTokenizer; 32 import java.util.zip.ZipException; 33 34 import javax.tools.FileObject; 35 import javax.tools.JavaFileObject; 36 import javax.tools.StandardJavaFileManager; 37 import javax.tools.StandardLocation; 38 import javax.tools.JavaFileObject.Kind; 39 40 import org.eclipse.jdt.core.compiler.IProblem; 41 import org.eclipse.jdt.internal.compiler.batch.FileSystem; 42 import org.eclipse.jdt.internal.compiler.batch.Main; 43 import org.eclipse.jdt.internal.compiler.batch.Main.ResourceBundleFactory; 44 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; 45 import org.eclipse.jdt.internal.compiler.env.AccessRestriction; 46 import org.eclipse.jdt.internal.compiler.env.AccessRule; 47 import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; 48 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; 49 50 /** 51 * Implementation of the Standard Java File Manager 52 */ 53 public class EclipseFileManager implements StandardJavaFileManager { 54 private static final String NO_EXTENSION = "";//$NON-NLS-1$ 55 static final int HAS_EXT_DIRS = 1; 56 static final int HAS_BOOTCLASSPATH = 2; 57 static final int HAS_ENDORSED_DIRS = 4; 58 static final int HAS_PROCESSORPATH = 8; 59 60 Map<File, Archive> archivesCache; 61 Charset charset; 62 Locale locale; 63 Map<String, Iterable<? extends File>> locations; 64 int flags; 65 public ResourceBundle bundle; 66 EclipseFileManager(Locale locale, Charset charset)67 public EclipseFileManager(Locale locale, Charset charset) { 68 this.locale = locale == null ? Locale.getDefault() : locale; 69 this.charset = charset == null ? Charset.defaultCharset() : charset; 70 this.locations = new HashMap<String, Iterable<? extends File>>(); 71 this.archivesCache = new HashMap<File, Archive>(); 72 try { 73 this.setLocation(StandardLocation.PLATFORM_CLASS_PATH, getDefaultBootclasspath()); 74 Iterable<? extends File> defaultClasspath = getDefaultClasspath(); 75 this.setLocation(StandardLocation.CLASS_PATH, defaultClasspath); 76 this.setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, defaultClasspath); 77 } catch (IOException e) { 78 // ignore 79 } 80 try { 81 this.bundle = ResourceBundleFactory.getBundle(this.locale); 82 } catch(MissingResourceException e) { 83 System.out.println("Missing resource : " + Main.bundleName.replace('.', '/') + ".properties for locale " + locale); //$NON-NLS-1$//$NON-NLS-2$ 84 } 85 } 86 addFiles(File[][] jars, ArrayList<File> files)87 private void addFiles(File[][] jars, ArrayList<File> files) { 88 if (jars != null) { 89 for (File[] currentJars : jars) { 90 if (currentJars != null) { 91 for (File currentJar : currentJars) { 92 if (currentJar.exists()) { 93 files.add(currentJar); 94 } 95 } 96 } 97 } 98 } 99 } 100 101 addFilesFrom(File javaHome, String propertyName, String defaultPath, ArrayList<File> files)102 private void addFilesFrom(File javaHome, String propertyName, String defaultPath, ArrayList<File> files) { 103 String extdirsStr = System.getProperty(propertyName); 104 File[] directoriesToCheck = null; 105 if (extdirsStr == null) { 106 if (javaHome != null) { 107 directoriesToCheck = new File[] { new File(javaHome, defaultPath) }; 108 } 109 } else { 110 StringTokenizer tokenizer = new StringTokenizer(extdirsStr, File.pathSeparator); 111 ArrayList<String> paths = new ArrayList<String>(); 112 while (tokenizer.hasMoreTokens()) { 113 paths.add(tokenizer.nextToken()); 114 } 115 if (paths.size() != 0) { 116 directoriesToCheck = new File[paths.size()]; 117 for (int i = 0; i < directoriesToCheck.length; i++) { 118 directoriesToCheck[i] = new File(paths.get(i)); 119 } 120 } 121 } 122 if (directoriesToCheck != null) { 123 addFiles(Main.getLibrariesFiles(directoriesToCheck), files); 124 } 125 126 } 127 128 /* (non-Javadoc) 129 * @see javax.tools.JavaFileManager#close() 130 */ close()131 public void close() throws IOException { 132 this.locations = null; 133 for (Archive archive : this.archivesCache.values()) { 134 archive.close(); 135 } 136 } 137 collectAllMatchingFiles(File file, String normalizedPackageName, Set<Kind> kinds, boolean recurse, ArrayList<JavaFileObject> collector)138 private void collectAllMatchingFiles(File file, String normalizedPackageName, Set<Kind> kinds, boolean recurse, ArrayList<JavaFileObject> collector) { 139 if (!isArchive(file)) { 140 // we must have a directory 141 File currentFile = new File(file, normalizedPackageName); 142 if (!currentFile.exists()) return; 143 String path; 144 try { 145 path = currentFile.getCanonicalPath(); 146 } catch (IOException e) { 147 return; 148 } 149 if (File.separatorChar == '/') { 150 if (!path.endsWith(normalizedPackageName)) return; 151 } else if (!path.endsWith(normalizedPackageName.replace('/', File.separatorChar))) return; 152 File[] files = currentFile.listFiles(); 153 if (files != null) { 154 // this was a directory 155 for (File f : files) { 156 if (f.isDirectory() && recurse) { 157 collectAllMatchingFiles(file, normalizedPackageName + '/' + f.getName(), kinds, recurse, collector); 158 } else { 159 final Kind kind = getKind(f); 160 if (kinds.contains(kind)) { 161 collector.add(new EclipseFileObject(normalizedPackageName + f.getName(), f.toURI(), kind, this.charset)); 162 } 163 } 164 } 165 } 166 } else { 167 Archive archive = this.getArchive(file); 168 String key = normalizedPackageName; 169 if (!normalizedPackageName.endsWith("/")) {//$NON-NLS-1$ 170 key += '/'; 171 } 172 // we have an archive file 173 if (recurse) { 174 for (String packageName : archive.allPackages()) { 175 if (packageName.startsWith(key)) { 176 ArrayList<String> types = archive.getTypes(packageName); 177 if (types != null) { 178 for (String typeName : types) { 179 final Kind kind = getKind(getExtension(typeName)); 180 if (kinds.contains(kind)) { 181 collector.add(archive.getArchiveFileObject(packageName + typeName, this.charset)); 182 } 183 } 184 } 185 } 186 } 187 } else { 188 ArrayList<String> types = archive.getTypes(key); 189 if (types != null) { 190 for (String typeName : types) { 191 final Kind kind = getKind(typeName); 192 if (kinds.contains(kind)) { 193 collector.add(archive.getArchiveFileObject(normalizedPackageName + typeName, this.charset)); 194 } 195 } 196 } 197 } 198 } 199 } 200 concatFiles(Iterable<? extends File> iterable, Iterable<? extends File> iterable2)201 private Iterable<? extends File> concatFiles(Iterable<? extends File> iterable, Iterable<? extends File> iterable2) { 202 ArrayList<File> list = new ArrayList<File>(); 203 if (iterable2 == null) return iterable; 204 for (Iterator<? extends File> iterator = iterable.iterator(); iterator.hasNext(); ) { 205 list.add(iterator.next()); 206 } 207 for (Iterator<? extends File> iterator = iterable2.iterator(); iterator.hasNext(); ) { 208 list.add(iterator.next()); 209 } 210 return list; 211 } 212 213 /* (non-Javadoc) 214 * @see javax.tools.JavaFileManager#flush() 215 */ flush()216 public void flush() throws IOException { 217 for (Archive archive : this.archivesCache.values()) { 218 archive.flush(); 219 } 220 } 221 getArchive(File f)222 private Archive getArchive(File f) { 223 // check the archive (jar/zip) cache 224 Archive archive = this.archivesCache.get(f); 225 if (archive == null) { 226 // create a new archive 227 if (f.exists()) { 228 try { 229 archive = new Archive(f); 230 } catch (ZipException e) { 231 // ignore 232 } catch (IOException e) { 233 // ignore 234 } 235 if (archive != null) { 236 this.archivesCache.put(f, archive); 237 } else { 238 this.archivesCache.put(f, Archive.UNKNOWN_ARCHIVE); 239 } 240 } else { 241 this.archivesCache.put(f, Archive.UNKNOWN_ARCHIVE); 242 } 243 } 244 return archive; 245 } 246 247 /* (non-Javadoc) 248 * @see javax.tools.JavaFileManager#getClassLoader(javax.tools.JavaFileManager.Location) 249 */ getClassLoader(Location location)250 public ClassLoader getClassLoader(Location location) { 251 Iterable<? extends File> files = getLocation(location); 252 if (files == null) { 253 // location is unknown 254 return null; 255 } 256 ArrayList<URL> allURLs = new ArrayList<URL>(); 257 for (File f : files) { 258 try { 259 allURLs.add(f.toURI().toURL()); 260 } catch (MalformedURLException e) { 261 // the url is malformed - this should not happen 262 throw new RuntimeException(e); 263 } 264 } 265 URL[] result = new URL[allURLs.size()]; 266 return new URLClassLoader(allURLs.toArray(result), getClass().getClassLoader()); 267 } 268 getPathsFrom(String path)269 private Iterable<? extends File> getPathsFrom(String path) { 270 ArrayList<FileSystem.Classpath> paths = new ArrayList<FileSystem.Classpath>(); 271 ArrayList<File> files = new ArrayList<File>(); 272 try { 273 this.processPathEntries(Main.DEFAULT_SIZE_CLASSPATH, paths, path, this.charset.name(), false, false); 274 } catch (IllegalArgumentException e) { 275 return null; 276 } 277 for (FileSystem.Classpath classpath : paths) { 278 files.add(new File(classpath.getPath())); 279 } 280 return files; 281 } 282 getDefaultBootclasspath()283 Iterable<? extends File> getDefaultBootclasspath() { 284 ArrayList<File> files = new ArrayList<File>(); 285 String javaversion = System.getProperty("java.version");//$NON-NLS-1$ 286 if(javaversion.length() > 3) 287 javaversion = javaversion.substring(0, 3); 288 long jdkLevel = CompilerOptions.versionToJdkLevel(javaversion); 289 if (jdkLevel < ClassFileConstants.JDK1_6) { 290 // wrong jdk - 1.6 or above is required 291 return null; 292 } 293 294 /* 295 * Handle >= JDK 1.6 296 */ 297 String javaHome = System.getProperty("java.home"); //$NON-NLS-1$ 298 File javaHomeFile = null; 299 if (javaHome != null) { 300 javaHomeFile = new File(javaHome); 301 if (!javaHomeFile.exists()) 302 javaHomeFile = null; 303 } 304 305 addFilesFrom(javaHomeFile, "java.endorsed.dirs", "/lib/endorsed", files);//$NON-NLS-1$//$NON-NLS-2$ 306 if (javaHomeFile != null) { 307 File[] directoriesToCheck = null; 308 if (System.getProperty("os.name").startsWith("Mac")) {//$NON-NLS-1$//$NON-NLS-2$ 309 directoriesToCheck = new File[] { new File(javaHomeFile, "../Classes"), //$NON-NLS-1$ 310 }; 311 } else { 312 directoriesToCheck = new File[] { new File(javaHomeFile, "lib") //$NON-NLS-1$ 313 }; 314 } 315 File[][] jars = Main.getLibrariesFiles(directoriesToCheck); 316 addFiles(jars, files); 317 } 318 addFilesFrom(javaHomeFile, "java.ext.dirs", "/lib/ext", files);//$NON-NLS-1$//$NON-NLS-2$ 319 return files; 320 } 321 getDefaultClasspath()322 Iterable<? extends File> getDefaultClasspath() { 323 // default classpath 324 ArrayList<File> files = new ArrayList<File>(); 325 String classProp = System.getProperty("java.class.path"); //$NON-NLS-1$ 326 if ((classProp == null) || (classProp.length() == 0)) { 327 return null; 328 } else { 329 StringTokenizer tokenizer = new StringTokenizer(classProp, File.pathSeparator); 330 String token; 331 while (tokenizer.hasMoreTokens()) { 332 token = tokenizer.nextToken(); 333 File file = new File(token); 334 if (file.exists()) { 335 files.add(file); 336 } 337 } 338 } 339 return files; 340 } 341 getEndorsedDirsFrom(String path)342 private Iterable<? extends File> getEndorsedDirsFrom(String path) { 343 ArrayList<FileSystem.Classpath> paths = new ArrayList<FileSystem.Classpath>(); 344 ArrayList<File> files = new ArrayList<File>(); 345 try { 346 this.processPathEntries(Main.DEFAULT_SIZE_CLASSPATH, paths, path, this.charset.name(), false, false); 347 } catch (IllegalArgumentException e) { 348 return null; 349 } 350 for (FileSystem.Classpath classpath : paths) { 351 files.add(new File(classpath.getPath())); 352 } 353 return files; 354 } 355 getExtdirsFrom(String path)356 private Iterable<? extends File> getExtdirsFrom(String path) { 357 ArrayList<FileSystem.Classpath> paths = new ArrayList<FileSystem.Classpath>(); 358 ArrayList<File> files = new ArrayList<File>(); 359 try { 360 this.processPathEntries(Main.DEFAULT_SIZE_CLASSPATH, paths, path, this.charset.name(), false, false); 361 } catch (IllegalArgumentException e) { 362 return null; 363 } 364 for (FileSystem.Classpath classpath : paths) { 365 files.add(new File(classpath.getPath())); 366 } 367 return files; 368 } 369 getExtension(File file)370 private String getExtension(File file) { 371 String name = file.getName(); 372 return getExtension(name); 373 } getExtension(String name)374 private String getExtension(String name) { 375 int index = name.lastIndexOf('.'); 376 if (index == -1) { 377 return EclipseFileManager.NO_EXTENSION; 378 } 379 return name.substring(index); 380 } 381 382 /* (non-Javadoc) 383 * @see javax.tools.JavaFileManager#getFileForInput(javax.tools.JavaFileManager.Location, java.lang.String, java.lang.String) 384 */ getFileForInput(Location location, String packageName, String relativeName)385 public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException { 386 Iterable<? extends File> files = getLocation(location); 387 if (files == null) { 388 throw new IllegalArgumentException("Unknown location : " + location);//$NON-NLS-1$ 389 } 390 String normalizedFileName = normalized(packageName) + '/' + relativeName.replace('\\', '/'); 391 for (File file : files) { 392 if (file.isDirectory()) { 393 // handle directory 394 File f = new File(file, normalizedFileName); 395 if (f.exists()) { 396 return new EclipseFileObject(packageName + File.separator + relativeName, f.toURI(), getKind(f), this.charset); 397 } else { 398 continue; // go to next entry in the location 399 } 400 } else if (isArchive(file)) { 401 // handle archive file 402 Archive archive = getArchive(file); 403 if (archive != Archive.UNKNOWN_ARCHIVE) { 404 if (archive.contains(normalizedFileName)) { 405 return archive.getArchiveFileObject(normalizedFileName, this.charset); 406 } 407 } 408 } 409 } 410 return null; 411 } 412 413 /* (non-Javadoc) 414 * @see javax.tools.JavaFileManager#getFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, java.lang.String, javax.tools.FileObject) 415 */ getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling)416 public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) 417 throws IOException { 418 Iterable<? extends File> files = getLocation(location); 419 if (files == null) { 420 throw new IllegalArgumentException("Unknown location : " + location);//$NON-NLS-1$ 421 } 422 final Iterator<? extends File> iterator = files.iterator(); 423 if (iterator.hasNext()) { 424 File file = iterator.next(); 425 String normalizedFileName = normalized(packageName) + '/' + relativeName.replace('\\', '/'); 426 File f = new File(file, normalizedFileName); 427 return new EclipseFileObject(packageName + File.separator + relativeName, f.toURI(), getKind(f), this.charset); 428 } else { 429 throw new IllegalArgumentException("location is empty : " + location);//$NON-NLS-1$ 430 } 431 } 432 433 /* (non-Javadoc) 434 * @see javax.tools.JavaFileManager#getJavaFileForInput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind) 435 */ getJavaFileForInput(Location location, String className, Kind kind)436 public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException { 437 if (kind != Kind.CLASS && kind != Kind.SOURCE) { 438 throw new IllegalArgumentException("Invalid kind : " + kind);//$NON-NLS-1$ 439 } 440 Iterable<? extends File> files = getLocation(location); 441 if (files == null) { 442 throw new IllegalArgumentException("Unknown location : " + location);//$NON-NLS-1$ 443 } 444 String normalizedFileName = normalized(className); 445 normalizedFileName += kind.extension; 446 for (File file : files) { 447 if (file.isDirectory()) { 448 // handle directory 449 File f = new File(file, normalizedFileName); 450 if (f.exists()) { 451 return new EclipseFileObject(className, f.toURI(), kind, this.charset); 452 } else { 453 continue; // go to next entry in the location 454 } 455 } else if (isArchive(file)) { 456 // handle archive file 457 Archive archive = getArchive(file); 458 if (archive != Archive.UNKNOWN_ARCHIVE) { 459 if (archive.contains(normalizedFileName)) { 460 return archive.getArchiveFileObject(normalizedFileName, this.charset); 461 } 462 } 463 } 464 } 465 return null; 466 } 467 468 /* (non-Javadoc) 469 * @see javax.tools.JavaFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject) 470 */ getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling)471 public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) 472 throws IOException { 473 if (kind != Kind.CLASS && kind != Kind.SOURCE) { 474 throw new IllegalArgumentException("Invalid kind : " + kind);//$NON-NLS-1$ 475 } 476 Iterable<? extends File> files = getLocation(location); 477 if (files == null) { 478 if (!location.equals(StandardLocation.CLASS_OUTPUT) 479 && !location.equals(StandardLocation.SOURCE_OUTPUT)) 480 throw new IllegalArgumentException("Unknown location : " + location);//$NON-NLS-1$ 481 // we will use either the sibling or user.dir 482 if (sibling != null) { 483 String normalizedFileName = normalized(className); 484 int index = normalizedFileName.lastIndexOf('/'); 485 if (index != -1) { 486 normalizedFileName = normalizedFileName.substring(index + 1); 487 } 488 normalizedFileName += kind.extension; 489 URI uri = sibling.toUri(); 490 URI uri2 = null; 491 try { 492 String path = uri.getPath(); 493 index = path.lastIndexOf('/'); 494 if (index != -1) { 495 path = path.substring(0, index + 1); 496 path += normalizedFileName; 497 } 498 uri2 = new URI(uri.getScheme(), uri.getHost(), path, uri.getFragment()); 499 } catch (URISyntaxException e) { 500 throw new IllegalArgumentException("invalid sibling");//$NON-NLS-1$ 501 } 502 return new EclipseFileObject(className, uri2, kind, this.charset); 503 } else { 504 String normalizedFileName = normalized(className); 505 normalizedFileName += kind.extension; 506 File f = new File(System.getProperty("user.dir"), normalizedFileName);//$NON-NLS-1$ 507 return new EclipseFileObject(className, f.toURI(), kind, this.charset); 508 } 509 } 510 final Iterator<? extends File> iterator = files.iterator(); 511 if (iterator.hasNext()) { 512 File file = iterator.next(); 513 String normalizedFileName = normalized(className); 514 normalizedFileName += kind.extension; 515 File f = new File(file, normalizedFileName); 516 return new EclipseFileObject(className, f.toURI(), kind, this.charset); 517 } else { 518 throw new IllegalArgumentException("location is empty : " + location);//$NON-NLS-1$ 519 } 520 } 521 522 /* (non-Javadoc) 523 * @see javax.tools.StandardJavaFileManager#getJavaFileObjects(java.io.File[]) 524 */ getJavaFileObjects(File... files)525 public Iterable<? extends JavaFileObject> getJavaFileObjects(File... files) { 526 return getJavaFileObjectsFromFiles(Arrays.asList(files)); 527 } 528 529 /* (non-Javadoc) 530 * @see javax.tools.StandardJavaFileManager#getJavaFileObjects(java.lang.String[]) 531 */ getJavaFileObjects(String... names)532 public Iterable<? extends JavaFileObject> getJavaFileObjects(String... names) { 533 return getJavaFileObjectsFromStrings(Arrays.asList(names)); 534 } 535 536 /* (non-Javadoc) 537 * @see javax.tools.StandardJavaFileManager#getJavaFileObjectsFromFiles(java.lang.Iterable) 538 */ getJavaFileObjectsFromFiles(Iterable<? extends File> files)539 public Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(Iterable<? extends File> files) { 540 ArrayList<JavaFileObject> javaFileArrayList = new ArrayList<JavaFileObject>(); 541 for (File f : files) { 542 if (f.isDirectory()) { 543 throw new IllegalArgumentException("file : " + f.getAbsolutePath() + " is a directory"); //$NON-NLS-1$ //$NON-NLS-2$ 544 } 545 javaFileArrayList.add(new EclipseFileObject(f.getAbsolutePath(), f.toURI(), getKind(f), this.charset)); 546 } 547 return javaFileArrayList; 548 } 549 550 /* (non-Javadoc) 551 * @see javax.tools.StandardJavaFileManager#getJavaFileObjectsFromStrings(java.lang.Iterable) 552 */ getJavaFileObjectsFromStrings(Iterable<String> names)553 public Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable<String> names) { 554 ArrayList<File> files = new ArrayList<File>(); 555 for (String name : names) { 556 files.add(new File(name)); 557 } 558 return getJavaFileObjectsFromFiles(files); 559 } 560 getKind(File f)561 public Kind getKind(File f) { 562 return getKind(getExtension(f)); 563 } 564 getKind(String extension)565 private Kind getKind(String extension) { 566 if (Kind.CLASS.extension.equals(extension)) { 567 return Kind.CLASS; 568 } else if (Kind.SOURCE.extension.equals(extension)) { 569 return Kind.SOURCE; 570 } else if (Kind.HTML.extension.equals(extension)) { 571 return Kind.HTML; 572 } 573 return Kind.OTHER; 574 } 575 576 /* (non-Javadoc) 577 * @see javax.tools.StandardJavaFileManager#getLocation(javax.tools.JavaFileManager.Location) 578 */ getLocation(Location location)579 public Iterable<? extends File> getLocation(Location location) { 580 if (this.locations == null) return null; 581 return this.locations.get(location.getName()); 582 } 583 getOutputDir(String string)584 private Iterable<? extends File> getOutputDir(String string) { 585 if ("none".equals(string)) {//$NON-NLS-1$ 586 return null; 587 } 588 File file = new File(string); 589 if (file.exists() && !file.isDirectory()) { 590 throw new IllegalArgumentException("file : " + file.getAbsolutePath() + " is not a directory");//$NON-NLS-1$//$NON-NLS-2$ 591 } 592 ArrayList<File> list = new ArrayList<File>(1); 593 list.add(file); 594 return list; 595 } 596 597 /* (non-Javadoc) 598 * @see javax.tools.JavaFileManager#handleOption(java.lang.String, java.util.Iterator) 599 */ handleOption(String current, Iterator<String> remaining)600 public boolean handleOption(String current, Iterator<String> remaining) { 601 try { 602 if ("-bootclasspath".equals(current)) {//$NON-NLS-1$ 603 remaining.remove(); // remove the current option 604 if (remaining.hasNext()) { 605 final Iterable<? extends File> bootclasspaths = getPathsFrom(remaining.next()); 606 if (bootclasspaths != null) { 607 Iterable<? extends File> iterable = getLocation(StandardLocation.PLATFORM_CLASS_PATH); 608 if ((this.flags & HAS_ENDORSED_DIRS) == 0 609 && (this.flags & HAS_EXT_DIRS) == 0) { 610 // override default bootclasspath 611 setLocation(StandardLocation.PLATFORM_CLASS_PATH, bootclasspaths); 612 } else if ((this.flags & HAS_ENDORSED_DIRS) != 0) { 613 // endorseddirs have been processed first 614 setLocation(StandardLocation.PLATFORM_CLASS_PATH, 615 concatFiles(iterable, bootclasspaths)); 616 } else { 617 // extdirs have been processed first 618 setLocation(StandardLocation.PLATFORM_CLASS_PATH, 619 prependFiles(iterable, bootclasspaths)); 620 } 621 } 622 remaining.remove(); 623 this.flags |= HAS_BOOTCLASSPATH; 624 return true; 625 } else { 626 throw new IllegalArgumentException(); 627 } 628 } 629 if ("-classpath".equals(current) || "-cp".equals(current)) {//$NON-NLS-1$//$NON-NLS-2$ 630 remaining.remove(); // remove the current option 631 if (remaining.hasNext()) { 632 final Iterable<? extends File> classpaths = getPathsFrom(remaining.next()); 633 if (classpaths != null) { 634 Iterable<? extends File> iterable = getLocation(StandardLocation.CLASS_PATH); 635 if (iterable != null) { 636 setLocation(StandardLocation.CLASS_PATH, 637 concatFiles(iterable, classpaths)); 638 } else { 639 setLocation(StandardLocation.CLASS_PATH, classpaths); 640 } 641 if ((this.flags & HAS_PROCESSORPATH) == 0) { 642 setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, classpaths); 643 } 644 } 645 remaining.remove(); 646 return true; 647 } else { 648 throw new IllegalArgumentException(); 649 } 650 } 651 if ("-encoding".equals(current)) {//$NON-NLS-1$ 652 remaining.remove(); // remove the current option 653 if (remaining.hasNext()) { 654 this.charset = Charset.forName(remaining.next()); 655 remaining.remove(); 656 return true; 657 } else { 658 throw new IllegalArgumentException(); 659 } 660 } 661 if ("-sourcepath".equals(current)) {//$NON-NLS-1$ 662 remaining.remove(); // remove the current option 663 if (remaining.hasNext()) { 664 final Iterable<? extends File> sourcepaths = getPathsFrom(remaining.next()); 665 if (sourcepaths != null) setLocation(StandardLocation.SOURCE_PATH, sourcepaths); 666 remaining.remove(); 667 return true; 668 } else { 669 throw new IllegalArgumentException(); 670 } 671 } 672 if ("-extdirs".equals(current)) {//$NON-NLS-1$ 673 remaining.remove(); // remove the current option 674 if (remaining.hasNext()) { 675 Iterable<? extends File> iterable = getLocation(StandardLocation.PLATFORM_CLASS_PATH); 676 setLocation(StandardLocation.PLATFORM_CLASS_PATH, 677 concatFiles(iterable, getExtdirsFrom(remaining.next()))); 678 remaining.remove(); 679 this.flags |= HAS_EXT_DIRS; 680 return true; 681 } else { 682 throw new IllegalArgumentException(); 683 } 684 } 685 if ("-endorseddirs".equals(current)) {//$NON-NLS-1$ 686 remaining.remove(); // remove the current option 687 if (remaining.hasNext()) { 688 Iterable<? extends File> iterable = getLocation(StandardLocation.PLATFORM_CLASS_PATH); 689 setLocation(StandardLocation.PLATFORM_CLASS_PATH, 690 prependFiles(iterable, getEndorsedDirsFrom(remaining.next()))); 691 remaining.remove(); 692 this.flags |= HAS_ENDORSED_DIRS; 693 return true; 694 } else { 695 throw new IllegalArgumentException(); 696 } 697 } 698 if ("-d".equals(current)) { //$NON-NLS-1$ 699 remaining.remove(); // remove the current option 700 if (remaining.hasNext()) { 701 final Iterable<? extends File> outputDir = getOutputDir(remaining.next()); 702 if (outputDir != null) { 703 setLocation(StandardLocation.CLASS_OUTPUT, outputDir); 704 } 705 remaining.remove(); 706 return true; 707 } else { 708 throw new IllegalArgumentException(); 709 } 710 } 711 if ("-s".equals(current)) { //$NON-NLS-1$ 712 remaining.remove(); // remove the current option 713 if (remaining.hasNext()) { 714 final Iterable<? extends File> outputDir = getOutputDir(remaining.next()); 715 if (outputDir != null) { 716 setLocation(StandardLocation.SOURCE_OUTPUT, outputDir); 717 } 718 remaining.remove(); 719 return true; 720 } else { 721 throw new IllegalArgumentException(); 722 } 723 } 724 if ("-processorpath".equals(current)) {//$NON-NLS-1$ 725 remaining.remove(); // remove the current option 726 if (remaining.hasNext()) { 727 final Iterable<? extends File> processorpaths = getPathsFrom(remaining.next()); 728 if (processorpaths != null) { 729 setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, processorpaths); 730 } 731 remaining.remove(); 732 this.flags |= HAS_PROCESSORPATH; 733 return true; 734 } else { 735 throw new IllegalArgumentException(); 736 } 737 } 738 } catch (IOException e) { 739 // ignore 740 } 741 return false; 742 } 743 744 /* (non-Javadoc) 745 * @see javax.tools.JavaFileManager#hasLocation(javax.tools.JavaFileManager.Location) 746 */ hasLocation(Location location)747 public boolean hasLocation(Location location) { 748 return this.locations != null && this.locations.containsKey(location.getName()); 749 } 750 751 /* (non-Javadoc) 752 * @see javax.tools.JavaFileManager#inferBinaryName(javax.tools.JavaFileManager.Location, javax.tools.JavaFileObject) 753 */ inferBinaryName(Location location, JavaFileObject file)754 public String inferBinaryName(Location location, JavaFileObject file) { 755 String name = file.getName(); 756 JavaFileObject javaFileObject = null; 757 int index = name.lastIndexOf('.'); 758 if (index != -1) { 759 name = name.substring(0, index); 760 } 761 try { 762 javaFileObject = getJavaFileForInput(location, name, file.getKind()); 763 } catch (IOException e) { 764 // ignore 765 } catch (IllegalArgumentException iae) { 766 return null; // Either unknown kind or location not present 767 } 768 if (javaFileObject == null) { 769 return null; 770 } 771 return normalized(name); 772 } 773 isArchive(File f)774 private boolean isArchive(File f) { 775 String extension = getExtension(f); 776 return extension.equalsIgnoreCase(".jar") || extension.equalsIgnoreCase(".zip");//$NON-NLS-1$//$NON-NLS-2$ 777 } 778 779 /* (non-Javadoc) 780 * @see javax.tools.StandardJavaFileManager#isSameFile(javax.tools.FileObject, javax.tools.FileObject) 781 */ isSameFile(FileObject fileObject1, FileObject fileObject2)782 public boolean isSameFile(FileObject fileObject1, FileObject fileObject2) { 783 // EclipseFileManager creates only EcliseFileObject 784 if (!(fileObject1 instanceof EclipseFileObject)) throw new IllegalArgumentException("Unsupported file object class : " + fileObject1.getClass());//$NON-NLS-1$ 785 if (!(fileObject2 instanceof EclipseFileObject)) throw new IllegalArgumentException("Unsupported file object class : " + fileObject2.getClass());//$NON-NLS-1$ 786 return fileObject1.equals(fileObject2); 787 } 788 /* (non-Javadoc) 789 * @see javax.tools.OptionChecker#isSupportedOption(java.lang.String) 790 */ isSupportedOption(String option)791 public int isSupportedOption(String option) { 792 return Options.processOptionsFileManager(option); 793 } 794 795 /* (non-Javadoc) 796 * @see javax.tools.JavaFileManager#list(javax.tools.JavaFileManager.Location, java.lang.String, java.util.Set, boolean) 797 */ list(Location location, String packageName, Set<Kind> kinds, boolean recurse)798 public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) 799 throws IOException { 800 801 Iterable<? extends File> allFilesInLocations = getLocation(location); 802 if (allFilesInLocations == null) { 803 throw new IllegalArgumentException("Unknown location : " + location);//$NON-NLS-1$ 804 } 805 806 ArrayList<JavaFileObject> collector = new ArrayList<JavaFileObject>(); 807 String normalizedPackageName = normalized(packageName); 808 for (File file : allFilesInLocations) { 809 collectAllMatchingFiles(file, normalizedPackageName, kinds, recurse, collector); 810 } 811 return collector; 812 } 813 normalized(String className)814 private String normalized(String className) { 815 char[] classNameChars = className.toCharArray(); 816 for (int i = 0, max = classNameChars.length; i < max; i++) { 817 switch(classNameChars[i]) { 818 case '\\' : 819 classNameChars[i] = '/'; 820 break; 821 case '.' : 822 classNameChars[i] = '/'; 823 } 824 } 825 return new String(classNameChars); 826 } 827 prependFiles(Iterable<? extends File> iterable, Iterable<? extends File> iterable2)828 private Iterable<? extends File> prependFiles(Iterable<? extends File> iterable, 829 Iterable<? extends File> iterable2) { 830 if (iterable2 == null) return iterable; 831 ArrayList<File> list = new ArrayList<File>(); 832 for (Iterator<? extends File> iterator = iterable2.iterator(); iterator.hasNext(); ) { 833 list.add(iterator.next()); 834 } 835 for (Iterator<? extends File> iterator = iterable.iterator(); iterator.hasNext(); ) { 836 list.add(iterator.next()); 837 } 838 return list; 839 } 840 841 /* (non-Javadoc) 842 * @see javax.tools.StandardJavaFileManager#setLocation(javax.tools.JavaFileManager.Location, java.lang.Iterable) 843 */ setLocation(Location location, Iterable<? extends File> path)844 public void setLocation(Location location, Iterable<? extends File> path) throws IOException { 845 if (path != null) { 846 if (location.isOutputLocation()) { 847 // output location 848 int count = 0; 849 for (Iterator<? extends File> iterator = path.iterator(); iterator.hasNext(); ) { 850 iterator.next(); 851 count++; 852 } 853 if (count != 1) { 854 throw new IllegalArgumentException("output location can only have one path");//$NON-NLS-1$ 855 } 856 } 857 this.locations.put(location.getName(), path); 858 } 859 } 860 setLocale(Locale locale)861 public void setLocale(Locale locale) { 862 this.locale = locale == null ? Locale.getDefault() : locale; 863 try { 864 this.bundle = ResourceBundleFactory.getBundle(this.locale); 865 } catch(MissingResourceException e) { 866 System.out.println("Missing resource : " + Main.bundleName.replace('.', '/') + ".properties for locale " + locale); //$NON-NLS-1$//$NON-NLS-2$ 867 throw e; 868 } 869 } 870 871 @SuppressWarnings({"rawtypes", "unchecked"}) processPathEntries(final int defaultSize, final ArrayList paths, final String currentPath, String customEncoding, boolean isSourceOnly, boolean rejectDestinationPathOnJars)872 public void processPathEntries(final int defaultSize, final ArrayList paths, 873 final String currentPath, String customEncoding, boolean isSourceOnly, 874 boolean rejectDestinationPathOnJars) { 875 876 String currentClasspathName = null; 877 String currentDestinationPath = null; 878 ArrayList currentRuleSpecs = new ArrayList(defaultSize); 879 StringTokenizer tokenizer = new StringTokenizer(currentPath, 880 File.pathSeparator + "[]", true); //$NON-NLS-1$ 881 ArrayList tokens = new ArrayList(); 882 while (tokenizer.hasMoreTokens()) { 883 tokens.add(tokenizer.nextToken()); 884 } 885 // state machine 886 final int start = 0; 887 final int readyToClose = 1; 888 // 'path' 'path1[rule];path2' 889 final int readyToCloseEndingWithRules = 2; 890 // 'path[rule]' 'path1;path2[rule]' 891 final int readyToCloseOrOtherEntry = 3; 892 // 'path[rule];' 'path;' 'path1;path2;' 893 final int rulesNeedAnotherRule = 4; 894 // 'path[rule1;' 895 final int rulesStart = 5; 896 // 'path[' 'path1;path2[' 897 final int rulesReadyToClose = 6; 898 // 'path[rule' 'path[rule1;rule2' 899 final int destinationPathReadyToClose = 7; 900 // 'path[-d bin' 901 final int readyToCloseEndingWithDestinationPath = 8; 902 // 'path[-d bin]' 'path[rule][-d bin]' 903 final int destinationPathStart = 9; 904 // 'path[rule][' 905 final int bracketOpened = 10; 906 // '.*[.*' 907 final int bracketClosed = 11; 908 // '.*([.*])+' 909 910 final int error = 99; 911 int state = start; 912 String token = null; 913 int cursor = 0, tokensNb = tokens.size(), bracket = -1; 914 while (cursor < tokensNb && state != error) { 915 token = (String) tokens.get(cursor++); 916 if (token.equals(File.pathSeparator)) { 917 switch (state) { 918 case start: 919 case readyToCloseOrOtherEntry: 920 case bracketOpened: 921 break; 922 case readyToClose: 923 case readyToCloseEndingWithRules: 924 case readyToCloseEndingWithDestinationPath: 925 state = readyToCloseOrOtherEntry; 926 addNewEntry(paths, currentClasspathName, currentRuleSpecs, 927 customEncoding, currentDestinationPath, isSourceOnly, 928 rejectDestinationPathOnJars); 929 currentRuleSpecs.clear(); 930 break; 931 case rulesReadyToClose: 932 state = rulesNeedAnotherRule; 933 break; 934 case destinationPathReadyToClose: 935 throw new IllegalArgumentException( 936 this.bind("configure.incorrectDestinationPathEntry", //$NON-NLS-1$ 937 currentPath)); 938 case bracketClosed: 939 cursor = bracket + 1; 940 state = rulesStart; 941 break; 942 default: 943 state = error; 944 } 945 } else if (token.equals("[")) { //$NON-NLS-1$ 946 switch (state) { 947 case start: 948 currentClasspathName = ""; //$NON-NLS-1$ 949 //$FALL-THROUGH$ 950 case readyToClose: 951 bracket = cursor - 1; 952 //$FALL-THROUGH$ 953 case bracketClosed: 954 state = bracketOpened; 955 break; 956 case readyToCloseEndingWithRules: 957 state = destinationPathStart; 958 break; 959 case readyToCloseEndingWithDestinationPath: 960 state = rulesStart; 961 break; 962 case bracketOpened: 963 default: 964 state = error; 965 } 966 } else if (token.equals("]")) { //$NON-NLS-1$ 967 switch (state) { 968 case rulesReadyToClose: 969 state = readyToCloseEndingWithRules; 970 break; 971 case destinationPathReadyToClose: 972 state = readyToCloseEndingWithDestinationPath; 973 break; 974 case bracketOpened: 975 state = bracketClosed; 976 break; 977 case bracketClosed: 978 default: 979 state = error; 980 } 981 } else { 982 // regular word 983 switch (state) { 984 case start: 985 case readyToCloseOrOtherEntry: 986 state = readyToClose; 987 currentClasspathName = token; 988 break; 989 case rulesStart: 990 if (token.startsWith("-d ")) { //$NON-NLS-1$ 991 if (currentDestinationPath != null) { 992 throw new IllegalArgumentException( 993 this.bind("configure.duplicateDestinationPathEntry", //$NON-NLS-1$ 994 currentPath)); 995 } 996 currentDestinationPath = token.substring(3).trim(); 997 state = destinationPathReadyToClose; 998 break; 999 } // else we proceed with a rule 1000 //$FALL-THROUGH$ 1001 case rulesNeedAnotherRule: 1002 if (currentDestinationPath != null) { 1003 throw new IllegalArgumentException( 1004 this.bind("configure.accessRuleAfterDestinationPath", //$NON-NLS-1$ 1005 currentPath)); 1006 } 1007 state = rulesReadyToClose; 1008 currentRuleSpecs.add(token); 1009 break; 1010 case destinationPathStart: 1011 if (!token.startsWith("-d ")) { //$NON-NLS-1$ 1012 state = error; 1013 } else { 1014 currentDestinationPath = token.substring(3).trim(); 1015 state = destinationPathReadyToClose; 1016 } 1017 break; 1018 case bracketClosed: 1019 for (int i = bracket; i < cursor ; i++) { 1020 currentClasspathName += (String) tokens.get(i); 1021 } 1022 state = readyToClose; 1023 break; 1024 case bracketOpened: 1025 break; 1026 default: 1027 state = error; 1028 } 1029 } 1030 if (state == bracketClosed && cursor == tokensNb) { 1031 cursor = bracket + 1; 1032 state = rulesStart; 1033 } 1034 } 1035 switch(state) { 1036 case readyToCloseOrOtherEntry: 1037 break; 1038 case readyToClose: 1039 case readyToCloseEndingWithRules: 1040 case readyToCloseEndingWithDestinationPath: 1041 addNewEntry(paths, currentClasspathName, currentRuleSpecs, 1042 customEncoding, currentDestinationPath, isSourceOnly, 1043 rejectDestinationPathOnJars); 1044 break; 1045 case bracketOpened: 1046 case bracketClosed: 1047 default : 1048 // we go on anyway 1049 } 1050 } 1051 @SuppressWarnings({"rawtypes", "unchecked"}) addNewEntry(ArrayList paths, String currentClasspathName, ArrayList currentRuleSpecs, String customEncoding, String destPath, boolean isSourceOnly, boolean rejectDestinationPathOnJars)1052 protected void addNewEntry(ArrayList paths, String currentClasspathName, 1053 ArrayList currentRuleSpecs, String customEncoding, 1054 String destPath, boolean isSourceOnly, 1055 boolean rejectDestinationPathOnJars) { 1056 1057 int rulesSpecsSize = currentRuleSpecs.size(); 1058 AccessRuleSet accessRuleSet = null; 1059 if (rulesSpecsSize != 0) { 1060 AccessRule[] accessRules = new AccessRule[currentRuleSpecs.size()]; 1061 boolean rulesOK = true; 1062 Iterator i = currentRuleSpecs.iterator(); 1063 int j = 0; 1064 while (i.hasNext()) { 1065 String ruleSpec = (String) i.next(); 1066 char key = ruleSpec.charAt(0); 1067 String pattern = ruleSpec.substring(1); 1068 if (pattern.length() > 0) { 1069 switch (key) { 1070 case '+': 1071 accessRules[j++] = new AccessRule(pattern 1072 .toCharArray(), 0); 1073 break; 1074 case '~': 1075 accessRules[j++] = new AccessRule(pattern 1076 .toCharArray(), 1077 IProblem.DiscouragedReference); 1078 break; 1079 case '-': 1080 accessRules[j++] = new AccessRule(pattern 1081 .toCharArray(), 1082 IProblem.ForbiddenReference); 1083 break; 1084 case '?': 1085 accessRules[j++] = new AccessRule(pattern 1086 .toCharArray(), 1087 IProblem.ForbiddenReference, true/*keep looking for accessible type*/); 1088 break; 1089 default: 1090 rulesOK = false; 1091 } 1092 } else { 1093 rulesOK = false; 1094 } 1095 } 1096 if (rulesOK) { 1097 accessRuleSet = new AccessRuleSet(accessRules, AccessRestriction.COMMAND_LINE, currentClasspathName); 1098 } else { 1099 return; 1100 } 1101 } 1102 if (Main.NONE.equals(destPath)) { 1103 destPath = Main.NONE; // keep == comparison valid 1104 } 1105 if (rejectDestinationPathOnJars && destPath != null && 1106 (currentClasspathName.endsWith(".jar") || //$NON-NLS-1$ 1107 currentClasspathName.endsWith(".zip"))) { //$NON-NLS-1$ 1108 throw new IllegalArgumentException( 1109 this.bind("configure.unexpectedDestinationPathEntryFile", //$NON-NLS-1$ 1110 currentClasspathName)); 1111 } 1112 FileSystem.Classpath currentClasspath = FileSystem.getClasspath( 1113 currentClasspathName, 1114 customEncoding, 1115 isSourceOnly, 1116 accessRuleSet, 1117 destPath); 1118 if (currentClasspath != null) { 1119 paths.add(currentClasspath); 1120 } 1121 } 1122 /* 1123 * Lookup the message with the given ID in this catalog and bind its 1124 * substitution locations with the given string. 1125 */ bind(String id, String binding)1126 private String bind(String id, String binding) { 1127 return bind(id, new String[] { binding }); 1128 } 1129 1130 /* 1131 * Lookup the message with the given ID in this catalog and bind its 1132 * substitution locations with the given string values. 1133 */ bind(String id, String[] arguments)1134 private String bind(String id, String[] arguments) { 1135 if (id == null) 1136 return "No message available"; //$NON-NLS-1$ 1137 String message = null; 1138 try { 1139 message = this.bundle.getString(id); 1140 } catch (MissingResourceException e) { 1141 // If we got an exception looking for the message, fail gracefully by just returning 1142 // the id we were looking for. In most cases this is semi-informative so is not too bad. 1143 return "Missing message: " + id + " in: " + Main.bundleName; //$NON-NLS-2$ //$NON-NLS-1$ 1144 } 1145 return MessageFormat.format(message, (Object[]) arguments); 1146 } 1147 } 1148