aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReinier Zwitserloot <r.zwitserloot@projectlombok.org>2019-07-09 00:46:03 +0200
committerReinier Zwitserloot <r.zwitserloot@projectlombok.org>2019-07-09 00:46:03 +0200
commitb7e67345b998e2a40ff63fabc893393cc9327596 (patch)
tree3e468ae52735d856b44821602001aeefd0c640b2
parent0984d14826c62c5b99a2887aa198766ef08fea16 (diff)
parent5ad6796b182c8071fe747493ed2f9ad2443dc212 (diff)
downloadlombok-b7e67345b998e2a40ff63fabc893393cc9327596.tar.gz
lombok-b7e67345b998e2a40ff63fabc893393cc9327596.tar.bz2
lombok-b7e67345b998e2a40ff63fabc893393cc9327596.zip
Merge branch 'customlog'
-rwxr-xr-xAUTHORS1
-rw-r--r--doc/changelog.markdown3
-rw-r--r--src/core/lombok/ConfigurationKeys.java47
-rw-r--r--src/core/lombok/CustomLog.java75
-rw-r--r--src/core/lombok/core/configuration/ConfigurationDataType.java71
-rw-r--r--src/core/lombok/core/configuration/ConfigurationValueType.java37
-rw-r--r--src/core/lombok/core/configuration/IdentifierName.java69
-rw-r--r--src/core/lombok/core/configuration/LogDeclaration.java171
-rw-r--r--src/core/lombok/core/configuration/TypeName.java30
-rw-r--r--src/core/lombok/core/handlers/LoggingFramework.java98
-rw-r--r--src/core/lombok/eclipse/handlers/EclipseHandlerUtil.java2
-rw-r--r--src/core/lombok/eclipse/handlers/HandleFieldNameConstants.java23
-rw-r--r--src/core/lombok/eclipse/handlers/HandleLog.java237
-rw-r--r--src/core/lombok/eclipse/handlers/HandleSynchronized.java2
-rw-r--r--src/core/lombok/extern/apachecommons/CommonsLog.java1
-rw-r--r--src/core/lombok/extern/flogger/Flogger.java1
-rw-r--r--src/core/lombok/extern/java/Log.java1
-rw-r--r--src/core/lombok/extern/jbosslog/JBossLog.java3
-rw-r--r--src/core/lombok/extern/log4j/Log4j.java1
-rw-r--r--src/core/lombok/extern/log4j/Log4j2.java1
-rw-r--r--src/core/lombok/extern/slf4j/Slf4j.java1
-rw-r--r--src/core/lombok/extern/slf4j/XSlf4j.java1
-rw-r--r--src/core/lombok/javac/handlers/HandleFieldNameConstants.java23
-rw-r--r--src/core/lombok/javac/handlers/HandleLog.java167
-rw-r--r--src/core/lombok/javac/handlers/JavacHandlerUtil.java4
-rw-r--r--test/transform/resource/after-delombok/LoggerCustom.java13
-rw-r--r--test/transform/resource/after-delombok/LoggerCustomWithPackage.java14
-rw-r--r--test/transform/resource/after-delombok/LoggerCustomWithTopicAndName.java10
-rw-r--r--test/transform/resource/after-ecj/LoggerCustom.java21
-rw-r--r--test/transform/resource/after-ecj/LoggerCustomWithPackage.java22
-rw-r--r--test/transform/resource/after-ecj/LoggerCustomWithTopicAndName.java16
-rw-r--r--test/transform/resource/before/LoggerCustom.java13
-rw-r--r--test/transform/resource/before/LoggerCustomWithPackage.java14
-rw-r--r--test/transform/resource/before/LoggerCustomWithTopicAndName.java10
-rw-r--r--test/transform/resource/messages-delombok/LoggerSlf4jTypes.java.messages4
-rw-r--r--website/templates/features/log.html45
36 files changed, 974 insertions, 278 deletions
diff --git a/AUTHORS b/AUTHORS
index e77bc462..f5d4f434 100755
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,5 +1,6 @@
Lombok contributors in alphabetical order:
+Adam Juraszek <juriad@gmail.com>
Bulgakov Alexander <buls@yandex.ru>
Christian Nüssgens <christian@nuessgens.com>
Christian Sterzl <christian.sterzl@gmail.com>
diff --git a/doc/changelog.markdown b/doc/changelog.markdown
index afb07aac..e486a713 100644
--- a/doc/changelog.markdown
+++ b/doc/changelog.markdown
@@ -2,12 +2,15 @@ Lombok Changelog
----------------
### v1.18.9 "Edgy Guinea Pig"
+* FEATURE: You can now configure a custom logger framework using the new `@CustomLog` annotation in combination with the `lombok.log.custom.declaration` configuration key. See the [log documentation](https://projectlombok.org/features/Log) for more information. [Pullrequest #2086](https://github.com/rzwitserloot/lombok/pull/2086) with thanks to Adam Juraszek.
* ENHANCEMENT: Thanks to Mark Haynes, the `staticConstructor` will now also be generated if a (private) constructor already exists. [Issue #2100](https://github.com/rzwitserloot/lombok/issues/2100)
* ENHANCEMENT: `val` is now capable of decoding the type of convoluted expressions (particularly if the right hand side involves lambdas and conditional (ternary) expressions). [Pull Request #2109](https://github.com/rzwitserloot/lombok/pull/2109) and [Pull Request #2138](https://github.com/rzwitserloot/lombok/pull/2138) with thanks to Alexander Bulgakov.
* ENHANCEMENT: You can now configure the generated builder class name via the config system, using key `lombok.builder.className`. See the [Builder documentation](https://projectlombok.org/features/Builder) and [SuperBuilder documentation](https://projectlombok.org/features/experimental/SuperBuilder)
* ENHANCEMENT: If you mix up eclipse's non-null support, such as `@NonNullByDefault`, with lombok's `@NonNull`, you get a bunch of warnings about dead code that are inappropriate. These warnings are now suppressed, thanks to a contribution from Till Brychcy! [Pull Request #2155](https://github.com/rzwitserloot/lombok/pull/2155)
* BUGFIX: Delombok would turn something like `List<byte[]>...` in a method parameter to `List<byte...>...` [Issue #2140](https://github.com/rzwitserloot/lombok/issues/2140)
* BUGFIX: Javac would generate the wrong equals and hashCode if a type-use annotation was put on an array type field [Issue #2165](https://github.com/rzwitserloot/lombok/issues/2165)
+* IMPROBABLE BREAKING CHANGE: Stricter validation of configuration keys dealing with identifiers and types (`lombok.log.fieldName`, `lombok.fieldNameConstants.innerTypeName`, `lombok.copyableAnnotations`).
+>>>>>>> customlog
### v1.18.8 (May 7th, 2019)
* FEATURE: You can now configure `@FieldNameConstants` to `CONSTANT_CASE` the generated constants, using a `lombok.config` option. See the [FieldNameConstants documentation](https://projectlombok.org/features/experimental/FieldNameConstants). [Issue #2092](https://github.com/rzwitserloot/lombok/issues/2092).
diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java
index 975cb72e..dda0b54b 100644
--- a/src/core/lombok/ConfigurationKeys.java
+++ b/src/core/lombok/ConfigurationKeys.java
@@ -25,7 +25,9 @@ import java.util.List;
import lombok.core.configuration.CallSuperType;
import lombok.core.configuration.ConfigurationKey;
+import lombok.core.configuration.LogDeclaration;
import lombok.core.configuration.FlagUsageType;
+import lombok.core.configuration.IdentifierName;
import lombok.core.configuration.NullCheckExceptionType;
import lombok.core.configuration.TypeName;
@@ -309,17 +311,16 @@ public class ConfigurationKeys {
// ----- NonNull -----
/**
- * lombok configuration: {@code lombok.nonNull.exceptionType} = &lt;String: <em>a java exception type</em>; either [{@code IllegalArgumentException} or: {@code NullPointerException}].
+ * lombok configuration: {@code lombok.nonNull.exceptionType} = one of: [{@code IllegalArgumentException}, {@code NullPointerException}, or {@code Assertion}].
*
- * Sets the exception to throw if {@code @NonNull} is applied to a method parameter, and a caller passes in {@code null}.
+ * Sets the exception to throw if {@code @NonNull} is applied to a method parameter, and a caller passes in {@code null}. If the chosen configuration is {@code Assertion}, an assertion is generated instead,
+ * which would mean your code throws an {@code AssertionError} if assertions are enabled, and does nothing if assertions are not enabled.
*/
public static final ConfigurationKey<NullCheckExceptionType> NON_NULL_EXCEPTION_TYPE = new ConfigurationKey<NullCheckExceptionType>("lombok.nonNull.exceptionType", "The type of the exception to throw if a passed-in argument is null (Default: NullPointerException).") {};
/**
* 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.") {};
@@ -423,7 +424,7 @@ public class ConfigurationKeys {
*
* 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').") {};
+ public static final ConfigurationKey<IdentifierName> LOG_ANY_FIELD_NAME = new ConfigurationKey<IdentifierName>("lombok.log.fieldName", "Use this name for the generated logger fields (default: 'log').") {};
/**
* lombok configuration: {@code lombok.log.fieldIsStatic} = {@code true} | {@code false}.
@@ -434,6 +435,40 @@ public class ConfigurationKeys {
*/
public static final ConfigurationKey<Boolean> LOG_ANY_FIELD_IS_STATIC = new ConfigurationKey<Boolean>("lombok.log.fieldIsStatic", "Make the generated logger fields static (default: true).") {};
+ // ----- Custom Logging -----
+
+ /**
+ * lombok configuration: {@code lombok.log.custom.flagUsage} = {@code WARNING} | {@code ERROR}.
+ *
+ * If set, <em>any</em> usage of {@code @CustomLog} results in a warning / error.
+ */
+ public static final ConfigurationKey<FlagUsageType> LOG_CUSTOM_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.log.custom.flagUsage", "Emit a warning or error if @CustomLog is used.") {};
+
+ /**
+ * lombok configuration: {@code lombok.log.custom.declaration} = &lt;logDeclaration string&gt;.
+ *
+ * The log declaration must follow the pattern:
+ * <br>
+ * {@code [LoggerType ]LoggerFactoryType.loggerFactoryMethod(loggerFactoryMethodParams)[(loggerFactoryMethodParams)]}
+ * <br>
+ * It consists of:
+ * <ul>
+ * <li>Optional fully qualified logger type, e.g. {@code my.cool.Logger}, followed by space. If not specified, it defaults to the <em>LoggerFactoryType</em>.
+ * <li>Fully qualified logger factory type, e.g. {@code my.cool.LoggerFactory}, followed by dot.
+ * <li>Factory method, e.g. {@code createLogger}. This must be a {@code public static} method in the <em>LoggerFactoryType</em>.
+ * <li>At least one definition of factory method parameters, e.g. {@code ()} or {@code (TOPIC,TYPE)}. The format inside the parentheses is a comma-separated list of parameter kinds.<br>
+ * The allowed parameters are: {@code TYPE} | {@code NAME} | {@code TOPIC} | {@code NULL}.<br>
+ * There can be at most one parameter definition with {@code TOPIC} and at most one without {@code TOPIC}. You can specify both.
+ * </ul>
+ *
+ * An example: {@code my.cool.Logger my.cool.LoggerFactory.createLogger(TYPE)(TYPE,TOPIC)}<br>
+ * If no topic is provided in the usage of {@code @CustomLog}, the above will invoke {@code LoggerFactory}'s {@code createLogger} method, passing in the type as a {@code java.lang.Class} variable.<br>
+ * If a topic is provided, the overload of that method is invoked with 2 parameters: First the type (as {@code Class}), then the topic (as {@code String}).
+ * <p>
+ * If this configuration key is not set, any usage of {@code @CustomLog} will result in an error.
+ */
+ public static final ConfigurationKey<LogDeclaration> LOG_CUSTOM_DECLARATION = new ConfigurationKey<LogDeclaration>("lombok.log.custom.declaration", "Define the generated custom logger field.") {};
+
// ##### Experimental #####
/**
@@ -548,7 +583,7 @@ public class ConfigurationKeys {
*
* The names of the constants generated by {@code @FieldNameConstants} will be prefixed with this value.
*/
- public static final ConfigurationKey<String> FIELD_NAME_CONSTANTS_INNER_TYPE_NAME = new ConfigurationKey<String>("lombok.fieldNameConstants.innerTypeName", "The default name of the inner type generated by @FieldNameConstants. (default: 'Fields').") {};
+ public static final ConfigurationKey<IdentifierName> FIELD_NAME_CONSTANTS_INNER_TYPE_NAME = new ConfigurationKey<IdentifierName>("lombok.fieldNameConstants.innerTypeName", "The default name of the inner type generated by @FieldNameConstants. (default: 'Fields').") {};
/**
* lombok configuration: {@code lombok.fieldNameConstants.uppercase} = {@code true} | {@code false}.
diff --git a/src/core/lombok/CustomLog.java b/src/core/lombok/CustomLog.java
new file mode 100644
index 00000000..d1f45f7c
--- /dev/null
+++ b/src/core/lombok/CustomLog.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 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.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 based on a custom logger implementation.
+ * <p>
+ * Complete documentation is found at <a href="https://projectlombok.org/features/Log">the project lombok features page for lombok log annotations</a>.
+ * <p>
+ * Example:
+ * <pre>
+ * &#64;CustomLog
+ * public class LogExample {
+ * }
+ * </pre>
+ * With configuration:
+ * <pre>
+ * lombok.log.custom.declaration=my.cool.Logger my.cool.LoggerFactory.getLogger(NAME)
+ * </pre>
+ *
+ * will generate:
+ *
+ * <pre>
+ * public class LogExample {
+ * private static final my.cool.Logger log = my.cool.LoggerFactory.getLogger(LogExample.class.getName());
+ * }
+ * </pre>
+ * <p>
+ * Configuration must be provided in lombok.config, otherwise any usage of this annotation will result in a compile-time error.
+ *
+ * This annotation is valid for classes and enumerations.<br>
+ * @see lombok.extern.java.Log &#64;Log
+ * @see lombok.extern.apachecommons.CommonsLog &#64;CommonsLog
+ * @see lombok.extern.log4j.Log4j &#64;Log4j
+ * @see lombok.extern.log4j.Log4j2 &#64;Log4j2
+ * @see lombok.extern.slf4j.Slf4j &#64;Slf4j
+ * @see lombok.extern.slf4j.XSlf4j &#64;XSlf4j
+ * @see lombok.extern.jbosslog.JBossLog &#64;JBossLog
+ * @see lombok.extern.flogger.Flogger &#64;Flogger
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.TYPE)
+public @interface CustomLog {
+ /**
+ *
+ * Sets a custom topic/category. Note that this requires you to specify a parameter configuration for your custom logger that includes {@code TOPIC}.
+ *
+ * @return The topic/category of the constructed Logger. By default (or for the empty string as topic), the parameter configuration without {@code TOPIC} is invoked.
+ */
+ String topic() default "";
+}
diff --git a/src/core/lombok/core/configuration/ConfigurationDataType.java b/src/core/lombok/core/configuration/ConfigurationDataType.java
index 7512d2e6..50b47a2e 100644
--- a/src/core/lombok/core/configuration/ConfigurationDataType.java
+++ b/src/core/lombok/core/configuration/ConfigurationDataType.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013-2014 The Project Lombok Authors.
+ * Copyright (C) 2013-2019 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.core.configuration;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
@@ -97,23 +99,10 @@ public final class ConfigurationDataType {
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) {
+ private static ConfigurationValueParser enumParser(Type enumType) {
final Class<?> type = (Class<?>) enumType;
@SuppressWarnings("rawtypes") final Class rawType = type;
@@ -145,6 +134,38 @@ public final class ConfigurationDataType {
};
}
+ private static ConfigurationValueParser valueTypeParser(Type argumentType) {
+ final Class<?> type = (Class<?>) argumentType;
+ final Method valueOfMethod = getMethod(type, "valueOf", String.class);
+ final Method descriptionMethod = getMethod(type, "description");
+ final Method exampleValueMethod = getMethod(type, "exampleValue");
+ return new ConfigurationValueParser() {
+ @Override public Object parse(String value) {
+ return invokeStaticMethod(valueOfMethod, value);
+ }
+
+ @Override public String description() {
+ return invokeStaticMethod(descriptionMethod);
+ }
+
+ @Override public String exampleValue() {
+ return invokeStaticMethod(exampleValueMethod);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <R> R invokeStaticMethod(Method method, Object... arguments) {
+ try {
+ return (R) method.invoke(null, arguments);
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException("The method " + method.getName() + " ", e);
+ } catch (InvocationTargetException e) {
+ // There shouldn't be any checked Exception, only IllegalArgumentException is expected
+ throw (RuntimeException) e.getTargetException();
+ }
+ }
+ };
+ }
+
private final boolean isList;
private final ConfigurationValueParser parser;
@@ -155,7 +176,7 @@ public final class ConfigurationDataType {
Type type = keyClass.getGenericSuperclass();
if (!(type instanceof ParameterizedType)) {
- throw new IllegalArgumentException("Missing type parameter in "+ type);
+ throw new IllegalArgumentException("Missing type parameter in " + type);
}
ParameterizedType parameterized = (ParameterizedType) type;
@@ -178,6 +199,10 @@ public final class ConfigurationDataType {
return new ConfigurationDataType(isList, enumParser(argumentType));
}
+ if (isConfigurationValueType(argumentType)) {
+ return new ConfigurationDataType(isList, valueTypeParser(argumentType));
+ }
+
throw new IllegalArgumentException("Unsupported type parameter in " + type);
}
@@ -203,4 +228,18 @@ public final class ConfigurationDataType {
private static boolean isEnum(Type argumentType) {
return argumentType instanceof Class && ((Class<?>) argumentType).isEnum();
}
+
+ private static boolean isConfigurationValueType(Type argumentType) {
+ return argumentType instanceof Class && ConfigurationValueType.class.isAssignableFrom((Class<?>) argumentType);
+ }
+
+ private static Method getMethod(Class<?> argumentType, String name, Class<?>... parameterTypes) {
+ try {
+ return argumentType.getMethod(name, parameterTypes);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException("Method " + name + " with parameters " + Arrays.toString(parameterTypes) + " was not found.", e);
+ } catch (SecurityException e) {
+ throw new IllegalStateException("Cannot inspect methods of type " + argumentType, e);
+ }
+ }
} \ No newline at end of file
diff --git a/src/core/lombok/core/configuration/ConfigurationValueType.java b/src/core/lombok/core/configuration/ConfigurationValueType.java
new file mode 100644
index 00000000..a44a5a83
--- /dev/null
+++ b/src/core/lombok/core/configuration/ConfigurationValueType.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 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;
+
+/**
+ * If a type used in {@link ConfigurationKey} type argument implements this interface,
+ * it is expected to provide the following three static methods:
+ * <ul>
+ * <li><code>public static SELF valueOf(String value)</code>
+ * <li><code>public static String description()</code>
+ * <li><code>public static String exampleValue()</code>
+ * </ul>
+ * None of them should throw checked exceptions.
+ * Based on these methods, an instance of {@link ConfigurationValueParser} is created
+ * and used by the configuration system.
+ */
+public interface ConfigurationValueType {
+}
diff --git a/src/core/lombok/core/configuration/IdentifierName.java b/src/core/lombok/core/configuration/IdentifierName.java
new file mode 100644
index 00000000..db83c2cc
--- /dev/null
+++ b/src/core/lombok/core/configuration/IdentifierName.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 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.JavaIdentifiers;
+
+public final class IdentifierName implements ConfigurationValueType {
+ private final String name;
+
+ private IdentifierName(String name) {
+ this.name = name;
+ }
+
+ public static IdentifierName valueOf(String name) {
+ if (name == null || name.trim().isEmpty()) return null;
+
+ String trimmedName = name.trim();
+ if (!JavaIdentifiers.isValidJavaIdentifier(trimmedName)) throw new IllegalArgumentException("Invalid identifier " + trimmedName);
+ return new IdentifierName(trimmedName);
+ }
+
+ public static String description() {
+ return "identifier-name";
+ }
+
+ public static String exampleValue() {
+ return "<javaIdentifier>";
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (!(obj instanceof IdentifierName)) return false;
+ return name.equals(((IdentifierName) obj).name);
+ }
+
+ @Override public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override public String toString() {
+ return name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public char[] getCharArray() {
+ return name.toCharArray();
+ }
+}
diff --git a/src/core/lombok/core/configuration/LogDeclaration.java b/src/core/lombok/core/configuration/LogDeclaration.java
new file mode 100644
index 00000000..ad4102d4
--- /dev/null
+++ b/src/core/lombok/core/configuration/LogDeclaration.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2019 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.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class LogDeclaration implements ConfigurationValueType {
+ private static final Pattern PARAMETERS_PATTERN = Pattern.compile("(?:\\(([A-Z,]*)\\))");
+ private static final Pattern DECLARATION_PATTERN = Pattern.compile("^(?:([^ ]+) )?([^(]+)\\.([^(]+)(" + PARAMETERS_PATTERN.pattern() + "+)$");
+
+ public enum LogFactoryParameter {
+ TYPE, NAME, TOPIC, NULL;
+ }
+
+ private final TypeName loggerType;
+ private final TypeName loggerFactoryType;
+ private final IdentifierName loggerFactoryMethod;
+ private final List<LogFactoryParameter> parametersWithoutTopic;
+ private final List<LogFactoryParameter> parametersWithTopic;
+
+ private LogDeclaration(TypeName loggerType, TypeName loggerFactoryType, IdentifierName loggerFactoryMethod, List<LogFactoryParameter> parametersWithoutTopic, List<LogFactoryParameter> parametersWithTopic) {
+ this.loggerType = loggerType;
+ this.loggerFactoryType = loggerFactoryType;
+ this.loggerFactoryMethod = loggerFactoryMethod;
+ this.parametersWithoutTopic = parametersWithoutTopic;
+ this.parametersWithTopic = parametersWithTopic;
+ }
+
+ public static LogDeclaration valueOf(String declaration) {
+ if (declaration == null) return null;
+
+ Matcher matcher = DECLARATION_PATTERN.matcher(declaration);
+ if (!matcher.matches()) throw new IllegalArgumentException("The declaration must follow the pattern: [LoggerType ]LoggerFactoryType.loggerFactoryMethod(loggerFactoryMethodParams)[(loggerFactoryMethodParams)]");
+
+ TypeName loggerFactoryType = TypeName.valueOf(matcher.group(2));
+ TypeName loggerType = TypeName.valueOf(matcher.group(1));
+ if (loggerType == null) loggerType = loggerFactoryType;
+ IdentifierName loggerFactoryMethod = IdentifierName.valueOf(matcher.group(3));
+ List<List<LogFactoryParameter>> allParameters = parseParameters(matcher.group(4));
+
+ List<LogFactoryParameter> parametersWithoutTopic = null;
+ List<LogFactoryParameter> parametersWithTopic = null;
+ for (List<LogFactoryParameter> parameters: allParameters) {
+ if (parameters.contains(LogFactoryParameter.TOPIC)) {
+ if (parametersWithTopic != null) throw new IllegalArgumentException("There is more than one parameter definition that includes TOPIC: " + parametersWithTopic + " and " + parameters);
+ parametersWithTopic = parameters;
+ } else {
+ if (parametersWithoutTopic != null) throw new IllegalArgumentException("There is more than one parmaeter definition that does not include TOPIC: " + parametersWithoutTopic + " and " + parameters);
+ parametersWithoutTopic = parameters;
+ }
+ }
+
+ // sanity check (the pattern should disallow this situation
+ if (parametersWithoutTopic == null && parametersWithTopic == null) throw new IllegalArgumentException("No logger factory method parameters specified.");
+
+ return new LogDeclaration(loggerType, loggerFactoryType, loggerFactoryMethod, parametersWithoutTopic, parametersWithTopic);
+ }
+
+ private static List<List<LogFactoryParameter>> parseParameters(String parametersDefinitions) {
+ List<List<LogFactoryParameter>> allParameters = new ArrayList<List<LogFactoryParameter>>();
+ Matcher matcher = PARAMETERS_PATTERN.matcher(parametersDefinitions);
+ while (matcher.find()) {
+ String parametersDefinition = matcher.group(1);
+ List<LogFactoryParameter> parameters = new ArrayList<LogFactoryParameter>();
+ if (!parametersDefinition.isEmpty()) {
+ for (String parameter : parametersDefinition.split(",")) {
+ parameters.add(LogFactoryParameter.valueOf(parameter));
+ }
+ }
+ allParameters.add(parameters);
+ }
+ return allParameters;
+ }
+
+ public static String description() {
+ return "custom-log-declaration";
+ }
+
+ public static String exampleValue() {
+ return "my.cool.Logger my.cool.LoggerFactory.createLogger()(TOPIC,TYPE)";
+ }
+
+ @Override public boolean equals(Object obj) {
+ if (!(obj instanceof LogDeclaration)) return false;
+ return loggerType.equals(((LogDeclaration) obj).loggerType)
+ && loggerFactoryType.equals(((LogDeclaration) obj).loggerFactoryType)
+ && loggerFactoryMethod.equals(((LogDeclaration) obj).loggerFactoryMethod)
+ && parametersWithoutTopic == ((LogDeclaration) obj).parametersWithoutTopic || parametersWithoutTopic.equals(((LogDeclaration) obj).parametersWithoutTopic)
+ && parametersWithTopic == ((LogDeclaration) obj).parametersWithTopic || parametersWithTopic.equals(((LogDeclaration) obj).parametersWithTopic);
+ }
+
+ @Override public int hashCode() {
+ final int prime = 31;