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 */
019
020package org.apache.isis.core.metamodel.facets;
021
022import java.lang.annotation.Annotation;
023import java.lang.reflect.Method;
024
025public final class Annotations  {
026    
027    private Annotations() {}
028
029    /**
030     * For convenience of the several annotations that apply only to
031     * {@link String}s.
032     */
033    public static boolean isString(final Class<?> cls) {
034        return cls.equals(String.class);
035    }
036
037    @SuppressWarnings("unchecked")
038    public static <T extends Annotation> T getDeclaredAnnotation(Class<?> cls, Class<T> annotationClass) {
039        final Annotation[] declaredAnnotations = cls.getDeclaredAnnotations();
040        if(declaredAnnotations == null) {
041            return null;
042        }
043        for (Annotation annotation : declaredAnnotations) {
044            if(annotationClass.isAssignableFrom(annotation.getClass())) {
045                return (T) annotation;
046            }
047        }
048        return null;
049    }
050
051    /**
052     * Searches for annotation on provided class, and if not found for the
053     * superclass.
054     * 
055     * <p>
056     * Added to allow bytecode-mangling libraries such as CGLIB to be supported.
057     */
058    public static <T extends Annotation> T getAnnotation(final Class<?> cls, final Class<T> annotationClass) {
059        if (cls == null) {
060            return null;
061        }
062        final T annotation = cls.getAnnotation(annotationClass);
063        if (annotation != null) {
064            return annotation;
065        }
066
067        // search superclasses
068        final Class<?> superclass = cls.getSuperclass();
069        if (superclass != null) {
070            try {
071                final T annotationFromSuperclass = getAnnotation(superclass, annotationClass);
072                if (annotationFromSuperclass != null) {
073                    return annotationFromSuperclass;
074                }
075            } catch (final SecurityException e) {
076                // fall through
077            }
078        }
079
080        // search implemented interfaces
081        final Class<?>[] interfaces = cls.getInterfaces();
082        for (final Class<?> iface : interfaces) {
083            final T annotationFromInterface = getAnnotation(iface, annotationClass);
084            if (annotationFromInterface != null) {
085                return annotationFromInterface;
086            }
087        }
088        return null;
089    }
090
091    /**
092     * Searches for annotation on provided method, and if not found for any
093     * inherited methods up from the superclass.
094     * 
095     * <p>
096     * Added to allow bytecode-mangling libraries such as CGLIB to be supported.
097     */
098    public static <T extends Annotation> T getAnnotation(final Method method, final Class<T> annotationClass) {
099        if (method == null) {
100            return null;
101        }
102        final T annotation = method.getAnnotation(annotationClass);
103        if (annotation != null) {
104            return annotation;
105        }
106
107        final Class<?> methodDeclaringClass = method.getDeclaringClass();
108
109        // search superclasses
110        final Class<?> superclass = methodDeclaringClass.getSuperclass();
111        if (superclass != null) {
112            try {
113                final Method parentClassMethod = superclass.getMethod(method.getName(), method.getParameterTypes());
114                return getAnnotation(parentClassMethod, annotationClass);
115            } catch (final SecurityException e) {
116                // fall through
117            } catch (final NoSuchMethodException e) {
118                // fall through
119            }
120        }
121
122        // search implemented interfaces
123        final Class<?>[] interfaces = methodDeclaringClass.getInterfaces();
124        for (final Class<?> iface : interfaces) {
125            try {
126                final Method ifaceMethod = iface.getMethod(method.getName(), method.getParameterTypes());
127                return getAnnotation(ifaceMethod, annotationClass);
128            } catch (final SecurityException e) {
129                // fall through
130            } catch (final NoSuchMethodException e) {
131                // fall through
132            }
133        }
134        return null;
135    }
136
137    /**
138     * Searches for annotation on provided method, and if not found for any
139     * inherited methods up from the superclass.
140     * 
141     * <p>
142     * Added to allow bytecode-mangling libraries such as CGLIB to be supported.
143     */
144    public static boolean isAnnotationPresent(final Method method, final Class<? extends Annotation> annotationClass) {
145        if (method == null) {
146            return false;
147        }
148        final boolean present = method.isAnnotationPresent(annotationClass);
149        if (present) {
150            return true;
151        }
152
153        final Class<?> methodDeclaringClass = method.getDeclaringClass();
154
155        // search superclasses
156        final Class<?> superclass = methodDeclaringClass.getSuperclass();
157        if (superclass != null) {
158            try {
159                final Method parentClassMethod = superclass.getMethod(method.getName(), method.getParameterTypes());
160                return isAnnotationPresent(parentClassMethod, annotationClass);
161            } catch (final SecurityException e) {
162                // fall through
163            } catch (final NoSuchMethodException e) {
164                // fall through
165            }
166        }
167
168        // search implemented interfaces
169        final Class<?>[] interfaces = methodDeclaringClass.getInterfaces();
170        for (final Class<?> iface : interfaces) {
171            try {
172                final Method ifaceMethod = iface.getMethod(method.getName(), method.getParameterTypes());
173                return isAnnotationPresent(ifaceMethod, annotationClass);
174            } catch (final SecurityException e) {
175                // fall through
176            } catch (final NoSuchMethodException e) {
177                // fall through
178            }
179        }
180        return false;
181    }
182
183    /**
184     * Searches for parameter annotations on provided method, and if not found
185     * for any inherited methods up from the superclass.
186     * 
187     * <p>
188     * Added to allow bytecode-mangling libraries such as CGLIB to be supported.
189     */
190    public static Annotation[][] getParameterAnnotations(final Method method) {
191        if (method == null) {
192            return new Annotation[0][0];
193        }
194        final Annotation[][] allParamAnnotations = method.getParameterAnnotations();
195
196        boolean foundAnnotationsForAnyParameter = false;
197        for (final Annotation[] singleParamAnnotations : allParamAnnotations) {
198            if (singleParamAnnotations.length > 0) {
199                foundAnnotationsForAnyParameter = true;
200                break;
201            }
202        }
203        if (foundAnnotationsForAnyParameter) {
204            return allParamAnnotations;
205        }
206
207        final Class<?> methodDeclaringClass = method.getDeclaringClass();
208
209        // search superclasses
210        final Class<?> superclass = methodDeclaringClass.getSuperclass();
211        if (superclass != null) {
212            try {
213                final Method parentClassMethod = superclass.getMethod(method.getName(), method.getParameterTypes());
214                return getParameterAnnotations(parentClassMethod);
215            } catch (final SecurityException e) {
216                // fall through
217            } catch (final NoSuchMethodException e) {
218                // fall through
219            }
220        }
221
222        // search implemented interfaces
223        final Class<?>[] interfaces = methodDeclaringClass.getInterfaces();
224        for (final Class<?> iface : interfaces) {
225            try {
226                final Method ifaceMethod = iface.getMethod(method.getName(), method.getParameterTypes());
227                return getParameterAnnotations(ifaceMethod);
228            } catch (final SecurityException e) {
229                // fall through
230            } catch (final NoSuchMethodException e) {
231                // fall through
232            }
233        }
234
235        return noParamAnnotationsFor(method);
236    }
237
238    private static Annotation[][] noParamAnnotationsFor(final Method method) {
239        return new Annotation[method.getParameterTypes().length][0];
240    }
241
242}