blob: 8e06ebc4c3cc236bf25228c8116c90df83cea723 [file] [log] [blame]
/*
* 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.commons.weaver.model;
import java.lang.annotation.Annotation;
import java.lang.annotation.RetentionPolicy; //NOPMD used in javadoc
import java.lang.reflect.AnnotatedElement;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.HashCodeBuilder;
/**
* {@link Weavable} extends {@link AnnotatedElement} to include
* {@link RetentionPolicy#CLASS} annotations.
*
* @param <SELF> own type
* @param <TARGET> target type
*/
public abstract class Weavable<SELF extends Weavable<SELF, TARGET>, TARGET> implements Comparable<SELF>,
AnnotatedElement {
private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
private final TARGET target;
private Set<Annotation> annotations;
/**
* Create a new {@link Weavable} instance.
* @param target {@code TARGET}
*/
protected Weavable(final TARGET target) {
this.target = target;
if (target instanceof AnnotatedElement) {
addAnnotations(((AnnotatedElement) target).getAnnotations());
}
}
/**
* Add one or more annotations.
* @param toAdd {@link Annotation}[]
* @return whether any change was made
*/
public final boolean addAnnotations(final Annotation... toAdd) {
Validate.noNullElements(toAdd);
return addAnnotations(Arrays.asList(toAdd));
}
/**
* Add annotations from an {@link Iterable}.
* @param toAdd {@link Iterable} of {@link Annotation}
* @return whether any change was made
*/
public final boolean addAnnotations(final Iterable<Annotation> toAdd) {
if (toAdd == null) {
return false;
}
synchronized (this) {
if (annotations == null) {
annotations = new LinkedHashSet<>();
}
boolean result = false;
for (final Annotation ann : toAdd) {
if (ann == null) {
continue;
}
result = annotations.add(ann) || result;
}
return result;
}
}
/**
* Get the target of this {@link Weavable}.
* @return {@code TARGET}
*/
public TARGET getTarget() {
return target;
}
/**
* Get all {@link Annotation}s associated with this element.
* @return {@link Annotation}[]
*/
@Override
public final synchronized Annotation[] getAnnotations() {
if (annotations == null) {
return EMPTY_ANNOTATION_ARRAY; //NOPMD - no problem sharing zero-length array
}
return annotations.toArray(new Annotation[0]);
}
/**
* Get any instance of {@code annotationClass} attached to {@link #getTarget()}.
* @param annotationClass {@link Class} annotation type
* @param <T> annotation type
* @return {@code T} instance if available, else {@code null}
*/
@Override
public synchronized <T extends Annotation> T getAnnotation(final Class<T> annotationClass) {
if (annotations == null) {
return null;
}
for (final Annotation prospect : annotations) {
if (annotationClass.equals(prospect.annotationType())) {
@SuppressWarnings("unchecked")
final T result = (T) prospect;
return result;
}
}
return null;
}
/**
* Overridden to return {@link #getAnnotations()}.
* @return {@link Annotation}[]
*/
@Override
public final Annotation[] getDeclaredAnnotations() {
return getAnnotations();
}
/**
* Learn whether an annotation of type {@code annotationClass} is present.
* @param annotationClass to find
* @return {@code boolean}
*/
@Override
public boolean isAnnotationPresent(final Class<? extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
/**
* Return a {@link String} representation of this {@link Weavable}.
* @return {@link String}
*/
@Override
public String toString() {
return "Weavable " + getTarget().toString();
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (!getClass().isInstance(obj)) {
return false;
}
return getTarget().equals(((Weavable<?, ?>) obj).getTarget());
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return new HashCodeBuilder().append(getTarget()).toHashCode();
}
}