/******************************************************************************* * Copyright (c) 2006, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.compiler.tool; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.nio.charset.Charset; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.Set; import java.util.StringTokenizer; import java.util.zip.ZipException; import javax.tools.FileObject; import javax.tools.JavaFileObject; import javax.tools.JavaFileObject.Kind; import javax.tools.StandardJavaFileManager; import javax.tools.StandardLocation; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.internal.compiler.batch.FileSystem; import org.eclipse.jdt.internal.compiler.batch.Main; import org.eclipse.jdt.internal.compiler.batch.Main.ResourceBundleFactory; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.env.AccessRestriction; import org.eclipse.jdt.internal.compiler.env.AccessRule; import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; /** * Implementation of the Standard Java File Manager */ public class EclipseFileManager implements StandardJavaFileManager { private static final String NO_EXTENSION = "";//$NON-NLS-1$ static final int HAS_EXT_DIRS = 1; static final int HAS_BOOTCLASSPATH = 2; static final int HAS_ENDORSED_DIRS = 4; static final int HAS_PROCESSORPATH = 8; Map<File, Archive> archivesCache; Charset charset; Locale locale; Map<String, Iterable<? extends File>> locations; int flags; public ResourceBundle bundle; public EclipseFileManager(Locale locale, Charset charset) { this.locale = locale == null ? Locale.getDefault() : locale; this.charset = charset == null ? Charset.defaultCharset() : charset; this.locations = new HashMap<>(); this.archivesCache = new HashMap<>(); try { this.setLocation(StandardLocation.PLATFORM_CLASS_PATH, getDefaultBootclasspath()); Iterable<? extends File> defaultClasspath = getDefaultClasspath(); this.setLocation(StandardLocation.CLASS_PATH, defaultClasspath); this.setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, defaultClasspath); } catch (IOException e) { // ignore } try { this.bundle = ResourceBundleFactory.getBundle(this.locale); } catch(MissingResourceException e) { System.out.println("Missing resource : " + Main.bundleName.replace('.', '/') + ".properties for locale " + locale); //$NON-NLS-1$//$NON-NLS-2$ } } /* (non-Javadoc) * @see javax.tools.JavaFileManager#close() */ @Override public void close() throws IOException { if (this.locations != null) this.locations.clear(); for (Archive archive : this.archivesCache.values()) { archive.close(); } this.archivesCache.clear(); } private void collectAllMatchingFiles(File file, String normalizedPackageName, Set<Kind> kinds, boolean recurse, ArrayList<JavaFileObject> collector) { if (!isArchive(file)) { // we must have a directory File currentFile = new File(file, normalizedPackageName); if (!currentFile.exists()) return; String path; try { path = currentFile.getCanonicalPath(); } catch (IOException e) { return; } if (File.separatorChar == '/') { if (!path.endsWith(normalizedPackageName)) return; } else if (!path.endsWith(normalizedPackageName.replace('/', File.separatorChar))) return; File[] files = currentFile.listFiles(); if (files != null) { // this was a directory for (File f : files) { if (f.isDirectory() && recurse) { collectAllMatchingFiles(file, normalizedPackageName + '/' + f.getName(), kinds, recurse, collector); } else { final Kind kind = getKind(f); if (kinds.contains(kind)) { collector.add(new EclipseFileObject(normalizedPackageName + f.getName(), f.toURI(), kind, this.charset)); } } } } } else { Archive archive = this.getArchive(file); if (archive == Archive.UNKNOWN_ARCHIVE) return; String key = normalizedPackageName; if (!normalizedPackageName.endsWith("/")) {//$NON-NLS-1$ key += '/'; } // we have an archive file if (recurse) { for (String packageName : archive.allPackages()) { if (packageName.startsWith(key)) { List<String> types = archive.getTypes(packageName); if (types != null) { for (String typeName : types) { final Kind kind = getKind(getExtension(typeName)); if (kinds.contains(kind)) { collector.add(archive.getArchiveFileObject(packageName + typeName, this.charset)); } } } } } } else { List<String> types = archive.getTypes(key); if (types != null) { for (String typeName : types) { final Kind kind = getKind(getExtension(typeName)); if (kinds.contains(kind)) { collector.add(archive.getArchiveFileObject(key + typeName, this.charset)); } } } } } } private Iterable<? extends File> concatFiles(Iterable<? extends File> iterable, Iterable<? extends File> iterable2) { ArrayList<File> list = new ArrayList<>(); if (iterable2 == null) return iterable; for (Iterator<? extends File> iterator = iterable.iterator(); iterator.hasNext(); ) { list.add(iterator.next()); } for (Iterator<? extends File> iterator = iterable2.iterator(); iterator.hasNext(); ) { list.add(iterator.next()); } return list; } /* (non-Javadoc) * @see javax.tools.JavaFileManager#flush() */ @Override public void flush() throws IOException { for (Archive archive : this.archivesCache.values()) { archive.flush(); } } private Archive getArchive(File f) { // check the archive (jar/zip) cache Archive archive = this.archivesCache.get(f); if (archive == null) { archive = Archive.UNKNOWN_ARCHIVE; // create a new archive if (f.exists()) { try { archive = new Archive(f); } catch (ZipException e) { // ignore } catch (IOException e) { // ignore } if (archive != null) { this.archivesCache.put(f, archive); } } this.archivesCache.put(f, archive); } return archive; } /* (non-Javadoc) * @see javax.tools.JavaFileManager#getClassLoader(javax.tools.JavaFileManager.Location) */ @Override public ClassLoader getClassLoader(Location location) { Iterable<? extends File> files = getLocation(location); if (files == null) { // location is unknown return null; } ArrayList<URL> allURLs = new ArrayList<>(); for (File f : files) { try { allURLs.add(f.toURI().toURL()); } catch (MalformedURLException e) { // the url is malformed - this should not happen throw new RuntimeException(e); } } URL[] result = new URL[allURLs.size()]; return new URLClassLoader(allURLs.toArray(result), getClass().getClassLoader()); } private Iterable<? extends File> getPathsFrom(String path) { ArrayList<FileSystem.Classpath> paths = new ArrayList<>(); ArrayList<File> files = new ArrayList<>(); try { this.processPathEntries(Main.DEFAULT_SIZE_CLASSPATH, paths, path, this.charset.name(), false, false); } catch (IllegalArgumentException e) { return null; } for (FileSystem.Classpath classpath : paths) { files.add(new File(classpath.getPath())); } return files; } Iterable<? extends File> getDefaultBootclasspath() { List<File> files = new ArrayList<>(); String javaversion = System.getProperty("java.version");//$NON-NLS-1$ if(javaversion.length() > 3) javaversion = javaversion.substring(0, 3); long jdkLevel = CompilerOptions.versionToJdkLevel(javaversion); if (jdkLevel < ClassFileConstants.JDK1_6) { // wrong jdk - 1.6 or above is required return null; } for (String fileName : org.eclipse.jdt.internal.compiler.util.Util.collectFilesNames()) { files.add(new File(fileName)); } return files; } Iterable<? extends File> getDefaultClasspath() { // default classpath ArrayList<File> files = new ArrayList<>(); String classProp = System.getProperty("java.class.path"); //$NON-NLS-1$ if ((classProp == null) || (classProp.length() == 0)) { return null; } else { StringTokenizer tokenizer = new StringTokenizer(classProp, File.pathSeparator); String token; while (tokenizer.hasMoreTokens()) { token = tokenizer.nextToken(); File file = new File(token); if (file.exists()) { files.add(file); } } } return files; } private Iterable<? extends File> getEndorsedDirsFrom(String path) { ArrayList<FileSystem.Classpath> paths = new ArrayList<>(); ArrayList<File> files = new ArrayList<>(); try { this.processPathEntries(Main.DEFAULT_SIZE_CLASSPATH, paths, path, this.charset.name(), false, false); } catch (IllegalArgumentException e) { return null; } for (FileSystem.Classpath classpath : paths) { files.add(new File(classpath.getPath())); } return files; } private Iterable<? extends File> getExtdirsFrom(String path) { ArrayList<FileSystem.Classpath> paths = new ArrayList<>(); ArrayList<File> files = new ArrayList<>(); try { this.processPathEntries(Main.DEFAULT_SIZE_CLASSPATH, paths, path, this.charset.name(), false, false); } catch (IllegalArgumentException e) { return null; } for (FileSystem.Classpath classpath : paths) { files.add(new File(classpath.getPath())); } return files; } private String getExtension(File file) { String name = file.getName(); return getExtension(name); } private String getExtension(String name) { int index = name.lastIndexOf('.'); if (index == -1) { return EclipseFileManager.NO_EXTENSION; } return name.substring(index); } /* (non-Javadoc) * @see javax.tools.JavaFileManager#getFileForInput(javax.tools.JavaFileManager.Location, java.lang.String, java.lang.String) */ @Override public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException { Iterable<? extends File> files = getLocation(location); if (files == null) { throw new IllegalArgumentException("Unknown location : " + location);//$NON-NLS-1$ } String normalizedFileName = normalized(packageName) + '/' + relativeName.replace('\\', '/'); for (File file : files) { if (file.isDirectory()) { // handle directory File f = new File(file, normalizedFileName); if (f.exists()) { return new EclipseFileObject(packageName + File.separator + relativeName, f.toURI(), getKind(f), this.charset); } else { continue; // go to next entry in the location } } else if (isArchive(file)) { // handle archive file Archive archive = getArchive(file); if (archive != Archive.UNKNOWN_ARCHIVE) { if (archive.contains(normalizedFileName)) { return archive.getArchiveFileObject(normalizedFileName, this.charset); } } } } return null; } /* (non-Javadoc) * @see javax.tools.JavaFileManager#getFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, java.lang.String, javax.tools.FileObject) */ @Override public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException { Iterable<? extends File> files = getLocation(location); if (files == null) { throw new IllegalArgumentException("Unknown location : " + location);//$NON-NLS-1$ } final Iterator<? extends File> iterator = files.iterator(); if (iterator.hasNext()) { File file = iterator.next(); String normalizedFileName = normalized(packageName) + '/' + relativeName.replace('\\', '/'); File f = new File(file, normalizedFileName); return new EclipseFileObject(packageName + File.separator + relativeName, f.toURI(), getKind(f), this.charset); } else { throw new IllegalArgumentException("location is empty : " + location);//$NON-NLS-1$ } } /* (non-Javadoc) * @see javax.tools.JavaFileManager#getJavaFileForInput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind) */ @Override public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind) throws IOException { if (kind != Kind.CLASS && kind != Kind.SOURCE) { throw new IllegalArgumentException("Invalid kind : " + kind);//$NON-NLS-1$ } Iterable<? extends File> files = getLocation(location); if (files == null) { throw new IllegalArgumentException("Unknown location : " + location);//$NON-NLS-1$ } String normalizedFileName = normalized(className); normalizedFileName += kind.extension; for (File file : files) { if (file.isDirectory()) { // handle directory File f = new File(file, normalizedFileName); if (f.exists()) { return new EclipseFileObject(className, f.toURI(), kind, this.charset); } else { continue; // go to next entry in the location } } else if (isArchive(file)) { // handle archive file Archive archive = getArchive(file); if (archive != Archive.UNKNOWN_ARCHIVE) { if (archive.contains(normalizedFileName)) { return archive.getArchiveFileObject(normalizedFileName, this.charset); } } } } return null; } /* (non-Javadoc) * @see javax.tools.JavaFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location, java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject) */ @Override public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException { if (kind != Kind.CLASS && kind != Kind.SOURCE) { throw new IllegalArgumentException("Invalid kind : " + kind);//$NON-NLS-1$ } Iterable<? extends File> files = getLocation(location); if (files == null) { if (!location.equals(StandardLocation.CLASS_OUTPUT) && !location.equals(StandardLocation.SOURCE_OUTPUT)) throw new IllegalArgumentException("Unknown location : " + location);//$NON-NLS-1$ // we will use either the sibling or user.dir if (sibling != null) { String normalizedFileName = normalized(className); int index = normalizedFileName.lastIndexOf('/'); if (index != -1) { normalizedFileName = normalizedFileName.substring(index + 1); } normalizedFileName += kind.extension; URI uri = sibling.toUri(); URI uri2 = null; try { String path = uri.getPath(); index = path.lastIndexOf('/'); if (index != -1) { path = path.substring(0, index + 1); path += normalizedFileName; } uri2 = new URI(uri.getScheme(), uri.getHost(), path, uri.getFragment()); } catch (URISyntaxException e) { throw new IllegalArgumentException("invalid sibling", e);//$NON-NLS-1$ } return new EclipseFileObject(className, uri2, kind, this.charset); } else { String normalizedFileName = normalized(className); normalizedFileName += kind.extension; File f = new File(System.getProperty("user.dir"), normalizedFileName);//$NON-NLS-1$ return new EclipseFileObject(className, f.toURI(), kind, this.charset); } } final Iterator<? extends File> iterator = files.iterator(); if (iterator.hasNext()) { File file = iterator.next(); String normalizedFileName = normalized(className); normalizedFileName += kind.extension; File f = new File(file, normalizedFileName); return new EclipseFileObject(className, f.toURI(), kind, this.charset); } else { throw new IllegalArgumentException("location is empty : " + location);//$NON-NLS-1$ } } /* (non-Javadoc) * @see javax.tools.StandardJavaFileManager#getJavaFileObjects(java.io.File[]) */ @Override public Iterable<? extends JavaFileObject> getJavaFileObjects(File... files) { return getJavaFileObjectsFromFiles(Arrays.asList(files)); } /* (non-Javadoc) * @see javax.tools.StandardJavaFileManager#getJavaFileObjects(java.lang.String[]) */ @Override public Iterable<? extends JavaFileObject> getJavaFileObjects(String... names) { return getJavaFileObjectsFromStrings(Arrays.asList(names)); } /* (non-Javadoc) * @see javax.tools.StandardJavaFileManager#getJavaFileObjectsFromFiles(java.lang.Iterable) */ @Override public Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(Iterable<? extends File> files) { ArrayList<JavaFileObject> javaFileArrayList = new ArrayList<>(); for (File f : files) { if (f.isDirectory()) { throw new IllegalArgumentException("file : " + f.getAbsolutePath() + " is a directory"); //$NON-NLS-1$ //$NON-NLS-2$ } javaFileArrayList.add(new EclipseFileObject(f.getAbsolutePath(), f.toURI(), getKind(f), this.charset)); } return javaFileArrayList; } /* (non-Javadoc) * @see javax.tools.StandardJavaFileManager#getJavaFileObjectsFromStrings(java.lang.Iterable) */ @Override public Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable<String> names) { ArrayList<File> files = new ArrayList<>(); for (String name : names) { files.add(new File(name)); } return getJavaFileObjectsFromFiles(files); } public Kind getKind(File f) { return getKind(getExtension(f)); } private Kind getKind(String extension) { if (Kind.CLASS.extension.equals(extension)) { return Kind.CLASS; } else if (Kind.SOURCE.extension.equals(extension)) { return Kind.SOURCE; } else if (Kind.HTML.extension.equals(extension)) { return Kind.HTML; } return Kind.OTHER; } /* (non-Javadoc) * @see javax.tools.StandardJavaFileManager#getLocation(javax.tools.JavaFileManager.Location) */ @Override public Iterable<? extends File> getLocation(Location location) { if (this.locations == null) return null; return this.locations.get(location.getName()); } private Iterable<? extends File> getOutputDir(String string) { if ("none".equals(string)) {//$NON-NLS-1$ return null; } File file = new File(string); if (file.exists() && !file.isDirectory()) { throw new IllegalArgumentException("file : " + file.getAbsolutePath() + " is not a directory");//$NON-NLS-1$//$NON-NLS-2$ } ArrayList<File> list = new ArrayList<>(1); list.add(file); return list; } /* (non-Javadoc) * @see javax.tools.JavaFileManager#handleOption(java.lang.String, java.util.Iterator) */ @Override public boolean handleOption(String current, Iterator<String> remaining) { try { if ("-bootclasspath".equals(current)) {//$NON-NLS-1$ if (remaining.hasNext()) { final Iterable<? extends File> bootclasspaths = getPathsFrom(remaining.next()); if (bootclasspaths != null) { Iterable<? extends File> iterable = getLocation(StandardLocation.PLATFORM_CLASS_PATH); if ((this.flags & EclipseFileManager.HAS_ENDORSED_DIRS) == 0 && (this.flags & EclipseFileManager.HAS_EXT_DIRS) == 0) { // override default bootclasspath setLocation(StandardLocation.PLATFORM_CLASS_PATH, bootclasspaths); } else if ((this.flags & EclipseFileManager.HAS_ENDORSED_DIRS) != 0) { // endorseddirs have been processed first setLocation(StandardLocation.PLATFORM_CLASS_PATH, concatFiles(iterable, bootclasspaths)); } else { // extdirs have been processed first setLocation(StandardLocation.PLATFORM_CLASS_PATH, prependFiles(iterable, bootclasspaths)); } } this.flags |= EclipseFileManager.HAS_BOOTCLASSPATH; return true; } else { throw new IllegalArgumentException(); } } if ("-classpath".equals(current) || "-cp".equals(current)) {//$NON-NLS-1$//$NON-NLS-2$ if (remaining.hasNext()) { final Iterable<? extends File> classpaths = getPathsFrom(remaining.next()); if (classpaths != null) { Iterable<? extends File> iterable = getLocation(StandardLocation.CLASS_PATH); if (iterable != null) { setLocation(StandardLocation.CLASS_PATH, concatFiles(iterable, classpaths)); } else { setLocation(StandardLocation.CLASS_PATH, classpaths); } if ((this.flags & EclipseFileManager.HAS_PROCESSORPATH) == 0) { setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, classpaths); } } return true; } else { throw new IllegalArgumentException(); } } if ("-encoding".equals(current)) {//$NON-NLS-1$ if (remaining.hasNext()) { this.charset = Charset.forName(remaining.next()); return true; } else { throw new IllegalArgumentException(); } } if ("-sourcepath".equals(current)) {//$NON-NLS-1$ if (remaining.hasNext()) { final Iterable<? extends File> sourcepaths = getPathsFrom(remaining.next()); if (sourcepaths != null) setLocation(StandardLocation.SOURCE_PATH, sourcepaths); return true; } else { throw new IllegalArgumentException(); } } if ("-extdirs".equals(current)) {//$NON-NLS-1$ if (remaining.hasNext()) { Iterable<? extends File> iterable = getLocation(StandardLocation.PLATFORM_CLASS_PATH); setLocation(StandardLocation.PLATFORM_CLASS_PATH, concatFiles(iterable, getExtdirsFrom(remaining.next()))); this.flags |= EclipseFileManager.HAS_EXT_DIRS; return true; } else { throw new IllegalArgumentException(); } } if ("-endorseddirs".equals(current)) {//$NON-NLS-1$ if (remaining.hasNext()) { Iterable<? extends File> iterable = getLocation(StandardLocation.PLATFORM_CLASS_PATH); setLocation(StandardLocation.PLATFORM_CLASS_PATH, prependFiles(iterable, getEndorsedDirsFrom(remaining.next()))); this.flags |= EclipseFileManager.HAS_ENDORSED_DIRS; return true; } else { throw new IllegalArgumentException(); } } if ("-d".equals(current)) { //$NON-NLS-1$ if (remaining.hasNext()) { final Iterable<? extends File> outputDir = getOutputDir(remaining.next()); if (outputDir != null) { setLocation(StandardLocation.CLASS_OUTPUT, outputDir); } return true; } else { throw new IllegalArgumentException(); } } if ("-s".equals(current)) { //$NON-NLS-1$ if (remaining.hasNext()) { final Iterable<? extends File> outputDir = getOutputDir(remaining.next()); if (outputDir != null) { setLocation(StandardLocation.SOURCE_OUTPUT, outputDir); } return true; } else { throw new IllegalArgumentException(); } } if ("-processorpath".equals(current)) {//$NON-NLS-1$ if (remaining.hasNext()) { final Iterable<? extends File> processorpaths = getPathsFrom(remaining.next()); if (processorpaths != null) { setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, processorpaths); } this.flags |= EclipseFileManager.HAS_PROCESSORPATH; return true; } else { throw new IllegalArgumentException(); } } } catch (IOException e) { // ignore } return false; } /* (non-Javadoc) * @see javax.tools.JavaFileManager#hasLocation(javax.tools.JavaFileManager.Location) */ @Override public boolean hasLocation(Location location) { return this.locations != null && this.locations.containsKey(location.getName()); } /* (non-Javadoc) * @see javax.tools.JavaFileManager#inferBinaryName(javax.tools.JavaFileManager.Location, javax.tools.JavaFileObject) */ @Override public String inferBinaryName(Location location, JavaFileObject file) { String name = file.getName(); JavaFileObject javaFileObject = null; int index = name.lastIndexOf('.'); if (index != -1) { name = name.substring(0, index); } try { javaFileObject = getJavaFileForInput(location, name, file.getKind()); } catch (IOException e) { // ignore } catch (IllegalArgumentException iae) { return null; // Either unknown kind or location not present } if (javaFileObject == null) { return null; } return name.replace('/', '.'); } private boolean isArchive(File f) { String extension = getExtension(f); return extension.equalsIgnoreCase(".jar") || extension.equalsIgnoreCase(".zip");//$NON-NLS-1$//$NON-NLS-2$ } /* (non-Javadoc) * @see javax.tools.StandardJavaFileManager#isSameFile(javax.tools.FileObject, javax.tools.FileObject) */ @Override public boolean isSameFile(FileObject fileObject1, FileObject fileObject2) { // EclipseFileManager creates only EcliseFileObject if (!(fileObject1 instanceof EclipseFileObject)) throw new IllegalArgumentException("Unsupported file object class : " + fileObject1.getClass());//$NON-NLS-1$ if (!(fileObject2 instanceof EclipseFileObject)) throw new IllegalArgumentException("Unsupported file object class : " + fileObject2.getClass());//$NON-NLS-1$ return fileObject1.equals(fileObject2); } /* (non-Javadoc) * @see javax.tools.OptionChecker#isSupportedOption(java.lang.String) */ @Override public int isSupportedOption(String option) { return Options.processOptionsFileManager(option); } /* (non-Javadoc) * @see javax.tools.JavaFileManager#list(javax.tools.JavaFileManager.Location, java.lang.String, java.util.Set, boolean) */ @Override public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse) throws IOException { Iterable<? extends File> allFilesInLocations = getLocation(location); if (allFilesInLocations == null) { throw new IllegalArgumentException("Unknown location : " + location);//$NON-NLS-1$ } ArrayList<JavaFileObject> collector = new ArrayList<>(); String normalizedPackageName = normalized(packageName); for (File file : allFilesInLocations) { collectAllMatchingFiles(file, normalizedPackageName, kinds, recurse, collector); } return collector; } private String normalized(String className) { char[] classNameChars = className.toCharArray(); for (int i = 0, max = classNameChars.length; i < max; i++) { switch(classNameChars[i]) { case '\\' : classNameChars[i] = '/'; break; case '.' : classNameChars[i] = '/'; } } return new String(classNameChars); } private Iterable<? extends File> prependFiles(Iterable<? extends File> iterable, Iterable<? extends File> iterable2) { if (iterable2 == null) return iterable; ArrayList<File> list = new ArrayList<>(); for (Iterator<? extends File> iterator = iterable2.iterator(); iterator.hasNext(); ) { list.add(iterator.next()); } for (Iterator<? extends File> iterator = iterable.iterator(); iterator.hasNext(); ) { list.add(iterator.next()); } return list; } /* (non-Javadoc) * @see javax.tools.StandardJavaFileManager#setLocation(javax.tools.JavaFileManager.Location, java.lang.Iterable) */ @Override public void setLocation(Location location, Iterable<? extends File> path) throws IOException { if (path != null) { if (location.isOutputLocation()) { // output location int count = 0; for (Iterator<? extends File> iterator = path.iterator(); iterator.hasNext(); ) { iterator.next(); count++; } if (count != 1) { throw new IllegalArgumentException("output location can only have one path");//$NON-NLS-1$ } } this.locations.put(location.getName(), path); } } public void setLocale(Locale locale) { this.locale = locale == null ? Locale.getDefault() : locale; try { this.bundle = ResourceBundleFactory.getBundle(this.locale); } catch(MissingResourceException e) { System.out.println("Missing resource : " + Main.bundleName.replace('.', '/') + ".properties for locale " + locale); //$NON-NLS-1$//$NON-NLS-2$ throw e; } } @SuppressWarnings({"unchecked", "rawtypes"}) public void processPathEntries(final int defaultSize, final ArrayList paths, final String currentPath, String customEncoding, boolean isSourceOnly, boolean rejectDestinationPathOnJars) { String currentClasspathName = null; String currentDestinationPath = null; ArrayList currentRuleSpecs = new ArrayList(defaultSize); StringTokenizer tokenizer = new StringTokenizer(currentPath, File.pathSeparator + "[]", true); //$NON-NLS-1$ ArrayList tokens = new ArrayList(); while (tokenizer.hasMoreTokens()) { tokens.add(tokenizer.nextToken()); } // state machine final int start = 0; final int readyToClose = 1; // 'path' 'path1[rule];path2' final int readyToCloseEndingWithRules = 2; // 'path[rule]' 'path1;path2[rule]' final int readyToCloseOrOtherEntry = 3; // 'path[rule];' 'path;' 'path1;path2;' final int rulesNeedAnotherRule = 4; // 'path[rule1;' final int rulesStart = 5; // 'path[' 'path1;path2[' final int rulesReadyToClose = 6; // 'path[rule' 'path[rule1;rule2' final int destinationPathReadyToClose = 7; // 'path[-d bin' final int readyToCloseEndingWithDestinationPath = 8; // 'path[-d bin]' 'path[rule][-d bin]' final int destinationPathStart = 9; // 'path[rule][' final int bracketOpened = 10; // '.*[.*' final int bracketClosed = 11; // '.*([.*])+' final int error = 99; int state = start; String token = null; int cursor = 0, tokensNb = tokens.size(), bracket = -1; while (cursor < tokensNb && state != error) { token = (String) tokens.get(cursor++); if (token.equals(File.pathSeparator)) { switch (state) { case start: case readyToCloseOrOtherEntry: case bracketOpened: break; case readyToClose: case readyToCloseEndingWithRules: case readyToCloseEndingWithDestinationPath: state = readyToCloseOrOtherEntry; addNewEntry(paths, currentClasspathName, currentRuleSpecs, customEncoding, currentDestinationPath, isSourceOnly, rejectDestinationPathOnJars); currentRuleSpecs.clear(); break; case rulesReadyToClose: state = rulesNeedAnotherRule; break; case destinationPathReadyToClose: throw new IllegalArgumentException( this.bind("configure.incorrectDestinationPathEntry", //$NON-NLS-1$ currentPath)); case bracketClosed: cursor = bracket + 1; state = rulesStart; break; default: state = error; } } else if (token.equals("[")) { //$NON-NLS-1$ switch (state) { case start: currentClasspathName = ""; //$NON-NLS-1$ //$FALL-THROUGH$ case readyToClose: bracket = cursor - 1; //$FALL-THROUGH$ case bracketClosed: state = bracketOpened; break; case readyToCloseEndingWithRules: state = destinationPathStart; break; case readyToCloseEndingWithDestinationPath: state = rulesStart; break; case bracketOpened: default: state = error; } } else if (token.equals("]")) { //$NON-NLS-1$ switch (state) { case rulesReadyToClose: state = readyToCloseEndingWithRules; break; case destinationPathReadyToClose: state = readyToCloseEndingWithDestinationPath; break; case bracketOpened: state = bracketClosed; break; case bracketClosed: default: state = error; } } else { // regular word switch (state) { case start: case readyToCloseOrOtherEntry: state = readyToClose; currentClasspathName = token; break; case rulesStart: if (token.startsWith("-d ")) { //$NON-NLS-1$ if (currentDestinationPath != null) { throw new IllegalArgumentException( this.bind("configure.duplicateDestinationPathEntry", //$NON-NLS-1$ currentPath)); } currentDestinationPath = token.substring(3).trim(); state = destinationPathReadyToClose; break; } // else we proceed with a rule //$FALL-THROUGH$ case rulesNeedAnotherRule: if (currentDestinationPath != null) { throw new IllegalArgumentException( this.bind("configure.accessRuleAfterDestinationPath", //$NON-NLS-1$ currentPath)); } state = rulesReadyToClose; currentRuleSpecs.add(token); break; case destinationPathStart: if (!token.startsWith("-d ")) { //$NON-NLS-1$ state = error; } else { currentDestinationPath = token.substring(3).trim(); state = destinationPathReadyToClose; } break; case bracketClosed: for (int i = bracket; i < cursor ; i++) { currentClasspathName += (String) tokens.get(i); } state = readyToClose; break; case bracketOpened: break; default: state = error; } } if (state == bracketClosed && cursor == tokensNb) { cursor = bracket + 1; state = rulesStart; } } switch(state) { case readyToCloseOrOtherEntry: break; case readyToClose: case readyToCloseEndingWithRules: case readyToCloseEndingWithDestinationPath: addNewEntry(paths, currentClasspathName, currentRuleSpecs, customEncoding, currentDestinationPath, isSourceOnly, rejectDestinationPathOnJars); break; case bracketOpened: case bracketClosed: default : // we go on anyway } } @SuppressWarnings({"rawtypes", "unchecked"}) protected void addNewEntry(ArrayList paths, String currentClasspathName, ArrayList currentRuleSpecs, String customEncoding, String destPath, boolean isSourceOnly, boolean rejectDestinationPathOnJars) { int rulesSpecsSize = currentRuleSpecs.size(); AccessRuleSet accessRuleSet = null; if (rulesSpecsSize != 0) { AccessRule[] accessRules = new AccessRule[currentRuleSpecs.size()]; boolean rulesOK = true; Iterator i = currentRuleSpecs.iterator(); int j = 0; while (i.hasNext()) { String ruleSpec = (String) i.next(); char key = ruleSpec.charAt(0); String pattern = ruleSpec.substring(1); if (pattern.length() > 0) { switch (key) { case '+': accessRules[j++] = new AccessRule(pattern .toCharArray(), 0); break; case '~': accessRules[j++] = new AccessRule(pattern .toCharArray(), IProblem.DiscouragedReference); break; case '-': accessRules[j++] = new AccessRule(pattern .toCharArray(), IProblem.ForbiddenReference); break; case '?': accessRules[j++] = new AccessRule(pattern .toCharArray(), IProblem.ForbiddenReference, true/*keep looking for accessible type*/); break; default: rulesOK = false; } } else { rulesOK = false; } } if (rulesOK) { accessRuleSet = new AccessRuleSet(accessRules, AccessRestriction.COMMAND_LINE, currentClasspathName); } else { return; } } if (Main.NONE.equals(destPath)) { destPath = Main.NONE; // keep == comparison valid } if (rejectDestinationPathOnJars && destPath != null && (currentClasspathName.endsWith(".jar") || //$NON-NLS-1$ currentClasspathName.endsWith(".zip"))) { //$NON-NLS-1$ throw new IllegalArgumentException( this.bind("configure.unexpectedDestinationPathEntryFile", //$NON-NLS-1$ currentClasspathName)); } FileSystem.Classpath currentClasspath = FileSystem.getClasspath( currentClasspathName, customEncoding, isSourceOnly, accessRuleSet, destPath, null); if (currentClasspath != null) { paths.add(currentClasspath); } } /* * Lookup the message with the given ID in this catalog and bind its * substitution locations with the given string. */ private String bind(String id, String binding) { return bind(id, new String[] { binding }); } /* * Lookup the message with the given ID in this catalog and bind its * substitution locations with the given string values. */ private String bind(String id, String[] arguments) { if (id == null) return "No message available"; //$NON-NLS-1$ String message = null; try { message = this.bundle.getString(id); } catch (MissingResourceException e) { // If we got an exception looking for the message, fail gracefully by just returning // the id we were looking for. In most cases this is semi-informative so is not too bad. return "Missing message: " + id + " in: " + Main.bundleName; //$NON-NLS-2$ //$NON-NLS-1$ } return MessageFormat.format(message, (Object[]) arguments); } }