You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ma...@apache.org on 2020/03/15 17:26:41 UTC
[logging-log4j2] 07/07: Improve annotation meta-modeling
This is an automated email from the ASF dual-hosted git repository.
mattsicker pushed a commit to branch mean-bean-machine
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit 0fe200301a248599fb515fcd62ee559a8c3698a4
Author: Matt Sicker <bo...@gmail.com>
AuthorDate: Sun Mar 15 12:26:14 2020 -0500
Improve annotation meta-modeling
This allows for more complex annotation backward compatibility mappings.
Signed-off-by: Matt Sicker <bo...@gmail.com>
---
.../api/{AliasFor.java => AnnotationAlias.java} | 11 +-
.../plugins/defaults/model/AbstractMetaMember.java | 9 +-
.../defaults/model/DefaultElementManager.java | 67 +++++++----
.../defaults/model/DefaultMetaAnnotation.java | 112 +++++++++++++++++
.../model/DefaultMetaAnnotationElement.java | 82 +++++++++++++
.../plugins/defaults/model/DefaultMetaClass.java | 9 +-
.../defaults/model/DefaultMetaParameter.java | 4 +-
.../plugins/defaults/model/DefaultQualifiers.java | 78 ++++++++++++
.../log4j/plugins/spi/model/ElementManager.java | 2 +-
.../model/MetaAnnotation.java} | 18 +--
.../model/MetaAnnotationElement.java} | 17 +--
.../log4j/plugins/spi/model/MetaElement.java | 24 ++--
.../log4j/plugins/spi/model/Qualifiers.java | 133 +--------------------
.../log4j/plugins/spi/model/QualifiersTest.java | 79 ------------
14 files changed, 361 insertions(+), 284 deletions(-)
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AnnotationAlias.java
similarity index 65%
copy from log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java
copy to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AnnotationAlias.java
index b7541d8..d97830d 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AnnotationAlias.java
@@ -24,10 +24,17 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-// TODO: documentation around where this can be applied
+/**
+ * Meta annotation for making an annotation an alias for another annotation. Annotations with this annotation will be
+ * interpreted as if they were implemented by the given annotation type instead. This applies to
+ * {@linkplain QualifierType qualifiers}, {@linkplain ScopeType scopes}, {@link Inject}, {@link Produces},
+ * {@link Disposes}, {@link PostConstruct}, and {@link PreDestroy}. Individual annotation elements are aliased to the
+ * same element names from the aliased annotation unless otherwise annotated with {@link Named} which should specify
+ * the aliased annotation element name.
+ */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@Documented
-public @interface AliasFor {
+public @interface AnnotationAlias {
Class<? extends Annotation> value();
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/AbstractMetaMember.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/AbstractMetaMember.java
index a6d976a..52dbad2 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/AbstractMetaMember.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/AbstractMetaMember.java
@@ -17,27 +17,26 @@
package org.apache.logging.log4j.plugins.defaults.model;
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotation;
import org.apache.logging.log4j.plugins.spi.model.MetaClass;
import org.apache.logging.log4j.plugins.spi.model.MetaMember;
-import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
-import java.util.Arrays;
import java.util.Collection;
abstract class AbstractMetaMember<D, T> implements MetaMember<D, T> {
private final String name;
- private final Collection<Annotation> annotations;
+ private final Collection<MetaAnnotation> annotations;
private final MetaClass<D> declaringClass;
private final MetaClass<T> type;
private final boolean isStatic;
AbstractMetaMember(final MetaClass<D> declaringClass, final Member member, final MetaClass<T> type) {
this.name = member.getName();
- this.annotations = Arrays.asList(((AnnotatedElement) member).getAnnotations());
+ this.annotations = DefaultMetaAnnotation.fromAnnotations(((AnnotatedElement) member).getAnnotations());
this.declaringClass = declaringClass;
this.type = type;
this.isStatic = Modifier.isStatic(member.getModifiers());
@@ -49,7 +48,7 @@ abstract class AbstractMetaMember<D, T> implements MetaMember<D, T> {
}
@Override
- public Collection<Annotation> getAnnotations() {
+ public Collection<MetaAnnotation> getAnnotations() {
return annotations;
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultElementManager.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultElementManager.java
index 6144a14..8193144 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultElementManager.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultElementManager.java
@@ -17,13 +17,18 @@
package org.apache.logging.log4j.plugins.defaults.model;
-import org.apache.logging.log4j.plugins.api.AliasFor;
+import org.apache.logging.log4j.plugins.api.AnnotationAlias;
+import org.apache.logging.log4j.plugins.api.Default;
import org.apache.logging.log4j.plugins.api.Dependent;
+import org.apache.logging.log4j.plugins.api.Ignore;
+import org.apache.logging.log4j.plugins.api.Named;
import org.apache.logging.log4j.plugins.api.QualifierType;
import org.apache.logging.log4j.plugins.api.ScopeType;
import org.apache.logging.log4j.plugins.spi.bean.Bean;
import org.apache.logging.log4j.plugins.spi.model.ElementManager;
import org.apache.logging.log4j.plugins.spi.model.InjectionPoint;
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotation;
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotationElement;
import org.apache.logging.log4j.plugins.spi.model.MetaClass;
import org.apache.logging.log4j.plugins.spi.model.MetaElement;
import org.apache.logging.log4j.plugins.spi.model.MetaExecutable;
@@ -33,6 +38,7 @@ import org.apache.logging.log4j.plugins.spi.model.MetaParameter;
import org.apache.logging.log4j.plugins.spi.model.Qualifiers;
import org.apache.logging.log4j.plugins.spi.model.Variable;
import org.apache.logging.log4j.plugins.util.Cache;
+import org.apache.logging.log4j.plugins.util.TypeUtil;
import org.apache.logging.log4j.plugins.util.WeakCache;
import java.beans.Introspector;
@@ -42,8 +48,10 @@ import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Objects;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
public class DefaultElementManager implements ElementManager {
@@ -58,8 +66,8 @@ public class DefaultElementManager implements ElementManager {
private final Cache<Class<? extends Annotation>, AnnotationType> annotationTypeCache = WeakCache.newCache(clazz -> {
for (final Annotation annotation : clazz.getAnnotations()) {
Class<? extends Annotation> type = annotation.annotationType();
- if (type == AliasFor.class) {
- type = ((AliasFor) annotation).value();
+ if (type == AnnotationAlias.class) {
+ type = ((AnnotationAlias) annotation).value();
}
if (type == QualifierType.class) {
return AnnotationType.QUALIFIER;
@@ -84,31 +92,48 @@ public class DefaultElementManager implements ElementManager {
@Override
public Qualifiers getQualifiers(final MetaElement<?> element) {
final String elementName = element.getName();
- final String defaultName;
+ final String defaultNamedValue;
if (element instanceof MetaMethod<?, ?>) {
final Matcher matcher = BEAN_METHOD.matcher(elementName);
if (matcher.matches()) {
- defaultName = Introspector.decapitalize(matcher.group(2));
+ defaultNamedValue = Introspector.decapitalize(matcher.group(2));
} else {
- defaultName = elementName;
+ defaultNamedValue = elementName;
}
} else if (element instanceof MetaClass<?>) {
- defaultName = Introspector.decapitalize(((MetaClass<?>) element).getJavaClass().getSimpleName());
+ defaultNamedValue = Introspector.decapitalize(((MetaClass<?>) element).getJavaClass().getSimpleName());
} else {
- defaultName = elementName;
+ defaultNamedValue = elementName;
}
- return Qualifiers.fromQualifierAnnotations(filterQualifiers(element.getAnnotations()), defaultName);
+ final Set<MetaAnnotation> qualifiers = element.getAnnotations().stream()
+ .filter(annotation -> isQualifierType(annotation.getAnnotationType()))
+ .map(annotation -> transformQualifier(annotation, defaultNamedValue))
+ .collect(Collectors.toCollection(LinkedHashSet::new));
+ if (qualifiers.stream().map(MetaAnnotation::getAnnotationType).noneMatch(type -> type != Named.class)) {
+ qualifiers.add(new DefaultMetaAnnotation(Default.class, Collections.emptySet()));
+ }
+ return new DefaultQualifiers(qualifiers);
}
- private Collection<Annotation> filterQualifiers(final Collection<Annotation> annotations) {
- final Collection<Annotation> qualifiers = new LinkedHashSet<>(annotations.size());
- for (final Annotation annotation : annotations) {
- final Class<? extends Annotation> annotationType = annotation.annotationType();
- if (isQualifierType(annotationType)) {
- qualifiers.add(annotation);
- }
- }
- return qualifiers;
+ private static MetaAnnotation transformQualifier(final MetaAnnotation annotation, final String defaultNamedValue) {
+ final Class<? extends Annotation> annotationType = annotation.getAnnotationType();
+ final Set<MetaAnnotationElement<?>> elements = annotation.getAnnotationElements().stream()
+ .filter(element -> !element.isAnnotationPresent(Ignore.class))
+ .map(element -> {
+ if (annotationType == Named.class && element.getName().equals("value")) {
+ final MetaAnnotationElement<String> namedValue = TypeUtil.cast(element);
+ final String value = namedValue.getValue();
+ if (value.isEmpty()) {
+ return namedValue.withNewValue(defaultNamedValue);
+ } else {
+ return namedValue;
+ }
+ } else {
+ return element;
+ }
+ })
+ .collect(Collectors.toCollection(LinkedHashSet::new));
+ return new DefaultMetaAnnotation(annotationType, elements);
}
private Class<? extends Annotation> getScopeType(final MetaElement<?> element) {
@@ -116,11 +141,11 @@ public class DefaultElementManager implements ElementManager {
return scopeTypes.isEmpty() ? Dependent.class : scopeTypes.iterator().next();
}
- private Collection<Class<? extends Annotation>> filterScopeTypes(final Collection<Annotation> annotations) {
+ private Collection<Class<? extends Annotation>> filterScopeTypes(final Collection<MetaAnnotation> annotations) {
// only expect at most one scope
final Collection<Class<? extends Annotation>> scopeTypes = new LinkedHashSet<>(1);
- for (final Annotation annotation : annotations) {
- final Class<? extends Annotation> annotationType = annotation.annotationType();
+ for (final MetaAnnotation annotation : annotations) {
+ final Class<? extends Annotation> annotationType = annotation.getAnnotationType();
if (isScopeType(annotationType)) {
scopeTypes.add(annotationType);
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaAnnotation.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaAnnotation.java
new file mode 100644
index 0000000..0751817
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaAnnotation.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.plugins.defaults.model;
+
+import org.apache.logging.log4j.plugins.api.AnnotationAlias;
+import org.apache.logging.log4j.plugins.api.Named;
+import org.apache.logging.log4j.plugins.spi.InitializationException;
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotation;
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotationElement;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+class DefaultMetaAnnotation implements MetaAnnotation {
+
+ static List<MetaAnnotation> fromAnnotations(final Annotation... annotations) {
+ if (annotations.length == 0) {
+ return Collections.emptyList();
+ }
+ final List<MetaAnnotation> metaAnnotations = new ArrayList<>(annotations.length);
+ for (final Annotation annotation : annotations) {
+ metaAnnotations.add(fromAnnotation(annotation));
+ }
+ return Collections.unmodifiableList(metaAnnotations);
+ }
+
+ private static DefaultMetaAnnotation fromAnnotation(final Annotation annotation) {
+ final Class<? extends Annotation> originalType = annotation.annotationType();
+ final Method[] annotationElements = originalType.getDeclaredMethods();
+ final Set<MetaAnnotationElement<?>> elements = new LinkedHashSet<>(annotationElements.length);
+ for (final Method element : annotationElements) {
+ final String name;
+ final Named named = element.getAnnotation(Named.class);
+ if (named != null && !named.value().isEmpty()) {
+ name = named.value();
+ } else {
+ name = element.getName();
+ }
+ final Object value = getAnnotationElementValue(annotation, element);
+ elements.add(new DefaultMetaAnnotationElement<>(name, value, fromAnnotations(element.getAnnotations())));
+ }
+ final AnnotationAlias alias = originalType.getAnnotation(AnnotationAlias.class);
+ final Class<? extends Annotation> annotationType = alias != null ? alias.value() : originalType;
+ return new DefaultMetaAnnotation(annotationType, elements);
+ }
+
+ private static Object getAnnotationElementValue(final Annotation annotation, final Method element) {
+ try {
+ return element.invoke(annotation);
+ } catch (final IllegalAccessException e) {
+ throw new InitializationException("Cannot access element " + element.getName() + " of annotation " + annotation, e);
+ } catch (final InvocationTargetException e) {
+ throw new InitializationException("Cannot access element " + element.getName() + " of annotation " + annotation,
+ e.getCause());
+ }
+ }
+
+ private final Class<? extends Annotation> annotationType;
+ private final Set<MetaAnnotationElement<?>> elements;
+
+ DefaultMetaAnnotation(final Class<? extends Annotation> annotationType, final Set<MetaAnnotationElement<?>> elements) {
+ this.annotationType = annotationType;
+ this.elements = Collections.unmodifiableSet(elements);
+ }
+
+ @Override
+ public Class<? extends Annotation> getAnnotationType() {
+ return annotationType;
+ }
+
+ @Override
+ public Collection<MetaAnnotationElement<?>> getAnnotationElements() {
+ return elements;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ final DefaultMetaAnnotation that = (DefaultMetaAnnotation) o;
+ return annotationType.equals(that.annotationType) &&
+ elements.equals(that.elements);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(annotationType, elements);
+ }
+}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaAnnotationElement.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaAnnotationElement.java
new file mode 100644
index 0000000..acd3e48
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaAnnotationElement.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.plugins.defaults.model;
+
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotation;
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotationElement;
+import org.apache.logging.log4j.plugins.util.TypeUtil;
+
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+
+class DefaultMetaAnnotationElement<T> implements MetaAnnotationElement<T> {
+ private final String name;
+ private final T value;
+ private final Collection<MetaAnnotation> annotations;
+
+ DefaultMetaAnnotationElement(final String name, final T value, final Collection<MetaAnnotation> annotations) {
+ this.name = name;
+ this.value = value;
+ this.annotations = Collections.unmodifiableCollection(annotations);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public Type getType() {
+ return value.getClass();
+ }
+
+ @Override
+ public Collection<Type> getTypeClosure() {
+ return TypeUtil.getTypeClosure(getType());
+ }
+
+ @Override
+ public Collection<MetaAnnotation> getAnnotations() {
+ return annotations;
+ }
+
+ @Override
+ public T getValue() {
+ return value;
+ }
+
+ @Override
+ public MetaAnnotationElement<T> withNewValue(final T value) {
+ return new DefaultMetaAnnotationElement<>(name, value, annotations);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ final DefaultMetaAnnotationElement<?> that = (DefaultMetaAnnotationElement<?>) o;
+ return name.equals(that.name) && value.equals(that.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, value);
+ }
+}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaClass.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaClass.java
index 80afe92..219ff77 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaClass.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaClass.java
@@ -17,6 +17,7 @@
package org.apache.logging.log4j.plugins.defaults.model;
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotation;
import org.apache.logging.log4j.plugins.spi.model.MetaClass;
import org.apache.logging.log4j.plugins.spi.model.MetaConstructor;
import org.apache.logging.log4j.plugins.spi.model.MetaField;
@@ -42,19 +43,19 @@ class DefaultMetaClass<T> implements MetaClass<T> {
}
static <T> MetaClass<T> newMetaClass(final Type baseType, final Class<T> javaClass, final Annotation... annotations) {
- return new DefaultMetaClass<>(baseType, javaClass, TypeUtil.getTypeClosure(baseType), Arrays.asList(annotations));
+ return new DefaultMetaClass<>(baseType, javaClass, TypeUtil.getTypeClosure(baseType), DefaultMetaAnnotation.fromAnnotations(annotations));
}
private final Type baseType;
private final Class<T> javaClass;
private final Collection<Type> typeClosure;
- private final Collection<Annotation> annotations;
+ private final Collection<MetaAnnotation> annotations;
private final Value<Collection<MetaConstructor<T>>> constructors;
private final Value<Collection<MetaMethod<T, ?>>> methods;
private final Value<Collection<MetaField<T, ?>>> fields;
private DefaultMetaClass(final Type baseType, final Class<T> javaClass, final Collection<Type> typeClosure,
- final Collection<Annotation> annotations) {
+ final Collection<MetaAnnotation> annotations) {
this.baseType = baseType;
this.javaClass = javaClass;
this.typeClosure = typeClosure;
@@ -77,7 +78,7 @@ class DefaultMetaClass<T> implements MetaClass<T> {
}
@Override
- public Collection<Annotation> getAnnotations() {
+ public Collection<MetaAnnotation> getAnnotations() {
return annotations;
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaParameter.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaParameter.java
index 960e81f..2b7af20 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaParameter.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultMetaParameter.java
@@ -17,11 +17,11 @@
package org.apache.logging.log4j.plugins.defaults.model;
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotation;
import org.apache.logging.log4j.plugins.spi.model.MetaClass;
import org.apache.logging.log4j.plugins.spi.model.MetaParameter;
import org.apache.logging.log4j.plugins.util.TypeUtil;
-import java.lang.annotation.Annotation;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.Collection;
@@ -45,7 +45,7 @@ class DefaultMetaParameter<T> implements MetaParameter<T> {
}
@Override
- public Collection<Annotation> getAnnotations() {
+ public Collection<MetaAnnotation> getAnnotations() {
return parameterClass.getAnnotations();
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultQualifiers.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultQualifiers.java
new file mode 100644
index 0000000..2a6eed2
--- /dev/null
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/defaults/model/DefaultQualifiers.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.plugins.defaults.model;
+
+import org.apache.logging.log4j.plugins.api.Default;
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotation;
+import org.apache.logging.log4j.plugins.spi.model.MetaAnnotationElement;
+import org.apache.logging.log4j.plugins.spi.model.Qualifiers;
+import org.apache.logging.log4j.util.StringBuilders;
+
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Represents a normalized set of {@linkplain org.apache.logging.log4j.plugins.api.QualifierType qualifier annotations}.
+ */
+class DefaultQualifiers implements Qualifiers {
+ private final Set<MetaAnnotation> qualifiers;
+
+ DefaultQualifiers(final Set<MetaAnnotation> qualifiers) {
+ this.qualifiers = qualifiers;
+ }
+
+ @Override
+ public boolean hasDefaultQualifier() {
+ return qualifiers.stream().anyMatch(q -> q.getAnnotationType() == Default.class);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ final DefaultQualifiers that = (DefaultQualifiers) o;
+ return qualifiers.equals(that.qualifiers);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(qualifiers);
+ }
+
+ @Override
+ public String toString() {
+ if (qualifiers.isEmpty()) {
+ return "[]";
+ }
+ StringBuilder sb = new StringBuilder().append('[');
+ for (final MetaAnnotation qualifier : qualifiers) {
+ sb.append('@').append(qualifier.getAnnotationType().getSimpleName());
+ final Collection<MetaAnnotationElement<?>> elements = qualifier.getAnnotationElements();
+ if (!elements.isEmpty()) {
+ sb.append('(');
+ for (final MetaAnnotationElement<?> element : elements) {
+ StringBuilders.appendKeyDqValue(sb, element.getName(), element.getValue()).append(", ");
+ }
+ sb.delete(sb.length() - 2, sb.length()).append(')');
+ }
+ sb.append(", ");
+ }
+ return sb.delete(sb.length() - 2, sb.length()).append(']').toString();
+ }
+}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/ElementManager.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/ElementManager.java
index c979eee..ee213c3 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/ElementManager.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/ElementManager.java
@@ -100,7 +100,7 @@ public interface ElementManager extends AutoCloseable {
*/
default boolean isInjectable(final MetaElement<?> element) {
return element.isAnnotationPresent(Inject.class) ||
- (element.getAnnotations().stream().map(Annotation::annotationType).anyMatch(this::isQualifierType) &&
+ (element.getAnnotations().stream().map(MetaAnnotation::getAnnotationType).anyMatch(this::isQualifierType) &&
!element.isAnnotationPresent(Produces.class));
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaAnnotation.java
similarity index 64%
copy from log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java
copy to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaAnnotation.java
index b7541d8..0038f7d 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaAnnotation.java
@@ -15,19 +15,13 @@
* limitations under the license.
*/
-package org.apache.logging.log4j.plugins.api;
+package org.apache.logging.log4j.plugins.spi.model;
import java.lang.annotation.Annotation;
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+import java.util.Collection;
-// TODO: documentation around where this can be applied
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.ANNOTATION_TYPE)
-@Documented
-public @interface AliasFor {
- Class<? extends Annotation> value();
+public interface MetaAnnotation {
+ Class<? extends Annotation> getAnnotationType();
+
+ Collection<MetaAnnotationElement<?>> getAnnotationElements();
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaAnnotationElement.java
similarity index 61%
rename from log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java
rename to log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaAnnotationElement.java
index b7541d8..28506fe 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/api/AliasFor.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaAnnotationElement.java
@@ -15,19 +15,10 @@
* limitations under the license.
*/
-package org.apache.logging.log4j.plugins.api;
+package org.apache.logging.log4j.plugins.spi.model;
-import java.lang.annotation.Annotation;
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+public interface MetaAnnotationElement<T> extends MetaElement<T> {
+ T getValue();
-// TODO: documentation around where this can be applied
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.ANNOTATION_TYPE)
-@Documented
-public @interface AliasFor {
- Class<? extends Annotation> value();
+ MetaAnnotationElement<T> withNewValue(final T value);
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaElement.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaElement.java
index 3de8421..dec4495 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaElement.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/MetaElement.java
@@ -17,7 +17,7 @@
package org.apache.logging.log4j.plugins.spi.model;
-import org.apache.logging.log4j.plugins.api.AliasFor;
+import org.apache.logging.log4j.plugins.api.AnnotationAlias;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
@@ -30,32 +30,24 @@ public interface MetaElement<T> {
*/
String getName();
+ Type getType();
+
+ Collection<Type> getTypeClosure();
+
/**
* Returns all the annotations present on this element.
*/
- Collection<Annotation> getAnnotations();
+ Collection<MetaAnnotation> getAnnotations();
/**
* Indicates whether or not an annotation is present on this element taking into account
- * {@linkplain AliasFor annotation aliasing}.
+ * {@linkplain AnnotationAlias annotation aliasing}.
*
* @param annotationType type of annotation to look for
* @return whether or not the annotation is directly or indirectly present on this element
*/
default boolean isAnnotationPresent(final Class<? extends Annotation> annotationType) {
- for (final Annotation annotation : getAnnotations()) {
- if (annotationType.equals(annotation.annotationType())) {
- return true;
- }
- if (annotation instanceof AliasFor) {
- return annotationType.equals(((AliasFor) annotation).value());
- }
- }
- return false;
+ return getAnnotations().stream().anyMatch(annotation -> annotationType == annotation.getAnnotationType());
}
- Type getType();
-
- Collection<Type> getTypeClosure();
-
}
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/Qualifiers.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/Qualifiers.java
index a3293c0..762f349 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/Qualifiers.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/spi/model/Qualifiers.java
@@ -17,137 +17,12 @@
package org.apache.logging.log4j.plugins.spi.model;
-import org.apache.logging.log4j.plugins.api.AliasFor;
-import org.apache.logging.log4j.plugins.api.Default;
-import org.apache.logging.log4j.plugins.api.Ignore;
-import org.apache.logging.log4j.plugins.api.Named;
-import org.apache.logging.log4j.plugins.spi.InitializationException;
-import org.apache.logging.log4j.util.StringBuilders;
-import org.apache.logging.log4j.util.Strings;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * Represents a normalized set of {@linkplain org.apache.logging.log4j.plugins.api.QualifierType qualifier annotations}.
- */
-public final class Qualifiers {
- private final Map<Class<? extends Annotation>, Map<String, Object>> qualifiers;
-
- private Qualifiers(final Map<Class<? extends Annotation>, Map<String, Object>> qualifiers) {
- this.qualifiers = qualifiers;
- }
-
- public boolean hasDefaultQualifier() {
- return qualifiers.containsKey(Default.class);
- }
-
- @Override
- public boolean equals(final Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- final Qualifiers that = (Qualifiers) o;
- return qualifiers.equals(that.qualifiers);
- }
+public interface Qualifiers {
+ boolean hasDefaultQualifier();
@Override
- public int hashCode() {
- return Objects.hash(qualifiers);
- }
+ boolean equals(Object o);
@Override
- public String toString() {
- final Set<Map.Entry<Class<? extends Annotation>, Map<String, Object>>> entries = qualifiers.entrySet();
- if (entries.isEmpty()) {
- return "[]";
- }
- StringBuilder sb = new StringBuilder().append('[');
- for (final Map.Entry<Class<? extends Annotation>, Map<String, Object>> entry : entries) {
- sb.append('@').append(entry.getKey().getSimpleName());
- final Map<String, Object> attributes = entry.getValue();
- if (!attributes.isEmpty()) {
- sb.append('(');
- for (final Map.Entry<String, Object> attribute : attributes.entrySet()) {
- StringBuilders.appendKeyDqValue(sb, attribute.getKey(), attribute.getValue());
- sb.append(", ");
- }
- sb.delete(sb.length() - 2, sb.length()).append(')');
- }
- sb.append(", ");
- }
- return sb.delete(sb.length() - 2, sb.length()).append(']').toString();
- }
-
- /**
- * Creates a normalized Qualifiers instance from a collection of qualifier annotation instances.
- */
- public static Qualifiers fromQualifierAnnotations(final Collection<Annotation> annotations) {
- return fromQualifierAnnotations(annotations, Strings.EMPTY);
- }
-
- /**
- * Creates a normalized Qualifiers instance from a collection of qualifier annotation instances and a default name
- * to use when {@linkplain Named named qualifiers} do not specify a value.
- */
- public static Qualifiers fromQualifierAnnotations(final Collection<Annotation> annotations, final String defaultName) {
- final Map<Class<? extends Annotation>, Map<String, Object>> qualifiers = new HashMap<>(annotations.size());
- for (final Annotation annotation : annotations) {
- final Class<? extends Annotation> annotationType = annotation.annotationType();
- final AliasFor alias = annotationType.getAnnotation(AliasFor.class);
- final Class<? extends Annotation> qualifierType = alias != null ? alias.value() : annotationType;
- qualifiers.put(qualifierType, getQualifierAttributes(annotation, defaultName));
- }
- if (needsDefaultQualifier(qualifiers.keySet())) {
- qualifiers.put(Default.class, Collections.emptyMap());
- }
- return new Qualifiers(Collections.unmodifiableMap(qualifiers));
- }
-
- private static Map<String, Object> getQualifierAttributes(final Annotation annotation, final String defaultName) {
- final Class<? extends Annotation> annotationType = annotation.annotationType();
- final Method[] elements = annotationType.getDeclaredMethods();
- final Map<String, Object> attributes = new HashMap<>(elements.length);
- for (final Method element : elements) {
- if (!element.isAnnotationPresent(Ignore.class)) {
- final String name = element.getName();
- final Object value = getAnnotationElementValue(annotation, element);
- if (annotationType == Named.class && name.equals("value") && value.toString().isEmpty()) {
- attributes.put("value", defaultName);
- } else {
- attributes.put(name, value);
- }
- }
- }
- return Collections.unmodifiableMap(attributes);
- }
-
- private static Object getAnnotationElementValue(final Annotation annotation, final Method element) {
- try {
- return element.invoke(annotation);
- } catch (final IllegalAccessException e) {
- throw new InitializationException("Cannot access element " + element.getName() + " of annotation " + annotation, e);
- } catch (final InvocationTargetException e) {
- throw new InitializationException("Cannot access element " + element.getName() + " of annotation " + annotation,
- e.getCause());
- }
- }
-
- private static boolean needsDefaultQualifier(final Collection<Class<? extends Annotation>> qualifierTypes) {
- if (qualifierTypes.contains(Default.class)) {
- return false;
- }
- for (final Class<? extends Annotation> qualifierType : qualifierTypes) {
- if (qualifierType != Named.class) {
- return false;
- }
- }
- return true;
- }
+ int hashCode();
}
diff --git a/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/spi/model/QualifiersTest.java b/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/spi/model/QualifiersTest.java
deleted file mode 100644
index 739ec6b..0000000
--- a/log4j-plugins/src/test/java/org/apache/logging/log4j/plugins/spi/model/QualifiersTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache license, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the license for the specific language governing permissions and
- * limitations under the license.
- */
-
-package org.apache.logging.log4j.plugins.spi.model;
-
-import org.apache.logging.log4j.plugins.api.AliasFor;
-import org.apache.logging.log4j.plugins.api.Named;
-import org.apache.logging.log4j.plugins.api.QualifierType;
-import org.junit.Test;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
-import java.util.Collections;
-
-import static org.junit.Assert.*;
-
-public class QualifiersTest {
- @Test
- public void emptyQualifiersShouldContainDefaultQualifier() {
- final Qualifiers qualifiers = Qualifiers.fromQualifierAnnotations(Collections.emptyList());
- assertTrue(qualifiers.hasDefaultQualifier());
- }
-
- @Test
- public void qualifiersWithNamedOnlyShouldContainDefaultQualifier() {
- @Named
- class Foo {
- }
- final Qualifiers qualifiers = Qualifiers.fromQualifierAnnotations(Arrays.asList(Foo.class.getAnnotations()));
- assertTrue(qualifiers.hasDefaultQualifier());
- }
-
- @Retention(RetentionPolicy.RUNTIME)
- @QualifierType
- public @interface Bar {
- }
-
- @Test
- public void qualifiersWithNonDefaultQualifiersShouldNotContainDefaultQualifier() {
- @Bar
- class Foo {
- }
- final Qualifiers qualifiers = Qualifiers.fromQualifierAnnotations(Arrays.asList(Foo.class.getAnnotations()));
- assertFalse(qualifiers.hasDefaultQualifier());
- }
-
- @Retention(RetentionPolicy.RUNTIME)
- @AliasFor(Bar.class)
- public @interface BarAlias {
- }
-
- @Test
- public void qualifiersShouldAccountForAliases() {
- @Bar
- class Foo {
- }
- @BarAlias
- class FooAlias {
- }
- final Qualifiers foo = Qualifiers.fromQualifierAnnotations(Arrays.asList(Foo.class.getAnnotations()));
- final Qualifiers fooAlias = Qualifiers.fromQualifierAnnotations(Arrays.asList(FooAlias.class.getAnnotations()));
- assertEquals(foo, fooAlias);
- }
-}
\ No newline at end of file