001/* $Id: MethodHandler.java 992091 2010-09-02 19:58:12Z simonetripodi $
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one or more
004 * contributor license agreements.  See the NOTICE file distributed with
005 * this work for additional information regarding copyright ownership.
006 * The ASF licenses this file to You under the Apache License, Version 2.0
007 * (the "License"); you may not use this file except in compliance with
008 * 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, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.commons.digester.annotations.handlers;
019
020import java.lang.annotation.Annotation;
021import java.lang.reflect.Method;
022import java.lang.reflect.Modifier;
023
024import org.apache.commons.digester.Rule;
025import org.apache.commons.digester.annotations.AnnotationRuleProvider;
026import org.apache.commons.digester.annotations.CreationRule;
027import org.apache.commons.digester.annotations.DigesterLoaderHandler;
028import org.apache.commons.digester.annotations.DigesterLoadingException;
029import org.apache.commons.digester.annotations.DigesterRule;
030import org.apache.commons.digester.annotations.DigesterRuleList;
031import org.apache.commons.digester.annotations.FromAnnotationsRuleSet;
032import org.apache.commons.digester.annotations.utils.AnnotationUtils;
033
034/**
035 * Handler that takes care to create the
036 * {@link org.apache.commons.digester.annotations.providers.SetNextRuleProvider}
037 * and
038 * {@link org.apache.commons.digester.annotations.providers.SetRootRuleProvider}.
039 *
040 * @since 2.1
041 */
042public final class MethodHandler implements DigesterLoaderHandler<Annotation, Method> {
043
044    /**
045     * The default args size the method has to have in order to be analyzed.
046     */
047    private static final int SUPPORTED_ARGS = 1;
048
049    /**
050     * {@inheritDoc}
051     */
052    public void handle(Annotation annotation, Method element, FromAnnotationsRuleSet ruleSet) {
053        if (SUPPORTED_ARGS != element.getParameterTypes().length) {
054            DigesterRule rule = annotation.annotationType().getAnnotation(DigesterRule.class);
055
056            throw new DigesterLoadingException("Methods annotated with digester annotation rule @"
057                    + rule.reflectsRule().getName()
058                    + " must have just one argument");
059        }
060
061        Object explicitTypesObject = AnnotationUtils.getAnnotationValue(annotation);
062        if (explicitTypesObject == null
063                || !explicitTypesObject.getClass().isArray()
064                || Class.class != explicitTypesObject.getClass().getComponentType()) {
065            throw new DigesterLoadingException("Impossible to apply this handler, @"
066                    + annotation.getClass().getName()
067                    + ".value() has to be of type 'Class<?>[]'");
068        }
069
070        Class<?>[] explicitTypes = (Class<?>[]) explicitTypesObject;
071        Class<?> paramType = element.getParameterTypes()[0];
072
073        if (explicitTypes.length > 0) {
074            for (Class<?> explicitType : explicitTypes) {
075                if (!paramType.isAssignableFrom(explicitType)) {
076                    throw new DigesterLoadingException("Impossible to handle annotation "
077                            + annotation
078                            + " on method "
079                            + element.toGenericString()
080                            + ", "
081                            + explicitType.getName()
082                            + " has to be a "
083                            + paramType.getName());
084                }
085
086                this.doHandle(annotation, element, explicitType, ruleSet);
087            }
088        } else {
089            this.doHandle(annotation, element, paramType, ruleSet);
090        }
091    }
092
093    private void doHandle(Annotation methodAnnotation, Method method, Class<?> type, FromAnnotationsRuleSet ruleSet) {
094        if (type.isInterface()
095                && Modifier.isAbstract(type.getModifiers())) {
096            throw new DigesterLoadingException("Impossible to proceed analyzing "
097                    + methodAnnotation
098                    + ", specified type '"
099                    + type.getName()
100                    + "' is an interface/abstract");
101        }
102
103        for (Annotation annotation : type.getAnnotations()) {
104            this.doHandle(methodAnnotation, annotation, method, type, ruleSet);
105        }
106    }
107
108    @SuppressWarnings("unchecked")
109    private <A extends Annotation, R extends Rule> void doHandle(A methodAnnotation,
110            Annotation annotation,
111            Method method,
112            Class<?> type,
113            FromAnnotationsRuleSet ruleSet) {
114        if (annotation.annotationType().isAnnotationPresent(DigesterRule.class)
115                && annotation.annotationType().isAnnotationPresent(CreationRule.class)) {
116            ruleSet.addRules(type);
117
118            DigesterRule digesterRule = methodAnnotation.annotationType().getAnnotation(DigesterRule.class);
119            Class<? extends AnnotationRuleProvider<A, Method, R>> providerType =
120                (Class<? extends AnnotationRuleProvider<A, Method, R>>) digesterRule.providedBy();
121            ruleSet.addRuleProvider(AnnotationUtils.getAnnotationPattern(annotation),
122                    providerType,
123                    methodAnnotation,
124                    method);
125        } else if (annotation.annotationType().isAnnotationPresent(DigesterRuleList.class)) {
126            // check if it is one of the *.List annotation
127            Annotation[] annotations = AnnotationUtils.getAnnotationsArrayValue(annotation);
128            if (annotations != null) {
129                // if it is an annotations array, process them
130                for (Annotation ptr : annotations) {
131                    this.doHandle(methodAnnotation, ptr, method, type, ruleSet);
132                }
133            }
134        }
135    }
136
137}