diff options
author | Roel Spilker <r.spilker@gmail.com> | 2014-01-09 23:16:18 +0100 |
---|---|---|
committer | Roel Spilker <r.spilker@gmail.com> | 2014-01-09 23:16:18 +0100 |
commit | 96e1c9e02e4b58733c2b076dee1c3e1772bb6a41 (patch) | |
tree | 95e5ffab3b6389279038569eb3fb624cbae6ae68 | |
parent | a23f56c21b4f8b7ddfe111abcb13856abecdd340 (diff) | |
download | lombok-96e1c9e02e4b58733c2b076dee1c3e1772bb6a41.tar.gz lombok-96e1c9e02e4b58733c2b076dee1c3e1772bb6a41.tar.bz2 lombok-96e1c9e02e4b58733c2b076dee1c3e1772bb6a41.zip |
[configuration] Bubbling now works
7 files changed, 309 insertions, 175 deletions
diff --git a/src/core/lombok/core/configuration/BubblingConfigurationResolver.java b/src/core/lombok/core/configuration/BubblingConfigurationResolver.java new file mode 100644 index 00000000..5673d9a8 --- /dev/null +++ b/src/core/lombok/core/configuration/BubblingConfigurationResolver.java @@ -0,0 +1,76 @@ +/* + * 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.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import lombok.core.configuration.ConfigurationSource.ListModification; +import lombok.core.configuration.ConfigurationSource.Result; + +public class BubblingConfigurationResolver implements ConfigurationResolver { + + private final List<ConfigurationSource> sources; + + public BubblingConfigurationResolver(List<ConfigurationSource> sources) { + this.sources = sources; + } + + @SuppressWarnings("unchecked") + @Override + public <T> T resolve(ConfigurationKey<T> key) { + boolean isList = key.getType().isList(); + List<ListModification> listModifications = null; + for (ConfigurationSource source : sources) { + Result<T> result = source.resolve(key); + if (result == null) continue; + if (isList) { + if (listModifications == null) { + listModifications = new ArrayList<ListModification>((List<ListModification>)result.getValue()); + } else { + listModifications.addAll(0, (List<ListModification>)result.getValue()); + } + } + if (result.isAuthoritative()) { + if (isList) { + break; + } + return result.getValue(); + } + } + if (!isList) { + return null; + } + if (listModifications == null) { + return (T) Collections.emptyList(); + } + List<Object> listValues = new ArrayList<Object>(); + for (ListModification modification : listModifications) { + listValues.remove(modification.getValue()); + if (modification.isAdded()) { + listValues.add(modification.getValue()); + } + } + return (T) listValues; + } +} diff --git a/src/core/lombok/core/configuration/ConfigurationDataType.java b/src/core/lombok/core/configuration/ConfigurationDataType.java index d33de9e4..72aff88a 100644 --- a/src/core/lombok/core/configuration/ConfigurationDataType.java +++ b/src/core/lombok/core/configuration/ConfigurationDataType.java @@ -82,7 +82,6 @@ public final class ConfigurationDataType { SIMPLE_TYPES = map; } - private static ConfigurationValueParser enumParser(Object enumType) { @SuppressWarnings("rawtypes") final Class rawType = (Class)enumType; return new ConfigurationValueParser(){ diff --git a/src/core/lombok/core/configuration/ConfigurationErrorReporter.java b/src/core/lombok/core/configuration/ConfigurationErrorReporter.java new file mode 100644 index 00000000..26fa9cc2 --- /dev/null +++ b/src/core/lombok/core/configuration/ConfigurationErrorReporter.java @@ -0,0 +1,32 @@ +/* + * 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 ConfigurationErrorReporter { + ConfigurationErrorReporter CONSOLE = new ConfigurationErrorReporter() { + @Override public void report(String error) { + System.err.println(error); + } + }; + + void report(String error); +}
\ No newline at end of file diff --git a/src/core/lombok/core/configuration/ConfigurationKey.java b/src/core/lombok/core/configuration/ConfigurationKey.java index aaa673af..81a5dfaa 100644 --- a/src/core/lombok/core/configuration/ConfigurationKey.java +++ b/src/core/lombok/core/configuration/ConfigurationKey.java @@ -22,8 +22,8 @@ package lombok.core.configuration; import java.util.Collections; -import java.util.HashMap; import java.util.Map; +import java.util.TreeMap; /** * Describes a configuration key and its type. @@ -34,7 +34,7 @@ import java.util.Map; * </pre> */ public abstract class ConfigurationKey<T> { - private static final Map<String, ConfigurationDataType> registeredKeys = new HashMap<String, ConfigurationDataType>(); + private static final TreeMap<String, ConfigurationDataType> registeredKeys = new TreeMap<String, ConfigurationDataType>(String.CASE_INSENSITIVE_ORDER); private static Map<String, ConfigurationDataType> copy; private final String keyName; @@ -57,7 +57,7 @@ public abstract class ConfigurationKey<T> { return type; } - @Override + @Override public final int hashCode() { final int prime = 31; int result = 1; @@ -69,7 +69,7 @@ public abstract class ConfigurationKey<T> { /** * Two configuration are considered equal if and only if their {@code keyName} and {@code type} are equal. */ - @Override + @Override public final boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof ConfigurationKey)) return false; @@ -86,9 +86,10 @@ public abstract class ConfigurationKey<T> { /** * Returns a copy of the currently registered keys. */ + @SuppressWarnings("unchecked") public static Map<String, ConfigurationDataType> registeredKeys() { synchronized (registeredKeys) { - if (copy == null) copy = Collections.unmodifiableMap(new HashMap<String, ConfigurationDataType>(registeredKeys)); + if (copy == null) copy = Collections.unmodifiableMap((Map<String, ConfigurationDataType>) registeredKeys.clone()); return copy; } } diff --git a/src/core/lombok/core/configuration/ConfigurationSource.java b/src/core/lombok/core/configuration/ConfigurationSource.java new file mode 100644 index 00000000..03c73674 --- /dev/null +++ b/src/core/lombok/core/configuration/ConfigurationSource.java @@ -0,0 +1,67 @@ +/* + * 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 ConfigurationSource { + + <T> Result<T> resolve(ConfigurationKey<T> key); + + public static final class Result<T> { + private final T value; + private final boolean authoritative; + + public Result(T value, boolean authoritative) { + this.value = value; + this.authoritative = authoritative; + } + + public T getValue() { + return value; + } + + public boolean isAuthoritative() { + return authoritative; + } + + @Override public String toString() { + return String.valueOf(value) + (authoritative ? " (set)" : " (delta)"); + } + } + + public static class ListModification { + private final Object value; + private final boolean added; + + public ListModification(Object value, boolean added) { + this.value = value; + this.added = added; + } + + public Object getValue() { + return value; + } + + public boolean isAdded() { + return added; + } + } +}
\ No newline at end of file diff --git a/src/core/lombok/core/configuration/StringConfigurationSource.java b/src/core/lombok/core/configuration/StringConfigurationSource.java new file mode 100644 index 00000000..3a6cd9e3 --- /dev/null +++ b/src/core/lombok/core/configuration/StringConfigurationSource.java @@ -0,0 +1,128 @@ +/* + * 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.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class StringConfigurationSource implements ConfigurationSource { + + private static final Pattern LINE = Pattern.compile("(?:clear\\s+([^=]+))|(?:(\\S*?)\\s*([-+]?=)\\s*(.*?))"); + + private final Map<String, Result<?>> values; + + public static ConfigurationSource forString(String content) { + return forString(content, ConfigurationErrorReporter.CONSOLE); + } + + public static ConfigurationSource forString(String content, ConfigurationErrorReporter reporter) { + if (reporter == null) throw new NullPointerException("reporter"); + + Map<String, Result<?>> values = new TreeMap<String, Result<?>>(String.CASE_INSENSITIVE_ORDER); + + Map<String, ConfigurationDataType> registeredKeys = ConfigurationKey.registeredKeys(); + for (String line : content.trim().split("\\s*\\n\\s*")) { + if (line.isEmpty() || line.startsWith("#")) continue; + Matcher matcher = LINE.matcher(line); + + String operator = null; + String keyName = null; + String value; + + if (matcher.matches()) { + if (matcher.group(1) == null) { + keyName = matcher.group(2); + operator = matcher.group(3); + value = matcher.group(4); + } else { + keyName = matcher.group(1); + operator = "clear"; + value = null; + } + ConfigurationDataType type = registeredKeys.get(keyName); + if (type == null) { + reporter.report("Unknown key '" + keyName + "' on line: " + line); + } else { + boolean listOperator = operator.equals("+=") || operator.equals("-="); + if (listOperator && !type.isList()) { + reporter.report("'" + keyName + "' is not a list and doesn't support " + operator + " (only = and clear): " + line); + } else if (operator.equals("=") && type.isList()) { + reporter.report("'" + keyName + "' is a list and cannot be assigned to (use +=, -= and clear instead): " + line); + } else { + processResult(values, keyName, operator, value, type, reporter); + } + } + } else { + reporter.report("No valid line: " + line); + } + } + + return new StringConfigurationSource(values); + } + + private StringConfigurationSource(Map<String, Result<?>> values) { + this.values = new TreeMap<String, Result<?>>(String.CASE_INSENSITIVE_ORDER); + for (Entry<String, Result<?>> entry : values.entrySet()) { + Result<?> result = entry.getValue(); + if (result.getValue() instanceof List<?>) { + this.values.put(entry.getKey(), new Result<List<?>>(Collections.unmodifiableList((List<?>) result.getValue()), result.isAuthoritative())); + } else { + this.values.put(entry.getKey(), result); + } + } + } + + private static void processResult(Map<String, Result<?>> values, String keyName, String operator, String value, ConfigurationDataType type, ConfigurationErrorReporter reporter) { + Object element = null; + if (value != null) try { + element = type.getParser().parse(value); + } catch (Exception e) { + reporter.report("Error while parsing the value for '" + keyName + "' value '" + value + "' (should be a " + type.getParser().description() + ")"); + return; + } + + if (operator.equals("clear") || operator.equals("=")) { + if (element == null && type.isList()) { + element = new ArrayList<ListModification>(); + } + values.put(keyName, new Result<Object>(element, true)); + } else { + Result<?> result = values.get(keyName); + @SuppressWarnings("unchecked") + List<ListModification> list = result == null ? new ArrayList<ListModification>() : (List<ListModification>) result.getValue(); + if (result == null) values.put(keyName, new Result<Object>(list, false)); + list.add(new ListModification(element, operator.equals("+="))); + } + } + + @SuppressWarnings("unchecked") + @Override + public <T> Result<T> resolve(ConfigurationKey<T> key) { + return (Result<T>) values.get(key.getKeyName()); + } +} diff --git a/src/core/lombok/core/configuration/StringResolver.java b/src/core/lombok/core/configuration/StringResolver.java deleted file mode 100644 index 81bd0a35..00000000 --- a/src/core/lombok/core/configuration/StringResolver.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2013 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.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class StringResolver implements ConfigurationResolver { - - private static final Pattern LINE = Pattern.compile("(?:clear\\s+([^=]+))|(?:(\\S*?)\\s*([-+]?=)\\s*(.*?))"); - - private final Map<String, Result> values = new TreeMap<String, Result>(String.CASE_INSENSITIVE_ORDER); - - private static class Result { - Object value; - boolean owned; - - public Result(Object value, boolean owned) { - this.value = value; - this.owned = owned; - } - - @Override public String toString() { - return String.valueOf(value) + (owned ? " (set)" : " (delta)"); - } - } - - public StringResolver(String content) { - Map<String, ConfigurationDataType> registeredKeys = ConfigurationKey.registeredKeys(); - for (String line : content.trim().split("\\s*\\n\\s*")) { - if (line.isEmpty() || line.startsWith("#")) continue; - Matcher matcher = LINE.matcher(line); - - String operator = null; - String keyName = null; - String value; - - if (matcher.matches()) { - if (matcher.group(1) == null) { - keyName = matcher.group(2); - operator = matcher.group(3); - value = matcher.group(4); - } else { - keyName = matcher.group(1); - operator = "clear"; - value = null; - } - ConfigurationDataType type = registeredKeys.get(keyName); - if (type == null) { - System.out.println("Unknown key " + keyName); - } else { - boolean listOperator = operator.equals("+=") || operator.equals("-="); - if (listOperator && !type.isList()) { - System.out.println(keyName + " is not a list"); - } else if (operator.equals("=") && type.isList()) { - System.out.println(keyName + " IS a list"); - } else { - processResult(keyName, operator, value, type); - } - } - } else { - System.out.println("no match:" + line); - } - } - for (Result r : values.values()) { - if (r.value instanceof List<?>) { - r.value = Collections.unmodifiableList(((List<?>) r.value)); - } - } - } - - private void processResult(String keyName, String operator, String value, ConfigurationDataType type) { - Object element = null; - if (value != null) try { - element = type.getParser().parse(value); - } - catch (Exception e) { - // log the wrong value - return; - } - - if (operator.equals("clear") || operator.equals("=")) { - if (element == null && type.isList()) { - element = new ArrayList<Object>(); - } - values.put(keyName, new Result(element, true)); - } else { - Result result = values.get(keyName); - @SuppressWarnings("unchecked") - List<Object> list = result == null ? new ArrayList<Object>() : (List<Object>) result.value; - if (result == null) values.put(keyName, new Result(list, false)); - list.remove(element); - if (operator.equals("+=")) list.add(element); - } - } - - @SuppressWarnings("unchecked") - @Override public <T> T resolve(ConfigurationKey<T> key) { - Result result = values.get(key.getKeyName()); - T value = result == null ? null: (T)result.value; - if (value == null && key.getType().isList()) { - value = (T)Collections.emptyList(); - } - return value; - } - - enum Flag { - WARNING, ERROR - } - - private static final ConfigurationKey<String> AAP = new ConfigurationKey<String>("aap") {}; - private static final ConfigurationKey<List<String>> NOOT = new ConfigurationKey<List<String>>("noot") {}; - private static final ConfigurationKey<Integer> MIES = new ConfigurationKey<Integer>("mies") {}; - private static final ConfigurationKey<Boolean> WIM = new ConfigurationKey<Boolean>("wim") {}; - private static final ConfigurationKey<TypeName> ZUS = new ConfigurationKey<TypeName>("zus") {}; - private static final ConfigurationKey<Flag> JET = new ConfigurationKey<Flag>("jet") {}; - - public static void main(String[] args) { - print("aap=text\nnoot+=first\nmies=5\nwim=true\nzus=foo.bar.Baz\njet=error"); - print("noot+=first\nnoot+=second\n"); - print("clear noot"); - print("noot+=before-clear\nclear noot\nnoot+=first\nnoot+=second\nnoot+=third\nnoot+=first\nnoot-=second\n"); - } - - private static void print(String content) { - StringResolver resolver = new StringResolver(content); - System.out.println("\n\n================================================"); - System.out.println(content); - System.out.println("================================================\n"); - System.out.println(resolver.values); - System.out.println("================================================\n"); - String aap = resolver.resolve(AAP); - System.out.println("aap: "+ aap); - List<String> noot = resolver.resolve(NOOT); - System.out.println("noot: "+ noot); - Integer mies = resolver.resolve(MIES); - System.out.println("mies: "+ mies); - Boolean wim = resolver.resolve(WIM); - System.out.println("wim: "+ wim); - TypeName zus = resolver.resolve(ZUS); - System.out.println("zus: "+ zus); - Flag jet = resolver.resolve(JET); - System.out.println("jet: "+ jet); - } -} |