001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.weaver.model; 020 021import java.lang.annotation.Annotation; 022import java.lang.annotation.RetentionPolicy; 023import java.lang.reflect.AnnotatedElement; 024import java.util.Arrays; 025import java.util.LinkedHashSet; 026import java.util.Set; 027 028import org.apache.commons.lang3.Validate; 029import org.apache.commons.lang3.builder.HashCodeBuilder; 030 031/** 032 * {@link Weavable} extends {@link AnnotatedElement} to include 033 * {@link RetentionPolicy#CLASS} annotations. 034 * 035 * @param <SELF> own type 036 * @param <TARGET> target type 037 */ 038public abstract class Weavable<SELF extends Weavable<SELF, TARGET>, TARGET> implements Comparable<SELF>, 039 AnnotatedElement { 040 private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0]; 041 042 private final TARGET target; 043 private Set<Annotation> annotations; 044 045 /** 046 * Create a new {@link Weavable} instance. 047 * @param target {@code TARGET} 048 */ 049 protected Weavable(final TARGET target) { 050 this.target = target; 051 if (target instanceof AnnotatedElement) { 052 addAnnotations(((AnnotatedElement) target).getAnnotations()); 053 } 054 } 055 056 /** 057 * Add one or more annotations. 058 * @param toAdd {@link Annotation}[] 059 * @return whether any change was made 060 */ 061 public final boolean addAnnotations(final Annotation... toAdd) { 062 Validate.noNullElements(toAdd); 063 return addAnnotations(Arrays.asList(toAdd)); 064 } 065 066 /** 067 * Add annotations from an {@link Iterable}. 068 * @param toAdd {@link Iterable} of {@link Annotation} 069 * @return whether any change was made 070 */ 071 public final boolean addAnnotations(final Iterable<Annotation> toAdd) { 072 if (toAdd == null) { 073 return false; 074 } 075 synchronized (this) { 076 if (annotations == null) { 077 annotations = new LinkedHashSet<Annotation>(); 078 } 079 boolean result = false; 080 for (final Annotation ann : toAdd) { 081 if (ann == null) { 082 continue; 083 } 084 result = annotations.add(ann) || result; 085 } 086 return result; 087 } 088 } 089 090 /** 091 * Get the target of this {@link Weavable}. 092 * @return {@code TARGET} 093 */ 094 public TARGET getTarget() { 095 return target; 096 } 097 098 /** 099 * Get all {@link Annotation}s associated with this element. 100 * @return {@link Annotation}[] 101 */ 102 @Override 103 public final synchronized Annotation[] getAnnotations() { 104 if (annotations == null) { 105 return EMPTY_ANNOTATION_ARRAY; //NOPMD - no problem sharing zero-length array 106 } 107 return annotations.toArray(new Annotation[annotations.size()]); 108 } 109 110 /** 111 * Get any instance of {@code annotationClass} attached to {@link #getTarget()}. 112 * @param annotationClass {@link Class} annotation type 113 * @param <T> annotation type 114 * @return {@code T} instance if available, else {@code null} 115 */ 116 @Override 117 public synchronized <T extends Annotation> T getAnnotation(final Class<T> annotationClass) { 118 if (annotations == null) { 119 return null; 120 } 121 for (final Annotation prospect : annotations) { 122 if (annotationClass.equals(prospect.annotationType())) { 123 @SuppressWarnings("unchecked") 124 final T result = (T) prospect; 125 return result; 126 } 127 } 128 return null; 129 } 130 131 /** 132 * Overridden to return {@link #getAnnotations()}. 133 * @return {@link Annotation}[] 134 */ 135 @Override 136 public final Annotation[] getDeclaredAnnotations() { 137 return getAnnotations(); 138 } 139 140 /** 141 * Learn whether an annotation of type {@code annotationClass} is present. 142 * @param annotationClass to find 143 * @return {@code boolean} 144 */ 145 @Override 146 public boolean isAnnotationPresent(final Class<? extends Annotation> annotationClass) { 147 return getAnnotation(annotationClass) != null; 148 } 149 150 /** 151 * Return a {@link String} representation of this {@link Weavable}. 152 * @return {@link String} 153 */ 154 @Override 155 public String toString() { 156 return "Weavable " + getTarget().toString(); 157 } 158 159 /** 160 * {@inheritDoc} 161 */ 162 @Override 163 public boolean equals(final Object obj) { 164 if (obj == this) { 165 return true; 166 } 167 if (!getClass().isInstance(obj)) { 168 return false; 169 } 170 return getTarget().equals(((Weavable<?, ?>) obj).getTarget()); 171 } 172 173 /** 174 * {@inheritDoc} 175 */ 176 @Override 177 public int hashCode() { 178 return new HashCodeBuilder().append(getTarget()).toHashCode(); 179 } 180}