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    }