diff options
122 files changed, 3420 insertions, 572 deletions
@@ -247,6 +247,7 @@ the common tasks and can be called on to run the main aspects of all the sub-scr <srcdir dir="test/transform/src" test="true" /> <srcdir dir="test/core/src" test="true" /> <srcdir dir="test/bytecode/src" test="true" /> + <srcdir dir="test/configuration/src" test="true" /> </module> <settings> <url url="http://projectlombok.org/downloads/lombok.intellij.settings" /> @@ -275,9 +276,10 @@ the common tasks and can be called on to run the main aspects of all the sub-scr <srcdir dir="test/transform/src" /> <srcdir dir="test/core/src" /> <srcdir dir="test/bytecode/src" /> - <local org="org.projectlombok" name="lombok.patcher" dir="../lombok.patcher" /> + <srcdir dir="test/configuration/src" /> <conf name="${eclipse.build.configname}" sources="contrib" /> <conf name="test" sources="contrib" /> + <local org="org.projectlombok" name="lombok.patcher" dir="../lombok.patcher" /> <settings> <url url="http://projectlombok.org/downloads/lombok.eclipse.settings" /> </settings> @@ -320,6 +322,8 @@ the common tasks and can be called on to run the main aspects of all the sub-scr <antcall target="-augmentClasspath"><param name="pluginName" value="org.eclipse.osgi" /></antcall> <antcall target="-augmentClasspath"><param name="pluginName" value="org.eclipse.jdt.core" /></antcall> <antcall target="-augmentClasspath"><param name="pluginName" value="org.eclipse.jdt.ui" /></antcall> + <antcall target="-augmentClasspath"><param name="pluginName" value="org.eclipse.core.resources" /></antcall> + <antcall target="-augmentClasspath"><param name="pluginName" value="org.eclipse.core.jobs" /></antcall> <!-- These are merely useful --> <antcall target="-augmentClasspath"><param name="pluginName" value="org.eclipse.text" /></antcall> @@ -387,10 +391,12 @@ ${sourceWarning}</echo> <src path="test/core/src" /> <src path="test/transform/src" /> <src path="test/bytecode/src" /> + <src path="test/configuration/src" /> </ivy:compile> <copy todir="build/tests"> <fileset dir="test/pretty/resource" /> <fileset dir="test/transform/resource" /> + <fileset dir="test/configuration/resource" /> </copy> </target> @@ -521,6 +527,9 @@ You can also create your own by writing a 'testenvironment.properties' file. The <fileset dir="test/bytecode/src"> <include name="**/Test*.java" /> </fileset> + <fileset dir="test/configuration/src"> + <include name="**/Test*.java" /> + </fileset> </batchtest> </junit> <echo level="info">All tests successful.</echo> diff --git a/buildScripts/ivy-repo/org.eclipse.custom-core.jobs-3.5.200.xml b/buildScripts/ivy-repo/org.eclipse.custom-core.jobs-3.5.200.xml new file mode 100644 index 00000000..ed4f5fd5 --- /dev/null +++ b/buildScripts/ivy-repo/org.eclipse.custom-core.jobs-3.5.200.xml @@ -0,0 +1,14 @@ +<ivy-module version="2.0"> + <info organisation="org.eclipse.custom" module="core.jobs" revision="3.5.200" publication="20120521234600"> + <license name="Eclipse Public Licence v1.0" url="http://www.eclipse.org/org/documents/epl-v10.php" /> + <description homepage="http://www.eclipse.org/eclipse/" /> + </info> + <configurations> + <conf name="default" /> + <conf name="sources" /> + </configurations> + <publications> + <artifact conf="default" url="http://projectlombok.org/ivyrepo/eclipse/org.eclipse.core.jobs_3.5.200.v20120521-2346.jar" /> + <artifact type="zip" conf="sources" url="http://projectlombok.org/ivyrepo/eclipse/org.eclipse.core.jobs_3.5.200.v20120521-2346-sources.jar" /> + </publications> +</ivy-module> diff --git a/buildScripts/ivy-repo/org.eclipse.custom-core.jobs-3.5.300.xml b/buildScripts/ivy-repo/org.eclipse.custom-core.jobs-3.5.300.xml new file mode 100644 index 00000000..4ac11822 --- /dev/null +++ b/buildScripts/ivy-repo/org.eclipse.custom-core.jobs-3.5.300.xml @@ -0,0 +1,14 @@ +<ivy-module version="2.0"> + <info organisation="org.eclipse.custom" module="core.jobs" revision="3.5.300" publication="20130429181300"> + <license name="Eclipse Public Licence v1.0" url="http://www.eclipse.org/org/documents/epl-v10.php" /> + <description homepage="http://www.eclipse.org/eclipse/" /> + </info> + <configurations> + <conf name="default" /> + <conf name="sources" /> + </configurations> + <publications> + <artifact conf="default" url="http://projectlombok.org/ivyrepo/eclipse/org.eclipse.core.jobs_3.5.300.v20130429-1813.jar" /> + <artifact type="zip" conf="sources" url="http://projectlombok.org/ivyrepo/eclipse/org.eclipse.core.jobs_3.5.300.v20130429-1813-sources.jar" /> + </publications> +</ivy-module> diff --git a/buildScripts/ivy-repo/org.eclipse.custom-core.resources-3.7.0.xml b/buildScripts/ivy-repo/org.eclipse.custom-core.resources-3.7.0.xml new file mode 100644 index 00000000..9fe7836e --- /dev/null +++ b/buildScripts/ivy-repo/org.eclipse.custom-core.resources-3.7.0.xml @@ -0,0 +1,14 @@ +<ivy-module version="2.0"> + <info organisation="org.eclipse.custom" module="core.resources" revision="3.7.0" publication="20110510071200"> + <license name="Eclipse Public Licence v1.0" url="http://www.eclipse.org/org/documents/epl-v10.php" /> + <description homepage="http://www.eclipse.org/eclipse/" /> + </info> + <configurations> + <conf name="default" /> + <conf name="sources" /> + </configurations> + <publications> + <artifact conf="default" url="http://projectlombok.org/ivyrepo/eclipse/org.eclipse.core.resources_3.7.100.v20110510-0712.jar" /> + <artifact type="zip" conf="sources" url="http://projectlombok.org/ivyrepo/eclipse/org.eclipse.core.resources_3.7.100.v20110510-0712-sources.jar" /> + </publications> +</ivy-module> diff --git a/buildScripts/ivy-repo/org.eclipse.custom-core.resources-3.8.100.xml b/buildScripts/ivy-repo/org.eclipse.custom-core.resources-3.8.100.xml new file mode 100644 index 00000000..c76dba24 --- /dev/null +++ b/buildScripts/ivy-repo/org.eclipse.custom-core.resources-3.8.100.xml @@ -0,0 +1,14 @@ +<ivy-module version="2.0"> + <info organisation="org.eclipse.custom" module="core.resources" revision="3.8.100" publication="20130521202600"> + <license name="Eclipse Public Licence v1.0" url="http://www.eclipse.org/org/documents/epl-v10.php" /> + <description homepage="http://www.eclipse.org/eclipse/" /> + </info> + <configurations> + <conf name="default" /> + <conf name="sources" /> + </configurations> + <publications> + <artifact conf="default" url="http://projectlombok.org/ivyrepo/eclipse/org.eclipse.core.resources_3.8.100.v20130521-2026.jar" /> + <artifact type="zip" conf="sources" url="http://projectlombok.org/ivyrepo/eclipse/org.eclipse.core.resources_3.8.100.v20130521-2026-sources.jar" /> + </publications> +</ivy-module> diff --git a/buildScripts/ivy.xml b/buildScripts/ivy.xml index 32f8d790..b2d3ab21 100644 --- a/buildScripts/ivy.xml +++ b/buildScripts/ivy.xml @@ -47,5 +47,7 @@ <dependency org="org.eclipse.custom" name="jdt.ui" rev="3.9.1" conf="eclipseBuild->default; contrib->sources" /> <dependency org="org.eclipse.custom" name="equinox.common" rev="3.6.200" conf="eclipseBuild->default; contrib->sources" /> <dependency org="org.eclipse.custom" name="osgi" rev="3.9.0" conf="eclipseBuild->default; contrib->sources" /> + <dependency org="org.eclipse.custom" name="core.resources" rev="3.8.100" conf="eclipseBuild->default; contrib->sources" /> + <dependency org="org.eclipse.custom" name="core.jobs" rev="3.5.300" conf="eclipseBuild->default; contrib->sources" /> </dependencies> </ivy-module> diff --git a/src/core/lombok/Cleanup.java b/src/core/lombok/Cleanup.java index eb223958..4b5c6fc2 100644 --- a/src/core/lombok/Cleanup.java +++ b/src/core/lombok/Cleanup.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Project Lombok Authors. + * Copyright (C) 2009-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 diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java new file mode 100644 index 00000000..9d9d17b4 --- /dev/null +++ b/src/core/lombok/ConfigurationKeys.java @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2013-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; + +import java.util.List; + +import lombok.core.FlagUsageType; +import lombok.core.configuration.ConfigurationKey; + +/** + * A container class containing all lombok configuration keys that do not belong to a specific annotation. + */ +public class ConfigurationKeys { + private ConfigurationKeys() {} + + // ##### main package features ##### + + // ----- *ArgsConstructor ----- + + /** + * lombok configuration: {@code lombok.AnyConstructor.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @AllArgsConstructor}, {@code @RequiredArgsConstructor}, or {@code @NoArgsConstructor} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> ANY_CONSTRUCTOR_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.AnyConstructor.flagUsage", "Emit a warning or error if any of the XxxArgsConstructor annotations are used.") {}; + + /** + * lombok configuration: {@code lombok.AllArgsConstructor.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @AllArgsConstructor} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> ALL_ARGS_CONSTRUCTOR_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.AllArgsConstructor.flagUsage", "Emit a warning or error if @AllArgsConstructor is used.") {}; + + /** + * lombok configuration: {@code lombok.NoArgsConstructor.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @NoArgsConstructor} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> NO_ARGS_CONSTRUCTOR_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.NoArgsConstructor.flagUsage", "Emit a warning or error if @NoArgsConstructor is used.") {}; + + /** + * lombok configuration: {@code lombok.RequiredArgsConstructor.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @RequiredArgsConstructor} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> REQUIRED_ARGS_CONSTRUCTOR_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.RequiredArgsConstructor.flagUsage", "Emit a warning or error if @RequiredArgsConstructor is used.") {}; + + // ##### Beanies ##### + + // ----- Data ----- + + /** + * lombok configuration: {@code lombok.Data.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @Data} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> DATA_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.Data.flagUsage", "Emit a warning or error if @Data is used.") {}; + + // ----- Value ----- + + /** + * lombok configuration: {@code lombok.Value.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @Value} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> VALUE_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.Value.flagUsage", "Emit a warning or error if @Value is used.") {}; + + // ----- Getter ----- + + /** + * lombok configuration: {@code lombok.Getter.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @Getter} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> GETTER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.Getter.flagUsage", "Emit a warning or error if @Getter is used.") {}; + + // ----- Setter ----- + + /** + * lombok configuration: {@code lombok.Setter.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @Setter} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> SETTER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.Setter.flagUsage", "Emit a warning or error if @Setter is used.") {}; + + // ----- EqualsAndHashCode ----- + + /** + * lombok configuration: {@code lombok.EqualsAndHashCode.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @EqualsAndHashCode} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> EQUALS_AND_HASH_CODE_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.EqualsAndHashCode.flagUsage", "Emit a warning or error if @EqualsAndHashCode is used.") {}; + + // ----- ToString ----- + + /** + * lombok configuration: {@code lombok.ToString.doNotUseGetters} = {@code true} | {@code false}. + * + * For any class without an {@code @ToString} that explicitly defines the {@code doNotUseGetters} option, this value is used. + */ + public static final ConfigurationKey<Boolean> TO_STRING_DO_NOT_USE_GETTERS = new ConfigurationKey<Boolean>("lombok.ToString.doNotUseGetters", "Don't call the getters but use the fields directly in the generated toString method.") {}; + + /** + * lombok configuration: {@code lombok.ToString.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @ToString} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> TO_STRING_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.ToString.flagUsage", "Emit a warning or error if @ToString is used.") {}; + + /** + * lombok configuration: {@code lombok.ToString.includeFieldNames} = {@code true} | {@code false}. + * + * For any class without an {@code @ToString} that explicitly defines the {@code includeFieldNames} option, this value is used. + */ + public static final ConfigurationKey<Boolean> TO_STRING_INCLUDE_FIELD_NAMES = new ConfigurationKey<Boolean>("lombok.ToString.includeFieldNames", "Include the field names in the generated toString method.") {}; + + // ##### Standalones ##### + + // ----- Cleanup ----- + + /** + * lombok configuration: {@code lombok.Cleanup.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @Cleanup} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> CLEANUP_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.Cleanup.flagUsage", "Emit a warning or error if @Cleanup is used.") {}; + + // ----- Delegate ----- + + /** + * lombok configuration: {@code lombok.Delegate.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @Delegate} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> DELEGATE_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.Delegate.flagUsage", "Emit a warning or error if @Delegate is used.") {}; + + // ----- NonNull ----- + + /** + * lombok configuration: {@code lombok.NonNull.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * <em>Implementation note: This field is supposed to be lombok.NonNull itself, but jdk6 and 7 have bugs where fields in annotations don't work well.</em> + * + * If set, <em>any</em> usage of {@code @NonNull} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> NON_NULL_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.NonNull.flagUsage", "Emit a warning or error if @NonNull is used.") {}; + + // ----- SneakyThrows ----- + + /** + * lombok configuration: {@code lombok.SneakyThrows.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @SneakyThrows} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> SNEAKY_THROWS_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.SneakyThrows.flagUsage", "Emit a warning or error if @SneakyThrows is used.") {}; + + // ----- Synchronized ----- + + /** + * lombok configuration: {@code lombok.Synchronized.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @Synchronized} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> SYNCHRONIZED_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.Synchronized.flagUsage", "Emit a warning or error if @Synchronized is used.") {}; + + // ----- val ----- + + /** + * lombok configuration: {@code lombok.val.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code val} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> VAL_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.val.flagUsage", "Emit a warning or error if 'val' is used.") {}; + + // ##### Extern ##### + + // ----- Logging ----- + /** + * lombok configuration: {@code lombok.log.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of any of the log annotations in {@code lombok.extern}{@code @Slf4j} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> LOG_ANY_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.log.flagUsage", "Emit a warning or error if any of the log annotations is used.") {}; + + /** + * lombok configuration: {@code lombok.log.apacheCommons.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @CommonsLog} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> LOG_COMMONS_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.log.apacheCommons.flagUsage", "Emit a warning or error if @CommonsLog is used.") {}; + + /** + * lombok configuration: {@code lombok.log.javaUtilLogging.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @Log} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> LOG_JUL_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.log.javaUtilLogging.flagUsage", "Emit a warning or error if @Log is used.") {}; + + /** + * lombok configuration: {@code lombok.log.log4j.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @Log4j} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> LOG_LOG4J_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.log.log4j.flagUsage", "Emit a warning or error if @Log4j is used.") {}; + + /** + * lombok configuration: {@code lombok.log.log4j2.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @Log4j2} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> LOG_LOG4J2_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.log.log4j2.flagUsage", "Emit a warning or error if @Log4j2 is used.") {}; + + /** + * lombok configuration: {@code lombok.log.slf4j.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @Slf4j} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> LOG_SLF4J_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.log.slf4j.flagUsage", "Emit a warning or error if @Slf4j is used.") {}; + + /** + * lombok configuration: {@code lombok.log.xslf4j.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @XSlf4j} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> LOG_XSLF4J_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.log.xslf4j.flagUsage", "Emit a warning or error if @XSlf4j is used.") {}; + + /** + * lombok configuration: {@code lombok.log.fieldName} = "aJavaIdentifier". + * + * If set the various log annotations (which make a log field) will use the stated identifier instead of {@code log} as a name. + */ + public static final ConfigurationKey<String> LOG_ANY_FIELD_NAME = new ConfigurationKey<String>("lombok.log.fieldName", "Use this name for the generated logger fields (default: 'log')") {}; + + /** + * lombok configuration: {@code lombok.log.fieldIsStatic} = {@code true} | {@code false}. + * + * If not set, or set to {@code true}, the log field generated by the various log annotations will be {@code static}. + * + * If set to {@code false}, these will be generated as instance fields instead. + */ + public static final ConfigurationKey<Boolean> LOG_ANY_FIELD_IS_STATIC = new ConfigurationKey<Boolean>("lombok.log.fieldIsStatic", "Make the generated logger fields static (default: true).") {}; + + // ##### Experimental ##### + + /** + * lombok configuration: {@code lombok.experimental.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of any experimental features (from package {@code lombok.experimental}) that haven't been + * promoted to a main feature results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> EXPERIMENTAL_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.experimental.flagUsage", "Emit a warning or error if an experimental feature is used.") {}; + + // ----- Accessors ----- + + /** + * lombok configuration: {@code lombok.Accessors.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @Accessors} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> ACCESSORS_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.Accessors.flagUsage", "Emit a warning or error if @Accessors is used.") {}; + + /** + * lombok configuration: {@code lombok.Accessors.prefix} += <String: prefix>. + * + * For any class without an {@code @Accessors} that explicitly defines the {@code prefix} option, this list of prefixes is used. + */ + public static final ConfigurationKey<List<String>> ACCESSORS_PREFIX = new ConfigurationKey<List<String>>("lombok.Accessors.prefix", "Strip this field prefix, like 'f' or 'm_', from the names of generated getters and setters.") {}; + + /** + * lombok configuration: {@code lombok.Accessors.chain} = {@code true} | {@code false}. + * + * For any class without an {@code @Accessors} that explicitly defines the {@code chain} option, this value is used. + */ + public static final ConfigurationKey<Boolean> ACCESSORS_CHAIN = new ConfigurationKey<Boolean>("lombok.Accessors.chain", "Generate setters that return 'this' instead of 'void'.") {}; + + /** + * lombok configuration: {@code lombok.Accessors.fluent} = {@code true} | {@code false}. + * + * For any class without an {@code @Accessors} that explicitly defines the {@code fluent} option, this value is used. + */ + public static final ConfigurationKey<Boolean> ACCESSORS_FLUENT = new ConfigurationKey<Boolean>("lombok.Accessors.fluent", "Generate getters and setters using only the field name (no get/set prefix).") {}; + + // ----- Builder ----- + + /** + * lombok configuration: {@code lombok.Builder.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @Builder} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> BUILDER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.Builder.flagUsage", "Emit a warning or error if @Builder is used.") {}; + + // ----- ExtensionMethod ----- + + /** + * lombok configuration: {@code lombok.ExtensionMethod.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @ExtensionMethod} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> EXTENSION_METHOD_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.ExtensionMethod.flagUsage", "Emit a warning or error if @ExtensionMethod is used.") {}; + + // ----- FieldDefaults ----- + + /** + * lombok configuration: {@code lombok.FieldDefaults.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @FieldDefaults} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> FIELD_DEFAULTS_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.FieldDefaults.flagUsage", "Emit a warning or error if @FieldDefaults is used.") {}; + + // ----- Wither ----- + + /** + * lombok configuration: {@code lombok.Wither.flagUsage} = {@code WARNING} | {@code ERROR}. + * + * If set, <em>any</em> usage of {@code @Value} results in a warning / error. + */ + public static final ConfigurationKey<FlagUsageType> WITHER_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.Wither.flagUsage", "Emit a warning or error if @Wither is used.") {}; + + + // ----- Configuration System ----- + + /** + * lombok configuration: {@code stop-bubbling} = {@code true} | {@code false}. + * + * If not set, or set to {@code false}, the configuration system will look for {@code lombok.config} files in the parent directory. + * + * If set to {@code true}, no futher {@code lombok.config} files will be checked. + */ + public static final ConfigurationKey<Boolean> STOP_BUBBLING = new ConfigurationKey<Boolean>("stop-bubbling", "Tell the configuration system it should stop looking for other configuration files (default: false).") {}; +} diff --git a/src/core/lombok/Delegate.java b/src/core/lombok/Delegate.java index b599e4f0..534cfb3d 100644 --- a/src/core/lombok/Delegate.java +++ b/src/core/lombok/Delegate.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 The Project Lombok Authors. + * Copyright (C) 2010-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 diff --git a/src/core/lombok/EqualsAndHashCode.java b/src/core/lombok/EqualsAndHashCode.java index 8b809c4b..60ed9e7a 100644 --- a/src/core/lombok/EqualsAndHashCode.java +++ b/src/core/lombok/EqualsAndHashCode.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2012 The Project Lombok Authors. + * Copyright (C) 2009-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 diff --git a/src/core/lombok/NonNull.java b/src/core/lombok/NonNull.java index 96813170..58538583 100644 --- a/src/core/lombok/NonNull.java +++ b/src/core/lombok/NonNull.java @@ -44,4 +44,5 @@ import java.lang.annotation.Target; @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE}) @Retention(RetentionPolicy.CLASS) @Documented -public @interface NonNull {} +public @interface NonNull { +} diff --git a/src/core/lombok/SneakyThrows.java b/src/core/lombok/SneakyThrows.java index 4bd79065..929b4578 100644 --- a/src/core/lombok/SneakyThrows.java +++ b/src/core/lombok/SneakyThrows.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2012 The Project Lombok Authors. + * Copyright (C) 2009-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 diff --git a/src/core/lombok/Synchronized.java b/src/core/lombok/Synchronized.java index fadea89a..c5601a0c 100644 --- a/src/core/lombok/Synchronized.java +++ b/src/core/lombok/Synchronized.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2012 The Project Lombok Authors. + * Copyright (C) 2009-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 diff --git a/src/core/lombok/ToString.java b/src/core/lombok/ToString.java index e39b9858..e87c71e7 100644 --- a/src/core/lombok/ToString.java +++ b/src/core/lombok/ToString.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2012 The Project Lombok Authors. + * Copyright (C) 2009-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 diff --git a/src/core/lombok/core/AST.java b/src/core/lombok/core/AST.java index 6fed0252..50eeb399 100644 --- a/src/core/lombok/core/AST.java +++ b/src/core/lombok/core/AST.java @@ -28,6 +28,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -35,6 +36,8 @@ import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import lombok.core.configuration.ConfigurationKey; + /** * Lombok wraps the AST produced by a target platform into its own AST system, mostly because both Eclipse and javac * do not allow upward traversal (from a method to its owning type, for example). @@ -64,6 +67,13 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>, this.imports = imports; } + /** + * Attempts to find the absolute path (in URI form) to the source file represented by this AST. + * + * May return {@code null} if this cannot be done. We don't yet know under which conditions this will happen. + */ + public abstract URI getAbsoluteFileLocation(); + public void setChanged() { this.changed = true; } @@ -407,4 +417,8 @@ public abstract class AST<A extends AST<A, L, N>, L extends LombokNode<A, L, N>, buildWithCollection(nodeType, v, list, dim-1); } } + + public final <T> T readConfiguration(ConfigurationKey<T> key) { + return LombokConfiguration.read(key, this); + } } diff --git a/src/core/lombok/core/FlagUsageType.java b/src/core/lombok/core/FlagUsageType.java new file mode 100644 index 00000000..42770ef1 --- /dev/null +++ b/src/core/lombok/core/FlagUsageType.java @@ -0,0 +1,6 @@ +package lombok.core; + +/** Used for lombok configuration to flag usages of certain lombok feature. */ +public enum FlagUsageType { + WARNING, ERROR; +} diff --git a/src/core/lombok/core/LombokConfiguration.java b/src/core/lombok/core/LombokConfiguration.java new file mode 100644 index 00000000..137a8a83 --- /dev/null +++ b/src/core/lombok/core/LombokConfiguration.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2013-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; + +import lombok.core.configuration.BubblingConfigurationResolver; +import lombok.core.configuration.ConfigurationKey; +import lombok.core.configuration.ConfigurationProblemReporter; +import lombok.core.configuration.ConfigurationResolver; +import lombok.core.configuration.ConfigurationResolverFactory; +import lombok.core.configuration.FileSystemSourceCache; + +public class LombokConfiguration { + private static FileSystemSourceCache cache = new FileSystemSourceCache(); + private static ConfigurationResolverFactory configurationResolverFactory = createFileSystemBubblingResolverFactory(); + + private LombokConfiguration() { + // prevent instantiation + } + + public static void overrideConfigurationResolverFactory(ConfigurationResolverFactory crf) { + configurationResolverFactory = crf == null ? createFileSystemBubblingResolverFactory() : crf; + } + + static <T> T read(ConfigurationKey<T> key, AST<?, ?, ?> ast) { + return configurationResolverFactory.createResolver(ast).resolve(key); + } + + private static ConfigurationResolverFactory createFileSystemBubblingResolverFactory() { + return new ConfigurationResolverFactory() { + @Override public ConfigurationResolver createResolver(AST<?, ?, ?> ast) { + return new BubblingConfigurationResolver(cache.sourcesForJavaFile(ast.getAbsoluteFileLocation(), ConfigurationProblemReporter.CONSOLE)); + } + }; + } +} diff --git a/src/core/lombok/core/TransformationsUtil.java b/src/core/lombok/core/TransformationsUtil.java deleted file mode 100644 index 8c7fbd3f..00000000 --- a/src/core/lombok/core/TransformationsUtil.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (C) 2009-2012 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; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.regex.Pattern; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import lombok.ToString; -import lombok.Value; -import lombok.experimental.Accessors; -import lombok.experimental.FieldDefaults; -import lombok.experimental.Wither; - -/** - * Container for static utility methods useful for some of the standard lombok transformations, regardless of - * target platform (e.g. useful for both javac and Eclipse lombok implementations). - */ -public class TransformationsUtil { - private TransformationsUtil() { - //Prevent instantiation - } - - @SuppressWarnings({"all", "unchecked", "deprecation"}) - public static final List<Class<? extends java.lang.annotation.Annotation>> INVALID_ON_BUILDERS = Collections.unmodifiableList( - Arrays.<Class<? extends java.lang.annotation.Annotation>>asList( - Getter.class, Setter.class, Wither.class, ToString.class, EqualsAndHashCode.class, - RequiredArgsConstructor.class, AllArgsConstructor.class, NoArgsConstructor.class, - Data.class, Value.class, lombok.experimental.Value.class, FieldDefaults.class)); - - /** - * Given the name of a field, return the 'base name' of that field. For example, {@code fFoobar} becomes {@code foobar} if {@code f} is in the prefix list. - * For prefixes that end in a letter character, the next character must be a non-lowercase character (i.e. {@code hashCode} is not {@code ashCode} even if - * {@code h} is in the prefix list, but {@code hAshcode} would become {@code ashCode}). The first prefix that matches is used. If the prefix list is empty, - * or the empty string is in the prefix list and no prefix before it matches, the fieldName will be returned verbatim. - * - * If no prefix matches and the empty string is not in the prefix list and the prefix list is not empty, {@code null} is returned. - * - * @param fieldName The full name of a field. - * @param prefixes A list of prefixes, usually provided by the {@code Accessors} settings annotation, listing field prefixes. - * @return The base name of the field. - */ - public static CharSequence removePrefix(CharSequence fieldName, String[] prefixes) { - if (prefixes == null || prefixes.length == 0) return fieldName; - - fieldName = fieldName.toString(); - - outer: - for (String prefix : prefixes) { - if (prefix.length() == 0) return fieldName; - if (fieldName.length() <= prefix.length()) continue outer; - for (int i = 0; i < prefix.length(); i++) { - if (fieldName.charAt(i) != prefix.charAt(i)) continue outer; - } - char followupChar = fieldName.charAt(prefix.length()); - // if prefix is a letter then follow up letter needs to not be lowercase, i.e. 'foo' is not a match - // as field named 'oo' with prefix 'f', but 'fOo' would be. - if (Character.isLetter(prefix.charAt(prefix.length() - 1)) && - Character.isLowerCase(followupChar)) continue outer; - return "" + Character.toLowerCase(followupChar) + fieldName.subSequence(prefix.length() + 1, fieldName.length()); - } - - return null; - } - - /* NB: 'notnull' is not part of the pattern because there are lots of @NotNull annotations out there that are crappily named and actually mean - something else, such as 'this field must not be null _when saved to the db_ but its perfectly okay to start out as such, and a no-args - constructor and the implied starts-out-as-null state that goes with it is in fact mandatory' which happens with javax.validation.constraints.NotNull. - Various problems with spring have also been reported. See issue #287, issue #271, and issue #43. */ - - /** Matches the simple part of any annotation that lombok considers as indicative of NonNull status. */ - public static final Pattern NON_NULL_PATTERN = Pattern.compile("^(?:nonnull)$", Pattern.CASE_INSENSITIVE); - - /** Matches the simple part of any annotation that lombok considers as indicative of Nullable status. */ - public static final Pattern NULLABLE_PATTERN = Pattern.compile("^(?:nullable|checkfornull)$", Pattern.CASE_INSENSITIVE); - - /** - * Generates a getter name from a given field name. - * - * Strategy: - * <ul> - * <li>Reduce the field's name to its base name by stripping off any prefix (from {@code Accessors}). If the field name does not fit - * the prefix list, this method immediately returns {@code null}.</li> - * <li>If {@code Accessors} has {@code fluent=true}, then return the basename.</li> - * <li>Pick a prefix. 'get' normally, but 'is' if {@code isBoolean} is true.</li> - * <li>Only if {@code isBoolean} is true: Check if the field starts with {@code is} followed by a non-lowercase character. If so, return the field name verbatim.</li> - * <li>Check if the first character of the field is lowercase. If so, check if the second character - * exists and is title or upper case. If so, uppercase the first character. If not, titlecase the first character.</li> - * <li>Return the prefix plus the possibly title/uppercased first character, and the rest of the field name.</li> - * </ul> - * - * @param accessors Accessors configuration. - * @param fieldName the name of the field. - * @param isBoolean if the field is of type 'boolean'. For fields of type {@code java.lang.Boolean}, you should provide {@code false}. - * @return The getter name for this field, or {@code null} if this field does not fit expected patterns and therefore cannot be turned into a getter name. - */ - public static String toGetterName(AnnotationValues<Accessors> accessors, CharSequence fieldName, boolean isBoolean) { - return toAccessorName(accessors, fieldName, isBoolean, "is", "get", true); - } - - /** - * Generates a setter name from a given field name. - * - * Strategy: - * <ul> - * <li>Reduce the field's name to its base name by stripping off any prefix (from {@code Accessors}). If the field name does not fit - * the prefix list, this method immediately returns {@code null}.</li> - * <li>If {@code Accessors} has {@code fluent=true}, then return the basename.</li> - * <li>Only if {@code isBoolean} is true: Check if the field starts with {@code is} followed by a non-lowercase character. - * If so, replace {@code is} with {@code set} and return that.</li> - * <li>Check if the first character of the field is lowercase. If so, check if the second character - * exists and is title or upper case. If so, uppercase the first character. If not, titlecase the first character.</li> - * <li>Return {@code "set"} plus the possibly title/uppercased first character, and the rest of the field name.</li> - * </ul> - * - * @param accessors Accessors configuration. - * @param fieldName the name of the field. - * @param isBoolean if the field is of type 'boolean'. For fields of type {@code java.lang.Boolean}, you should provide {@code false}. - * @return The setter name for this field, or {@code null} if this field does not fit expected patterns and therefore cannot be turned into a getter name. - */ - public static String toSetterName(AnnotationValues<Accessors> accessors, CharSequence fieldName, boolean isBoolean) { - return toAccessorName(accessors, fieldName, isBoolean, "set", "set", true); - } - - /** - * Generates a wither name from a given field name. - * - * Strategy: - * <ul> - * <li>Reduce the field's name to its base name by stripping off any prefix (from {@code Accessors}). If the field name does not fit - * the prefix list, this method immediately returns {@code null}.</li> - * <li>Only if {@code isBoolean} is true: Check if the field starts with {@code is} followed by a non-lowercase character. - * If so, replace {@code is} with {@code with} and return that.</li> - * <li>Check if the first character of the field is lowercase. If so, check if the second character - * exists and is title or upper case. If so, uppercase the first character. If not, titlecase the first character.</li> - * <li>Return {@code "with"} plus the possibly title/uppercased first character, and the rest of the field name.</li> - * </ul> - * - * @param accessors Accessors configuration. - * @param fieldName the name of the field. - * @param isBoolean if the field is of type 'boolean'. For fields of type {@code java.lang.Boolean}, you should provide {@code false}. - * @return The wither name for this field, or {@code null} if this field does not fit expected patterns and therefore cannot be turned into a getter name. - */ - public static String toWitherName(AnnotationValues<Accessors> accessors, CharSequence fieldName, boolean isBoolean) { - return toAccessorName(accessors, fieldName, isBoolean, "with", "with", false); - } - - private static String toAccessorName(AnnotationValues<Accessors> accessors, CharSequence fieldName, boolean isBoolean, - String booleanPrefix, String normalPrefix, boolean adhereToFluent) { - - fieldName = fieldName.toString(); - if (fieldName.length() == 0) return null; - - Accessors ac = accessors == null ? null : accessors.getInstance(); - fieldName = removePrefix(fieldName, ac == null ? new String[0] : ac.prefix()); - if (fieldName == null) return null; - - String fName = fieldName.toString(); - if (adhereToFluent && ac != null && ac.fluent()) return fName; - - if (isBoolean && fName.startsWith("is") && fieldName.length() > 2 && !Character.isLowerCase(fieldName.charAt(2))) { - // The field is for example named 'isRunning'. - return booleanPrefix + fName.substring(2); - } - - return buildName(isBoolean ? booleanPrefix : normalPrefix, fName); - } - - /** - * Returns all names of methods that would represent the getter for a field with the provided name. - * - * For example if {@code isBoolean} is true, then a field named {@code isRunning} would produce:<br /> - * {@code [isRunning, getRunning, isIsRunning, getIsRunning]} - * - * @param accessors Accessors configuration. - * @param fieldName the name of the field. - * @param isBoolean if the field is of type 'boolean'. For fields of type 'java.lang.Boolean', you should provide {@code false}. - */ - public static List<String> toAllGetterNames(AnnotationValues<Accessors> accessors, CharSequence fieldName, boolean isBoolean) { - return toAllAccessorNames(accessors, fieldName, isBoolean, "is", "get", true); - } - - /** - * Returns all names of methods that would represent the setter for a field with the provided name. - * - * For example if {@code isBoolean} is true, then a field named {@code isRunning} would produce:<br /> - * {@code [setRunning, setIsRunning]} - * - * @param accessors Accessors configuration. - * @param fieldName the name of the field. - * @param isBoolean if the field is of type 'boolean'. For fields of type 'java.lang.Boolean', you should provide {@code false}. - */ - public static List<String> toAllSetterNames(AnnotationValues<Accessors> accessors, CharSequence fieldName, boolean isBoolean) { - return toAllAccessorNames(accessors, fieldName, isBoolean, "set", "set", true); - } - - /** - * Returns all names of methods that would represent the wither for a field with the provided name. - * - * For example if {@code isBoolean} is true, then a field named {@code isRunning} would produce:<br /> - * {@code [withRunning, withIsRunning]} - * - * @param accessors Accessors configuration. - * @param fieldName the name of the field. - * @param isBoolean if the field is of type 'boolean'. For fields of type 'java.lang.Boolean', you should provide {@code false}. - */ - public static List<String> toAllWitherNames(AnnotationValues<Accessors> accessors, CharSequence fieldName, boolean isBoolean) { - return toAllAccessorNames(accessors, fieldName, isBoolean, "with", "with", false); - } - - private static List<String> toAllAccessorNames(AnnotationValues<Accessors> accessors, CharSequence fieldName, boolean isBoolean, - String booleanPrefix, String normalPrefix, boolean adhereToFluent) { - - if (!isBoolean) { - String accessorName = toAccessorName(accessors, fieldName, false, booleanPrefix, normalPrefix, adhereToFluent); - return (accessorName == null) ? Collections.<String>emptyList() : Collections.singletonList(accessorName); - } - - Accessors acc = accessors.getInstance(); - fieldName = removePrefix(fieldName, acc.prefix()); - if (fieldName == null) return Collections.emptyList(); - - List<String> baseNames = toBaseNames(fieldName, isBoolean, acc.fluent()); - - Set<String> names = new HashSet<String>(); - for (String baseName : baseNames) { - if (adhereToFluent && acc.fluent()) { - names.add(baseName); - } else { - names.add(buildName(normalPrefix, baseName)); - if (!normalPrefix.equals(booleanPrefix)) names.add(buildName(booleanPrefix, baseName)); - } - } - - return new ArrayList<String>(names); - - } - - private static List<String> toBaseNames(CharSequence fieldName, boolean isBoolean, boolean fluent) { - List<String> baseNames = new ArrayList<String>(); - baseNames.add(fieldName.toString()); - - // isPrefix = field is called something like 'isRunning', so 'running' could also be the fieldname. - String fName = fieldName.toString(); - if (fName.startsWith("is") && fName.length() > 2 && !Character.isLowerCase(fName.charAt(2))) { - String baseName = fName.substring(2); - if (fluent) { - baseNames.add("" + Character.toLowerCase(baseName.charAt(0)) + baseName.substring(1)); - } else { - baseNames.add(baseName); - } - } - - return baseNames; - } - - /** - * @param prefix Something like {@code get} or {@code set} or {@code is}. - * @param suffix Something like {@code running}. - * @return prefix + smartly title-cased suffix. For example, {@code setRunning}. - */ - private static String buildName(String prefix, String suffix) { - if (suffix.length() == 0) return prefix; - if (prefix.length() == 0) return suffix; - - char first = suffix.charAt(0); - if (Character.isLowerCase(first)) { - boolean useUpperCase = suffix.length() > 2 && - (Character.isTitleCase(suffix.charAt(1)) || Character.isUpperCase(suffix.charAt(1))); - suffix = String.format("%s%s", - useUpperCase ? Character.toUpperCase(first) : Character.toTitleCase(first), - suffix.subSequence(1, suffix.length())); - } - return String.format("%s%s", prefix, suffix); - } -} diff --git a/src/core/lombok/core/configuration/BubblingConfigurationResolver.java b/src/core/lombok/core/configuration/BubblingConfigurationResolver.java new file mode 100644 index 00000000..f96b4468 --- /dev/null +++ b/src/core/lombok/core/configuration/BubblingConfigurationResolver.java @@ -0,0 +1,78 @@ +/* + * 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 Iterable<ConfigurationSource> sources; + + public BubblingConfigurationResolver(Iterable<ConfigurationSource> sources) { + this.sources = sources; + } + + @SuppressWarnings("unchecked") + @Override + public <T> T resolve(ConfigurationKey<T> key) { + boolean isList = key.getType().isList(); + List<List<ListModification>> listModificationsList = null; + for (ConfigurationSource source : sources) { + Result result = source.resolve(key); + if (result == null) continue; + if (isList) { + if (listModificationsList == null) { + listModificationsList = new ArrayList<List<ListModification>>(); + } + listModificationsList.add((List<ListModification>)result.getValue()); + } + if (result.isAuthoritative()) { + if (isList) { + break; + } + return (T) result.getValue(); + } + } + if (!isList) { + return null; + } + if (listModificationsList == null) { + return (T) Collections.emptyList(); + } + List<Object> listValues = new ArrayList<Object>(); + Collections.reverse(listModificationsList); + for (List<ListModification> listModifications : listModificationsList) { + if (listModifications != null) 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/ConfigurationApp.java b/src/core/lombok/core/configuration/ConfigurationApp.java new file mode 100644 index 00000000..e441b4de --- /dev/null +++ b/src/core/lombok/core/configuration/ConfigurationApp.java @@ -0,0 +1,376 @@ +/* + * 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 static lombok.core.configuration.FileSystemSourceCache.fileToString; + +import java.io.File; +import java.io.PrintStream; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +import lombok.ConfigurationKeys; +import lombok.core.LombokApp; +import lombok.core.configuration.ConfigurationParser.Collector; + +import org.mangosdk.spi.ProviderFor; + +import com.zwitserloot.cmdreader.CmdReader; +import com.zwitserloot.cmdreader.Description; +import com.zwitserloot.cmdreader.Excludes; +import com.zwitserloot.cmdreader.InvalidCommandLineException; +import com.zwitserloot.cmdreader.Mandatory; +import com.zwitserloot.cmdreader.Sequential; +import com.zwitserloot.cmdreader.Shorthand; + +@ProviderFor(LombokApp.class) +public class ConfigurationApp extends LombokApp { + private static final URI NO_CONFIG = URI.create(""); + + private PrintStream out = System.out; + private PrintStream err = System.err; + + @Override public String getAppName() { + return "config"; + } + + @Override public String getAppDescription() { + return "Prints the configurations for the provided paths to standard out."; + } + + @Override public List<String> getAppAliases() { + return Arrays.asList("configuration", "config", "conf", "settings"); + } + + public static class CmdArgs { + @Sequential + @Mandatory(onlyIfNot={"help", "generate"}) + @Description("Paths to java files or directories the configuration is to be printed for.") + private List<String> paths = new ArrayList<String>(); + + @Shorthand("g") + @Excludes("paths") + @Description("Generates a list containing all the available configuration parameters. Add --verbose to print more information.") + boolean generate = false; + + @Shorthand("v") + @Description("Displays more information.") + boolean verbose = false; + + @Shorthand("k") + @Description("Limit the result to these keys.") + private List<String> key = new ArrayList<String>(); + + @Shorthand({"h", "?"}) + @Description("Shows this help text.") + boolean help = false; + } + + @Override public int runApp(List<String> raw) throws Exception { + CmdReader<CmdArgs> reader = CmdReader.of(CmdArgs.class); + CmdArgs args; + try { + args = reader.make(raw.toArray(new String[0])); + if (args.help) { + out.println(reader.generateCommandLineHelp("java -jar lombok.jar configuration")); + return 0; + } + } catch (InvalidCommandLineException e) { + err.println(e.getMessage()); + err.println(reader.generateCommandLineHelp("java -jar lombok.jar configuration")); + return 1; + } + + ConfigurationKeysLoader.LoaderLoader.loadAllConfigurationKeys(); + Collection<ConfigurationKey<?>> keys = checkKeys(args.key); + if (keys == null) return 1; + + boolean verbose = args.verbose; + if (args.generate) { + return generate(keys, verbose); + } + + return display(keys, verbose, args.paths, !args.key.isEmpty()); + } + + public ConfigurationApp redirectOutput(PrintStream out, PrintStream err) { + if (out != null) this.out = out; + if (err != null) this.err = err; + return this; + } + + public int generate(Collection<ConfigurationKey<?>> keys, boolean verbose) { + for (ConfigurationKey<?> key : keys) { + String keyName = key.getKeyName(); + ConfigurationDataType type = key.getType(); + String description = key.getDescription(); + boolean hasDescription = description != null && !description.isEmpty(); + if (!verbose) { + out.println(keyName); + if (hasDescription) { + out.print(" "); + out.println(description); + } + out.println(); + continue; + } + out.printf("##%n## Key : %s%n## Type: %s%n", keyName, type); + if (hasDescription) { + out.printf("##%n## %s%n", description); + } + out.printf("##%n## Examples:%n#%n"); + out.printf("# clear %s%n", keyName); + String exampleValue = type.getParser().exampleValue(); + if (type.isList()) { + out.printf("# %s += %s%n", keyName, exampleValue); + out.printf("# %s -= %s%n", keyName, exampleValue); + } else { + out.printf("# %s = %s%n", keyName, exampleValue); + } + out.printf("#%n%n"); + } + if (!verbose) { + out.println("Use --verbose for more information."); + } + return 0; + } + + public int display(Collection<ConfigurationKey<?>> keys, boolean verbose, Collection<String> argsPaths, boolean explicitKeys) throws Exception { + TreeMap<URI, Set<String>> sharedDirectories = findSharedDirectories(argsPaths); + + if (sharedDirectories == null) return 1; + + Set<String> none = sharedDirectories.remove(NO_CONFIG); + if (none != null) { + if (none.size() == 1) { + out.printf("No 'lombok.config' found for '%s'.%n", none.iterator().next()); + } else { + out.println("No 'lombok.config' found for: "); + for (String path : none) out.printf("- %s%n", path); + } + } + + final List<String> problems = new ArrayList<String>(); + ConfigurationProblemReporter reporter = new ConfigurationProblemReporter() { + @Override public void report(String sourceDescription, String problem, int lineNumber, CharSequence line) { + problems.add(String.format("%s: %s (%s:%d)", problem, line, sourceDescription, lineNumber)); + } + }; + + FileSystemSourceCache cache = new FileSystemSourceCache(); + boolean first = true; + for (Entry<URI, Set<String>> entry : sharedDirectories.entrySet()) { + if (!first) { + out.printf("%n%n"); + } + Set<String> paths = entry.getValue(); + if (paths.size() == 1) { + if (!(argsPaths.size() == 1)) out.printf("Configuration for '%s'.%n%n", paths.iterator().next()); + } else { + out.printf("Configuration for:%n", paths.iterator().next()); + for (String path : paths) out.printf("- %s%n", path); + out.println(); + } + URI directory = entry.getKey(); + ConfigurationResolver resolver = new BubblingConfigurationResolver(cache.sourcesForDirectory(directory, reporter)); + Map<ConfigurationKey<?>, ? extends Collection<String>> traces = trace(keys, directory); + boolean printed = false; + for (ConfigurationKey<?> key : keys) { + Object value = resolver.resolve(key); + Collection<String> modifications = traces.get(key); + if (!modifications.isEmpty() || explicitKeys) { + if (printed && verbose) out.println(); + printValue(key, value, verbose, modifications); + printed = true; + } + } + if (!printed) out.println("<default>"); + first = false; + } + + if (!problems.isEmpty()) { + out.printf("%nProblems in the configuration files: %n"); + for (String problem : problems) out.printf("- %s%n", problem); + } + + return 0; + } + + private void printValue(ConfigurationKey<?> key, Object value, boolean verbose, Collection<String> history) { + if (verbose) out.printf("# %s%n", key.getDescription()); + if (value == null) { + out.printf("clear %s%n", key.getKeyName()); + } else if (value instanceof List<?>) { + List<?> list = (List<?>)value; + if (list.isEmpty()) out.printf("clear %s%n", key.getKeyName()); + for (Object element : list) out.printf("%s += %s%n", key.getKeyName(), element); + } else { + out.printf("%s = %s%n", key.getKeyName(), value); + } + if (!verbose) return; + for (String modification : history) out.printf("# %s%n", modification); + } + + private static final ConfigurationProblemReporter VOID = new ConfigurationProblemReporter() { + @Override public void report(String sourceDescription, String problem, int lineNumber, CharSequence line) {} + }; + + private Map<ConfigurationKey<?>, ? extends Collection<String>> trace(Collection<ConfigurationKey<?>> keys, URI directory) throws Exception { + Map<ConfigurationKey<?>, List<String>> result = new HashMap<ConfigurationKey<?>, List<String>>(); + for (ConfigurationKey<?> key : keys) result.put(key, new ArrayList<String>()); + Set<ConfigurationKey<?>> used = new HashSet<ConfigurationKey<?>>(); + + boolean stopBubbling = false; + String previousFileName = null; + for (File currentDirectory = new File(directory); currentDirectory != null && !stopBubbling; currentDirectory = currentDirectory.getParentFile()) { + File configFile = new File(currentDirectory, "lombok.config"); + if (!configFile.exists() || !configFile.isFile()) continue; + + Map<ConfigurationKey<?>, List<String>> traces = trace(fileToString(configFile), configFile.getAbsolutePath(), keys); + + stopBubbling = stopBubbling(traces.get(ConfigurationKeys.STOP_BUBBLING)); + for (ConfigurationKey<?> key : keys) { + List<String> modifications = traces.get(key); + if (modifications == null) { + modifications = new ArrayList<String>(); + modifications.add(" <'" + key.getKeyName() + "' not mentioned>"); + } else { + used.add(key); + } + if (previousFileName != null) { + modifications.add(""); + modifications.add(previousFileName + ":"); + } + result.get(key).addAll(0, modifications); + } + previousFileName = configFile.getAbsolutePath(); + } + for (ConfigurationKey<?> key : keys) { + if (used.contains(key)) { + result.get(key).add(0, previousFileName + (stopBubbling ? " (stopped bubbling):" : ":")); + } else { + result.put(key, Collections.<String>emptyList()); + } + } + return result; + } + + private Map<ConfigurationKey<?>, List<String>> trace(String content, String contentDescription, final Collection<ConfigurationKey<?>> keys) { + final Map<ConfigurationKey<?>, List<String>> result = new HashMap<ConfigurationKey<?>, List<String>>(); + + Collector collector = new Collector() { + @Override public void clear(ConfigurationKey<?> key, String contentDescription, int lineNumber) { + trace(key, "clear " + key.getKeyName(), lineNumber); + } + + @Override public void set(ConfigurationKey<?> key, Object value, String contentDescription, int lineNumber) { + trace(key, key.getKeyName() + " = " + value, lineNumber); + } + + @Override public void add(ConfigurationKey<?> key, Object value, String contentDescription, int lineNumber) { + trace(key, key.getKeyName() + " += " + value, lineNumber); + } + + @Override public void remove(ConfigurationKey<?> key, Object value, String contentDescription, int lineNumber) { + trace(key, key.getKeyName() + " -= " + value, lineNumber); + } + + private void trace(ConfigurationKey<?> key, String message, int lineNumber) { + if (!keys.contains(key)) return; + List<String> traces = result.get(key); + if (traces == null) { + traces = new ArrayList<String>(); + result.put(key, traces); + } + traces.add(String.format("%4d: %s", lineNumber, message)); + } + }; + new ConfigurationParser(VOID).parse(content, contentDescription, collector); + return result; + } + + private boolean stopBubbling(List<String> stops) { + return stops != null && !stops.isEmpty() && stops.get(stops.size() -1).endsWith("true"); + } + + private Collection<ConfigurationKey<?>> checkKeys(List<String> keyList) { + Map<String, ConfigurationKey<?>> registeredKeys = ConfigurationKey.registeredKeys(); + if (keyList.isEmpty()) return registeredKeys.values(); + + Collection<ConfigurationKey<?>> keys = new ArrayList<ConfigurationKey<?>>(); + for (String keyName : keyList) { + ConfigurationKey<?> key = registeredKeys.get(keyName); + if (key == null) { + err.printf("Unknown key '%s'%n", keyName); + return null; + } + keys.remove(key); + keys.add(key); + } + return keys; + } + + private TreeMap<URI, Set<String>> findSharedDirectories(Collection<String> paths) { + TreeMap<URI,Set<String>> sharedDirectories = new TreeMap<URI, Set<String>>(new Comparator<URI>() { + @Override public int compare(URI o1, URI o2) { + return o1.toString().compareTo(o2.toString()); + } + }); + for (String path : paths) { + File file = new File(path); + if (!file.exists()) { + err.printf("File not found: '%s'%n", path); + return null; + } + URI first = findFirstLombokDirectory(file); + Set<String> sharedBy = sharedDirectories.get(first); + if (sharedBy == null) { + sharedBy = new TreeSet<String>(); + sharedDirectories.put(first, sharedBy); + } + sharedBy.add(path); + } + return sharedDirectories; + } + + private URI findFirstLombokDirectory(File file) { + File current = new File(file.toURI().normalize()); + if (file.isFile()) current = current.getParentFile(); + while (current != null) { + if (new File(current, "lombok.config").exists()) return current.toURI(); + current = current.getParentFile(); + } + return NO_CONFIG; + } +} diff --git a/src/core/lombok/core/configuration/ConfigurationDataType.java b/src/core/lombok/core/configuration/ConfigurationDataType.java new file mode 100644 index 00000000..ca0302ff --- /dev/null +++ b/src/core/lombok/core/configuration/ConfigurationDataType.java @@ -0,0 +1,182 @@ +/* + * 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.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public final class ConfigurationDataType { + private static final Map<Class<?>, ConfigurationValueParser> SIMPLE_TYPES; + static { + Map<Class<?>, ConfigurationValueParser> map = new HashMap<Class<?>, ConfigurationValueParser>(); + map.put(String.class, new ConfigurationValueParser() { + @Override public Object parse(String value) { + return value; + } + @Override public String description() { + return "string"; + } + @Override public String exampleValue() { + return "<text>"; + } + }); + map.put(Integer.class, new ConfigurationValueParser() { + @Override public Object parse(String value) { + return Integer.parseInt(value); + } + @Override public String description() { + return "int"; + } + @Override public String exampleValue() { + return "<int>"; + } + }); + map.put(Long.class, new ConfigurationValueParser() { + @Override public Object parse(String value) { + return Long.parseLong(value); + } + @Override public String description() { + return "long"; + } + @Override public String exampleValue() { + return "<long>"; + } + }); + map.put(Double.class, new ConfigurationValueParser() { + @Override public Object parse(String value) { + return Double.parseDouble(value); + } + @Override public String description() { + return "double"; + } + @Override public String exampleValue() { + return "<double>"; + } + }); + map.put(Boolean.class, new ConfigurationValueParser() { + @Override public Object parse(String value) { + return Boolean.parseBoolean(value); + } + @Override public String description() { + return "boolean"; + } + @Override public String exampleValue() { + return "[false | true]"; + } + }); + map.put(TypeName.class, new ConfigurationValueParser() { + @Override public Object parse(String value) { + return TypeName.valueOf(value); + } + @Override public String description() { + return "type-name"; + } + @Override public String exampleValue() { + return "<fully.qualified.Type>"; + } + }); + SIMPLE_TYPES = map; + } + + private static ConfigurationValueParser enumParser(Object enumType) { + @SuppressWarnings("rawtypes") final Class rawType = (Class)enumType; + return new ConfigurationValueParser(){ + @SuppressWarnings("unchecked") + @Override public Object parse(String value) { + try { + return Enum.valueOf(rawType, value); + } catch (Exception e) { + return Enum.valueOf(rawType, value.toUpperCase()); + } + } + @Override public String description() { + return "enum (" + rawType.getName() + ")"; + } + @Override public String exampleValue() { + return Arrays.toString(rawType.getEnumConstants()).replace(",", " |"); + } + }; + } + + private final boolean isList; + private final ConfigurationValueParser parser; + + public static ConfigurationDataType toDataType(Class<? extends ConfigurationKey<?>> keyClass) { + if (keyClass.getSuperclass() != ConfigurationKey.class) { + throw new IllegalArgumentException("No direct subclass of ConfigurationKey: " + keyClass.getName()); + } + + Type type = keyClass.getGenericSuperclass(); + if (!(type instanceof ParameterizedType)) { + throw new IllegalArgumentException("Missing type parameter in "+ type); + } + + ParameterizedType parameterized = (ParameterizedType) type; + Type argumentType = parameterized.getActualTypeArguments()[0]; + + boolean isList = false; + if (argumentType instanceof ParameterizedType) { + ParameterizedType parameterizedArgument = (ParameterizedType) argumentType; + if (parameterizedArgument.getRawType() == List.class) { + isList = true; + argumentType = parameterizedArgument.getActualTypeArguments()[0]; + } + } + + if (SIMPLE_TYPES.containsKey(argumentType)) { + return new ConfigurationDataType(isList, SIMPLE_TYPES.get(argumentType)); + } + + if (isEnum(argumentType)) { + return new ConfigurationDataType(isList, enumParser(argumentType)); + } + + throw new IllegalArgumentException("Unsupported type parameter in " + type); + } + + private ConfigurationDataType(boolean isList, ConfigurationValueParser parser) { + this.isList = isList; + this.parser = parser; + } + + boolean isList() { + return isList; + } + + ConfigurationValueParser getParser() { + return parser; + } + + @Override + public String toString() { + if (isList) return "list of " + parser.description(); + return parser.description(); + } + + private static boolean isEnum(Type argumentType) { + return argumentType instanceof Class && ((Class<?>) argumentType).isEnum(); + } +}
\ No newline at end of file diff --git a/src/core/lombok/core/configuration/ConfigurationKey.java b/src/core/lombok/core/configuration/ConfigurationKey.java new file mode 100644 index 00000000..d46a70b0 --- /dev/null +++ b/src/core/lombok/core/configuration/ConfigurationKey.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2013-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.Collections; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Pattern; + +/** + * Describes a configuration key and its type. + * <p> + * The recommended usage is to create a type token: + * <pre> + * private static ConfigurationKey<String> KEY = new ConfigurationKey<String>("keyName", "description") {}; + * </pre> + */ +public abstract class ConfigurationKey<T> { + private static final Pattern VALID_NAMES = Pattern.compile("[-_a-zA-Z][-.\\w]*(?<![-.])"); + + private static final TreeMap<String, ConfigurationKey<?>> registeredKeys = new TreeMap<String, ConfigurationKey<?>>(String.CASE_INSENSITIVE_ORDER); + private static Map<String, ConfigurationKey<?>> copy; + + private final String keyName; + private final String description; + private final ConfigurationDataType type; + + public ConfigurationKey(String keyName, String description) { + this.keyName = checkName(keyName); + @SuppressWarnings("unchecked") + ConfigurationDataType type = ConfigurationDataType.toDataType((Class<? extends ConfigurationKey<?>>)getClass()); + this.type = type; + this.description = description; + registerKey(keyName, this); + } + + public final String getKeyName() { + return keyName; + } + + public final String getDescription() { + return description; + } + + public final ConfigurationDataType getType() { + return type; + } + + @Override public String toString() { + return keyName + " (" + type + "): " + description; + } + + private static String checkName(String keyName) { + if (keyName == null) throw new NullPointerException("keyName"); + if (!VALID_NAMES.matcher(keyName).matches()) throw new IllegalArgumentException("Invalid keyName: " + keyName); + return keyName; + } + + /** + * Returns a copy of the currently registered keys. + */ + @SuppressWarnings("unchecked") + public static Map<String, ConfigurationKey<?>> registeredKeys() { + synchronized (registeredKeys) { + if (copy == null) copy = Collections.unmodifiableMap((Map<String, ConfigurationKey<?>>) registeredKeys.clone()); + return copy; + } + } + + private static void registerKey(String keyName, ConfigurationKey<?> key) { + synchronized (registeredKeys) { + if (registeredKeys.containsKey(keyName)) throw new IllegalArgumentException("Key '" + keyName + "' already registered"); + registeredKeys.put(keyName, key); + copy = null; + } + } +}
\ No newline at end of file diff --git a/src/core/lombok/core/configuration/ConfigurationKeysLoader.java b/src/core/lombok/core/configuration/ConfigurationKeysLoader.java new file mode 100644 index 00000000..4e6e4fb1 --- /dev/null +++ b/src/core/lombok/core/configuration/ConfigurationKeysLoader.java @@ -0,0 +1,57 @@ +/* + * 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.IOException; +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicBoolean; + +import lombok.ConfigurationKeys; +import lombok.core.SpiLoadUtil; + +public interface ConfigurationKeysLoader { + public class LoaderLoader { + private static final AtomicBoolean alreadyLoaded = new AtomicBoolean(false); + private LoaderLoader() {} + + public static void loadAllConfigurationKeys() { + if (alreadyLoaded.get()) return; + + try { + Class.forName(ConfigurationKeys.class.getName()); + } catch (Throwable ignore) {} + + try { + Iterator<ConfigurationKeysLoader> iterator = SpiLoadUtil.findServices(ConfigurationKeysLoader.class, ConfigurationKeysLoader.class.getClassLoader()).iterator(); + while (iterator.hasNext()) { + try { + iterator.next(); + } catch (Exception ignore) {} + } + } catch (IOException e) { + throw new RuntimeException("Can't load config keys; services file issue.", e); + } finally { + alreadyLoaded.set(true); + } + } + } +} diff --git a/src/core/lombok/core/configuration/ConfigurationParser.java b/src/core/lombok/core/configuration/ConfigurationParser.java new file mode 100644 index 00000000..f0a9e142 --- /dev/null +++ b/src/core/lombok/core/configuration/ConfigurationParser.java @@ -0,0 +1,109 @@ +/* + * 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.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ConfigurationParser { + private static final Pattern LINE = Pattern.compile("(?:clear\\s+([^=]+))|(?:(\\S*?)\\s*([-+]?=)\\s*(.*?))"); + private static final Pattern NEWLINE_FINDER = Pattern.compile("^\\s*(.*?)\\s*$", Pattern.MULTILINE); + + private ConfigurationProblemReporter reporter; + + public ConfigurationParser(ConfigurationProblemReporter reporter) { + if (reporter == null) throw new NullPointerException("reporter"); + this.reporter = reporter; + } + + public void parse(CharSequence content, String contentDescription, Collector collector) { + Map<String, ConfigurationKey<?>> registeredKeys = ConfigurationKey.registeredKeys(); + int lineNumber = 0; + Matcher lineMatcher = NEWLINE_FINDER.matcher(content); + while (lineMatcher.find()) { + CharSequence line = content.subSequence(lineMatcher.start(1), lineMatcher.end(1)); + lineNumber++; + if (line.length() == 0 || line.charAt(0) == '#') continue; + + Matcher matcher = LINE.matcher(line); + if (!matcher.matches()) { + reporter.report(contentDescription, "Invalid line", lineNumber, line); + continue; + } + + String operator = null; + String keyName = null; + String stringValue; + if (matcher.group(1) == null) { + keyName = matcher.group(2); + operator = matcher.group(3); + stringValue = matcher.group(4); + } else { + keyName = matcher.group(1); + operator = "clear"; + stringValue = null; + } + ConfigurationKey<?> key = registeredKeys.get(keyName); + if (key == null) { + reporter.report(contentDescription, "Unknown key '" + keyName + "'", lineNumber, line); + continue; + } + + ConfigurationDataType type = key.getType(); + boolean listOperator = operator.equals("+=") || operator.equals("-="); + if (listOperator && !type.isList()) { + reporter.report(contentDescription, "'" + keyName + "' is not a list and doesn't support " + operator + " (only = and clear)", lineNumber, line); + continue; + } + if (operator.equals("=") && type.isList()) { + reporter.report(contentDescription, "'" + keyName + "' is a list and cannot be assigned to (use +=, -= and clear instead)", lineNumber, line); + continue; + } + + Object value = null; + if (stringValue != null) try { + value = type.getParser().parse(stringValue); + } catch (Exception e) { + reporter.report(contentDescription, "Error while parsing the value for '" + keyName + "' value '" + stringValue + "' (should be " + type.getParser().exampleValue() + ")", lineNumber, line); + continue; + } + + if (operator.equals("clear")) { + collector.clear(key, contentDescription, lineNumber); + } else if (operator.equals("=")) { + collector.set(key, value, contentDescription, lineNumber); + } else if (operator.equals("+=")) { + collector.add(key, value, contentDescription, lineNumber); + } else { + collector.remove(key, value, contentDescription, lineNumber); + } + } + } + + public interface Collector { + void clear(ConfigurationKey<?> key, String contentDescription, int lineNumber); + void set(ConfigurationKey<?> key, Object value, String contentDescription, int lineNumber); + void add(ConfigurationKey<?> key, Object value, String contentDescription, int lineNumber); + void remove(ConfigurationKey<?> key, Object value, String contentDescription, int lineNumber); + } +} diff --git a/src/core/lombok/core/configuration/ConfigurationProblemReporter.java b/src/core/lombok/core/configuration/ConfigurationProblemReporter.java new file mode 100644 index 00000000..5dbf99a8 --- /dev/null +++ b/src/core/lombok/core/configuration/ConfigurationProblemReporter.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 ConfigurationProblemReporter { + void report(String sourceDescription, String problem, int lineNumber, CharSequence line); + + ConfigurationProblemReporter CONSOLE = new ConfigurationProblemReporter() { + @Override public void report(String sourceDescription, String problem, int lineNumber, CharSequence line) { + System.err.printf("%s (%s:%d)\n", problem, sourceDescription, lineNumber); + } + }; +} diff --git a/src/core/lombok/core/configuration/ConfigurationResolver.java b/src/core/lombok/core/configuration/ConfigurationResolver.java new file mode 100644 index 00000000..6f52fc6e --- /dev/null +++ b/src/core/lombok/core/configuration/ConfigurationResolver.java @@ -0,0 +1,26 @@ +/* + * 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; + +public interface ConfigurationResolver { + <T> T resolve(ConfigurationKey<T> key); +} diff --git a/src/core/lombok/core/configuration/ConfigurationResolverFactory.java b/src/core/lombok/core/configuration/ConfigurationResolverFactory.java new file mode 100644 index 00000000..83b58c2f --- /dev/null +++ b/src/core/lombok/core/configuration/ConfigurationResolverFactory.java @@ -0,0 +1,28 @@ +/* + * 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 lombok.core.AST; + +public interface ConfigurationResolverFactory { + ConfigurationResolver createResolver(AST<?, ?, ?> ast); +} diff --git a/src/core/lombok/core/configuration/ConfigurationSource.java b/src/core/lombok/core/configuration/ConfigurationSource.java new file mode 100644 index 00000000..4a2b5808 --- /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 { + + Result resolve(ConfigurationKey<?> key); + + public static final class Result { + private final Object value; + private final boolean authoritative; + + public Result(Object value, boolean authoritative) { + this.value = value; + this.authoritative = authoritative; + } + + public Object 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/ConfigurationValueParser.java b/src/core/lombok/core/configuration/ConfigurationValueParser.java new file mode 100644 index 00000000..cf3bd5ab --- /dev/null +++ b/src/core/lombok/core/configuration/ConfigurationValueParser.java @@ -0,0 +1,28 @@ +/* + * 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; + +interface ConfigurationValueParser { + Object parse(String value); + String description(); + String exampleValue(); +}
\ No newline at end of file diff --git a/src/core/lombok/core/configuration/FileSystemSourceCache.java b/src/core/lombok/core/configuration/FileSystemSourceCache.java new file mode 100644 index 00000000..03e6d338 --- /dev/null +++ b/src/core/lombok/core/configuration/FileSystemSourceCache.java @@ -0,0 +1,184 @@ +/* + * 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.net.URI; +import java.util.Collections; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; + +import lombok.ConfigurationKeys; +import lombok.core.configuration.ConfigurationSource.Result; + +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>(); + + public Iterable<ConfigurationSource> sourcesForJavaFile(URI javaFile, ConfigurationProblemReporter reporter) { + if (javaFile == null) return Collections.emptyList(); + return sourcesForDirectory(new File(javaFile.normalize()).getParentFile(), reporter); + } + + public Iterable<ConfigurationSource> sourcesForDirectory(URI directory, ConfigurationProblemReporter reporter) { + if (directory == null) return Collections.emptyList(); + return sourcesForDirectory(new File(directory.normalize()), reporter); + } + + private Iterable<ConfigurationSource> sourcesForDirectory(final File directory, final ConfigurationProblemReporter reporter) { + return new Iterable<ConfigurationSource>() { + @Override + public Iterator<ConfigurationSource> iterator() { + return new Iterator<ConfigurationSource>() { + File currentDirectory = directory; + ConfigurationSource next; + boolean stopBubbling = false; + + @Override + public boolean hasNext() { + if (next != null) return true; + if (stopBubbling) return false; + 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, reporter); + currentDirectory = currentDirectory.getParentFile(); + } + if (next != null) { + Result stop = next.resolve(ConfigurationKeys.STOP_BUBBLING); + stopBubbling = (stop != null && Boolean.TRUE.equals(stop.getValue())); + } + return next; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + + ConfigurationSource getSourceForDirectory(File directory, ConfigurationProblemReporter reporter) { + 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 && now - content.lastChecked < RECHECK_FILESYSTEM && getLastModified(configFile) == content.lastModified) { + return content.source; + } + content.lastChecked = now; + long previouslyModified = content.lastModified; + content.lastModified = getLastModified(configFile); + if (content.lastModified != previouslyModified) content.source = content.lastModified == MISSING ? null : parse(configFile, reporter); + 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, ConfigurationProblemReporter reporter) { + String contentDescription = configFile.getAbsolutePath(); + try { + return StringConfigurationSource.forString(fileToString(configFile), reporter, contentDescription); + } catch (Exception e) { + reporter.report(contentDescription, "Exception while reading file: " + e.getMessage(), 0, null); + return null; + } + } + + private static final ThreadLocal<byte[]> buffers = new ThreadLocal<byte[]>() { + protected byte[] initialValue() { + return new byte[65536]; + } + }; + + static 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); + } + } + + public void reset() { + cache.clear(); + } +}
\ 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..dd2f0319 --- /dev/null +++ b/src/core/lombok/core/configuration/StringConfigurationSource.java @@ -0,0 +1,90 @@ +/* + * 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.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import lombok.core.configuration.ConfigurationParser.Collector; + +public class StringConfigurationSource implements ConfigurationSource { + private final Map<ConfigurationKey<?>, Result> values; + + public static ConfigurationSource forString(CharSequence content, ConfigurationProblemReporter reporter, String contentDescription) { + + final Map<ConfigurationKey<?>, Result> values = new HashMap<ConfigurationKey<?>, Result>(); + + new ConfigurationParser(reporter).parse(content, contentDescription, new Collector() { + @Override public void clear(ConfigurationKey<?> key, String contentDescription, int lineNumber) { + values.put(key, new Result(null, true)); + } + + @Override public void set(ConfigurationKey<?> key, Object value, String contentDescription, int lineNumber) { + values.put(key, new Result(value, true)); + } + + @Override public void add(ConfigurationKey<?> key, Object value, String contentDescription, int lineNumber) { + modifyList(key, value, true); + } + + @Override public void remove(ConfigurationKey<?> key, Object value, String contentDescription, int lineNumber) { + modifyList(key, value, false); + } + + @SuppressWarnings("unchecked") + private void modifyList(ConfigurationKey<?> key, Object value, boolean add) { + Result result = values.get(key); + List<ListModification> list; + if (result == null || result.getValue() == null) { + list = new ArrayList<ConfigurationSource.ListModification>(); + values.put(key, new Result(list, result != null)); + } else { + list = (List<ListModification>) result.getValue(); + } + list.add(new ListModification(value, add)); + } + }); + + return new StringConfigurationSource(values); + } + + private StringConfigurationSource(Map<ConfigurationKey<?>, Result> values) { + this.values = new HashMap<ConfigurationKey<?>, Result>(); + for (Entry<ConfigurationKey<?>, Result> entry : values.entrySet()) { + Result result = entry.getValue(); + if (result.getValue() instanceof List<?>) { + this.values.put(entry.getKey(), new Result(Collections.unmodifiableList((List<?>) result.getValue()), result.isAuthoritative())); + } else { + this.values.put(entry.getKey(), result); + } + } + } + + @Override + public Result resolve(ConfigurationKey<?> key) { + return values.get(key); + } +} diff --git a/src/core/lombok/core/configuration/TypeName.java b/src/core/lombok/core/configuration/TypeName.java new file mode 100644 index 00000000..989e1b97 --- /dev/null +++ b/src/core/lombok/core/configuration/TypeName.java @@ -0,0 +1,47 @@ +/* + * 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; + +public final class TypeName { + private final String name; + + private TypeName(String name) { + this.name = name; + } + + public static TypeName valueOf(String name) { + return new TypeName(name); + } + + @Override public boolean equals(Object obj) { + if (!(obj instanceof TypeName)) return false; + return name.equals(((TypeName) obj).name); + } + + @Override public int hashCode() { + return name.hashCode(); + } + + @Override public String toString() { + return name; + } +} diff --git a/src/core/lombok/core/handlers/HandlerUtil.java b/src/core/lombok/core/handlers/HandlerUtil.java index 23b8ccc7..fe2f3406 100644 --- a/src/core/lombok/core/handlers/HandlerUtil.java +++ b/src/core/lombok/core/handlers/HandlerUtil.java @@ -21,9 +21,38 @@ */ package lombok.core.handlers; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +import lombok.AllArgsConstructor; +import lombok.ConfigurationKeys; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import lombok.Value; +import lombok.core.AST; +import lombok.core.AnnotationValues; +import lombok.core.FlagUsageType; import lombok.core.JavaIdentifiers; import lombok.core.LombokNode; +import lombok.core.configuration.ConfigurationKey; +import lombok.experimental.Accessors; +import lombok.experimental.FieldDefaults; +import lombok.experimental.Wither; +/** + * Container for static utility methods useful for some of the standard lombok handlers, regardless of + * target platform (e.g. useful for both javac and Eclipse lombok implementations). + */ public class HandlerUtil { private HandlerUtil() {} @@ -57,4 +86,336 @@ public class HandlerUtil { return true; } + + public static void handleFlagUsage(LombokNode<?, ?, ?> node, ConfigurationKey<FlagUsageType> key, String featureName) { + FlagUsageType fut = node.getAst().readConfiguration(key); + + if (fut != null) { + String msg = "Use of " + featureName + " is flagged according to lombok configuration."; + if (fut == FlagUsageType.WARNING) node.addWarning(msg); + else node.addError(msg); + } + } + + public static void handleExperimentalFlagUsage(LombokNode<?, ?, ?> node, ConfigurationKey<FlagUsageType> key, String featureName) { + handleFlagUsage(node, key, featureName, ConfigurationKeys.EXPERIMENTAL_FLAG_USAGE, "any lombok.experimental feature"); + } + + public static void handleFlagUsage(LombokNode<?, ?, ?> node, ConfigurationKey<FlagUsageType> key1, String featureName1, ConfigurationKey<FlagUsageType> key2, String featureName2) { + FlagUsageType fut1 = node.getAst().readConfiguration(key1); + FlagUsageType fut2 = node.getAst().readConfiguration(key2); + + FlagUsageType fut = null; + String featureName = null; + if (fut1 == FlagUsageType.ERROR) { + fut = fut1; + featureName = featureName1; + } else if (fut2 == FlagUsageType.ERROR) { + fut = fut2; + featureName = featureName2; + } else if (fut1 == FlagUsageType.WARNING) { + fut = fut1; + featureName = featureName1; + } else { + fut = fut2; + featureName = featureName2; + } + + if (fut != null) { + String msg = "Use of " + featureName + " is flagged according to lombok configuration."; + if (fut == FlagUsageType.WARNING) node.addWarning(msg); + else node.addError(msg); + } + } + + public static boolean shouldReturnThis0(AnnotationValues<Accessors> accessors, AST<?, ?, ?> ast) { + boolean chainForced = accessors.isExplicit("chain"); + boolean fluentForced = accessors.isExplicit("fluent"); + Accessors instance = accessors.getInstance(); + + boolean chain = instance.chain(); + boolean fluent = instance.fluent(); + + if (chainForced) return chain; + + if (!chainForced) { + Boolean chainConfig = ast.readConfiguration(ConfigurationKeys.ACCESSORS_CHAIN); + if (chainConfig != null) return chainConfig; + } + + if (!fluentForced) { + Boolean fluentConfig = ast.readConfiguration(ConfigurationKeys.ACCESSORS_FLUENT); + if (fluentConfig != null) fluent = fluentConfig; + } + + return chain || fluent; + } + + @SuppressWarnings({"all", "unchecked", "deprecation"}) + public static final List<Class<? extends java.lang.annotation.Annotation>> INVALID_ON_BUILDERS = Collections.unmodifiableList( + Arrays.<Class<? extends java.lang.annotation.Annotation>>asList( + Getter.class, Setter.class, Wither.class, ToString.class, EqualsAndHashCode.class, + RequiredArgsConstructor.class, AllArgsConstructor.class, NoArgsConstructor.class, + Data.class, Value.class, lombok.experimental.Value.class, FieldDefaults.class)); + + /** + * Given the name of a field, return the 'base name' of that field. For example, {@code fFoobar} becomes {@code foobar} if {@code f} is in the prefix list. + * For prefixes that end in a letter character, the next character must be a non-lowercase character (i.e. {@code hashCode} is not {@code ashCode} even if + * {@code h} is in the prefix list, but {@code hAshcode} would become {@code ashCode}). The first prefix that matches is used. If the prefix list is empty, + * or the empty string is in the prefix list and no prefix before it matches, the fieldName will be returned verbatim. + * + * If no prefix matches and the empty string is not in the prefix list and the prefix list is not empty, {@code null} is returned. + * + * @param fieldName The full name of a field. + * @param prefixes A list of prefixes, usually provided by the {@code Accessors} settings annotation, listing field prefixes. + * @return The base name of the field. + */ + public static CharSequence removePrefix(CharSequence fieldName, List<String> prefixes) { + if (prefixes == null || prefixes.isEmpty()) return fieldName; + + fieldName = fieldName.toString(); + + outer: + for (String prefix : prefixes) { + if (prefix.length() == 0) return fieldName; + if (fieldName.length() <= prefix.length()) continue outer; + for (int i = 0; i < prefix.length(); i++) { + if (fieldName.charAt(i) != prefix.charAt(i)) continue outer; + } + char followupChar = fieldName.charAt(prefix.length()); + // if prefix is a letter then follow up letter needs to not be lowercase, i.e. 'foo' is not a match + // as field named 'oo' with prefix 'f', but 'fOo' would be. + if (Character.isLetter(prefix.charAt(prefix.length() - 1)) && + Character.isLowerCase(followupChar)) continue outer; + return "" + Character.toLowerCase(followupChar) + fieldName.subSequence(prefix.length() + 1, fieldName.length()); + } + + return null; + } + + /* NB: 'notnull' is not part of the pattern because there are lots of @NotNull annotations out there that are crappily named and actually mean + something else, such as 'this field must not be null _when saved to the db_ but its perfectly okay to start out as such, and a no-args + constructor and the implied starts-out-as-null state that goes with it is in fact mandatory' which happens with javax.validation.constraints.NotNull. + Various problems with spring have also been reported. See issue #287, issue #271, and issue #43. */ + + /** Matches the simple part of any annotation that lombok considers as indicative of NonNull status. */ + public static final Pattern NON_NULL_PATTERN = Pattern.compile("^(?:nonnull)$", Pattern.CASE_INSENSITIVE); + + /** Matches the simple part of any annotation that lombok considers as indicative of Nullable status. */ + public static final Pattern NULLABLE_PATTERN = Pattern.compile("^(?:nullable|checkfornull)$", Pattern.CASE_INSENSITIVE); + + /** + * Generates a getter name from a given field name. + * + * Strategy: + * <ul> + * <li>Reduce the field's name to its base name by stripping off any prefix (from {@code Accessors}). If the field name does not fit + * the prefix list, this method immediately returns {@code null}.</li> + * <li>If {@code Accessors} has {@code fluent=true}, then return the basename.</li> + * <li>Pick a prefix. 'get' normally, but 'is' if {@code isBoolean} is true.</li> + * <li>Only if {@code isBoolean} is true: Check if the field starts with {@code is} followed by a non-lowercase character. If so, return the field name verbatim.</li> + * <li>Check if the first character of the field is lowercase. If so, check if the second character + * exists and is title or upper case. If so, uppercase the first character. If not, titlecase the first character.</li> + * <li>Return the prefix plus the possibly title/uppercased first character, and the rest of the field name.</li> + * </ul> + * + * @param accessors Accessors configuration. + * @param fieldName the name of the field. + * @param isBoolean if the field is of type 'boolean'. For fields of type {@code java.lang.Boolean}, you should provide {@code false}. + * @return The getter name for this field, or {@code null} if this field does not fit expected patterns and therefore cannot be turned into a getter name. + */ + public static String toGetterName(AST<?, ?, ?> ast, AnnotationValues<Accessors> accessors, CharSequence fieldName, boolean isBoolean) { + return toAccessorName(ast, accessors, fieldName, isBoolean, "is", "get", true); + } + + /** + * Generates a setter name from a given field name. + * + * Strategy: + * <ul> + * <li>Reduce the field's name to its base name by stripping off any prefix (from {@code Accessors}). If the field name does not fit + * the prefix list, this method immediately returns {@code null}.</li> + * <li>If {@code Accessors} has {@code fluent=true}, then return the basename.</li> + * <li>Only if {@code isBoolean} is true: Check if the field starts with {@code is} followed by a non-lowercase character. + * If so, replace {@code is} with {@code set} and return that.</li> + * <li>Check if the first character of the field is lowercase. If so, check if the second character + * exists and is title or upper case. If so, uppercase the first character. If not, titlecase the first character.</li> + * <li>Return {@code "set"} plus the possibly title/uppercased first character, and the rest of the field name.</li> + * </ul> + * + * @param accessors Accessors configuration. + * @param fieldName the name of the field. + * @param isBoolean if the field is of type 'boolean'. For fields of type {@code java.lang.Boolean}, you should provide {@code false}. + * @return The setter name for this field, or {@code null} if this field does not fit expected patterns and therefore cannot be turned into a getter name. + */ + public static String toSetterName(AST<?, ?, ?> ast, AnnotationValues<Accessors> accessors, CharSequence fieldName, boolean isBoolean) { + return toAccessorName(ast, accessors, fieldName, isBoolean, "set", "set", true); + } + + /** + * Generates a wither name from a given field name. + * + * Strategy: + * <ul> + * <li>Reduce the field's name to its base name by stripping off any prefix (from {@code Accessors}). If the field name does not fit + * the prefix list, this method immediately returns {@code null}.</li> + * <li>Only if {@code isBoolean} is true: Check if the field starts with {@code is} followed by a non-lowercase character. + * If so, replace {@code is} with {@code with} and return that.</li> + * <li>Check if the first character of the field is lowercase. If so, check if the second character + * exists and is title or upper case. If so, uppercase the first character. If not, titlecase the first character.</li> + * <li>Return {@code "with"} plus the possibly title/uppercased first character, and the rest of the field name.</li> + * </ul> + * + * @param accessors Accessors configuration. + * @param fieldName the name of the field. + * @param isBoolean if the field is of type 'boolean'. For fields of type {@code java.lang.Boolean}, you should provide {@code false}. + * @return The wither name for this field, or {@code null} if this field does not fit expected patterns and therefore cannot be turned into a getter name. + */ + public static String toWitherName(AST<?, ?, ?> ast, AnnotationValues<Accessors> accessors, CharSequence fieldName, boolean isBoolean) { + return toAccessorName(ast, accessors, fieldName, isBoolean, "with", "with", false); + } + + private static String toAccessorName(AST<?, ?, ?> ast, AnnotationValues<Accessors> accessors, CharSequence fieldName, boolean isBoolean, + String booleanPrefix, String normalPrefix, boolean adhereToFluent) { + + fieldName = fieldName.toString(); + if (fieldName.length() == 0) return null; + + boolean explicitPrefix = accessors != null && accessors.isExplicit("prefix"); +// System.out.printf("accessors: %s actual expr: %s val: %s\n", accessors, accessors != null ? accessors.getActualExpression("prefix") : "(null)", accessors == null ? "(null)" : Arrays.toString(accessors.getInstance().prefix())); + boolean explicitFluent = accessors != null && accessors.isExplicit("fluent"); + + Accessors ac = (explicitPrefix || explicitFluent) ? accessors.getInstance() : null; + + List<String> prefix = explicitPrefix ? Arrays.asList(ac.prefix()) : ast.readConfiguration(ConfigurationKeys.ACCESSORS_PREFIX); + boolean fluent = explicitFluent ? ac.fluent() : Boolean.TRUE.equals(ast.readConfiguration(ConfigurationKeys.ACCESSORS_FLUENT)); + + fieldName = removePrefix(fieldName, prefix); + if (fieldName == null) return null; + + String fName = fieldName.toString(); + if (adhereToFluent && fluent) return fName; + + if (isBoolean && fName.startsWith("is") && fieldName.length() > 2 && !Character.isLowerCase(fieldName.charAt(2))) { + // The field is for example named 'isRunning'. + return booleanPrefix + fName.substring(2); + } + + return buildName(isBoolean ? booleanPrefix : normalPrefix, fName); + } + + /** + * Returns all names of methods that would represent the getter for a field with the provided name. + * + * For example if {@code isBoolean} is true, then a field named {@code isRunning} would produce:<br /> + * {@code [isRunning, getRunning, isIsRunning, getIsRunning]} + * + * @param accessors Accessors configuration. + * @param fieldName the name of the field. + * @param isBoolean if the field is of type 'boolean'. For fields of type 'java.lang.Boolean', you should provide {@code false}. + */ + public static List<String> toAllGetterNames(AST<?, ?, ?> ast, AnnotationValues<Accessors> accessors, CharSequence fieldName, boolean isBoolean) { + return toAllAccessorNames(ast, accessors, fieldName, isBoolean, "is", "get", true); + } + + /** + * Returns all names of methods that would represent the setter for a field with the provided name. + * + * For example if {@code isBoolean} is true, then a field named {@code isRunning} would produce:<br /> + * {@code [setRunning, setIsRunning]} + * + * @param accessors Accessors configuration. + * @param fieldName the name of the field. + * @param isBoolean if the field is of type 'boolean'. For fields of type 'java.lang.Boolean', you should provide {@code false}. + */ + public static List<String> toAllSetterNames(AST<?, ?, ?> ast, AnnotationValues<Accessors> accessors, CharSequence fieldName, boolean isBoolean) { + return toAllAccessorNames(ast, accessors, fieldName, isBoolean, "set", "set", true); + } + + /** + * Returns all names of methods that would represent the wither for a field with the provided name. + * + * For example if {@code isBoolean} is true, then a field named {@code isRunning} would produce:<br /> + * {@code [withRunning, withIsRunning]} + * + * @param accessors Accessors configuration. + * @param fieldName the name of the field. + * @param isBoolean if the field is of type 'boolean'. For fields of type 'java.lang.Boolean', you should provide {@code false}. + */ + public static List<String> toAllWitherNames(AST<?, ?, ?> ast, AnnotationValues<Accessors> accessors, CharSequence fieldName, boolean isBoolean) { + return toAllAccessorNames(ast, accessors, fieldName, isBoolean, "with", "with", false); + } + + private static List<String> toAllAccessorNames(AST<?, ?, ?> ast, AnnotationValues<Accessors> accessors, CharSequence fieldName, boolean isBoolean, + String booleanPrefix, String normalPrefix, boolean adhereToFluent) { + + if (!isBoolean) { + String accessorName = toAccessorName(ast, accessors, fieldName, false, booleanPrefix, normalPrefix, adhereToFluent); + return (accessorName == null) ? Collections.<String>emptyList() : Collections.singletonList(accessorName); + } + + boolean explicitPrefix = accessors != null && accessors.isExplicit("prefix"); + boolean explicitFluent = accessors != null && accessors.isExplicit("fluent"); + + Accessors ac = (explicitPrefix || explicitFluent) ? accessors.getInstance() : null; + + List<String> prefix = explicitPrefix ? Arrays.asList(ac.prefix()) : ast.readConfiguration(ConfigurationKeys.ACCESSORS_PREFIX); + boolean fluent = explicitFluent ? ac.fluent() : Boolean.TRUE.equals(ast.readConfiguration(ConfigurationKeys.ACCESSORS_FLUENT)); + + fieldName = removePrefix(fieldName, prefix); + if (fieldName == null) return Collections.emptyList(); + + List<String> baseNames = toBaseNames(fieldName, isBoolean, fluent); + + Set<String> names = new HashSet<String>(); + for (String baseName : baseNames) { + if (adhereToFluent && fluent) { + names.add(baseName); + } else { + names.add(buildName(normalPrefix, baseName)); + if (!normalPrefix.equals(booleanPrefix)) names.add(buildName(booleanPrefix, baseName)); + } + } + + return new ArrayList<String>(names); + + } + + private static List<String> toBaseNames(CharSequence fieldName, boolean isBoolean, boolean fluent) { + List<String> baseNames = new ArrayList<String>(); + baseNames.add(fieldName.toString()); + + // isPrefix = field is called something like 'isRunning', so 'running' could also be the fieldname. + String fName = fieldName.toString(); + if (fName.startsWith("is") && fName.length() > 2 && !Character.isLowerCase(fName.charAt(2))) { + String baseName = fName.substring(2); + if (fluent) { + baseNames.add("" + Character.toLowerCase(baseName.charAt(0)) + baseName.substring(1)); + } else { + baseNames.add(baseName); + } + } + + return baseNames; + } + + /** + * @param prefix Something like {@code get} or {@code set} or {@code is}. + * @param suffix Something like {@code running}. + * @return prefix + smartly title-cased suffix. For example, {@code setRunning}. + */ + private static String buildName(String prefix, String suffix) { + if (suffix.length() == 0) return prefix; + if (prefix.length() == 0) return suffix; + + char first = suffix.charAt(0); + if (Character.isLowerCase(first)) { + boolean useUpperCase = suffix.length() > 2 && + (Character.isTitleCase(suffix.charAt(1)) || Character.isUpperCase(suffix.charAt(1))); + suffix = String.format("%s%s", + useUpperCase ? Character.toUpperCase(first) : Character.toTitleCase(first), + suffix.subSequence(1, suffix.length())); + } + return String.format("%s%s", prefix, suffix); + } } diff --git a/src/core/lombok/eclipse/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java index 370b40fc..45551d1c 100644 --- a/src/core/lombok/eclipse/EclipseAST.java +++ b/src/core/lombok/eclipse/EclipseAST.java @@ -21,10 +21,11 @@ */ package lombok.eclipse; +import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.net.URI; import java.util.ArrayList; -import org.eclipse.jdt.internal.compiler.CompilationResult; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -33,6 +34,9 @@ import lombok.Lombok; import lombok.core.AST; import lombok.core.LombokImmutableList; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Annotation; @@ -64,6 +68,27 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { clearChanged(); } + private static volatile boolean skipEclipseWorkspaceBasedFileResolver = false; + public URI getAbsoluteFileLocation() { + if (!skipEclipseWorkspaceBasedFileResolver) { + try { + return EclipseWorkspaceBasedFileResolver.resolve(getFileName()); + } catch (NoClassDefFoundError e) { + skipEclipseWorkspaceBasedFileResolver = true; + } + } + + // Our fancy workspace based source file to absolute disk location algorithm only works in a fully fledged eclipse. + // This fallback works when using 'ecj', which has a much simpler project/path system. For example, no 'linked' resources. + return new File(getFileName()).getAbsoluteFile().toURI(); + } + + private static class EclipseWorkspaceBasedFileResolver { + public static URI resolve(String path) { + return ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(path)).getLocationURI(); + } + } + private static String packageDeclaration(CompilationUnitDeclaration cud) { ImportReference pkg = cud.currentPackage; return pkg == null ? null : Eclipse.toQualifiedName(pkg.getImportName()); diff --git a/src/core/lombok/eclipse/HandlerLibrary.java b/src/core/lombok/eclipse/HandlerLibrary.java index 242e923c..ad9c87b9 100644 --- a/src/core/lombok/eclipse/HandlerLibrary.java +++ b/src/core/lombok/eclipse/HandlerLibrary.java @@ -38,6 +38,7 @@ import java.util.WeakHashMap; import lombok.Lombok; import lombok.core.AnnotationValues; import lombok.core.AnnotationValues.AnnotationValueDecodeFail; +import lombok.core.configuration.ConfigurationKeysLoader; import lombok.core.HandlerPriority; import lombok.core.SpiLoadUtil; import lombok.core.TypeLibrary; @@ -58,7 +59,9 @@ public class HandlerLibrary { * Creates a new HandlerLibrary. Errors will be reported to the Eclipse Error log. * You probably want to use {@link #load()} instead. */ - public HandlerLibrary() {} + public HandlerLibrary() { + ConfigurationKeysLoader.LoaderLoader.loadAllConfigurationKeys(); + } private TypeLibrary typeLibrary = new TypeLibrary(); diff --git a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java index fbad53d4..94fdffad 100644 --- a/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java +++ b/src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java @@ -22,7 +22,7 @@ package lombok.eclipse.handlers; import static lombok.eclipse.Eclipse.*; -import static lombok.core.TransformationsUtil.*; +import static lombok.core.handlers.HandlerUtil.*; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -38,14 +38,15 @@ import java.util.Map; import java.util.WeakHashMap; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.Data; import lombok.Getter; import lombok.Lombok; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.AnnotationValues.AnnotationValue; -import lombok.core.TransformationsUtil; import lombok.core.TypeResolver; +import lombok.core.handlers.HandlerUtil; import lombok.eclipse.EclipseAST; import lombok.eclipse.EclipseNode; import lombok.experimental.Accessors; @@ -1043,7 +1044,7 @@ public class EclipseHandlerUtil { * Convenient wrapper around {@link TransformationsUtil#toAllGetterNames(lombok.core.AnnotationValues, CharSequence, boolean)}. */ public static List<String> toAllGetterNames(EclipseNode field, boolean isBoolean) { - return TransformationsUtil.toAllGetterNames(getAccessorsForField(field), field.getName(), isBoolean); + return HandlerUtil.toAllGetterNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean); } /** @@ -1052,7 +1053,7 @@ public class EclipseHandlerUtil { * Convenient wrapper around {@link TransformationsUtil#toGetterName(lombok.core.AnnotationValues, CharSequence, boolean)}. */ public static String toGetterName(EclipseNode field, boolean isBoolean) { - return TransformationsUtil.toGetterName(getAccessorsForField(field), field.getName(), isBoolean); + return HandlerUtil.toGetterName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean); } /** @@ -1060,7 +1061,7 @@ public class EclipseHandlerUtil { * Convenient wrapper around {@link TransformationsUtil#toAllSetterNames(lombok.core.AnnotationValues, CharSequence, boolean)}. */ public static java.util.List<String> toAllSetterNames(EclipseNode field, boolean isBoolean) { - return TransformationsUtil.toAllSetterNames(getAccessorsForField(field), field.getName(), isBoolean); + return HandlerUtil.toAllSetterNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean); } /** @@ -1069,7 +1070,7 @@ public class EclipseHandlerUtil { * Convenient wrapper around {@link TransformationsUtil#toSetterName(lombok.core.AnnotationValues, CharSequence, boolean)}. */ public static String toSetterName(EclipseNode field, boolean isBoolean) { - return TransformationsUtil.toSetterName(getAccessorsForField(field), field.getName(), isBoolean); + return HandlerUtil.toSetterName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean); } /** @@ -1077,7 +1078,7 @@ public class EclipseHandlerUtil { * Convenient wrapper around {@link TransformationsUtil#toAllWitherNames(lombok.core.AnnotationValues, CharSequence, boolean)}. */ public static java.util.List<String> toAllWitherNames(EclipseNode field, boolean isBoolean) { - return TransformationsUtil.toAllWitherNames(getAccessorsForField(field), field.getName(), isBoolean); + return HandlerUtil.toAllWitherNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean); } /** @@ -1086,19 +1087,17 @@ public class EclipseHandlerUtil { * Convenient wrapper around {@link TransformationsUtil#toWitherName(lombok.core.AnnotationValues, CharSequence, boolean)}. */ public static String toWitherName(EclipseNode field, boolean isBoolean) { - return TransformationsUtil.toWitherName(getAccessorsForField(field), field.getName(), isBoolean); + return HandlerUtil.toWitherName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean); } /** * When generating a setter, the setter either returns void (beanspec) or Self (fluent). - * This method scans for the {@code Accessors} annotation to figure that out. + * This method scans for the {@code Accessors} annotation and associated config properties to figure that out. */ public static boolean shouldReturnThis(EclipseNode field) { if ((((FieldDeclaration) field.get()).modifiers & ClassFileConstants.AccStatic) != 0) return false; AnnotationValues<Accessors> accessors = EclipseHandlerUtil.getAccessorsForField(field); - boolean forced = (accessors.getActualExpression("chain") != null); - Accessors instance = accessors.getInstance(); - return instance.chain() || (instance.fluent() && !forced); + return shouldReturnThis0(accessors, field.getAst()); } /** @@ -1126,10 +1125,11 @@ public class EclipseHandlerUtil { } public static char[] removePrefixFromField(EclipseNode field) { - String[] prefixes = null; + List<String> prefixes = null; for (EclipseNode node : field.down()) { if (annotationTypeMatches(Accessors.class, node)) { - prefixes = createAnnotation(Accessors.class, node).getInstance().prefix(); + AnnotationValues<Accessors> ann = createAnnotation(Accessors.class, node); + if (ann.isExplicit("prefix")) prefixes = Arrays.asList(ann.getInstance().prefix()); break; } } @@ -1140,7 +1140,8 @@ public class EclipseHandlerUtil { while (current != null) { for (EclipseNode node : current.down()) { if (annotationTypeMatches(Accessors.class, node)) { - prefixes = createAnnotation(Accessors.class, node).getInstance().prefix(); + AnnotationValues<Accessors> ann = createAnnotation(Accessors.class, node); + if (ann.isExplicit("prefix")) prefixes = Arrays.asList(ann.getInstance().prefix()); break outer; } } @@ -1148,8 +1149,9 @@ public class EclipseHandlerUtil { } } - if (prefixes != null && prefixes.length > 0) { - CharSequence newName = TransformationsUtil.removePrefix(field.getName(), prefixes); + if (prefixes == null) prefixes = field.getAst().readConfiguration(ConfigurationKeys.ACCESSORS_PREFIX); + if (!prefixes.isEmpty()) { + CharSequence newName = removePrefix(field.getName(), prefixes); if (newName != null) return newName.toString().toCharArray(); } diff --git a/src/core/lombok/eclipse/handlers/HandleAccessors.java b/src/core/lombok/eclipse/handlers/HandleAccessors.java new file mode 100644 index 00000000..864ff50b --- /dev/null +++ b/src/core/lombok/eclipse/handlers/HandleAccessors.java @@ -0,0 +1,43 @@ +/* + * 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.eclipse.handlers; + +import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage; +import lombok.ConfigurationKeys; +import lombok.core.AnnotationValues; +import lombok.core.HandlerPriority; +import lombok.eclipse.EclipseAnnotationHandler; +import lombok.eclipse.EclipseNode; +import lombok.experimental.Accessors; + +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.mangosdk.spi.ProviderFor; + +@ProviderFor(EclipseAnnotationHandler.class) +@HandlerPriority(65536) +public class HandleAccessors extends EclipseAnnotationHandler<Accessors> { + @Override public void handle(AnnotationValues<Accessors> annotation, Annotation ast, EclipseNode annotationNode) { + // Accessors itself is handled by HandleGetter/Setter; this is just to ensure that usages are flagged if requested. + + handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.ACCESSORS_FLAG_USAGE, "@Accessors"); + } +} diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index ea282b3b..12b16934 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -56,10 +56,10 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.mangosdk.spi.ProviderFor; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; -import lombok.core.TransformationsUtil; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; @@ -71,6 +71,8 @@ import lombok.experimental.NonFinal; @HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes. public class HandleBuilder extends EclipseAnnotationHandler<Builder> { @Override public void handle(AnnotationValues<Builder> annotation, Annotation ast, EclipseNode annotationNode) { + handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder"); + long p = (long) ast.sourceStart << 32 | ast.sourceEnd; Builder builderInstance = annotation.getInstance(); @@ -347,7 +349,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> { } boolean isBoolean = isBoolean(fd.type); - String setterName = fluent ? fieldNode.getName() : TransformationsUtil.toSetterName(null, fieldNode.getName(), isBoolean); + String setterName = fluent ? fieldNode.getName() : toSetterName(builderType.getAst(), null, fieldNode.getName(), isBoolean); return HandleSetter.createSetter(td, fieldNode, setterName, chain, ClassFileConstants.AccPublic, source, Collections.<Annotation>emptyList(), Collections.<Annotation>emptyList()); diff --git a/src/core/lombok/eclipse/handlers/HandleCleanup.java b/src/core/lombok/eclipse/handlers/HandleCleanup.java index b8ea1669..dde7cd08 100644 --- a/src/core/lombok/eclipse/handlers/HandleCleanup.java +++ b/src/core/lombok/eclipse/handlers/HandleCleanup.java @@ -21,11 +21,13 @@ */ package lombok.eclipse.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.util.Arrays; import lombok.Cleanup; +import lombok.ConfigurationKeys; import lombok.core.AnnotationValues; import lombok.core.AST.Kind; import lombok.eclipse.EclipseAnnotationHandler; @@ -58,6 +60,8 @@ import org.mangosdk.spi.ProviderFor; @ProviderFor(EclipseAnnotationHandler.class) public class HandleCleanup extends EclipseAnnotationHandler<Cleanup> { public void handle(AnnotationValues<Cleanup> annotation, Annotation ast, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.CLEANUP_FLAG_USAGE, "@Cleanup"); + String cleanupName = annotation.getInstance().value(); if (cleanupName.length() == 0) { annotationNode.addError("cleanupName cannot be the empty string."); diff --git a/src/core/lombok/eclipse/handlers/HandleConstructor.java b/src/core/lombok/eclipse/handlers/HandleConstructor.java index d86aadee..6b7df349 100644 --- a/src/core/lombok/eclipse/handlers/HandleConstructor.java +++ b/src/core/lombok/eclipse/handlers/HandleConstructor.java @@ -21,6 +21,7 @@ */ package lombok.eclipse.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; @@ -32,11 +33,11 @@ import java.util.List; import lombok.AccessLevel; import lombok.AllArgsConstructor; +import lombok.ConfigurationKeys; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; -import lombok.core.TransformationsUtil; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.experimental.Builder; @@ -69,6 +70,8 @@ public class HandleConstructor { @ProviderFor(EclipseAnnotationHandler.class) public static class HandleNoArgsConstructor extends EclipseAnnotationHandler<NoArgsConstructor> { @Override public void handle(AnnotationValues<NoArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.NO_ARGS_CONSTRUCTOR_FLAG_USAGE, "@NoArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor"); + EclipseNode typeNode = annotationNode.up(); if (!checkLegality(typeNode, annotationNode, NoArgsConstructor.class.getSimpleName())) return; NoArgsConstructor ann = annotation.getInstance(); @@ -86,6 +89,8 @@ public class HandleConstructor { @ProviderFor(EclipseAnnotationHandler.class) public static class HandleRequiredArgsConstructor extends EclipseAnnotationHandler<RequiredArgsConstructor> { @Override public void handle(AnnotationValues<RequiredArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.REQUIRED_ARGS_CONSTRUCTOR_FLAG_USAGE, "@RequiredArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor"); + EclipseNode typeNode = annotationNode.up(); if (!checkLegality(typeNode, annotationNode, RequiredArgsConstructor.class.getSimpleName())) return; RequiredArgsConstructor ann = annotation.getInstance(); @@ -108,7 +113,7 @@ public class HandleConstructor { FieldDeclaration fieldDecl = (FieldDeclaration) child.get(); if (!filterField(fieldDecl)) continue; boolean isFinal = (fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0; - boolean isNonNull = findAnnotations(fieldDecl, TransformationsUtil.NON_NULL_PATTERN).length != 0; + boolean isNonNull = findAnnotations(fieldDecl, NON_NULL_PATTERN).length != 0; if ((isFinal || isNonNull) && fieldDecl.initialization == null) fields.add(child); } return fields; @@ -132,6 +137,8 @@ public class HandleConstructor { @ProviderFor(EclipseAnnotationHandler.class) public static class HandleAllArgsConstructor extends EclipseAnnotationHandler<AllArgsConstructor> { @Override public void handle(AnnotationValues<AllArgsConstructor> annotation, Annotation ast, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.ALL_ARGS_CONSTRUCTOR_FLAG_USAGE, "@AllArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor"); + EclipseNode typeNode = annotationNode.up(); if (!checkLegality(typeNode, annotationNode, AllArgsConstructor.class.getSimpleName())) return; AllArgsConstructor ann = annotation.getInstance(); @@ -285,8 +292,8 @@ public class HandleConstructor { assigns.add(assignment); long fieldPos = (((long)field.sourceStart) << 32) | field.sourceEnd; Argument parameter = new Argument(fieldName, fieldPos, copyType(field.type, source), Modifier.FINAL); - Annotation[] nonNulls = findAnnotations(field, TransformationsUtil.NON_NULL_PATTERN); - Annotation[] nullables = findAnnotations(field, TransformationsUtil.NULLABLE_PATTERN); + Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN); + Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN); if (nonNulls.length != 0) { Statement nullCheck = generateNullCheck(field, source); if (nullCheck != null) nullChecks.add(nullCheck); @@ -355,7 +362,7 @@ public class HandleConstructor { Argument parameter = new Argument(field.name, fieldPos, copyType(field.type, source), Modifier.FINAL); - Annotation[] copiedAnnotations = copyAnnotations(source, findAnnotations(field, TransformationsUtil.NON_NULL_PATTERN), findAnnotations(field, TransformationsUtil.NULLABLE_PATTERN)); + Annotation[] copiedAnnotations = copyAnnotations(source, findAnnotations(field, NON_NULL_PATTERN), findAnnotations(field, NULLABLE_PATTERN)); if (copiedAnnotations.length != 0) parameter.annotations = copiedAnnotations; params.add(parameter); } diff --git a/src/core/lombok/eclipse/handlers/HandleData.java b/src/core/lombok/eclipse/handlers/HandleData.java index aa309489..a6a8842d 100644 --- a/src/core/lombok/eclipse/handlers/HandleData.java +++ b/src/core/lombok/eclipse/handlers/HandleData.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2009-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 @@ -21,9 +21,12 @@ */ package lombok.eclipse.handlers; +import static lombok.core.handlers.HandlerUtil.*; + import java.util.Collections; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.Data; import lombok.core.AnnotationValues; import lombok.eclipse.EclipseAnnotationHandler; @@ -40,7 +43,9 @@ import org.mangosdk.spi.ProviderFor; */ @ProviderFor(EclipseAnnotationHandler.class) public class HandleData extends EclipseAnnotationHandler<Data> { - public void handle(AnnotationValues<Data> annotation, Annotation ast, EclipseNode annotationNode) { + @Override public void handle(AnnotationValues<Data> annotation, Annotation ast, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.DATA_FLAG_USAGE, "@Data"); + Data ann = annotation.getInstance(); EclipseNode typeNode = annotationNode.up(); diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java index 0cc0836e..2ae7aba4 100644 --- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java @@ -21,6 +21,7 @@ */ package lombok.eclipse.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.lang.reflect.Modifier; @@ -33,6 +34,7 @@ import java.util.List; import java.util.Set; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.EqualsAndHashCode; import lombok.core.AST.Kind; import lombok.core.handlers.HandlerUtil; @@ -117,8 +119,9 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH generateMethods(typeNode, errorNode, null, null, null, false, FieldAccess.GETTER); } - @Override public void handle(AnnotationValues<EqualsAndHashCode> annotation, - Annotation ast, EclipseNode annotationNode) { + @Override public void handle(AnnotationValues<EqualsAndHashCode> annotation, Annotation ast, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.EQUALS_AND_HASH_CODE_FLAG_USAGE, "@EqualsAndHashCode"); + EqualsAndHashCode ann = annotation.getInstance(); List<String> excludes = Arrays.asList(ann.exclude()); List<String> includes = Arrays.asList(ann.of()); diff --git a/src/core/lombok/eclipse/handlers/HandleExtensionMethod.java b/src/core/lombok/eclipse/handlers/HandleExtensionMethod.java index 3d30e8fe..8e53d873 100644 --- a/src/core/lombok/eclipse/handlers/HandleExtensionMethod.java +++ b/src/core/lombok/eclipse/handlers/HandleExtensionMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Project Lombok Authors. + * Copyright (C) 2012-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 @@ -21,6 +21,8 @@ */ package lombok.eclipse.handlers; +import static lombok.core.handlers.HandlerUtil.*; + import java.util.List; import org.eclipse.jdt.internal.compiler.ast.Annotation; @@ -28,6 +30,7 @@ import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.mangosdk.spi.ProviderFor; +import lombok.ConfigurationKeys; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; import lombok.eclipse.EclipseAnnotationHandler; @@ -39,6 +42,8 @@ import lombok.experimental.ExtensionMethod; @HandlerPriority(66560) // 2^16 + 2^10; we must run AFTER HandleVal which is at 2^16 public class HandleExtensionMethod extends EclipseAnnotationHandler<ExtensionMethod> { @Override public void handle(AnnotationValues<ExtensionMethod> annotation, Annotation ast, EclipseNode annotationNode) { + handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.EXTENSION_METHOD_FLAG_USAGE, "@ExtensionMethod"); + TypeDeclaration typeDecl = null; EclipseNode owner = annotationNode.up(); if (owner.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) owner.get(); diff --git a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java index d6d839cc..7d0702db 100644 --- a/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java +++ b/src/core/lombok/eclipse/handlers/HandleFieldDefaults.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Project Lombok Authors. + * Copyright (C) 2012-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 @@ -21,8 +21,10 @@ */ package lombok.eclipse.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; @@ -102,6 +104,8 @@ public class HandleFieldDefaults extends EclipseAnnotationHandler<FieldDefaults> } public void handle(AnnotationValues<FieldDefaults> annotation, Annotation ast, EclipseNode annotationNode) { + handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults"); + EclipseNode node = annotationNode.up(); FieldDefaults instance = annotation.getInstance(); AccessLevel level = instance.level(); diff --git a/src/core/lombok/eclipse/handlers/HandleGetter.java b/src/core/lombok/eclipse/handlers/HandleGetter.java index 188dfce2..8cffaa2c 100644 --- a/src/core/lombok/eclipse/handlers/HandleGetter.java +++ b/src/core/lombok/eclipse/handlers/HandleGetter.java @@ -21,6 +21,7 @@ */ package lombok.eclipse.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; @@ -32,11 +33,11 @@ import java.util.List; import java.util.Map; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.Delegate; import lombok.Getter; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; -import lombok.core.TransformationsUtil; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.eclipse.agent.PatchDelegate; @@ -132,6 +133,8 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> { } public void handle(AnnotationValues<Getter> annotation, Annotation ast, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.GETTER_FLAG_USAGE, "@Getter"); + EclipseNode node = annotationNode.up(); Getter annotationInstance = annotation.getInstance(); AccessLevel level = annotationInstance.value(); @@ -269,8 +272,8 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> { Annotation[] copiedAnnotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), - findAnnotations(field, TransformationsUtil.NON_NULL_PATTERN), - findAnnotations(field, TransformationsUtil.NULLABLE_PATTERN), + findAnnotations(field, NON_NULL_PATTERN), + findAnnotations(field, NULLABLE_PATTERN), findDelegatesAndMarkAsHandled(fieldNode), deprecated); diff --git a/src/core/lombok/eclipse/handlers/HandleLog.java b/src/core/lombok/eclipse/handlers/HandleLog.java index cf1e8018..830190a2 100644 --- a/src/core/lombok/eclipse/handlers/HandleLog.java +++ b/src/core/lombok/eclipse/handlers/HandleLog.java @@ -21,15 +21,18 @@ */ package lombok.eclipse.handlers; -import static lombok.eclipse.Eclipse.*; +import static lombok.core.handlers.HandlerUtil.*; +import static lombok.eclipse.Eclipse.fromQualifiedName; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.lang.reflect.Modifier; import java.util.Arrays; +import lombok.ConfigurationKeys; import lombok.core.AnnotationValues; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.eclipse.handlers.EclipseHandlerUtil.MemberExistsResult; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess; @@ -49,10 +52,16 @@ public class HandleLog { throw new UnsupportedOperationException(); } - public static void processAnnotation(LoggingFramework framework, AnnotationValues<? extends java.lang.annotation.Annotation> annotation, Annotation source, EclipseNode annotationNode, String loggerCategory) { + public static void processAnnotation(LoggingFramework framework, AnnotationValues<? extends java.lang.annotation.Annotation> annotation, Annotation source, EclipseNode annotationNode, String loggerTopic) { EclipseNode owner = annotationNode.up(); + switch (owner.getKind()) { case TYPE: + String logFieldName = annotationNode.getAst().readConfiguration(ConfigurationKeys.LOG_ANY_FIELD_NAME); + if (logFieldName == null) logFieldName = "log"; + + boolean useStatic = !Boolean.FALSE.equals(annotationNode.getAst().readConfiguration(ConfigurationKeys.LOG_ANY_FIELD_IS_STATIC)); + TypeDeclaration typeDecl = null; if (owner.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) owner.get(); int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; @@ -65,14 +74,14 @@ public class HandleLog { return; } - if (fieldExists("log", owner) != MemberExistsResult.NOT_EXISTS) { - annotationNode.addWarning("Field 'log' already exists."); + if (fieldExists(logFieldName, owner) != MemberExistsResult.NOT_EXISTS) { + annotationNode.addWarning("Field '" + logFieldName + "' already exists."); return; } ClassLiteralAccess loggingType = selfType(owner, source); - FieldDeclaration fieldDeclaration = createField(framework, source, loggingType, loggerCategory); + FieldDeclaration fieldDeclaration = createField(framework, source, loggingType, logFieldName, useStatic, loggerTopic); fieldDeclaration.traverse(new SetGeneratedByVisitor(source), typeDecl.staticInitializerScope); // TODO temporary workaround for issue 217. http://code.google.com/p/projectlombok/issues/detail?id=217 // injectFieldSuppressWarnings(owner, fieldDeclaration); @@ -98,16 +107,15 @@ public class HandleLog { return result; } - public static FieldDeclaration createField(LoggingFramework framework, Annotation source, ClassLiteralAccess loggingType, String loggerCategory) { + private static FieldDeclaration createField(LoggingFramework framework, Annotation source, ClassLiteralAccess loggingType, String logFieldName, boolean useStatic, String loggerTopic) { int pS = source.sourceStart, pE = source.sourceEnd; long p = (long)pS << 32 | pE; // private static final <loggerType> log = <factoryMethod>(<parameter>); - - FieldDeclaration fieldDecl = new FieldDeclaration("log".toCharArray(), 0, -1); + FieldDeclaration fieldDecl = new FieldDeclaration(logFieldName.toCharArray(), 0, -1); setGeneratedBy(fieldDecl, source); fieldDecl.declarationSourceEnd = -1; - fieldDecl.modifiers = Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL; + fieldDecl.modifiers = Modifier.PRIVATE | (useStatic ? Modifier.STATIC : 0) | Modifier.FINAL; fieldDecl.type = createTypeReference(framework.getLoggerTypeName(), source); @@ -118,10 +126,10 @@ public class HandleLog { factoryMethodCall.selector = framework.getLoggerFactoryMethodName().toCharArray(); Expression parameter; - if (loggerCategory == null || loggerCategory.trim().length() == 0) { + if (loggerTopic == null || loggerTopic.trim().length() == 0) { parameter = framework.createFactoryParameter(loggingType, source); } else { - parameter = new StringLiteral(loggerCategory.toCharArray(), pS, pE, 0); + parameter = new StringLiteral(loggerTopic.toCharArray(), pS, pE, 0); } factoryMethodCall.arguments = new Expression[] { parameter }; @@ -161,6 +169,7 @@ public class HandleLog { @ProviderFor(EclipseAnnotationHandler.class) public static class HandleCommonsLog extends EclipseAnnotationHandler<lombok.extern.apachecommons.CommonsLog> { @Override public void handle(AnnotationValues<lombok.extern.apachecommons.CommonsLog> annotation, Annotation source, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.LOG_COMMONS_FLAG_USAGE, "@apachecommons.CommonsLog", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); processAnnotation(LoggingFramework.COMMONS, annotation, source, annotationNode, annotation.getInstance().topic()); } } @@ -171,6 +180,7 @@ public class HandleLog { @ProviderFor(EclipseAnnotationHandler.class) public static class HandleJulLog extends EclipseAnnotationHandler<lombok.extern.java.Log> { @Override public void handle(AnnotationValues<lombok.extern.java.Log> annotation, Annotation source, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.LOG_JUL_FLAG_USAGE, "@java.Log", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); processAnnotation(LoggingFramework.JUL, annotation, source, annotationNode, annotation.getInstance().topic()); } } @@ -181,6 +191,7 @@ public class HandleLog { @ProviderFor(EclipseAnnotationHandler.class) public static class HandleLog4jLog extends EclipseAnnotationHandler<lombok.extern.log4j.Log4j> { @Override public void handle(AnnotationValues<lombok.extern.log4j.Log4j> annotation, Annotation source, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.LOG_LOG4J_FLAG_USAGE, "@Log4j", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); processAnnotation(LoggingFramework.LOG4J, annotation, source, annotationNode, annotation.getInstance().topic()); } } @@ -191,6 +202,7 @@ public class HandleLog { @ProviderFor(EclipseAnnotationHandler.class) public static class HandleLog4j2Log extends EclipseAnnotationHandler<lombok.extern.log4j.Log4j2> { @Override public void handle(AnnotationValues<lombok.extern.log4j.Log4j2> annotation, Annotation source, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.LOG_LOG4J2_FLAG_USAGE, "@Log4j2", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); processAnnotation(LoggingFramework.LOG4J2, annotation, source, annotationNode, annotation.getInstance().topic()); } } @@ -201,6 +213,7 @@ public class HandleLog { @ProviderFor(EclipseAnnotationHandler.class) public static class HandleSlf4jLog extends EclipseAnnotationHandler<lombok.extern.slf4j.Slf4j> { @Override public void handle(AnnotationValues<lombok.extern.slf4j.Slf4j> annotation, Annotation source, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.LOG_SLF4J_FLAG_USAGE, "@Slf4j", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); processAnnotation(LoggingFramework.SLF4J, annotation, source, annotationNode, annotation.getInstance().topic()); } } @@ -211,6 +224,7 @@ public class HandleLog { @ProviderFor(EclipseAnnotationHandler.class) public static class HandleXSlf4jLog extends EclipseAnnotationHandler<lombok.extern.slf4j.XSlf4j> { @Override public void handle(AnnotationValues<lombok.extern.slf4j.XSlf4j> annotation, Annotation source, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.LOG_XSLF4J_FLAG_USAGE, "@XSlf4j", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); processAnnotation(LoggingFramework.XSLF4J, annotation, source, annotationNode, annotation.getInstance().topic()); } } diff --git a/src/core/lombok/eclipse/handlers/HandleNonNull.java b/src/core/lombok/eclipse/handlers/HandleNonNull.java index 7fd12ca3..79bb7a08 100644 --- a/src/core/lombok/eclipse/handlers/HandleNonNull.java +++ b/src/core/lombok/eclipse/handlers/HandleNonNull.java @@ -21,11 +21,13 @@ */ package lombok.eclipse.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.Eclipse.isPrimitive; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.util.Arrays; +import lombok.ConfigurationKeys; import lombok.NonNull; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; @@ -57,6 +59,8 @@ import org.mangosdk.spi.ProviderFor; @HandlerPriority(value = 512) // 2^9; onParameter=@__(@NonNull) has to run first. public class HandleNonNull extends EclipseAnnotationHandler<NonNull> { @Override public void handle(AnnotationValues<NonNull> annotation, Annotation ast, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.NON_NULL_FLAG_USAGE, "@NonNull"); + if (annotationNode.up().getKind() == Kind.FIELD) { // This is meaningless unless the field is used to generate a method (@Setter, @RequiredArgsConstructor, etc), // but in that case those handlers will take care of it. However, we DO check if the annotation is applied to diff --git a/src/core/lombok/eclipse/handlers/HandlePrintAST.java b/src/core/lombok/eclipse/handlers/HandlePrintAST.java index da6fa2a2..65cda98a 100644 --- a/src/core/lombok/eclipse/handlers/HandlePrintAST.java +++ b/src/core/lombok/eclipse/handlers/HandlePrintAST.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2012 The Project Lombok Authors. + * Copyright (C) 2009-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 @@ -52,7 +52,16 @@ public class HandlePrintAST extends EclipseAnnotationHandler<PrintAST> { } catch (FileNotFoundException e) { Lombok.sneakyThrow(e); } - - annotationNode.up().traverse(new EclipseASTVisitor.Printer(annotation.getInstance().printContent(), stream, annotation.getInstance().printPositions())); + try { + annotationNode.up().traverse(new EclipseASTVisitor.Printer(annotation.getInstance().printContent(), stream, annotation.getInstance().printPositions())); + } finally { + if (stream != System.out) { + try { + stream.close(); + } catch (Exception e) { + Lombok.sneakyThrow(e); + } + } + } } -} +}
\ No newline at end of file diff --git a/src/core/lombok/eclipse/handlers/HandleSetter.java b/src/core/lombok/eclipse/handlers/HandleSetter.java index caa5329a..74ef07b5 100644 --- a/src/core/lombok/eclipse/handlers/HandleSetter.java +++ b/src/core/lombok/eclipse/handlers/HandleSetter.java @@ -21,6 +21,7 @@ */ package lombok.eclipse.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; @@ -31,10 +32,10 @@ import java.util.Collections; import java.util.List; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.Setter; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; -import lombok.core.TransformationsUtil; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess; @@ -118,6 +119,8 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> { } public void handle(AnnotationValues<Setter> annotation, Annotation ast, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.SETTER_FLAG_USAGE, "@Setter"); + EclipseNode node = annotationNode.up(); AccessLevel level = annotation.getInstance().value(); if (level == AccessLevel.NONE || node == null) return; @@ -230,8 +233,8 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> { method.bodyStart = method.declarationSourceStart = method.sourceStart = source.sourceStart; method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = source.sourceEnd; - Annotation[] nonNulls = findAnnotations(field, TransformationsUtil.NON_NULL_PATTERN); - Annotation[] nullables = findAnnotations(field, TransformationsUtil.NULLABLE_PATTERN); + Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN); + Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN); List<Statement> statements = new ArrayList<Statement>(5); if (nonNulls.length == 0) { statements.add(assignment); diff --git a/src/core/lombok/eclipse/handlers/HandleSneakyThrows.java b/src/core/lombok/eclipse/handlers/HandleSneakyThrows.java index d8261326..481dbcde 100644 --- a/src/core/lombok/eclipse/handlers/HandleSneakyThrows.java +++ b/src/core/lombok/eclipse/handlers/HandleSneakyThrows.java @@ -21,6 +21,7 @@ */ package lombok.eclipse.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.lang.reflect.Modifier; @@ -28,6 +29,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import lombok.ConfigurationKeys; import lombok.SneakyThrows; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; @@ -75,6 +77,8 @@ public class HandleSneakyThrows extends EclipseAnnotationHandler<SneakyThrows> { } @Override public void handle(AnnotationValues<SneakyThrows> annotation, Annotation source, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.SNEAKY_THROWS_FLAG_USAGE, "@SneakyThrows"); + List<String> exceptionNames = annotation.getRawExpressions("value"); List<DeclaredException> exceptions = new ArrayList<DeclaredException>(); diff --git a/src/core/lombok/eclipse/handlers/HandleSynchronized.java b/src/core/lombok/eclipse/handlers/HandleSynchronized.java index b3e51da0..a45a499c 100644 --- a/src/core/lombok/eclipse/handlers/HandleSynchronized.java +++ b/src/core/lombok/eclipse/handlers/HandleSynchronized.java @@ -21,10 +21,12 @@ */ package lombok.eclipse.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.lang.reflect.Modifier; +import lombok.ConfigurationKeys; import lombok.Synchronized; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; @@ -105,6 +107,8 @@ public class HandleSynchronized extends EclipseAnnotationHandler<Synchronized> { } @Override public void handle(AnnotationValues<Synchronized> annotation, Annotation source, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.SYNCHRONIZED_FLAG_USAGE, "@Synchronized"); + int p1 = source.sourceStart -1; int p2 = source.sourceStart -2; long pos = (((long)p1) << 32) | p2; diff --git a/src/core/lombok/eclipse/handlers/HandleToString.java b/src/core/lombok/eclipse/handlers/HandleToString.java index 31ff3021..a4ed254a 100644 --- a/src/core/lombok/eclipse/handlers/HandleToString.java +++ b/src/core/lombok/eclipse/handlers/HandleToString.java @@ -21,6 +21,7 @@ */ package lombok.eclipse.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.util.ArrayList; @@ -32,12 +33,14 @@ import java.util.List; import java.util.Set; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.ToString; -import lombok.core.AnnotationValues; import lombok.core.AST.Kind; +import lombok.core.AnnotationValues; import lombok.eclipse.Eclipse; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; +import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.Annotation; @@ -88,12 +91,15 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> { boolean includeFieldNames = true; try { - includeFieldNames = ((Boolean)ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue(); + Boolean configuration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); + includeFieldNames = configuration != null ? configuration : ((Boolean)ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue(); } catch (Exception ignore) {} generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, FieldAccess.GETTER); } public void handle(AnnotationValues<ToString> annotation, Annotation ast, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.TO_STRING_FLAG_USAGE, "@ToString"); + ToString ann = annotation.getInstance(); List<String> excludes = Arrays.asList(ann.exclude()); List<String> includes = Arrays.asList(ann.of()); @@ -111,9 +117,14 @@ public class HandleToString extends EclipseAnnotationHandler<ToString> { checkForBogusFieldNames(typeNode, annotation); - FieldAccess fieldAccess = ann.doNotUseGetters() ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER; + Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS); + boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration; + FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER; - generateToString(typeNode, annotationNode, excludes, includes, ann.includeFieldNames(), callSuper, true, fieldAccess); + Boolean fieldNamesConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); + boolean includeFieldNames = annotation.isExplicit("includeFieldNames") || fieldNamesConfiguration == null ? ann.includeFieldNames() : fieldNamesConfiguration; + + generateToString(typeNode, annotationNode, excludes, includes, includeFieldNames, callSuper, true, fieldAccess); } public void generateToString(EclipseNode typeNode, EclipseNode errorNode, List<String> excludes, List<String> includes, diff --git a/src/core/lombok/eclipse/handlers/HandleVal.java b/src/core/lombok/eclipse/handlers/HandleVal.java index c8339f35..d4ae417c 100644 --- a/src/core/lombok/eclipse/handlers/HandleVal.java +++ b/src/core/lombok/eclipse/handlers/HandleVal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 The Project Lombok Authors. + * Copyright (C) 2010-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 @@ -21,6 +21,8 @@ */ package lombok.eclipse.handlers; +import static lombok.core.handlers.HandlerUtil.*; +import lombok.ConfigurationKeys; import lombok.val; import lombok.core.HandlerPriority; import lombok.eclipse.DeferUntilPostDiet; @@ -43,6 +45,8 @@ import org.mangosdk.spi.ProviderFor; public class HandleVal extends EclipseASTAdapter { @Override public void visitLocal(EclipseNode localNode, LocalDeclaration local) { if (!EclipseHandlerUtil.typeMatches(val.class, localNode, local.type)) return; + handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val"); + boolean variableOfForEach = false; if (localNode.directUp().get() instanceof ForeachStatement) { diff --git a/src/core/lombok/eclipse/handlers/HandleValue.java b/src/core/lombok/eclipse/handlers/HandleValue.java index 0607137b..211b337a 100644 --- a/src/core/lombok/eclipse/handlers/HandleValue.java +++ b/src/core/lombok/eclipse/handlers/HandleValue.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 The Project Lombok Authors. + * Copyright (C) 2012-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 @@ -21,11 +21,13 @@ */ package lombok.eclipse.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; import java.util.Collections; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; import lombok.eclipse.EclipseAnnotationHandler; @@ -46,6 +48,8 @@ import org.mangosdk.spi.ProviderFor; @HandlerPriority(-512) //-2^9; to ensure @EqualsAndHashCode and such pick up on this handler making the class final and messing with the fields' access levels, run earlier. public class HandleValue extends EclipseAnnotationHandler<Value> { public void handle(AnnotationValues<Value> annotation, Annotation ast, EclipseNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.VALUE_FLAG_USAGE, "@Value"); + Value ann = annotation.getInstance(); EclipseNode typeNode = annotationNode.up(); diff --git a/src/core/lombok/eclipse/handlers/HandleWither.java b/src/core/lombok/eclipse/handlers/HandleWither.java index 305e0a16..30306b72 100644 --- a/src/core/lombok/eclipse/handlers/HandleWither.java +++ b/src/core/lombok/eclipse/handlers/HandleWither.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 The Project Lombok Authors. + * Copyright (C) 2012-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 @@ -21,6 +21,7 @@ */ package lombok.eclipse.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.eclipse.Eclipse.*; import static lombok.eclipse.handlers.EclipseHandlerUtil.*; @@ -31,9 +32,9 @@ import java.util.Collections; import java.util.List; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; -import lombok.core.TransformationsUtil; import lombok.eclipse.EclipseAnnotationHandler; import lombok.eclipse.EclipseNode; import lombok.eclipse.handlers.EclipseHandlerUtil.FieldAccess; @@ -120,6 +121,8 @@ public class HandleWither extends EclipseAnnotationHandler<Wither> { } @Override public void handle(AnnotationValues<Wither> annotation, Annotation ast, EclipseNode annotationNode) { + handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.WITHER_FLAG_USAGE, "@Wither"); + EclipseNode node = annotationNode.up(); AccessLevel level = annotation.getInstance().value(); if (level == AccessLevel.NONE || node == null) return; @@ -266,8 +269,8 @@ public class HandleWither extends EclipseAnnotationHandler<Wither> { method.bodyStart = method.declarationSourceStart = method.sourceStart = source.sourceStart; method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = source.sourceEnd; - Annotation[] nonNulls = findAnnotations(field, TransformationsUtil.NON_NULL_PATTERN); - Annotation[] nullables = findAnnotations(field, TransformationsUtil.NULLABLE_PATTERN); + Annotation[] nonNulls = findAnnotations(field, NON_NULL_PATTERN); + Annotation[] nullables = findAnnotations(field, NULLABLE_PATTERN); List<Statement> statements = new ArrayList<Statement>(5); if (nonNulls.length > 0) { Statement nullCheck = generateNullCheck(field, source); diff --git a/src/core/lombok/experimental/Accessors.java b/src/core/lombok/experimental/Accessors.java index 2748bfa9..c2a0ca16 100644 --- a/src/core/lombok/experimental/Accessors.java +++ b/src/core/lombok/experimental/Accessors.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Project Lombok Authors. + * Copyright (C) 2012-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 diff --git a/src/core/lombok/experimental/ExtensionMethod.java b/src/core/lombok/experimental/ExtensionMethod.java index 44f28d04..7de8a136 100644 --- a/src/core/lombok/experimental/ExtensionMethod.java +++ b/src/core/lombok/experimental/ExtensionMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Project Lombok Authors. + * Copyright (C) 2012-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 diff --git a/src/core/lombok/experimental/FieldDefaults.java b/src/core/lombok/experimental/FieldDefaults.java index 8a3fb534..1c621f3c 100644 --- a/src/core/lombok/experimental/FieldDefaults.java +++ b/src/core/lombok/experimental/FieldDefaults.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Project Lombok Authors. + * Copyright (C) 2012-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 diff --git a/src/core/lombok/extern/apachecommons/CommonsLog.java b/src/core/lombok/extern/apachecommons/CommonsLog.java index 2e31edf7..45345098 100644 --- a/src/core/lombok/extern/apachecommons/CommonsLog.java +++ b/src/core/lombok/extern/apachecommons/CommonsLog.java @@ -63,4 +63,4 @@ public @interface CommonsLog { * Sets the category of the constructed Logger. By default, it will use the type where the annotation is placed. */ String topic() default ""; -}
\ No newline at end of file +} diff --git a/src/core/lombok/extern/java/Log.java b/src/core/lombok/extern/java/Log.java index f8cbf03f..bac2742e 100644 --- a/src/core/lombok/extern/java/Log.java +++ b/src/core/lombok/extern/java/Log.java @@ -62,4 +62,4 @@ public @interface Log { * Sets the category of the constructed Logger. By default, it will use the type where the annotation is placed. */ String topic() default ""; -}
\ No newline at end of file +} diff --git a/src/core/lombok/extern/log4j/Log4j.java b/src/core/lombok/extern/log4j/Log4j.java index d3164047..9490acb8 100644 --- a/src/core/lombok/extern/log4j/Log4j.java +++ b/src/core/lombok/extern/log4j/Log4j.java @@ -63,4 +63,4 @@ public @interface Log4j { * Sets the category of the constructed Logger. By default, it will use the type where the annotation is placed. */ String topic() default ""; -}
\ No newline at end of file +} diff --git a/src/core/lombok/extern/log4j/Log4j2.java b/src/core/lombok/extern/log4j/Log4j2.java index 0267d98c..43125e6b 100644 --- a/src/core/lombok/extern/log4j/Log4j2.java +++ b/src/core/lombok/extern/log4j/Log4j2.java @@ -63,4 +63,4 @@ public @interface Log4j2 { * Sets the category of the constructed Logger. By default, it will use the type where the annotation is placed. */ String topic() default ""; -}
\ No newline at end of file +} diff --git a/src/core/lombok/extern/slf4j/Slf4j.java b/src/core/lombok/extern/slf4j/Slf4j.java index 5d6e7d8c..04df6498 100644 --- a/src/core/lombok/extern/slf4j/Slf4j.java +++ b/src/core/lombok/extern/slf4j/Slf4j.java @@ -25,6 +25,7 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; + /** * Causes lombok to generate a logger field. * <p> @@ -62,3 +63,4 @@ public @interface Slf4j { */ String topic() default ""; } + diff --git a/src/core/lombok/extern/slf4j/XSlf4j.java b/src/core/lombok/extern/slf4j/XSlf4j.java index 0f2efe26..8a311c79 100644 --- a/src/core/lombok/extern/slf4j/XSlf4j.java +++ b/src/core/lombok/extern/slf4j/XSlf4j.java @@ -25,6 +25,7 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; + /** * Causes lombok to generate a logger field. * <p> diff --git a/src/core/lombok/javac/HandlerLibrary.java b/src/core/lombok/javac/HandlerLibrary.java index 4306b5f2..0905170b 100644 --- a/src/core/lombok/javac/HandlerLibrary.java +++ b/src/core/lombok/javac/HandlerLibrary.java @@ -40,6 +40,7 @@ import lombok.core.SpiLoadUtil; import lombok.core.TypeLibrary; import lombok.core.TypeResolver; import lombok.core.AnnotationValues.AnnotationValueDecodeFail; +import lombok.core.configuration.ConfigurationKeysLoader; import lombok.javac.handlers.JavacHandlerUtil; import com.sun.tools.javac.tree.JCTree; @@ -63,6 +64,7 @@ public class HandlerLibrary { * You probably want to use {@link #load(Messager)} instead. */ public HandlerLibrary(Messager messager) { + ConfigurationKeysLoader.LoaderLoader.loadAllConfigurationKeys(); this.messager = messager; } diff --git a/src/core/lombok/javac/JavacAST.java b/src/core/lombok/javac/JavacAST.java index 31bdc3a6..4e553063 100644 --- a/src/core/lombok/javac/JavacAST.java +++ b/src/core/lombok/javac/JavacAST.java @@ -22,6 +22,7 @@ package lombok.javac; import java.lang.reflect.Field; +import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -88,7 +89,16 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { this.javacTypes = JavacTypes.instance(context); clearChanged(); } - + + @Override public URI getAbsoluteFileLocation() { + try { + JCCompilationUnit cu = (JCCompilationUnit) top().get(); + return cu.sourcefile.toUri(); + } catch (Exception e) { + return null; + } + } + private static String sourceName(JCCompilationUnit cu) { return cu.sourcefile == null ? null : cu.sourcefile.toString(); } @@ -385,7 +395,7 @@ public class JavacAST extends AST<JavacAST, JavacNode, JCTree> { } } - private void removeFromDeferredDiagnostics(int startPos, int endPos) { + public void removeFromDeferredDiagnostics(int startPos, int endPos) { JCCompilationUnit self = (JCCompilationUnit) top().get(); new CompilerMessageSuppressor(getContext()).removeAllBetween(self.sourcefile, startPos, endPos); } diff --git a/src/core/lombok/javac/handlers/HandleAccessors.java b/src/core/lombok/javac/handlers/HandleAccessors.java index e2489bda..46fe1cd2 100644 --- a/src/core/lombok/javac/handlers/HandleAccessors.java +++ b/src/core/lombok/javac/handlers/HandleAccessors.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Project Lombok Authors. + * Copyright (C) 2012-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 @@ -21,12 +21,14 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import org.mangosdk.spi.ProviderFor; import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import lombok.ConfigurationKeys; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; import lombok.experimental.Accessors; @@ -39,6 +41,9 @@ public class HandleAccessors extends JavacAnnotationHandler<Accessors> { @Override public void handle(AnnotationValues<Accessors> annotation, JCAnnotation ast, JavacNode annotationNode) { // Accessors itself is handled by HandleGetter/Setter; this is just to ensure that the annotation is removed // from the AST when delomboking. + + handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.ACCESSORS_FLAG_USAGE, "@Accessors"); + deleteAnnotationIfNeccessary(annotationNode, Accessors.class); } } diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index 1fc2941e..b382395e 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -46,10 +46,10 @@ import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; -import lombok.core.TransformationsUtil; import lombok.experimental.Builder; import lombok.experimental.NonFinal; import lombok.javac.JavacAnnotationHandler; @@ -65,6 +65,8 @@ import static lombok.javac.JavacTreeMaker.TypeTag.*; @HandlerPriority(-1024) //-2^10; to ensure we've picked up @FieldDefault's changes (-2048) but @Value hasn't removed itself yet (-512), so that we can error on presence of it on the builder classes. public class HandleBuilder extends JavacAnnotationHandler<Builder> { @Override public void handle(AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder"); + Builder builderInstance = annotation.getInstance(); String builderMethodName = builderInstance.builderMethodName(); String buildMethodName = builderInstance.buildMethodName(); @@ -307,7 +309,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> { } boolean isBoolean = isBoolean(fieldNode); - String setterName = fluent ? fieldNode.getName() : TransformationsUtil.toSetterName(null, fieldNode.getName(), isBoolean); + String setterName = fluent ? fieldNode.getName() : toSetterName(builderType.getAst(), null, fieldNode.getName(), isBoolean); JavacTreeMaker maker = builderType.getTreeMaker(); return HandleSetter.createSetter(Flags.PUBLIC, fieldNode, maker, setterName, chain, source, List.<JCAnnotation>nil(), List.<JCAnnotation>nil()); diff --git a/src/core/lombok/javac/handlers/HandleCleanup.java b/src/core/lombok/javac/handlers/HandleCleanup.java index 12e7227d..4aa61764 100644 --- a/src/core/lombok/javac/handlers/HandleCleanup.java +++ b/src/core/lombok/javac/handlers/HandleCleanup.java @@ -21,9 +21,11 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import static lombok.javac.Javac.*; import lombok.Cleanup; +import lombok.ConfigurationKeys; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.delombok.LombokOptionsFactory; @@ -61,6 +63,8 @@ import com.sun.tools.javac.util.Name; @ProviderFor(JavacAnnotationHandler.class) public class HandleCleanup extends JavacAnnotationHandler<Cleanup> { @Override public void handle(AnnotationValues<Cleanup> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.CLEANUP_FLAG_USAGE, "@Cleanup"); + if (inNetbeansEditor(annotationNode)) return; deleteAnnotationIfNeccessary(annotationNode, Cleanup.class); diff --git a/src/core/lombok/javac/handlers/HandleConstructor.java b/src/core/lombok/javac/handlers/HandleConstructor.java index b8762ed8..adfa253f 100644 --- a/src/core/lombok/javac/handlers/HandleConstructor.java +++ b/src/core/lombok/javac/handlers/HandleConstructor.java @@ -21,13 +21,14 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import lombok.AccessLevel; import lombok.AllArgsConstructor; +import lombok.ConfigurationKeys; import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; import lombok.core.AnnotationValues; -import lombok.core.TransformationsUtil; import lombok.core.AST.Kind; import lombok.delombok.LombokOptionsFactory; import lombok.experimental.Builder; @@ -59,6 +60,8 @@ public class HandleConstructor { @ProviderFor(JavacAnnotationHandler.class) public static class HandleNoArgsConstructor extends JavacAnnotationHandler<NoArgsConstructor> { @Override public void handle(AnnotationValues<NoArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.NO_ARGS_CONSTRUCTOR_FLAG_USAGE, "@NoArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor"); + deleteAnnotationIfNeccessary(annotationNode, NoArgsConstructor.class); deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); JavacNode typeNode = annotationNode.up(); @@ -76,6 +79,8 @@ public class HandleConstructor { @ProviderFor(JavacAnnotationHandler.class) public static class HandleRequiredArgsConstructor extends JavacAnnotationHandler<RequiredArgsConstructor> { @Override public void handle(AnnotationValues<RequiredArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.REQUIRED_ARGS_CONSTRUCTOR_FLAG_USAGE, "@RequiredArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor"); + deleteAnnotationIfNeccessary(annotationNode, RequiredArgsConstructor.class); deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); JavacNode typeNode = annotationNode.up(); @@ -102,7 +107,7 @@ public class HandleConstructor { //Skip static fields. if ((fieldFlags & Flags.STATIC) != 0) continue; boolean isFinal = (fieldFlags & Flags.FINAL) != 0; - boolean isNonNull = !findAnnotations(child, TransformationsUtil.NON_NULL_PATTERN).isEmpty(); + boolean isNonNull = !findAnnotations(child, NON_NULL_PATTERN).isEmpty(); if ((isFinal || isNonNull) && fieldDecl.init == null) fields.append(child); } return fields.toList(); @@ -111,6 +116,8 @@ public class HandleConstructor { @ProviderFor(JavacAnnotationHandler.class) public static class HandleAllArgsConstructor extends JavacAnnotationHandler<AllArgsConstructor> { @Override public void handle(AnnotationValues<AllArgsConstructor> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.ALL_ARGS_CONSTRUCTOR_FLAG_USAGE, "@AllArgsConstructor", ConfigurationKeys.ANY_CONSTRUCTOR_FLAG_USAGE, "any @xArgsConstructor"); + deleteAnnotationIfNeccessary(annotationNode, AllArgsConstructor.class); deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); JavacNode typeNode = annotationNode.up(); @@ -234,8 +241,8 @@ public class HandleConstructor { JCVariableDecl field = (JCVariableDecl) fieldNode.get(); Name fieldName = removePrefixFromField(fieldNode); Name rawName = field.name; - List<JCAnnotation> nonNulls = findAnnotations(fieldNode, TransformationsUtil.NON_NULL_PATTERN); - List<JCAnnotation> nullables = findAnnotations(fieldNode, TransformationsUtil.NULLABLE_PATTERN); + List<JCAnnotation> nonNulls = findAnnotations(fieldNode, NON_NULL_PATTERN); + List<JCAnnotation> nullables = findAnnotations(fieldNode, NULLABLE_PATTERN); long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, nonNulls.appendList(nullables)), fieldName, field.vartype, null); params.append(param); @@ -297,8 +304,8 @@ public class HandleConstructor { JCVariableDecl field = (JCVariableDecl) fieldNode.get(); Name fieldName = removePrefixFromField(fieldNode); JCExpression pType = cloneType(maker, field.vartype, source, typeNode.getContext()); - List<JCAnnotation> nonNulls = findAnnotations(fieldNode, TransformationsUtil.NON_NULL_PATTERN); - List<JCAnnotation> nullables = findAnnotations(fieldNode, TransformationsUtil.NULLABLE_PATTERN); + List<JCAnnotation> nonNulls = findAnnotations(fieldNode, NON_NULL_PATTERN); + List<JCAnnotation> nullables = findAnnotations(fieldNode, NULLABLE_PATTERN); long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext()); JCVariableDecl param = maker.VarDef(maker.Modifiers(flags, nonNulls.appendList(nullables)), fieldName, pType, null); params.append(param); diff --git a/src/core/lombok/javac/handlers/HandleData.java b/src/core/lombok/javac/handlers/HandleData.java index 858fb543..9ecf8754 100644 --- a/src/core/lombok/javac/handlers/HandleData.java +++ b/src/core/lombok/javac/handlers/HandleData.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2011 The Project Lombok Authors. + * Copyright (C) 2009-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 @@ -21,8 +21,10 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.Data; import lombok.core.AnnotationValues; import lombok.javac.JavacAnnotationHandler; @@ -39,6 +41,8 @@ import com.sun.tools.javac.tree.JCTree.JCAnnotation; @ProviderFor(JavacAnnotationHandler.class) public class HandleData extends JavacAnnotationHandler<Data> { @Override public void handle(AnnotationValues<Data> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.DATA_FLAG_USAGE, "@Data"); + deleteAnnotationIfNeccessary(annotationNode, Data.class); JavacNode typeNode = annotationNode.up(); boolean notAClass = !isClass(typeNode); diff --git a/src/core/lombok/javac/handlers/HandleDelegate.java b/src/core/lombok/javac/handlers/HandleDelegate.java index 5e603777..ec6ea20c 100644 --- a/src/core/lombok/javac/handlers/HandleDelegate.java +++ b/src/core/lombok/javac/handlers/HandleDelegate.java @@ -21,6 +21,7 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import static com.sun.tools.javac.code.Flags.*; @@ -40,6 +41,7 @@ import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import lombok.ConfigurationKeys; import lombok.Delegate; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; @@ -97,6 +99,8 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> { private static final String LEGALITY_OF_DELEGATE = "@Delegate is legal only on instance fields or no-argument instance methods."; @Override public void handle(AnnotationValues<Delegate> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.DELEGATE_FLAG_USAGE, "@Delegate"); + deleteAnnotationIfNeccessary(annotationNode, Delegate.class); Type delegateType; diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java index 05c07c7a..2c998f48 100644 --- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java +++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java @@ -21,6 +21,7 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; @@ -28,6 +29,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import lombok.ConfigurationKeys; import lombok.EqualsAndHashCode; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; @@ -85,6 +87,8 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas } @Override public void handle(AnnotationValues<EqualsAndHashCode> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.EQUALS_AND_HASH_CODE_FLAG_USAGE, "@EqualsAndHashCode"); + deleteAnnotationIfNeccessary(annotationNode, EqualsAndHashCode.class); EqualsAndHashCode ann = annotation.getInstance(); List<String> excludes = List.from(ann.exclude()); diff --git a/src/core/lombok/javac/handlers/HandleExtensionMethod.java b/src/core/lombok/javac/handlers/HandleExtensionMethod.java index 3aee2f51..345c5f8e 100644 --- a/src/core/lombok/javac/handlers/HandleExtensionMethod.java +++ b/src/core/lombok/javac/handlers/HandleExtensionMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Project Lombok Authors. + * Copyright (C) 2012-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 @@ -22,6 +22,7 @@ package lombok.javac.handlers; import static com.sun.tools.javac.code.Flags.*; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import static lombok.javac.handlers.JavacResolver.*; @@ -30,6 +31,7 @@ import java.util.List; import javax.lang.model.element.ElementKind; +import lombok.ConfigurationKeys; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; import lombok.experimental.ExtensionMethod; @@ -64,6 +66,8 @@ import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; public class HandleExtensionMethod extends JavacAnnotationHandler<ExtensionMethod> { @Override public void handle(final AnnotationValues<ExtensionMethod> annotation, final JCAnnotation source, final JavacNode annotationNode) { + handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.EXTENSION_METHOD_FLAG_USAGE, "@ExtensionMethod"); + deleteAnnotationIfNeccessary(annotationNode, ExtensionMethod.class); JavacNode typeNode = annotationNode.up(); boolean isClassOrEnum = isClassOrEnum(typeNode); diff --git a/src/core/lombok/javac/handlers/HandleFieldDefaults.java b/src/core/lombok/javac/handlers/HandleFieldDefaults.java index 038f3e3f..335ab1fe 100644 --- a/src/core/lombok/javac/handlers/HandleFieldDefaults.java +++ b/src/core/lombok/javac/handlers/HandleFieldDefaults.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Project Lombok Authors. + * Copyright (C) 2012-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 @@ -21,8 +21,10 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; @@ -96,6 +98,8 @@ public class HandleFieldDefaults extends JavacAnnotationHandler<FieldDefaults> { } @Override public void handle(AnnotationValues<FieldDefaults> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.FIELD_DEFAULTS_FLAG_USAGE, "@FieldDefaults"); + deleteAnnotationIfNeccessary(annotationNode, FieldDefaults.class); deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); JavacNode node = annotationNode.up(); diff --git a/src/core/lombok/javac/handlers/HandleGetter.java b/src/core/lombok/javac/handlers/HandleGetter.java index 6b055193..48a13bde 100644 --- a/src/core/lombok/javac/handlers/HandleGetter.java +++ b/src/core/lombok/javac/handlers/HandleGetter.java @@ -21,6 +21,7 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.Javac.*; import static lombok.javac.JavacTreeMaker.TypeTag.*; import static lombok.javac.handlers.JavacHandlerUtil.*; @@ -31,11 +32,11 @@ import java.util.HashMap; import java.util.Map; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.Delegate; import lombok.Getter; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; -import lombok.core.TransformationsUtil; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; import lombok.javac.JavacTreeMaker; @@ -127,6 +128,8 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { } @Override public void handle(AnnotationValues<Getter> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.GETTER_FLAG_USAGE, "@Getter"); + Collection<JavacNode> fields = annotationNode.upFromAnnotationToFields(); deleteAnnotationIfNeccessary(annotationNode, Getter.class); deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); @@ -239,8 +242,8 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> { List<JCExpression> throwsClauses = List.nil(); JCExpression annotationMethodDefaultValue = null; - List<JCAnnotation> nonNulls = findAnnotations(field, TransformationsUtil.NON_NULL_PATTERN); - List<JCAnnotation> nullables = findAnnotations(field, TransformationsUtil.NULLABLE_PATTERN); + List<JCAnnotation> nonNulls = findAnnotations(field, NON_NULL_PATTERN); + List<JCAnnotation> nullables = findAnnotations(field, NULLABLE_PATTERN); List<JCAnnotation> delegates = findDelegatesAndRemoveFromField(field); diff --git a/src/core/lombok/javac/handlers/HandleLog.java b/src/core/lombok/javac/handlers/HandleLog.java index 36f3bbb5..ee7268f7 100644 --- a/src/core/lombok/javac/handlers/HandleLog.java +++ b/src/core/lombok/javac/handlers/HandleLog.java @@ -21,10 +21,12 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.lang.annotation.Annotation; +import lombok.ConfigurationKeys; import lombok.core.AnnotationValues; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; @@ -48,24 +50,28 @@ public class HandleLog { throw new UnsupportedOperationException(); } - public static void processAnnotation(LoggingFramework framework, AnnotationValues<?> annotation, JavacNode annotationNode, String loggerCategory) { + public static void processAnnotation(LoggingFramework framework, AnnotationValues<?> annotation, JavacNode annotationNode, String loggerTopic) { deleteAnnotationIfNeccessary(annotationNode, framework.getAnnotationClass()); JavacNode typeNode = annotationNode.up(); switch (typeNode.getKind()) { case TYPE: - if ((((JCClassDecl)typeNode.get()).mods.flags & Flags.INTERFACE)!= 0) { + String logFieldName = annotationNode.getAst().readConfiguration(ConfigurationKeys.LOG_ANY_FIELD_NAME); + if (logFieldName == null) logFieldName = "log"; + + boolean useStatic = !Boolean.FALSE.equals(annotationNode.getAst().readConfiguration(ConfigurationKeys.LOG_ANY_FIELD_IS_STATIC)); + + if ((((JCClassDecl)typeNode.get()).mods.flags & Flags.INTERFACE) != 0) { annotationNode.addError("@Log is legal only on classes and enums."); return; } - - if (fieldExists("log", typeNode)!= MemberExistsResult.NOT_EXISTS) { - annotationNode.addWarning("Field 'log' already exists."); + if (fieldExists(logFieldName, typeNode) != MemberExistsResult.NOT_EXISTS) { + annotationNode.addWarning("Field '" + logFieldName + "' already exists."); return; } JCFieldAccess loggingType = selfType(typeNode); - createField(framework, typeNode, loggingType, annotationNode.get(), loggerCategory); + createField(framework, typeNode, loggingType, annotationNode.get(), logFieldName, useStatic, loggerTopic); break; default: annotationNode.addError("@Log is legal only on types."); @@ -79,7 +85,7 @@ public class HandleLog { return maker.Select(maker.Ident(name), typeNode.toName("class")); } - public static boolean createField(LoggingFramework framework, JavacNode typeNode, JCFieldAccess loggingType, JCTree source, String loggerCategory) { + private static boolean createField(LoggingFramework framework, JavacNode typeNode, JCFieldAccess loggingType, JCTree source, String logFieldName, boolean useStatic, String loggerTopic) { JavacTreeMaker maker = typeNode.getTreeMaker(); // private static final <loggerType> log = <factoryMethod>(<parameter>); @@ -87,17 +93,17 @@ public class HandleLog { JCExpression factoryMethod = chainDotsString(typeNode, framework.getLoggerFactoryMethodName()); JCExpression loggerName; - if (loggerCategory == null || loggerCategory.trim().length() == 0) { + if (loggerTopic == null || loggerTopic.trim().length() == 0) { loggerName = framework.createFactoryParameter(typeNode, loggingType); } else { - loggerName = maker.Literal(loggerCategory); + loggerName = maker.Literal(loggerTopic); } JCMethodInvocation factoryMethodCall = maker.Apply(List.<JCExpression>nil(), factoryMethod, List.<JCExpression>of(loggerName)); JCVariableDecl fieldDecl = recursiveSetGeneratedBy(maker.VarDef( - maker.Modifiers(Flags.PRIVATE | Flags.FINAL | Flags.STATIC), - typeNode.toName("log"), loggerType, factoryMethodCall), source, typeNode.getContext()); + maker.Modifiers(Flags.PRIVATE | Flags.FINAL | (useStatic ? Flags.STATIC : 0)), + typeNode.toName(logFieldName), loggerType, factoryMethodCall), source, typeNode.getContext()); injectFieldSuppressWarnings(typeNode, fieldDecl); return true; @@ -109,6 +115,7 @@ public class HandleLog { @ProviderFor(JavacAnnotationHandler.class) public static class HandleCommonsLog extends JavacAnnotationHandler<lombok.extern.apachecommons.CommonsLog> { @Override public void handle(AnnotationValues<lombok.extern.apachecommons.CommonsLog> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.LOG_COMMONS_FLAG_USAGE, "@apachecommons.CommonsLog", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); processAnnotation(LoggingFramework.COMMONS, annotation, annotationNode, annotation.getInstance().topic()); } } @@ -119,6 +126,7 @@ public class HandleLog { @ProviderFor(JavacAnnotationHandler.class) public static class HandleJulLog extends JavacAnnotationHandler<lombok.extern.java.Log> { @Override public void handle(AnnotationValues<lombok.extern.java.Log> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.LOG_JUL_FLAG_USAGE, "@java.Log", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); processAnnotation(LoggingFramework.JUL, annotation, annotationNode, annotation.getInstance().topic()); } } @@ -129,6 +137,7 @@ public class HandleLog { @ProviderFor(JavacAnnotationHandler.class) public static class HandleLog4jLog extends JavacAnnotationHandler<lombok.extern.log4j.Log4j> { @Override public void handle(AnnotationValues<lombok.extern.log4j.Log4j> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.LOG_LOG4J_FLAG_USAGE, "@Log4j", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); processAnnotation(LoggingFramework.LOG4J, annotation, annotationNode, annotation.getInstance().topic()); } } @@ -139,6 +148,7 @@ public class HandleLog { @ProviderFor(JavacAnnotationHandler.class) public static class HandleLog4j2Log extends JavacAnnotationHandler<lombok.extern.log4j.Log4j2> { @Override public void handle(AnnotationValues<lombok.extern.log4j.Log4j2> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.LOG_LOG4J2_FLAG_USAGE, "@Log4j2", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); processAnnotation(LoggingFramework.LOG4J2, annotation, annotationNode, annotation.getInstance().topic()); } } @@ -149,6 +159,7 @@ public class HandleLog { @ProviderFor(JavacAnnotationHandler.class) public static class HandleSlf4jLog extends JavacAnnotationHandler<lombok.extern.slf4j.Slf4j> { @Override public void handle(AnnotationValues<lombok.extern.slf4j.Slf4j> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.LOG_SLF4J_FLAG_USAGE, "@Slf4j", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); processAnnotation(LoggingFramework.SLF4J, annotation, annotationNode, annotation.getInstance().topic()); } } @@ -159,6 +170,7 @@ public class HandleLog { @ProviderFor(JavacAnnotationHandler.class) public static class HandleXSlf4jLog extends JavacAnnotationHandler<lombok.extern.slf4j.XSlf4j> { @Override public void handle(AnnotationValues<lombok.extern.slf4j.XSlf4j> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.LOG_XSLF4J_FLAG_USAGE, "@XSlf4j", ConfigurationKeys.LOG_ANY_FLAG_USAGE, "any @Log"); processAnnotation(LoggingFramework.XSLF4J, annotation, annotationNode, annotation.getInstance().topic()); } } diff --git a/src/core/lombok/javac/handlers/HandleNonNull.java b/src/core/lombok/javac/handlers/HandleNonNull.java index 4cab48cf..172e70b3 100644 --- a/src/core/lombok/javac/handlers/HandleNonNull.java +++ b/src/core/lombok/javac/handlers/HandleNonNull.java @@ -21,6 +21,7 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; @@ -42,6 +43,7 @@ import com.sun.tools.javac.tree.JCTree.JCTry; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.util.List; +import lombok.ConfigurationKeys; import lombok.NonNull; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; @@ -55,6 +57,8 @@ import static lombok.javac.JavacTreeMaker.TreeTag.*; @HandlerPriority(value = 512) // 2^9; onParameter=@__(@NonNull) has to run first. public class HandleNonNull extends JavacAnnotationHandler<NonNull> { @Override public void handle(AnnotationValues<NonNull> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.NON_NULL_FLAG_USAGE, "@NonNull"); + if (annotationNode.up().getKind() == Kind.FIELD) { // This is meaningless unless the field is used to generate a method (@Setter, @RequiredArgsConstructor, etc), // but in that case those handlers will take care of it. However, we DO check if the annotation is applied to diff --git a/src/core/lombok/javac/handlers/HandlePrintAST.java b/src/core/lombok/javac/handlers/HandlePrintAST.java index 7b6d942c..2c229f2b 100644 --- a/src/core/lombok/javac/handlers/HandlePrintAST.java +++ b/src/core/lombok/javac/handlers/HandlePrintAST.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2011 The Project Lombok Authors. + * Copyright (C) 2009-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 @@ -52,6 +52,16 @@ public class HandlePrintAST extends JavacAnnotationHandler<PrintAST> { Lombok.sneakyThrow(e); } - annotationNode.up().traverse(new JavacASTVisitor.Printer(annotation.getInstance().printContent(), stream)); + try { + annotationNode.up().traverse(new JavacASTVisitor.Printer(annotation.getInstance().printContent(), stream)); + } finally { + if (stream != System.out) { + try { + stream.close(); + } catch (Exception e) { + Lombok.sneakyThrow(e); + } + } + } } } diff --git a/src/core/lombok/javac/handlers/HandleSetter.java b/src/core/lombok/javac/handlers/HandleSetter.java index ae8741de..fbc9ef46 100644 --- a/src/core/lombok/javac/handlers/HandleSetter.java +++ b/src/core/lombok/javac/handlers/HandleSetter.java @@ -22,15 +22,16 @@ package lombok.javac.handlers; import static lombok.javac.Javac.*; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.Collection; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.Setter; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; -import lombok.core.TransformationsUtil; import lombok.javac.Javac; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; @@ -118,6 +119,8 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { } @Override public void handle(AnnotationValues<Setter> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.SETTER_FLAG_USAGE, "@Setter"); + Collection<JavacNode> fields = annotationNode.upFromAnnotationToFields(); deleteAnnotationIfNeccessary(annotationNode, Setter.class); deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); @@ -205,8 +208,8 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> { JCAssign assign = treeMaker.Assign(fieldRef, treeMaker.Ident(fieldDecl.name)); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); - List<JCAnnotation> nonNulls = findAnnotations(field, TransformationsUtil.NON_NULL_PATTERN); - List<JCAnnotation> nullables = findAnnotations(field, TransformationsUtil.NULLABLE_PATTERN); + List<JCAnnotation> nonNulls = findAnnotations(field, NON_NULL_PATTERN); + List<JCAnnotation> nullables = findAnnotations(field, NULLABLE_PATTERN); Name methodName = field.toName(setterName); List<JCAnnotation> annsOnParam = copyAnnotations(onParam).appendList(nonNulls).appendList(nullables); diff --git a/src/core/lombok/javac/handlers/HandleSneakyThrows.java b/src/core/lombok/javac/handlers/HandleSneakyThrows.java index aa0c3c7e..ffe37a4c 100644 --- a/src/core/lombok/javac/handlers/HandleSneakyThrows.java +++ b/src/core/lombok/javac/handlers/HandleSneakyThrows.java @@ -21,12 +21,14 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import lombok.ConfigurationKeys; import lombok.SneakyThrows; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; @@ -57,6 +59,8 @@ import lombok.javac.Javac; @HandlerPriority(value = 1024) // 2^10; @NonNull must have run first, so that we wrap around the statements generated by it. public class HandleSneakyThrows extends JavacAnnotationHandler<SneakyThrows> { @Override public void handle(AnnotationValues<SneakyThrows> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.SNEAKY_THROWS_FLAG_USAGE, "@SneakyThrows"); + deleteAnnotationIfNeccessary(annotationNode, SneakyThrows.class); Collection<String> exceptionNames = annotation.getRawExpressions("value"); if (exceptionNames.isEmpty()) { diff --git a/src/core/lombok/javac/handlers/HandleSynchronized.java b/src/core/lombok/javac/handlers/HandleSynchronized.java index 13b8ffcb..fb6678e6 100644 --- a/src/core/lombok/javac/handlers/HandleSynchronized.java +++ b/src/core/lombok/javac/handlers/HandleSynchronized.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2009-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 @@ -21,8 +21,10 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; +import lombok.ConfigurationKeys; import lombok.Synchronized; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; @@ -54,6 +56,8 @@ public class HandleSynchronized extends JavacAnnotationHandler<Synchronized> { private static final String STATIC_LOCK_NAME = "$LOCK"; @Override public void handle(AnnotationValues<Synchronized> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.SYNCHRONIZED_FLAG_USAGE, "@Synchronized"); + if (inNetbeansEditor(annotationNode)) return; deleteAnnotationIfNeccessary(annotationNode, Synchronized.class); diff --git a/src/core/lombok/javac/handlers/HandleToString.java b/src/core/lombok/javac/handlers/HandleToString.java index f1edae0c..743e7b26 100644 --- a/src/core/lombok/javac/handlers/HandleToString.java +++ b/src/core/lombok/javac/handlers/HandleToString.java @@ -21,11 +21,13 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import static lombok.javac.Javac.*; import java.util.Collection; +import lombok.ConfigurationKeys; import lombok.ToString; import lombok.core.AnnotationValues; import lombok.core.AST.Kind; @@ -71,6 +73,8 @@ public class HandleToString extends JavacAnnotationHandler<ToString> { } @Override public void handle(AnnotationValues<ToString> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleFlagUsage(annotationNode, ConfigurationKeys.TO_STRING_FLAG_USAGE, "@ToString"); + deleteAnnotationIfNeccessary(annotationNode, ToString.class); ToString ann = annotation.getInstance(); @@ -91,9 +95,14 @@ public class HandleToString extends JavacAnnotationHandler<ToString> { annotation.setWarning("exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored."); } - FieldAccess fieldAccess = ann.doNotUseGetters() ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER; + Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_DO_NOT_USE_GETTERS); + boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration; + FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER; + + Boolean fieldNamesConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); + boolean includeFieldNames = annotation.isExplicit("includeFieldNames") || fieldNamesConfiguration == null ? ann.includeFieldNames() : fieldNamesConfiguration; - generateToString(typeNode, annotationNode, excludes, includes, ann.includeFieldNames(), callSuper, true, fieldAccess); + generateToString(typeNode, annotationNode, excludes, includes, includeFieldNames, callSuper, true, fieldAccess); } public void generateToStringForType(JavacNode typeNode, JavacNode errorNode) { @@ -105,7 +114,8 @@ public class HandleToString extends JavacAnnotationHandler<ToString> { boolean includeFieldNames = true; try { - includeFieldNames = ((Boolean)ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue(); + Boolean configuration = typeNode.getAst().readConfiguration(ConfigurationKeys.TO_STRING_INCLUDE_FIELD_NAMES); + includeFieldNames = configuration != null ? configuration : ((Boolean)ToString.class.getMethod("includeFieldNames").getDefaultValue()).booleanValue(); } catch (Exception ignore) {} generateToString(typeNode, errorNode, null, null, includeFieldNames, null, false, FieldAccess.GETTER); } diff --git a/src/core/lombok/javac/handlers/HandleVal.java b/src/core/lombok/javac/handlers/HandleVal.java index 8dc8e865..75464195 100644 --- a/src/core/lombok/javac/handlers/HandleVal.java +++ b/src/core/lombok/javac/handlers/HandleVal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 The Project Lombok Authors. + * Copyright (C) 2010-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 @@ -21,8 +21,9 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; - +import lombok.ConfigurationKeys; import lombok.val; import lombok.core.HandlerPriority; import lombok.javac.JavacASTAdapter; @@ -55,6 +56,8 @@ public class HandleVal extends JavacASTAdapter { if (!typeMatches(val.class, localNode, local.vartype)) return; + handleFlagUsage(localNode, ConfigurationKeys.VAL_FLAG_USAGE, "val"); + JCTree parentRaw = localNode.directUp().get(); if (parentRaw instanceof JCForLoop) { localNode.addError("'val' is not allowed in old-style for loops"); diff --git a/src/core/lombok/javac/handlers/HandleValue.java b/src/core/lombok/javac/handlers/HandleValue.java index 15fb4781..90f6a98d 100644 --- a/src/core/lombok/javac/handlers/HandleValue.java +++ b/src/core/lombok/javac/handlers/HandleValue.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 The Project Lombok Authors. + * Copyright (C) 2012-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 @@ -21,11 +21,13 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.lang.annotation.Annotation; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.core.AnnotationValues; import lombok.core.HandlerPriority; import lombok.experimental.NonFinal; @@ -50,6 +52,9 @@ public class HandleValue extends JavacAnnotationHandler<Value> { @Override public void handle(AnnotationValues<Value> annotation, JCAnnotation ast, JavacNode annotationNode) { @SuppressWarnings("deprecation") Class<? extends Annotation> oldExperimentalValue = lombok.experimental.Value.class; + + handleFlagUsage(annotationNode, ConfigurationKeys.VALUE_FLAG_USAGE, "@Value"); + deleteAnnotationIfNeccessary(annotationNode, Value.class, oldExperimentalValue); JavacNode typeNode = annotationNode.up(); boolean notAClass = !isClass(typeNode); diff --git a/src/core/lombok/javac/handlers/HandleWither.java b/src/core/lombok/javac/handlers/HandleWither.java index f6277bc3..7b55b671 100644 --- a/src/core/lombok/javac/handlers/HandleWither.java +++ b/src/core/lombok/javac/handlers/HandleWither.java @@ -21,15 +21,16 @@ */ package lombok.javac.handlers; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.Javac.*; import static lombok.javac.handlers.JavacHandlerUtil.*; import java.util.Collection; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; -import lombok.core.TransformationsUtil; import lombok.experimental.Wither; import lombok.javac.JavacAnnotationHandler; import lombok.javac.JavacNode; @@ -119,6 +120,8 @@ public class HandleWither extends JavacAnnotationHandler<Wither> { } @Override public void handle(AnnotationValues<Wither> annotation, JCAnnotation ast, JavacNode annotationNode) { + handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.WITHER_FLAG_USAGE, "@Wither"); + Collection<JavacNode> fields = annotationNode.upFromAnnotationToFields(); deleteAnnotationIfNeccessary(annotationNode, Wither.class); deleteImportFromCompilationUnit(annotationNode, "lombok.AccessLevel"); @@ -208,8 +211,8 @@ public class HandleWither extends JavacAnnotationHandler<Wither> { JCVariableDecl fieldDecl = (JCVariableDecl) field.get(); ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>(); - List<JCAnnotation> nonNulls = findAnnotations(field, TransformationsUtil.NON_NULL_PATTERN); - List<JCAnnotation> nullables = findAnnotations(field, TransformationsUtil.NULLABLE_PATTERN); + List<JCAnnotation> nonNulls = findAnnotations(field, NON_NULL_PATTERN); + List<JCAnnotation> nullables = findAnnotations(field, NULLABLE_PATTERN); Name methodName = field.toName(witherName); List<JCAnnotation> annsOnParam = copyAnnotations(onParam).appendList(nonNulls).appendList(nullables); diff --git a/src/core/lombok/javac/handlers/JavacHandlerUtil.java b/src/core/lombok/javac/handlers/JavacHandlerUtil.java index bc4dda8e..37164a6c 100644 --- a/src/core/lombok/javac/handlers/JavacHandlerUtil.java +++ b/src/core/lombok/javac/handlers/JavacHandlerUtil.java @@ -21,7 +21,7 @@ */ package lombok.javac.handlers; -import static lombok.core.TransformationsUtil.INVALID_ON_BUILDERS; +import static lombok.core.handlers.HandlerUtil.*; import static lombok.javac.Javac.*; import java.lang.annotation.Annotation; @@ -29,6 +29,7 @@ import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; @@ -36,12 +37,13 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.AccessLevel; +import lombok.ConfigurationKeys; import lombok.Data; import lombok.Getter; import lombok.core.AST.Kind; import lombok.core.AnnotationValues; import lombok.core.AnnotationValues.AnnotationValue; -import lombok.core.TransformationsUtil; +import lombok.core.handlers.HandlerUtil; import lombok.core.TypeResolver; import lombok.delombok.LombokOptionsFactory; import lombok.experimental.Accessors; @@ -391,7 +393,7 @@ public class JavacHandlerUtil { * Convenient wrapper around {@link TransformationsUtil#toAllGetterNames(lombok.core.AnnotationValues, CharSequence, boolean)}. */ public static java.util.List<String> toAllGetterNames(JavacNode field) { - return TransformationsUtil.toAllGetterNames(getAccessorsForField(field), field.getName(), isBoolean(field)); + return HandlerUtil.toAllGetterNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field)); } /** @@ -400,7 +402,7 @@ public class JavacHandlerUtil { * Convenient wrapper around {@link TransformationsUtil#toGetterName(lombok.core.AnnotationValues, CharSequence, boolean)}. */ public static String toGetterName(JavacNode field) { - return TransformationsUtil.toGetterName(getAccessorsForField(field), field.getName(), isBoolean(field)); + return HandlerUtil.toGetterName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field)); } /** @@ -408,7 +410,7 @@ public class JavacHandlerUtil { * Convenient wrapper around {@link TransformationsUtil#toAllSetterNames(lombok.core.AnnotationValues, CharSequence, boolean)}. */ public static java.util.List<String> toAllSetterNames(JavacNode field) { - return TransformationsUtil.toAllSetterNames(getAccessorsForField(field), field.getName(), isBoolean(field)); + return HandlerUtil.toAllSetterNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field)); } /** @@ -417,7 +419,7 @@ public class JavacHandlerUtil { * Convenient wrapper around {@link TransformationsUtil#toSetterName(lombok.core.AnnotationValues, CharSequence, boolean)}. */ public static String toSetterName(JavacNode field) { - return TransformationsUtil.toSetterName(getAccessorsForField(field), field.getName(), isBoolean(field)); + return HandlerUtil.toSetterName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field)); } /** @@ -425,7 +427,7 @@ public class JavacHandlerUtil { * Convenient wrapper around {@link TransformationsUtil#toAllWitherNames(lombok.core.AnnotationValues, CharSequence, boolean)}. */ public static java.util.List<String> toAllWitherNames(JavacNode field) { - return TransformationsUtil.toAllWitherNames(getAccessorsForField(field), field.getName(), isBoolean(field)); + return HandlerUtil.toAllWitherNames(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field)); } /** @@ -434,7 +436,7 @@ public class JavacHandlerUtil { * Convenient wrapper around {@link TransformationsUtil#toWitherName(lombok.core.AnnotationValues, CharSequence, boolean)}. */ public static String toWitherName(JavacNode field) { - return TransformationsUtil.toWitherName(getAccessorsForField(field), field.getName(), isBoolean(field)); + return HandlerUtil.toWitherName(field.getAst(), getAccessorsForField(field), field.getName(), isBoolean(field)); } /** @@ -446,9 +448,7 @@ public class JavacHandlerUtil { AnnotationValues<Accessors> accessors = JavacHandlerUtil.getAccessorsForField(field); - boolean forced = (accessors.getActualExpression("chain") != null); - Accessors instance = accessors.getInstance(); - return instance.chain() || (instance.fluent() && !forced); + return HandlerUtil.shouldReturnThis0(accessors, field.getAst()); } public static JCExpression cloneSelfType(JavacNode field) { @@ -481,10 +481,11 @@ public class JavacHandlerUtil { } public static Name removePrefixFromField(JavacNode field) { - String[] prefixes = null; + java.util.List<String> prefixes = null; for (JavacNode node : field.down()) { if (annotationTypeMatches(Accessors.class, node)) { - prefixes = createAnnotation(Accessors.class, node).getInstance().prefix(); + AnnotationValues<Accessors> ann = createAnnotation(Accessors.class, node); + if (ann.isExplicit("prefix")) prefixes = Arrays.asList(ann.getInstance().prefix()); break; } } @@ -495,7 +496,8 @@ public class JavacHandlerUtil { while (current != null) { for (JavacNode node : current.down()) { if (annotationTypeMatches(Accessors.class, node)) { - prefixes = createAnnotation(Accessors.class, node).getInstance().prefix(); + AnnotationValues<Accessors> ann = createAnnotation(Accessors.class, node); + if (ann.isExplicit("prefix")) prefixes = Arrays.asList(ann.getInstance().prefix()); break outer; } } @@ -503,8 +505,10 @@ public class JavacHandlerUtil { } } - if (prefixes != null && prefixes.length > 0) { - CharSequence newName = TransformationsUtil.removePrefix(field.getName(), prefixes); + if (prefixes == null) prefixes = field.getAst().readConfiguration(ConfigurationKeys.ACCESSORS_PREFIX); + + if (!prefixes.isEmpty()) { + CharSequence newName = removePrefix(field.getName(), prefixes); if (newName != null) return field.toName(newName.toString()); } @@ -1073,11 +1077,20 @@ public class JavacHandlerUtil { return problematic.toList(); } - static List<JCAnnotation> unboxAndRemoveAnnotationParameter(JCAnnotation ast, String parameterName, String errorName, JavacNode errorNode) { + static List<JCAnnotation> unboxAndRemoveAnnotationParameter(JCAnnotation ast, String parameterName, String errorName, JavacNode annotationNode) { ListBuffer<JCExpression> params = new ListBuffer<JCExpression>(); ListBuffer<JCAnnotation> result = new ListBuffer<JCAnnotation>(); - errorNode.removeDeferredErrors(); + try { + for (JCExpression arg : ast.args) { + String argName = "value"; + if (arg instanceof JCAssign) { + JCAssign as = (JCAssign) arg; + argName = as.lhs.toString(); + } + if (!argName.equals(parameterName)) continue; + } + } catch (Exception ignore) {} outer: for (JCExpression param : ast.args) { @@ -1097,11 +1110,14 @@ public class JavacHandlerUtil { continue outer; } + int endPos = Javac.getEndPosition(param.pos(), (JCCompilationUnit) annotationNode.top().get()); + annotationNode.getAst().removeFromDeferredDiagnostics(param.pos, endPos); + if (valueOfParam instanceof JCAnnotation) { String dummyAnnotationName = ((JCAnnotation) valueOfParam).annotationType.toString(); dummyAnnotationName = dummyAnnotationName.replace("_", "").replace("$", "").replace("x", "").replace("X", ""); if (dummyAnnotationName.length() > 0) { - errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); + annotationNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); continue outer; } for (JCExpression expr : ((JCAnnotation) valueOfParam).args) { @@ -1110,7 +1126,7 @@ public class JavacHandlerUtil { if ("value".equals(id.name.toString())) { expr = ((JCAssign) expr).rhs; } else { - errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); + annotationNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); continue outer; } } @@ -1122,12 +1138,12 @@ public class JavacHandlerUtil { if (expr2 instanceof JCAnnotation) { result.append((JCAnnotation) expr2); } else { - errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); + annotationNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); continue outer; } } } else { - errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); + annotationNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); continue outer; } } @@ -1135,7 +1151,7 @@ public class JavacHandlerUtil { if (valueOfParam instanceof JCNewArray && ((JCNewArray) valueOfParam).elems.isEmpty()) { // Then we just remove it and move on (it's onMethod={} for example). } else { - errorNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); + annotationNode.addError("The correct format is " + errorName + "@__({@SomeAnnotation, @SomeOtherAnnotation}))"); } } } diff --git a/src/core/lombok/val.java b/src/core/lombok/val.java index 7e495c8d..cd8652d6 100644 --- a/src/core/lombok/val.java +++ b/src/core/lombok/val.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 The Project Lombok Authors. + * Copyright (C) 2010-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 @@ -30,4 +30,5 @@ package lombok; * <p> * Complete documentation is found at <a href="http://projectlombok.org/features/val.html">the project lombok features page for @val</a>. */ -public @interface val {} +public @interface val { +} diff --git a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java index 9309e05d..1657d2a1 100644 --- a/src/delombok/lombok/delombok/PrettyCommentsPrinter.java +++ b/src/delombok/lombok/delombok/PrettyCommentsPrinter.java @@ -632,8 +632,7 @@ public class PrettyCommentsPrinter extends JCTree.Visitor { */ public void printUnit(JCCompilationUnit tree, JCClassDecl cdef) throws IOException { Object dc = getDocComments(tree); - if (dc instanceof Map) this.docComments = (Map) dc; - else if (dc instanceof DocCommentTable) this.docTable = (DocCommentTable) dc; + loadDocCommentsTable(dc); printDocComment(tree); if (tree.pid != null) { consumeComments(tree.pos, tree); @@ -668,6 +667,12 @@ public class PrettyCommentsPrinter extends JCTree.Visitor { } } // where + @SuppressWarnings("unchecked") + private void loadDocCommentsTable(Object dc) { + if (dc instanceof Map<?, ?>) this.docComments = (Map) dc; + else if (dc instanceof DocCommentTable) this.docTable = (DocCommentTable) dc; + } + boolean isUsed(final Symbol t, JCTree cdef) { class UsedVisitor extends TreeScanner { public void scan(JCTree tree) { diff --git a/test/configuration/resource/configurationRoot/d1/d11/d111/f1.txt b/test/configuration/resource/configurationRoot/d1/d11/d111/f1.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/configuration/resource/configurationRoot/d1/d11/d111/f1.txt diff --git a/test/configuration/resource/configurationRoot/d1/d11/d111/lombok.config b/test/configuration/resource/configurationRoot/d1/d11/d111/lombok.config new file mode 100644 index 00000000..bcea2a72 --- /dev/null +++ b/test/configuration/resource/configurationRoot/d1/d11/d111/lombok.config @@ -0,0 +1,2 @@ +clear lombok.Accessors.chain +lombok.Accessors.prefix += m_
\ No newline at end of file diff --git a/test/configuration/resource/configurationRoot/d1/d11/lombok.config b/test/configuration/resource/configurationRoot/d1/d11/lombok.config new file mode 100644 index 00000000..fcdc4823 --- /dev/null +++ b/test/configuration/resource/configurationRoot/d1/d11/lombok.config @@ -0,0 +1,5 @@ +stop-bubbling=true + +lombok.Accessors.chain = false +lombok.Accessors.flagUsage = ERROR +lombok.Accessors.prefix += f
\ No newline at end of file diff --git a/test/configuration/resource/configurationRoot/d1/d12/lombok.config b/test/configuration/resource/configurationRoot/d1/d12/lombok.config new file mode 100644 index 00000000..a18a4756 --- /dev/null +++ b/test/configuration/resource/configurationRoot/d1/d12/lombok.config @@ -0,0 +1,3 @@ +stop-bubbling=true + +lombok.Accessors.chain = true
\ No newline at end of file diff --git a/test/configuration/resource/configurationRoot/d1/lombok.config b/test/configuration/resource/configurationRoot/d1/lombok.config new file mode 100644 index 00000000..dec482e6 --- /dev/null +++ b/test/configuration/resource/configurationRoot/d1/lombok.config @@ -0,0 +1,3 @@ +stop-bubbling=true +# this file shouldn't be read +no error expected
\ No newline at end of file diff --git a/test/configuration/resource/configurationRoot/err.txt b/test/configuration/resource/configurationRoot/err.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/configuration/resource/configurationRoot/err.txt diff --git a/test/configuration/resource/configurationRoot/out.txt b/test/configuration/resource/configurationRoot/out.txt new file mode 100644 index 00000000..54e91094 --- /dev/null +++ b/test/configuration/resource/configurationRoot/out.txt @@ -0,0 +1,90 @@ +Configuration for 'BASE/d1/d11'. + +# Emit a warning or error if @Accessors is used. +lombok.Accessors.flagUsage = ERROR +# BASE/d1/lombok.config: +# <'lombok.Accessors.flagUsage' not mentioned> +# +# BASE/d1/d11/lombok.config: +# 3: lombok.Accessors.flagUsage = ERROR + +# Generate setters that return 'this' instead of 'void'. +lombok.Accessors.chain = false +# BASE/d1/lombok.config: +# <'lombok.Accessors.chain' not mentioned> +# +# BASE/d1/d11/lombok.config: +# 2: lombok.Accessors.chain = false + +# Strip this field prefix, like 'f' or 'm_', from the names of generated getters and setters. +lombok.Accessors.prefix += f +# BASE/d1/lombok.config: +# <'lombok.Accessors.prefix' not mentioned> +# +# BASE/d1/d11/lombok.config: +# 4: lombok.Accessors.prefix += f + +# Use this name for the generated logger fields (default: 'log') +clear lombok.log.fieldName + + +Configuration for: +- BASE/d1/d11/d111 +- BASE/d1/d11/d111/f1.txt + +# Emit a warning or error if @Accessors is used. +lombok.Accessors.flagUsage = ERROR +# BASE/d1/lombok.config: +# <'lombok.Accessors.flagUsage' not mentioned> +# +# BASE/d1/d11/lombok.config: +# 3: lombok.Accessors.flagUsage = ERROR +# +# BASE/d1/d11/d111/lombok.config: +# <'lombok.Accessors.flagUsage' not mentioned> + +# Generate setters that return 'this' instead of 'void'. +clear lombok.Accessors.chain +# BASE/d1/lombok.config: +# <'lombok.Accessors.chain' not mentioned> +# +# BASE/d1/d11/lombok.config: +# 2: lombok.Accessors.chain = false +# +# BASE/d1/d11/d111/lombok.config: +# 1: clear lombok.Accessors.chain + +# Strip this field prefix, like 'f' or 'm_', from the names of generated getters and setters. +lombok.Accessors.prefix += f +lombok.Accessors.prefix += m_ +# BASE/d1/lombok.config: +# <'lombok.Accessors.prefix' not mentioned> +# +# BASE/d1/d11/lombok.config: +# 4: lombok.Accessors.prefix += f +# +# BASE/d1/d11/d111/lombok.config: +# 2: lombok.Accessors.prefix += m_ + +# Use this name for the generated logger fields (default: 'log') +clear lombok.log.fieldName + + +Configuration for 'BASE/d1/d12'. + +# Emit a warning or error if @Accessors is used. +clear lombok.Accessors.flagUsage + +# Generate setters that return 'this' instead of 'void'. +lombok.Accessors.chain = true +# BASE/d1/lombok.config: +# <'lombok.Accessors.chain' not mentioned> +# +# BASE/d1/d12/lombok.config: +# 2: lombok.Accessors.chain = true + +# Strip this field prefix, like 'f' or 'm_', from the names of generated getters and setters. +clear lombok.Accessors.prefix + +# Use this name for the generated logger fields (default: 'log') +clear lombok.log.fieldName
\ No newline at end of file diff --git a/test/configuration/src/lombok/core/configuration/RunConfigurationTests.java b/test/configuration/src/lombok/core/configuration/RunConfigurationTests.java new file mode 100644 index 00000000..40e0a7f4 --- /dev/null +++ b/test/configuration/src/lombok/core/configuration/RunConfigurationTests.java @@ -0,0 +1,31 @@ +/* + * 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 org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +@RunWith(Suite.class) +@SuiteClasses({TestConfiguration.class}) +public class RunConfigurationTests { +} diff --git a/test/configuration/src/lombok/core/configuration/TestConfiguration.java b/test/configuration/src/lombok/core/configuration/TestConfiguration.java new file mode 100644 index 00000000..d2599334 --- /dev/null +++ b/test/configuration/src/lombok/core/configuration/TestConfiguration.java @@ -0,0 +1,95 @@ +/* + * 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 static lombok.ConfigurationKeys.*; +import static org.junit.Assert.assertEquals; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.PrintStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.regex.Pattern; + +import org.junit.Test; + +public class TestConfiguration { + + @Test + public void testDisplayVerbose() throws Exception { + + @SuppressWarnings("unchecked") + Collection<ConfigurationKey<?>> keys = Arrays.asList(ACCESSORS_FLAG_USAGE, ACCESSORS_CHAIN, ACCESSORS_PREFIX, LOG_ANY_FIELD_NAME); + + String baseName = "test/configuration/resource/configurationRoot/"; + File directory = new File(baseName); + String normalizedName = new File(directory.getAbsoluteFile().toURI().normalize()).toString().replace('\\', '/') + "/"; + Collection<String> paths = Arrays.asList(normalizedName + "d1/d11", normalizedName + "d1/d12", normalizedName + "d1/d11/d111", normalizedName + "d1/d11/d111/f1.txt"); + + ByteArrayOutputStream rawOut = new ByteArrayOutputStream(); + ByteArrayOutputStream rawErr = new ByteArrayOutputStream(); + PrintStream outStream = new PrintStream(rawOut); + PrintStream errStream = new PrintStream(rawErr); + + int result = new ConfigurationApp().redirectOutput(outStream, errStream).display(keys, true, paths, true); + + outStream.flush(); + errStream.flush(); + + String out = new String(rawOut.toByteArray()).replace("\r\n", "\n").replace('\\', '/').replaceAll(Pattern.quote(normalizedName) + "|" + Pattern.quote(baseName), "BASE/").trim(); + String err = new String(rawErr.toByteArray()).replace("\r\n", "\n").replace('\\', '/').replaceAll(Pattern.quote(normalizedName) + "|" + Pattern.quote(baseName), "BASE/").trim(); + + checkContent(directory, out, "out"); + checkContent(directory, err, "err"); + assertEquals(0, result); + } + + private void checkContent(File dir, String actual, String type) throws Exception { + String expected = fileToString(new File(dir, type + ".txt")).trim(); + if (!expected.equals(actual)) { + System.out.printf("**** Expected %s:\n", type); + System.out.println(expected); + System.out.printf("**** Actual %s:\n", type); + System.out.println(actual); + System.out.println("****"); + } + assertEquals(expected, actual); + } + + static String fileToString(File configFile) throws Exception { + byte[] b = new byte[65536]; + 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(); + } + } +} diff --git a/test/core/src/lombok/AbstractRunTests.java b/test/core/src/lombok/AbstractRunTests.java index e84aec0d..34f6cbf4 100644 --- a/test/core/src/lombok/AbstractRunTests.java +++ b/test/core/src/lombok/AbstractRunTests.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2009-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 @@ -25,21 +25,25 @@ import static org.junit.Assert.*; import java.io.BufferedReader; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; -import java.io.InputStream; -import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import org.junit.Assert; + +import lombok.core.AST; +import lombok.core.LombokConfiguration; +import lombok.core.LombokImmutableList; +import lombok.core.configuration.ConfigurationKeysLoader; +import lombok.core.configuration.ConfigurationResolver; +import lombok.core.configuration.ConfigurationResolverFactory; import lombok.javac.CapturingDiagnosticListener.CompilerMessage; public abstract class AbstractRunTests { @@ -50,39 +54,27 @@ public abstract class AbstractRunTests { } public boolean compareFile(DirectoryRunner.TestParams params, File file) throws Throwable { + ConfigurationKeysLoader.LoaderLoader.loadAllConfigurationKeys(); + final LombokTestSource sourceDirectives = LombokTestSource.readDirectives(file); + if (sourceDirectives.isIgnore() || !sourceDirectives.versionWithinLimit(params.getVersion())) return false; + + String fileName = file.getName(); + LombokTestSource expected = LombokTestSource.read(params.getAfterDirectory(), params.getMessagesDirectory(), fileName); + + if (expected.isIgnore() || !expected.versionWithinLimit(params.getVersion())) return false; + LinkedHashSet<CompilerMessage> messages = new LinkedHashSet<CompilerMessage>(); StringWriter writer = new StringWriter(); - transformCode(messages, writer, file); - String expectedFile = readFile(params.getAfterDirectory(), file, false); - List<CompilerMessageMatcher> expectedMessages = Collections.emptyList(); - if (params.getMessagesDirectory() != null) { - try { - InputStream in = new FileInputStream(new File(params.getMessagesDirectory(), file.getName() + ".messages")); - try { - expectedMessages = CompilerMessageMatcher.readAll(in); - } finally { - in.close(); - } - } catch (FileNotFoundException ex) { - // That's okay - then we expect no messages, and expectedMessages already gets initialized to the empty list. - } - } - if (expectedFile != null) { - StringReader r = new StringReader(expectedFile); - BufferedReader br = new BufferedReader(r); - String firstLine = br.readLine(); - if (firstLine != null && (firstLine.startsWith("//ignore") || params.shouldIgnoreBasedOnVersion(firstLine))) return false; - } + LombokConfiguration.overrideConfigurationResolverFactory(new ConfigurationResolverFactory() { + @Override public ConfigurationResolver createResolver(AST<?, ?, ?> ast) { + return sourceDirectives.getConfiguration(); + } + }); - compare( - file.getName(), - expectedFile, - writer.toString(), - expectedMessages, - messages, - params.printErrors()); + transformCode(messages, writer, file); + compare(file.getName(), expected, writer.toString(), messages, params.printErrors(), sourceDirectives.isSkipCompareContent() || expected.isSkipCompareContent()); return true; } @@ -105,11 +97,6 @@ public abstract class AbstractRunTests { return result.toString(); } - private String readFile(File dir, File file, boolean messages) throws IOException { - if (dir == null) return null; - return readFile(new File(dir, file.getName() + (messages ? ".messages" : ""))); - } - private static File findPlaceToDumpActualFiles() { String location = System.getProperty("lombok.tests.dump_actual_files"); if (location != null) { @@ -141,17 +128,15 @@ public abstract class AbstractRunTests { } } - private void compare(String name, String expectedFile, String actualFile, List<CompilerMessageMatcher> expectedMessages, LinkedHashSet<CompilerMessage> actualMessages, boolean printErrors) throws Throwable { - if (expectedFile == null && expectedMessages.isEmpty()) expectedFile = ""; - - if (expectedFile != null) try { - compareContent(name, expectedFile, actualFile); + private void compare(String name, LombokTestSource expected, String actualFile, LinkedHashSet<CompilerMessage> actualMessages, boolean printErrors, boolean skipCompareContent) throws Throwable { + if (!skipCompareContent) try { + compareContent(name, expected.getContent(), actualFile); } catch (Throwable e) { if (printErrors) { System.out.println("***** " + name + " *****"); System.out.println(e.getMessage()); System.out.println("**** Expected ******"); - System.out.println(expectedFile); + System.out.println(expected.getContent()); System.out.println("**** Actual ******"); System.out.println(actualFile); if (actualMessages != null && !actualMessages.isEmpty()) { @@ -169,13 +154,13 @@ public abstract class AbstractRunTests { } try { - compareMessages(name, expectedMessages, actualMessages); + compareMessages(name, expected.getMessages(), actualMessages); } catch (Throwable e) { if (printErrors) { System.out.println("***** " + name + " *****"); System.out.println(e.getMessage()); System.out.println("**** Expected ******"); - for (CompilerMessageMatcher expectedMessage : expectedMessages) { + for (CompilerMessageMatcher expectedMessage : expected.getMessages()) { System.out.println(expectedMessage); } System.out.println("**** Actual ******"); @@ -191,7 +176,7 @@ public abstract class AbstractRunTests { } } - private static void compareMessages(String name, List<CompilerMessageMatcher> expected, LinkedHashSet<CompilerMessage> actual) { + private static void compareMessages(String name, LombokImmutableList<CompilerMessageMatcher> expected, LinkedHashSet<CompilerMessage> actual) { Iterator<CompilerMessageMatcher> expectedIterator = expected.iterator(); Iterator<CompilerMessage> actualIterator = actual.iterator(); @@ -215,15 +200,23 @@ public abstract class AbstractRunTests { private static void compareContent(String name, String expectedFile, String actualFile) { String[] expectedLines = expectedFile.split("(\\r?\\n)"); String[] actualLines = actualFile.split("(\\r?\\n)"); - if (expectedLines[0].startsWith("// Generated by delombok at ")) { - expectedLines[0] = ""; + + for (int i = 0; i < expectedLines.length; i++) { + if (expectedLines[i].isEmpty() || expectedLines[i].startsWith("//")) expectedLines[i] = ""; + else break; } - if (actualLines[0].startsWith("// Generated by delombok at ")) { - actualLines[0] = ""; + for (int i = 0; i < actualLines.length; i++) { + if (actualLines[i].isEmpty() || actualLines[i].startsWith("//")) actualLines[i] = ""; + else break; } expectedLines = removeBlanks(expectedLines); actualLines = removeBlanks(actualLines); + int size = Math.min(expectedLines.length, actualLines.length); + if (size == 0 && expectedLines.length + actualLines.length > 0) { + Assert.fail("Missing / empty expected file."); + } + for (int i = 0; i < size; i++) { String expected = trimRight(expectedLines[i]); String actual = trimRight(actualLines[i]); diff --git a/test/core/src/lombok/DirectoryRunner.java b/test/core/src/lombok/DirectoryRunner.java index 5325c1e3..9062f523 100644 --- a/test/core/src/lombok/DirectoryRunner.java +++ b/test/core/src/lombok/DirectoryRunner.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2009-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 @@ -21,16 +21,10 @@ */ package lombok; -import java.io.BufferedReader; import java.io.File; import java.io.FileFilter; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; import java.util.Map; import java.util.TreeMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import lombok.eclipse.Eclipse; import lombok.javac.Javac; @@ -75,46 +69,8 @@ public class DirectoryRunner extends Runner { public boolean accept(File file) { return true; } - - private static final Pattern P1 = Pattern.compile("^(\\d+)$"); - private static final Pattern P2 = Pattern.compile("^\\:(\\d+)$"); - private static final Pattern P3 = Pattern.compile("^(\\d+):$"); - private static final Pattern P4 = Pattern.compile("^(\\d+):(\\d+)$"); - - public boolean shouldIgnoreBasedOnVersion(String firstLine) { - int thisVersion = getVersion(); - if (!firstLine.startsWith("//version ")) return false; - - String spec = firstLine.substring("//version ".length()); - - /* Single version: '5' */ { - Matcher m = P1.matcher(spec); - if (m.matches()) return Integer.parseInt(m.group(1)) != thisVersion; - } - - /* Upper bound: ':5' (inclusive) */ { - Matcher m = P2.matcher(spec); - if (m.matches()) return Integer.parseInt(m.group(1)) < thisVersion; - } - - /* Lower bound '5:' (inclusive) */ { - Matcher m = P3.matcher(spec); - if (m.matches()) return Integer.parseInt(m.group(1)) > thisVersion; - } - - /* Range '7:8' (inclusive) */ { - Matcher m = P4.matcher(spec); - if (m.matches()) { - if (Integer.parseInt(m.group(1)) < thisVersion) return true; - if (Integer.parseInt(m.group(2)) > thisVersion) return true; - return false; - } - } - - throw new IllegalArgumentException("Version validity spec not valid: " + spec); - } } - + private static final FileFilter JAVA_FILE_FILTER = new FileFilter() { @Override public boolean accept(File file) { return file.isFile() && file.getName().endsWith(".java"); @@ -180,9 +136,7 @@ public class DirectoryRunner extends Runner { private boolean runTest(String fileName) throws Throwable { File file = new File(params.getBeforeDirectory(), fileName); - if (mustIgnore(file)) { - return false; - } + switch (params.getCompiler()) { case DELOMBOK: return new RunTestsViaDelombok().compareFile(params, file); @@ -193,11 +147,4 @@ public class DirectoryRunner extends Runner { throw new UnsupportedOperationException(); } } - - private boolean mustIgnore(File file) throws FileNotFoundException, IOException { - BufferedReader reader = new BufferedReader(new FileReader(file)); - String line = reader.readLine(); - reader.close(); - return line != null && (line.startsWith("//ignore") || params.shouldIgnoreBasedOnVersion(line)); - } } diff --git a/test/core/src/lombok/LombokTestSource.java b/test/core/src/lombok/LombokTestSource.java new file mode 100644 index 00000000..318c5885 --- /dev/null +++ b/test/core/src/lombok/LombokTestSource.java @@ -0,0 +1,239 @@ +/* + * 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; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.junit.Assert; + +import lombok.core.LombokImmutableList; +import lombok.core.configuration.BubblingConfigurationResolver; +import lombok.core.configuration.ConfigurationProblemReporter; +import lombok.core.configuration.ConfigurationResolver; +import lombok.core.configuration.StringConfigurationSource; + +public class LombokTestSource { + private final File file; + private final String content; + private final LombokImmutableList<CompilerMessageMatcher> messages; + private final boolean ignore; + private final boolean skipCompareContent; + private final int versionLowerLimit, versionUpperLimit; + private final ConfigurationResolver configuration; + + public boolean versionWithinLimit(int version) { + return version >= versionLowerLimit && version <= versionUpperLimit; + } + + public File getFile() { + return file; + } + + public String getContent() { + return content; + } + + public LombokImmutableList<CompilerMessageMatcher> getMessages() { + return messages; + } + + public boolean isIgnore() { + return ignore; + } + + public boolean isSkipCompareContent() { + return skipCompareContent; + } + + public ConfigurationResolver getConfiguration() { + return configuration; + } + + private static final Pattern VERSION_STYLE_1 = Pattern.compile("^(\\d+)$"); + private static final Pattern VERSION_STYLE_2 = Pattern.compile("^\\:(\\d+)$"); + private static final Pattern VERSION_STYLE_3 = Pattern.compile("^(\\d+):$"); + private static final Pattern VERSION_STYLE_4 = Pattern.compile("^(\\d+):(\\d+)$"); + + private int[] parseVersionLimit(String spec) { + /* Single version: '5' */ { + Matcher m = VERSION_STYLE_1.matcher(spec); + if (m.matches()) { + int v = Integer.parseInt(m.group(1)); + return new int[] {v, v}; + } + } + + /* Upper bound: ':5' (inclusive) */ { + Matcher m = VERSION_STYLE_2.matcher(spec); + if (m.matches()) return new int[] {0, Integer.parseInt(m.group(1))}; + } + + /* Lower bound '5:' (inclusive) */ { + Matcher m = VERSION_STYLE_3.matcher(spec); + if (m.matches()) return new int[] {Integer.parseInt(m.group(1)), 0}; + } + + /* Range '7:8' (inclusive) */ { + Matcher m = VERSION_STYLE_4.matcher(spec); + if (m.matches()) return new int[] {Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2))}; + } + + return null; + } + + private static final Pattern IGNORE_PATTERN = Pattern.compile("^\\s*ignore\\s*(?:[-:].*)?$", Pattern.CASE_INSENSITIVE); + private static final Pattern SKIP_COMPARE_CONTENT_PATTERN = Pattern.compile("^\\s*skip[- ]?compare[- ]?content\\s*(?:[-:].*)?$", Pattern.CASE_INSENSITIVE); + + private LombokTestSource(File file, String content, List<CompilerMessageMatcher> messages, List<String> directives) { + this.file = file; + this.content = content; + this.messages = messages == null ? LombokImmutableList.<CompilerMessageMatcher>of() : LombokImmutableList.copyOf(messages); + + StringBuilder conf = new StringBuilder(); + int versionLower = 0; + int versionUpper = Integer.MAX_VALUE; + boolean ignore = false; + boolean skipCompareContent = false; + + for (String directive : directives) { + directive = directive.trim(); + String lc = directive.toLowerCase(); + if (IGNORE_PATTERN.matcher(directive).matches()) { + ignore = true; + continue; + } + + if (SKIP_COMPARE_CONTENT_PATTERN.matcher(directive).matches()) { + skipCompareContent = true; + continue; + } + + if (lc.startsWith("version ")) { + int[] limits = parseVersionLimit(lc.substring(7).trim()); + if (limits == null) { + Assert.fail("Directive line \"" + directive + "\" in '" + file.getAbsolutePath() + "' invalid: version must be followed by a single integer."); + throw new RuntimeException(); + } + versionLower = limits[0]; + versionUpper = limits[1]; + continue; + } + + if (lc.startsWith("conf:")) { + String confLine = directive.substring(5).trim(); + conf.append(confLine).append("\n"); + continue; + } + + Assert.fail("Directive line \"" + directive + "\" in '" + file.getAbsolutePath() + "' invalid: unrecognized directive."); + throw new RuntimeException(); + } + + this.versionLowerLimit = versionLower; + this.versionUpperLimit = versionUpper; + this.ignore = ignore; + this.skipCompareContent = skipCompareContent; + ConfigurationProblemReporter reporter = new ConfigurationProblemReporter() { + @Override public void report(String sourceDescription, String problem, int lineNumber, CharSequence line) { + Assert.fail("Problem on directive line: " + problem + " at conf line #" + lineNumber + " (" + line + ")"); + } + }; + + this.configuration = new BubblingConfigurationResolver(Collections.singleton(StringConfigurationSource.forString(conf, reporter, file.getAbsolutePath()))); + } + + public static LombokTestSource readDirectives(File file) throws IOException { + List<String> directives = new ArrayList<String>(); + + { + @Cleanup val rawIn = new FileInputStream(file); + BufferedReader in = new BufferedReader(new InputStreamReader(rawIn, "UTF-8")); + for (String i = in.readLine(); i != null; i = in.readLine()) { + if (i.isEmpty()) continue; + + if (i.startsWith("//")) { + directives.add(i.substring(2)); + } else { + break; + } + } + in.close(); + rawIn.close(); + } + + return new LombokTestSource(file, "", null, directives); + } + + public static LombokTestSource read(File sourceFolder, File messagesFolder, String fileName) throws IOException { + StringBuilder content = null; + List<String> directives = new ArrayList<String>(); + + File sourceFile = new File(sourceFolder, fileName); + if (sourceFile.exists()) { + @Cleanup val rawIn = new FileInputStream(sourceFile); + BufferedReader in = new BufferedReader(new InputStreamReader(rawIn, "UTF-8")); + for (String i = in.readLine(); i != null; i = in.readLine()) { + if (content != null) { + content.append(i).append("\n"); + continue; + } + + if (i.isEmpty()) continue; + + if (i.startsWith("//")) { + directives.add(i.substring(2)); + } else { + content = new StringBuilder(); + content.append(i).append("\n"); + } + } + in.close(); + rawIn.close(); + } + + if (content == null) content = new StringBuilder(); + + List<CompilerMessageMatcher> messages = null; + if (messagesFolder != null) { + File messagesFile = new File(messagesFolder, fileName + ".messages"); + try { + @Cleanup val rawIn = new FileInputStream(messagesFile); + messages = CompilerMessageMatcher.readAll(rawIn); + rawIn.close(); + } catch (FileNotFoundException e) { + messages = null; + } + } + + return new LombokTestSource(sourceFile, content.toString(), messages, directives); + } +} diff --git a/test/core/src/lombok/RunAllTests.java b/test/core/src/lombok/RunAllTests.java index 0076e662..9f56b45b 100644 --- a/test/core/src/lombok/RunAllTests.java +++ b/test/core/src/lombok/RunAllTests.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Project Lombok Authors. + * Copyright (C) 2011-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 @@ -26,6 +26,6 @@ import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) -@SuiteClasses({lombok.transform.RunTransformTests.class, lombok.bytecode.RunBytecodeTests.class}) +@SuiteClasses({lombok.transform.RunTransformTests.class, lombok.bytecode.RunBytecodeTests.class, lombok.core.configuration.RunConfigurationTests.class}) public class RunAllTests { } diff --git a/test/core/src/lombok/RunTestsViaDelombok.java b/test/core/src/lombok/RunTestsViaDelombok.java index 17665173..6a08642b 100644 --- a/test/core/src/lombok/RunTestsViaDelombok.java +++ b/test/core/src/lombok/RunTestsViaDelombok.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 The Project Lombok Authors. + * Copyright (C) 2009-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 diff --git a/test/core/src/lombok/RunTestsViaEcj.java b/test/core/src/lombok/RunTestsViaEcj.java index 4f3e2794..2734eb43 100644 --- a/test/core/src/lombok/RunTestsViaEcj.java +++ b/test/core/src/lombok/RunTestsViaEcj.java @@ -107,6 +107,8 @@ public class RunTestsViaEcj extends AbstractRunTests { } }; + // TODO: Create a configuration based on confLines and set this up so that this compile run will use them. + ecjCompiler.compile(new ICompilationUnit[] {sourceUnit}); CompilationResult compilationResult = compilationResult_.get(); diff --git a/test/transform/resource/after-delombok/AccessorsConfiguration.java b/test/transform/resource/after-delombok/AccessorsConfiguration.java new file mode 100644 index 00000000..fd60b152 --- /dev/null +++ b/test/transform/resource/after-delombok/AccessorsConfiguration.java @@ -0,0 +1,26 @@ +class AccessorsConfiguration { + private String m_FieldName = ""; + @java.lang.SuppressWarnings("all") + public String fieldName() { + return this.m_FieldName; + } + @java.lang.SuppressWarnings("all") + public void fieldName(final String m_FieldName) { + this.m_FieldName = m_FieldName; + } +} +class AccessorsConfiguration2 { + private String m_FieldName = ""; + @java.lang.SuppressWarnings("all") + public void setM_FieldName(final String m_FieldName) { + this.m_FieldName = m_FieldName; + } +} +class AccessorsConfiguration3 { + private String fFieldName = ""; + @java.lang.SuppressWarnings("all") + public AccessorsConfiguration3 setFieldName(final String fFieldName) { + this.fFieldName = fFieldName; + return this; + } +}
\ No newline at end of file diff --git a/test/transform/resource/after-delombok/LoggerConfig.java b/test/transform/resource/after-delombok/LoggerConfig.java new file mode 100644 index 00000000..ba955638 --- /dev/null +++ b/test/transform/resource/after-delombok/LoggerConfig.java @@ -0,0 +1,4 @@ +class LoggerWithConfig { + @java.lang.SuppressWarnings("all") + private final org.slf4j.Logger myLogger = org.slf4j.LoggerFactory.getLogger(LoggerWithConfig.class); +}
\ No newline at end of file diff --git a/test/transform/resource/after-delombok/ToStringConfiguration.java b/test/transform/resource/after-delombok/ToStringConfiguration.java new file mode 100644 index 00000000..035ecdb2 --- /dev/null +++ b/test/transform/resource/after-delombok/ToStringConfiguration.java @@ -0,0 +1,32 @@ +class ToStringConfiguration { + int x; + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "ToStringConfiguration(" + this.x + ")"; + } + @java.lang.SuppressWarnings("all") + public int getX() { + return this.x; + } +} +class ToStringConfiguration2 { + int x; + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "ToStringConfiguration2(x=" + this.x + ")"; + } +} +class ToStringConfiguration3 { + int x; + @java.lang.Override + @java.lang.SuppressWarnings("all") + public java.lang.String toString() { + return "ToStringConfiguration3(" + this.getX() + ")"; + } + @java.lang.SuppressWarnings("all") + public int getX() { + return this.x; + } +}
\ No newline at end of file diff --git a/test/transform/resource/after-ecj/AccessorsConfiguration.java b/test/transform/resource/after-ecj/AccessorsConfiguration.java new file mode 100644 index 00000000..6678e020 --- /dev/null +++ b/test/transform/resource/after-ecj/AccessorsConfiguration.java @@ -0,0 +1,31 @@ +class AccessorsConfiguration { + private @lombok.Getter @lombok.Setter @lombok.experimental.Accessors(fluent = true) String m_FieldName = ""; + AccessorsConfiguration() { + super(); + } + public @java.lang.SuppressWarnings("all") String fieldName() { + return this.m_FieldName; + } + public @java.lang.SuppressWarnings("all") void fieldName(final String m_FieldName) { + this.m_FieldName = m_FieldName; + } +} +@lombok.experimental.Accessors(prefix = {}) class AccessorsConfiguration2 { + private @lombok.Setter String m_FieldName = ""; + AccessorsConfiguration2() { + super(); + } + public @java.lang.SuppressWarnings("all") void setM_FieldName(final String m_FieldName) { + this.m_FieldName = m_FieldName; + } +} +@lombok.experimental.Accessors(chain = true) class AccessorsConfiguration3 { + private @lombok.Setter String fFieldName = ""; + AccessorsConfiguration3() { + super(); + } + public @java.lang.SuppressWarnings("all") AccessorsConfiguration3 setFieldName(final String fFieldName) { + this.fFieldName = fFieldName; + return this; + } +} diff --git a/test/transform/resource/after-ecj/LoggerConfig.java b/test/transform/resource/after-ecj/LoggerConfig.java new file mode 100644 index 00000000..907a7167 --- /dev/null +++ b/test/transform/resource/after-ecj/LoggerConfig.java @@ -0,0 +1,6 @@ +@lombok.extern.slf4j.Slf4j class LoggerWithConfig { + private final org.slf4j.Logger myLogger = org.slf4j.LoggerFactory.getLogger(LoggerWithConfig.class); + LoggerWithConfig() { + super(); + } +}
\ No newline at end of file diff --git a/test/transform/resource/after-ecj/ToStringConfiguration.java b/test/transform/resource/after-ecj/ToStringConfiguration.java new file mode 100644 index 00000000..8bb99146 --- /dev/null +++ b/test/transform/resource/after-ecj/ToStringConfiguration.java @@ -0,0 +1,35 @@ +import lombok.ToString; +import lombok.Getter; +@ToString @Getter class ToStringConfiguration { + int x; + ToStringConfiguration() { + super(); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (("ToStringConfiguration(" + this.x) + ")"); + } + public @java.lang.SuppressWarnings("all") int getX() { + return this.x; + } +} +@ToString(includeFieldNames = true) class ToStringConfiguration2 { + int x; + ToStringConfiguration2() { + super(); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (("ToStringConfiguration2(x=" + this.x) + ")"); + } +} +@ToString(doNotUseGetters = false) @Getter class ToStringConfiguration3 { + int x; + ToStringConfiguration3() { + super(); + } + public @java.lang.Override @java.lang.SuppressWarnings("all") java.lang.String toString() { + return (("ToStringConfiguration3(" + this.getX()) + ")"); + } + public @java.lang.SuppressWarnings("all") int getX() { + return this.x; + } +}
\ No newline at end of file diff --git a/test/transform/resource/before/AccessorsConfiguration.java b/test/transform/resource/before/AccessorsConfiguration.java new file mode 100644 index 00000000..187fd393 --- /dev/null +++ b/test/transform/resource/before/AccessorsConfiguration.java @@ -0,0 +1,20 @@ +//CONF: lombok.Accessors.prefix += m_ +//CONF: lombok.Accessors.prefix += f +//CONF: lombok.Accessors.chain = false + +class AccessorsConfiguration { + @lombok.Getter @lombok.Setter @lombok.experimental.Accessors(fluent=true) + private String m_FieldName = ""; +} + +@lombok.experimental.Accessors(prefix = {}) +class AccessorsConfiguration2 { + @lombok.Setter + private String m_FieldName = ""; +} + +@lombok.experimental.Accessors(chain = true) +class AccessorsConfiguration3 { + @lombok.Setter + private String fFieldName = ""; +} diff --git a/test/transform/resource/before/BuilderInvalidUse.java b/test/transform/resource/before/BuilderInvalidUse.java index 07f37d3d..d7052e1e 100644 --- a/test/transform/resource/before/BuilderInvalidUse.java +++ b/test/transform/resource/before/BuilderInvalidUse.java @@ -1,3 +1,4 @@ +//skip compare content @lombok.experimental.Builder class BuilderInvalidUse { private int something; diff --git a/test/transform/resource/before/DelegateOnStatic.java b/test/transform/resource/before/DelegateOnStatic.java index ef56ef54..84d99636 100644 --- a/test/transform/resource/before/DelegateOnStatic.java +++ b/test/transform/resource/before/DelegateOnStatic.java @@ -1,3 +1,4 @@ +//skip compare content import lombok.Delegate; import lombok.Getter; diff --git a/test/transform/resource/before/FlagUsages.java b/test/transform/resource/before/FlagUsages.java new file mode 100644 index 00000000..6631224f --- /dev/null +++ b/test/transform/resource/before/FlagUsages.java @@ -0,0 +1,11 @@ +//skip compare content +//CONF: lombok.Getter.flagUsage = WARNING +//CONF: lombok.experimental.flagUsage = ERROR +public class FlagUsages { + @lombok.Getter String x; + + @lombok.experimental.Wither String z; + + public FlagUsages(String x, String y) { + } +} diff --git a/test/transform/resource/before/LoggerConfig.java b/test/transform/resource/before/LoggerConfig.java new file mode 100644 index 00000000..14195e03 --- /dev/null +++ b/test/transform/resource/before/LoggerConfig.java @@ -0,0 +1,5 @@ +//CONF: lombok.log.fieldName = myLogger +//CONF: lombok.log.fieldIsStatic = false +@lombok.extern.slf4j.Slf4j +class LoggerWithConfig { +} diff --git a/test/transform/resource/before/ToStringConfiguration.java b/test/transform/resource/before/ToStringConfiguration.java new file mode 100644 index 00000000..7630cda5 --- /dev/null +++ b/test/transform/resource/before/ToStringConfiguration.java @@ -0,0 +1,13 @@ +//CONF: lombok.ToString.includeFieldNames = false +//CONF: lombok.ToString.doNotUseGetters = true +import lombok.ToString; +import lombok.Getter; +@ToString @Getter class ToStringConfiguration { + int x; +} +@ToString(includeFieldNames=true) class ToStringConfiguration2 { + int x; +} +@ToString(doNotUseGetters=false) @Getter class ToStringConfiguration3 { + int x; +} diff --git a/test/transform/resource/messages-delombok/BuilderInvalidUse.java.messages b/test/transform/resource/messages-delombok/BuilderInvalidUse.java.messages index a04b4f9b..506a3426 100644 --- a/test/transform/resource/messages-delombok/BuilderInvalidUse.java.messages +++ b/test/transform/resource/messages-delombok/BuilderInvalidUse.java.messages @@ -1,2 +1,2 @@ -1 @Getter, @Setter, @Wither, @Data, @ToString, @EqualsAndHashCode, @AllArgsConstructor are not allowed on builder classes. -12 @Value is not allowed on builder classes.
\ No newline at end of file +2 @Getter, @Setter, @Wither, @Data, @ToString, @EqualsAndHashCode, @AllArgsConstructor are not allowed on builder classes. +13 @Value is not allowed on builder classes.
\ No newline at end of file diff --git a/test/transform/resource/messages-delombok/DelegateOnStatic.java.messages b/test/transform/resource/messages-delombok/DelegateOnStatic.java.messages index b807b155..51dcf501 100644 --- a/test/transform/resource/messages-delombok/DelegateOnStatic.java.messages +++ b/test/transform/resource/messages-delombok/DelegateOnStatic.java.messages @@ -1,2 +1,2 @@ -5 @Delegate is legal only on instance fields or no-argument instance methods. -9 @Delegate is legal only on instance fields or no-argument instance methods.
\ No newline at end of file +6 @Delegate is legal only on instance fields or no-argument instance methods. +10 @Delegate is legal only on instance fields or no-argument instance methods.
\ No newline at end of file diff --git a/test/transform/resource/messages-delombok/FlagUsages.java.messages b/test/transform/resource/messages-delombok/FlagUsages.java.messages new file mode 100644 index 00000000..13a148b1 --- /dev/null +++ b/test/transform/resource/messages-delombok/FlagUsages.java.messages @@ -0,0 +1,2 @@ +5 Use of @Getter is flagged according to lombok configuration. +7 Use of any lombok.experimental feature is flagged according to lombok configuration. diff --git a/test/transform/resource/messages-ecj/BuilderInvalidUse.java.messages b/test/transform/resource/messages-ecj/BuilderInvalidUse.java.messages index 84942101..c5571b92 100644 --- a/test/transform/resource/messages-ecj/BuilderInvalidUse.java.messages +++ b/test/transform/resource/messages-ecj/BuilderInvalidUse.java.messages @@ -1,2 +1,2 @@ -1 @Getter, @Setter, @FieldDefaults, @Wither, @Data, @ToString, @EqualsAndHashCode, @AllArgsConstructor are not allowed on builder classes. -12 @Value is not allowed on builder classes.
\ No newline at end of file +2 @Getter, @Setter, @FieldDefaults, @Wither, @Data, @ToString, @EqualsAndHashCode, @AllArgsConstructor are not allowed on builder classes. +13 @Value is not allowed on builder classes.
\ No newline at end of file diff --git a/test/transform/resource/messages-ecj/DelegateOnStatic.java.messages b/test/transform/resource/messages-ecj/DelegateOnStatic.java.messages index b807b155..51dcf501 100644 --- a/test/transform/resource/messages-ecj/DelegateOnStatic.java.messages +++ b/test/transform/resource/messages-ecj/DelegateOnStatic.java.messages @@ -1,2 +1,2 @@ -5 @Delegate is legal only on instance fields or no-argument instance methods. -9 @Delegate is legal only on instance fields or no-argument instance methods.
\ No newline at end of file +6 @Delegate is legal only on instance fields or no-argument instance methods. +10 @Delegate is legal only on instance fields or no-argument instance methods.
\ No newline at end of file diff --git a/test/transform/resource/messages-ecj/FlagUsages.java.messages b/test/transform/resource/messages-ecj/FlagUsages.java.messages new file mode 100644 index 00000000..13a148b1 --- /dev/null +++ b/test/transform/resource/messages-ecj/FlagUsages.java.messages @@ -0,0 +1,2 @@ +5 Use of @Getter is flagged according to lombok configuration. +7 Use of any lombok.experimental feature is flagged according to lombok configuration. |