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.utils;
020
021import java.lang.annotation.Annotation;
022import java.lang.reflect.InvocationHandler;
023import java.lang.reflect.Method;
024import java.lang.reflect.Proxy;
025import java.util.Arrays;
026import java.util.Map;
027
028import org.apache.commons.lang3.AnnotationUtils;
029import org.apache.commons.lang3.Validate;
030
031/**
032 * Provide annotation-related utility methods.
033 */
034public final class Annotations {
035    private Annotations() {
036    }
037
038    /**
039     * Create an annotation instance.
040     * @param annotationType type
041     * @param elements values
042     * @param <A> generic annotation type
043     * @return {@code A}
044     */
045    public static <A extends Annotation> A instanceOf(final Class<A> annotationType, final Map<String, ?> elements) {
046        final ClassLoader proxyClassLoader = Validate.notNull(annotationType, "annotationType").getClassLoader();
047        final InvocationHandler invocationHandler = new InvocationHandler() {
048
049            @Override
050            @SuppressWarnings("PMD.UseVarargs") // overridden method
051            public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
052                if (method.getDeclaringClass().equals(annotationType)) {
053                    if (elements.containsKey(method.getName())) {
054                        return elements.get(method.getName());
055                    }
056                    return method.getDefaultValue();
057                }
058                if ("annotationType".equals(method.getName()) && method.getParameterTypes().length == 0) {
059                    return annotationType;
060                }
061                if ("equals".equals(method.getName())
062                    && Arrays.equals(method.getParameterTypes(), new Class[] { Object.class })) {
063                    return AnnotationUtils.equals((Annotation) proxy, (Annotation) args[0]);
064                }
065                if ("hashCode".equals(method.getName()) && method.getParameterTypes().length == 0) {
066                    return AnnotationUtils.hashCode((Annotation) proxy);
067                }
068                if ("toString".equals(method.getName()) && method.getParameterTypes().length == 0) {
069                    return AnnotationUtils.toString((Annotation) proxy);
070                }
071                throw new UnsupportedOperationException();
072            }
073        };
074        @SuppressWarnings("unchecked")
075        final A result =
076            (A) Proxy.newProxyInstance(proxyClassLoader, new Class[] { annotationType }, invocationHandler);
077        return result;
078    }
079}