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 */
017package org.apache.xbean.finder;
018
019import java.lang.annotation.Annotation;
020import java.lang.annotation.Documented;
021import java.lang.annotation.ElementType;
022import java.lang.annotation.Retention;
023import java.lang.annotation.Target;
024import java.lang.reflect.AnnotatedElement;
025import java.lang.reflect.Constructor;
026import java.lang.reflect.Method;
027import java.util.ArrayList;
028import java.util.Collection;
029import java.util.Collections;
030import java.util.HashMap;
031import java.util.List;
032import java.util.Map;
033
034import static java.util.Arrays.asList;
035
036/**
037* @version $Rev$ $Date$
038*/
039public class MetaAnnotatedElement<T extends AnnotatedElement> implements  AnnotatedElement, MetaAnnotated<T> {
040    protected final Map<Class<? extends Annotation>, MetaAnnotation<?>> annotations = new HashMap<Class<? extends Annotation>, MetaAnnotation<?>>();
041    protected final T target;
042
043    public MetaAnnotatedElement(T element) {
044        this(element, unroll(element));
045    }
046
047    MetaAnnotatedElement(T target, Map<Class<? extends Annotation>, MetaAnnotation<?>> annotations) {
048        this.target = target;
049        this.annotations.putAll(annotations);
050    }
051
052    public T get() {
053        return target;
054    }
055
056    public Annotation[] getDeclaredAnnotations() {
057        return target.getDeclaredAnnotations();
058    }
059
060    public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
061        return annotations.containsKey(annotationClass);
062    }
063
064    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
065        MetaAnnotation<T> annotation = (MetaAnnotation<T>) annotations.get(annotationClass);
066        return (annotation == null) ? null : annotation.get();
067    }
068
069    public Annotation[] getAnnotations() {
070        Annotation[] annotations = new Annotation[this.annotations.size()];
071
072        int i = 0;
073        for (MetaAnnotation annotation : this.annotations.values()) {
074            annotations[i++] = annotation.get();
075        }
076
077        return annotations;
078    }
079
080    public Collection<MetaAnnotation<?>> getMetaAnnotations() {
081        return Collections.unmodifiableCollection(annotations.values());
082    }
083
084    @Override
085    public boolean equals(Object obj) {
086        return get().equals(obj);
087    }
088
089    @Override
090    public int hashCode() {
091        return get().hashCode();
092    }
093
094    @Override
095    public String toString() {
096        return get().toString();
097    }
098
099
100    private static void unroll(Class<? extends Annotation> clazz, int depth, Map<Class<? extends Annotation>, MetaAnnotation<?>> found) {
101        if (!isMetaAnnotation(clazz)) return;
102
103        for (Annotation annotation : getDeclaredMetaAnnotations(clazz)) {
104            Class<? extends Annotation> type = annotation.annotationType();
105
106            MetaAnnotation existing = found.get(type);
107
108            if (existing != null) {
109
110                if (existing.getDepth() > depth) {
111
112                    // OVERWRITE
113
114                    found.put(type, new MetaAnnotation(annotation, depth));
115
116                    unroll(type, depth + 1, found);
117
118                } else if (existing.getDepth() < depth) {
119
120                    // IGNORE
121
122                    // ignore, what we have already is higher priority
123
124                } else {
125
126                    // CONFLICT
127
128                    // They are the same depth and therefore conflicting
129                    existing.getConflicts().add(new MetaAnnotation(annotation, depth));
130
131                }
132
133            } else {
134
135                // NEW
136
137                found.put(type, new MetaAnnotation(annotation, depth));
138
139                unroll(type, depth + 1, found);
140
141            }
142        }
143    }
144
145    private static Collection<Annotation> getDeclaredMetaAnnotations(Class<? extends Annotation> clazz) {
146
147        Map<Class, Annotation> map = new HashMap<Class, Annotation>();
148
149        // pull in the annotations declared on this annotation
150
151        for (Annotation annotation : clazz.getDeclaredAnnotations()) {
152            map.put(annotation.annotationType(), annotation);
153        }
154
155        List<Annotation[]> groups = new ArrayList<Annotation[]>();
156
157        Class<? extends Annotation> metatype = getMetatype(clazz);
158        if (metatype != null) {
159            try {
160                Class<?> def = clazz.getClassLoader().loadClass(clazz.getName() + "$$");
161
162                List<AnnotatedElement> elements = new ArrayList<AnnotatedElement>();
163
164                elements.addAll(asList(def.getDeclaredFields()));
165                elements.addAll(asList(def.getDeclaredConstructors()));
166                elements.addAll(asList(def.getDeclaredMethods()));
167
168                for (Method method : def.getDeclaredMethods()) {
169                    for (Annotation[] array : method.getParameterAnnotations()) {
170                        groups.add(array);
171                    }
172                }
173
174                for (Constructor constructor : def.getDeclaredConstructors()) {
175                    for (Annotation[] array : constructor.getParameterAnnotations()) {
176                        groups.add(array);
177                    }
178                }
179
180                for (AnnotatedElement element : elements) {
181                    groups.add(element.getDeclaredAnnotations());
182                }
183
184                for (Annotation[] annotations : groups) {
185                    if (contains(annotations, clazz)) {
186                        for (Annotation annotation : annotations) {
187                            map.put(annotation.annotationType(), annotation);
188                        }
189                    }
190                }
191            } catch (ClassNotFoundException e) {
192                // inner class is optional
193            }
194        }
195
196        map.remove(Target.class);
197        map.remove(Retention.class);
198        map.remove(Documented.class);
199        map.remove(metatype);
200        map.remove(clazz);
201
202        return map.values();
203    }
204
205    private static boolean contains(Annotation[] annotations, Class<? extends Annotation> clazz) {
206        for (Annotation annotation : annotations) {
207            if (clazz.equals(annotation.annotationType())) return true;
208        }
209        return false;
210    }
211
212    private static Class<? extends Annotation> getMetatype(Class<? extends Annotation> clazz) {
213        for (Annotation annotation : clazz.getDeclaredAnnotations()) {
214            Class<? extends Annotation> type = annotation.annotationType();
215
216            if (isMetatypeAnnotation(type)) return type;
217        }
218
219        return null;
220    }
221
222    private static boolean isMetaAnnotation(Class<? extends Annotation> clazz) {
223        for (Annotation annotation : clazz.getDeclaredAnnotations()) {
224            if (isMetatypeAnnotation(annotation.annotationType())) return true;
225        }
226
227        return false;
228    }
229
230    private static boolean isMetatypeAnnotation(Class<? extends Annotation> type) {
231        if (type.getName().equals("javax.annotation.Metatype")) return true;
232        if (isSelfAnnotated(type, "Metatype")) return true;
233
234        for (Annotation annotation : type.getAnnotations()) {
235            if (type.getName().equals("javax.annotation.Metaroot")) return true;
236            if (isSelfAnnotated(annotation.annotationType(), "Metaroot")) return true;
237        }
238
239        return false;
240    }
241
242    private static boolean isSelfAnnotated(Class<? extends Annotation> type, String name) {
243        return type.isAnnotationPresent(type) && type.getSimpleName().equals(name) && validTarget(type);
244    }
245
246    private static boolean validTarget(Class<? extends Annotation> type) {
247        final Target target = type.getAnnotation(Target.class);
248
249        if (target == null) return false;
250
251        final ElementType[] targets = target.value();
252
253        return targets.length == 1 && targets[0] == ElementType.ANNOTATION_TYPE;
254    }
255
256    protected static Map<Class<? extends Annotation>, MetaAnnotation<?>> unroll(AnnotatedElement element) {
257        return unroll(element.getDeclaredAnnotations());
258    }
259
260    protected static Map<Class<? extends Annotation>, MetaAnnotation<?>> unroll(Annotation[] annotations) {
261        final Map<Class<? extends Annotation>, MetaAnnotation<?>> map = new HashMap<Class<? extends Annotation>, MetaAnnotation<?>>();
262
263        for (Annotation annotation : annotations) {
264
265            map.put(annotation.annotationType(), new MetaAnnotation(annotation, 0));
266
267            unroll(annotation.annotationType(), 1, map);
268
269        }
270
271        return map;
272    }
273
274    protected Annotation[][] unrollParameters(Annotation[][] parameterAnnotations) {
275        final Annotation[][] unrolledParameters = new Annotation[parameterAnnotations.length][];
276
277        int i = 0;
278        for (Annotation[] annotations : parameterAnnotations) {
279            final Map<Class<? extends Annotation>, MetaAnnotation<?>> map = unroll(annotations);
280
281            int j = 0;
282
283            final Annotation[] unrolled = new Annotation[map.size()];
284            for (MetaAnnotation<?> metaAnnotation : map.values()) {
285                unrolled[j++] = metaAnnotation.get();
286            }
287
288            unrolledParameters[i++] = unrolled;
289        }
290        return unrolledParameters;
291    }
292}