001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.xbean.finder;
018
019 import java.lang.annotation.Annotation;
020 import java.lang.annotation.Documented;
021 import java.lang.annotation.Retention;
022 import java.lang.annotation.Target;
023 import java.lang.reflect.AnnotatedElement;
024 import java.lang.reflect.Constructor;
025 import java.lang.reflect.Method;
026 import java.util.ArrayList;
027 import java.util.Collection;
028 import java.util.Collections;
029 import java.util.HashMap;
030 import java.util.List;
031 import java.util.Map;
032
033 import static java.util.Arrays.asList;
034
035 /**
036 * @version $Rev$ $Date$
037 */
038 public abstract class MetaAnnotatedObject<T> implements MetaAnnotated<T> {
039 protected final Map<Class<? extends Annotation>, MetaAnnotation<?>> annotations = new HashMap<Class<? extends Annotation>, MetaAnnotation<?>>();
040 protected final T target;
041
042 MetaAnnotatedObject(T target, Map<Class<? extends Annotation>, MetaAnnotation<?>> annotations) {
043 this.target = target;
044 this.annotations.putAll(annotations);
045 }
046
047 @Override
048 public T get() {
049 return target;
050 }
051
052 @Override
053 public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
054 return annotations.containsKey(annotationClass);
055 }
056
057 @Override
058 public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
059 MetaAnnotation<T> annotation = (MetaAnnotation<T>) annotations.get(annotationClass);
060 return (annotation == null) ? null : annotation.get();
061 }
062
063 @Override
064 public Annotation[] getAnnotations() {
065 Annotation[] annotations = new Annotation[this.annotations.size()];
066
067 int i = 0;
068 for (MetaAnnotation annotation : this.annotations.values()) {
069 annotations[i++] = annotation.get();
070 }
071
072 return annotations;
073 }
074
075 @Override
076 public Collection<MetaAnnotation<?>> getMetaAnnotations() {
077 return Collections.unmodifiableCollection(annotations.values());
078 }
079
080 @Override
081 public boolean equals(Object obj) {
082 return get().equals(obj);
083 }
084
085 @Override
086 public int hashCode() {
087 return get().hashCode();
088 }
089
090 @Override
091 public String toString() {
092 return get().toString();
093 }
094
095
096 private static void unroll(Class<? extends Annotation> clazz, int depth, Map<Class<? extends Annotation>, MetaAnnotation<?>> found) {
097 if (!isMetaAnnotation(clazz)) return;
098
099 for (Annotation annotation : getDeclaredMetaAnnotations(clazz)) {
100 Class<? extends Annotation> type = annotation.annotationType();
101
102 MetaAnnotation existing = found.get(type);
103
104 if (existing != null) {
105
106 if (existing.getDepth() > depth) {
107
108 // OVERWRITE
109
110 found.put(type, new MetaAnnotation(annotation, depth, clazz));
111
112 unroll(type, depth + 1, found);
113
114 } else if (existing.getDepth() < depth) {
115
116 // IGNORE
117
118 // ignore, what we have already is higher priority
119
120 } else {
121
122 // CONFLICT
123
124 // They are the same depth and therefore conflicting
125 existing.getConflicts().add(new MetaAnnotation(annotation, depth, clazz));
126
127 }
128
129 } else {
130
131 // NEW
132
133 found.put(type, new MetaAnnotation(annotation, depth, clazz));
134
135 unroll(type, depth + 1, found);
136
137 }
138 }
139 }
140
141 private static Collection<Annotation> getDeclaredMetaAnnotations(Class<? extends Annotation> clazz) {
142
143 Map<Class, Annotation> map = new HashMap<Class, Annotation>();
144
145 // pull in the annotations declared on this annotation
146
147 for (Annotation annotation : clazz.getDeclaredAnnotations()) {
148 map.put(annotation.annotationType(), annotation);
149 }
150
151 List<Annotation[]> groups = new ArrayList<Annotation[]>();
152
153 Class<? extends Annotation> metatype = getMetatype(clazz);
154 if (metatype != null) {
155 try {
156 Class<?> def = clazz.getClassLoader().loadClass(clazz.getName() + "$$");
157
158 List<AnnotatedElement> elements = new ArrayList<AnnotatedElement>();
159
160 elements.addAll(asList(def.getDeclaredFields()));
161 elements.addAll(asList(def.getDeclaredConstructors()));
162 elements.addAll(asList(def.getDeclaredMethods()));
163
164 for (Method method : def.getDeclaredMethods()) {
165 for (Annotation[] array : method.getParameterAnnotations()) {
166 groups.add(array);
167 }
168 }
169
170 for (Constructor constructor : def.getDeclaredConstructors()) {
171 for (Annotation[] array : constructor.getParameterAnnotations()) {
172 groups.add(array);
173 }
174 }
175
176 for (AnnotatedElement element : elements) {
177 groups.add(element.getDeclaredAnnotations());
178 }
179
180 for (Annotation[] annotations : groups) {
181 if (contains(annotations, clazz)) {
182 for (Annotation annotation : annotations) {
183 map.put(annotation.annotationType(), annotation);
184 }
185 }
186 }
187 } catch (ClassNotFoundException e) {
188 // inner class is optional
189 }
190 }
191
192 map.remove(Target.class);
193 map.remove(Retention.class);
194 map.remove(Documented.class);
195 map.remove(metatype);
196 map.remove(clazz);
197
198 return map.values();
199 }
200
201 private static boolean contains(Annotation[] annotations, Class<? extends Annotation> clazz) {
202 for (Annotation annotation : annotations) {
203 if (clazz.equals(annotation.annotationType())) return true;
204 }
205 return false;
206 }
207
208 private static Class<? extends Annotation> getMetatype(Class<? extends Annotation> clazz) {
209 for (Annotation annotation : clazz.getDeclaredAnnotations()) {
210 Class<? extends Annotation> type = annotation.annotationType();
211
212 if (isMetatypeAnnotation(type)) return type;
213 }
214
215 return null;
216 }
217
218 private static boolean isMetaAnnotation(Class<? extends Annotation> clazz) {
219 for (Annotation annotation : clazz.getDeclaredAnnotations()) {
220 if (isMetatypeAnnotation(annotation.annotationType())) return true;
221 }
222
223 return false;
224 }
225
226 private static boolean isMetatypeAnnotation(Class<? extends Annotation> type) {
227 return type.getSimpleName().equals("Metatype") && type.isAnnotationPresent(type);
228 }
229
230 protected static Map<Class<? extends Annotation>, MetaAnnotation<?>> unroll(Class<?> declaringClass, AnnotatedElement element) {
231
232 Map<Class<? extends Annotation>, MetaAnnotation<?>> map = new HashMap<Class<? extends Annotation>, MetaAnnotation<?>>();
233
234 for (Annotation annotation : element.getDeclaredAnnotations()) {
235
236 map.put(annotation.annotationType(), new MetaAnnotation(annotation, 0, declaringClass));
237
238 unroll(annotation.annotationType(), 1, map);
239
240 }
241
242 return map;
243 }
244 }