diff options
author | Roel Spilker <r.spilker@gmail.com> | 2014-01-15 21:02:45 +0100 |
---|---|---|
committer | Roel Spilker <r.spilker@gmail.com> | 2014-01-15 21:02:45 +0100 |
commit | bb56c53547256f6ea828ef39ebb26a3af553a82e (patch) | |
tree | 3a0ee0f19e7a5cb0533fb652bb143a337e1f7fc3 | |
parent | 033356b6c6a20b929babd0e6a2ee39f280743684 (diff) | |
download | lombok-bb56c53547256f6ea828ef39ebb26a3af553a82e.tar.gz lombok-bb56c53547256f6ea828ef39ebb26a3af553a82e.tar.bz2 lombok-bb56c53547256f6ea828ef39ebb26a3af553a82e.zip |
[configuration] Use the file system to look for 'lombok.config' files and parse those and cache the results
-rw-r--r-- | src/core/lombok/core/configuration/ConfigurationErrorReporterFactory.java | 38 | ||||
-rw-r--r-- | src/core/lombok/core/configuration/FileSystemSourceCache.java | 165 |
2 files changed, 203 insertions, 0 deletions
diff --git a/src/core/lombok/core/configuration/ConfigurationErrorReporterFactory.java b/src/core/lombok/core/configuration/ConfigurationErrorReporterFactory.java new file mode 100644 index 00000000..b5bd9056 --- /dev/null +++ b/src/core/lombok/core/configuration/ConfigurationErrorReporterFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.core.configuration; + +public interface ConfigurationErrorReporterFactory { + ConfigurationErrorReporterFactory CONSOLE = new ConfigurationErrorReporterFactory() { + @Override + public ConfigurationErrorReporter createFor(final String description) { + return new ConfigurationErrorReporter() { + @Override + public void report(String error) { + System.err.printf("Error in %s: %s\n", description, error); + } + }; + } + }; + + ConfigurationErrorReporter createFor(String description); +} diff --git a/src/core/lombok/core/configuration/FileSystemSourceCache.java b/src/core/lombok/core/configuration/FileSystemSourceCache.java new file mode 100644 index 00000000..50a3bac7 --- /dev/null +++ b/src/core/lombok/core/configuration/FileSystemSourceCache.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2014 The Project Lombok Authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package lombok.core.configuration; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; + +public class FileSystemSourceCache { + + private static String LOMBOK_CONFIG_FILENAME = "lombok.config"; + private static final long RECHECK_FILESYSTEM = TimeUnit.SECONDS.toMillis(2); + private static final long MISSING = -1; + + private final ConcurrentMap<File, Content> cache = new ConcurrentHashMap<File, Content>(); + private final ConfigurationErrorReporterFactory reporterFactory; + + public FileSystemSourceCache(ConfigurationErrorReporterFactory reporterFactory) { + this.reporterFactory = reporterFactory; + } + + public Iterable<ConfigurationSource> sourcesForJavaFile(File javaFile) { + final File directory = new File(javaFile.toURI().normalize()).getParentFile(); + return new Iterable<ConfigurationSource>() { + @Override + public Iterator<ConfigurationSource> iterator() { + return new Iterator<ConfigurationSource>() { + File currentDirectory = directory; + ConfigurationSource next; + + @Override + public boolean hasNext() { + if (next != null) return true; + next = findNext(); + return next != null; + } + + @Override + public ConfigurationSource next() { + if (!hasNext()) throw new NoSuchElementException(); + ConfigurationSource result = next; + next = null; + return result; + } + + private ConfigurationSource findNext() { + while (currentDirectory != null && next == null) { + next = getSourceForDirectory(currentDirectory); + currentDirectory = currentDirectory.getParentFile(); + } + return next; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + + ConfigurationSource getSourceForDirectory(File directory) { + if (!directory.exists() && !directory.isDirectory()) throw new IllegalArgumentException("Not a directory: " + directory); + long now = System.currentTimeMillis(); + File configFile = new File(directory, LOMBOK_CONFIG_FILENAME); + + Content content = ensureContent(directory); + synchronized (content) { + if (content.lastChecked != MISSING && content.lastChecked - now < RECHECK_FILESYSTEM && getLastModified(configFile) == content.lastModified) { + return content.source; + } + content.lastChecked = now; + content.lastModified = getLastModified(configFile); + content.source = content.lastModified == MISSING ? null : parse(configFile); + return content.source; + } + } + + private Content ensureContent(File directory) { + Content content = cache.get(directory); + if (content != null) { + return content; + } + cache.putIfAbsent(directory, Content.empty()); + return cache.get(directory); + } + + private ConfigurationSource parse(File configFile) { + ConfigurationErrorReporter reporter = reporterFactory.createFor(configFile.getAbsolutePath()); + try { + return StringConfigurationSource.forString(fileToString(configFile), reporter); + } catch (Exception e) { + reporter.report("Exception while reading file: " + e.getMessage()); + return null; + } + } + + private static final ThreadLocal<byte[]> buffers = new ThreadLocal<byte[]>() { + protected byte[] initialValue() { + return new byte[65536]; + } + }; + + private String fileToString(File configFile) throws Exception { + byte[] b = buffers.get(); + FileInputStream fis = new FileInputStream(configFile); + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + while (true) { + int r = fis.read(b); + if (r == -1) break; + out.write(b, 0, r); + } + return new String(out.toByteArray(), "UTF-8"); + } finally { + fis.close(); + } + } + + private static final long getLastModified(File file) { + if (!file.exists() || !file.isFile()) return MISSING; + return file.lastModified(); + } + + private static class Content { + ConfigurationSource source; + long lastModified; + long lastChecked; + + private Content(ConfigurationSource source, long lastModified, long lastChecked) { + this.source = source; + this.lastModified = lastModified; + this.lastChecked = lastChecked; + } + + static Content empty() { + return new Content(null, MISSING, MISSING); + } + } +}
\ No newline at end of file |