001 /* $Id: DigesterLoader.java 992084 2010-09-02 19:52:17Z 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 */ 018 package org.apache.commons.digester.annotations; 019 020 import java.lang.annotation.Annotation; 021 import java.lang.reflect.AnnotatedElement; 022 import java.lang.reflect.Field; 023 import java.lang.reflect.Method; 024 025 import org.apache.commons.digester.Digester; 026 import org.apache.commons.digester.Rule; 027 import org.apache.commons.digester.RuleSet; 028 import org.apache.commons.digester.annotations.handlers.DefaultLoaderHandler; 029 import org.apache.commons.digester.annotations.internal.RuleSetCache; 030 import org.apache.commons.digester.annotations.reflect.MethodArgument; 031 import org.apache.commons.digester.annotations.spi.AnnotationRuleProviderFactory; 032 import org.apache.commons.digester.annotations.spi.DigesterLoaderHandlerFactory; 033 import org.apache.commons.digester.annotations.utils.AnnotationUtils; 034 035 /** 036 * This class manages the creation of Digester instances analyzing target classes 037 * annotated with digester annotations. 038 * 039 * @since 2.1 040 */ 041 public final class DigesterLoader { 042 043 /** 044 * In-memory LRU cache that stores already analyzed classes and relative 045 * {@link RuleSet}. 046 */ 047 private final RuleSetCache cachedRuleSet = new RuleSetCache(); 048 049 private final AnnotationRuleProviderFactory annotationRuleProviderFactory; 050 051 private final DigesterLoaderHandlerFactory digesterLoaderHandlerFactory; 052 053 /** 054 * Creates a new {@link DigesterLoader} instance. 055 * 056 * @param annotationRuleProviderFactory 057 * @param digesterLoaderHandlerFactory 058 */ 059 protected DigesterLoader(AnnotationRuleProviderFactory annotationRuleProviderFactory, 060 DigesterLoaderHandlerFactory digesterLoaderHandlerFactory) { 061 this.annotationRuleProviderFactory = annotationRuleProviderFactory; 062 this.digesterLoaderHandlerFactory = digesterLoaderHandlerFactory; 063 } 064 065 protected AnnotationRuleProviderFactory getAnnotationRuleProviderFactory() { 066 return annotationRuleProviderFactory; 067 } 068 069 protected DigesterLoaderHandlerFactory getDigesterLoaderHandlerFactory() { 070 return digesterLoaderHandlerFactory; 071 } 072 073 /** 074 * Creates a new digester which rules are defined by analyzing the digester 075 * annotations in the target class. 076 * 077 * @param target the class has to be analyzed. 078 * @return a new Digester instance. 079 */ 080 public Digester createDigester(final Class<?> target) { 081 Digester digester = new Digester(); 082 digester.setClassLoader(target.getClassLoader()); 083 addRules(target, digester); 084 return digester; 085 } 086 087 /** 088 * Add rules to an already created Digester instance, analyzing the digester 089 * annotations in the target class. 090 * 091 * @param target the class has to be analyzed. 092 * @param digester the Digester instance reference. 093 */ 094 public void addRules(final Class<?> target, final Digester digester) { 095 RuleSet ruleSet = getRuleSet(target); 096 ruleSet.addRuleInstances(digester); 097 } 098 099 /** 100 * Builds a new {@link RuleSet} analyzing the digester annotations in the 101 * target class. 102 * 103 * It avoids iterate the annotations analysis for already analyzed classes, 104 * using an in-memory LRU cache. 105 * 106 * @param target the class has to be analyzed. 107 * @return a new {@link RuleSet}. 108 */ 109 public RuleSet getRuleSet(final Class<?> target) { 110 if (this.cachedRuleSet.containsKey(target)) { 111 return this.cachedRuleSet.get(target); 112 } 113 114 FromAnnotationsRuleSet ruleSet = new FromAnnotationsRuleSet(this); 115 addRulesTo(target, ruleSet); 116 this.cachedRuleSet.put(target, ruleSet); 117 118 return ruleSet; 119 } 120 121 /** 122 * Analyzes the target class and adds the {@link AnnotationRuleProvider}s to 123 * the existing {@link FromAnnotationsRuleSet}. 124 * 125 * @param target the class has to be analyzed. 126 * @param ruleSet the RuleSet where adding the providers. 127 */ 128 public void addRulesTo(final Class<?> target, FromAnnotationsRuleSet ruleSet) { 129 if (target == Object.class 130 || target.isInterface() 131 || ruleSet.mapsClass(target)) { 132 return; 133 } 134 135 if (this.cachedRuleSet.containsKey(target)) { 136 ruleSet.addRulesProviderFrom(this.cachedRuleSet.get(target)); 137 ruleSet.addMappedClass(target); 138 return; 139 } 140 141 // current analyzed class 142 handle(target, ruleSet); 143 144 // class fields 145 for (Field field : target.getDeclaredFields()) { 146 handle(field, ruleSet); 147 } 148 149 // class methods 150 for (Method method : target.getDeclaredMethods()) { 151 handle(method, ruleSet); 152 153 // method args 154 Annotation[][] parameterAnnotations = method.getParameterAnnotations(); 155 Class<?>[] parameterTypes = method.getParameterTypes(); 156 for (int i = 0; i < parameterTypes.length; i++) { 157 handle(new MethodArgument(i, parameterTypes[i], parameterAnnotations[i]), ruleSet); 158 } 159 } 160 161 ruleSet.addMappedClass(target); 162 addRulesTo(target.getSuperclass(), ruleSet); 163 } 164 165 /** 166 * Executes an analysis for each annotation present in the element. 167 * 168 * @param element the current element under analysis. 169 * @param ruleSet the ruleSet where add providers. 170 */ 171 private void handle(AnnotatedElement element, FromAnnotationsRuleSet ruleSet) { 172 for (Annotation annotation : element.getAnnotations()) { 173 handle(annotation, element, ruleSet); 174 } 175 } 176 177 /** 178 * Handles the current visited element and related annotation, invoking the 179 * right handler putting the rule provider in the rule set. 180 * 181 * @param annotation the current visited annotation. 182 * @param element the current visited element. 183 */ 184 @SuppressWarnings("unchecked") 185 private <A extends Annotation, E extends AnnotatedElement, R extends Rule> void handle(A annotation, 186 E element, 187 FromAnnotationsRuleSet ruleSet) { 188 Class<?> annotationType = annotation.annotationType(); 189 190 // check if it is one of the @*.List annotation 191 if (annotationType.isAnnotationPresent(DigesterRuleList.class)) { 192 Annotation[] annotations = AnnotationUtils.getAnnotationsArrayValue(annotation); 193 if (annotations != null && annotations.length > 0) { 194 // if it is an annotations array, process them 195 for (Annotation ptr : annotations) { 196 handle(ptr, element, ruleSet); 197 } 198 } 199 } else if (annotationType.isAnnotationPresent(DigesterRule.class)) { 200 DigesterRule digesterRule = annotationType.getAnnotation(DigesterRule.class); 201 202 if (DefaultLoaderHandler.class == digesterRule.handledBy()) { 203 Class<? extends AnnotationRuleProvider<A, E, R>> providerType = 204 (Class<? extends AnnotationRuleProvider<A, E, R>>) digesterRule.providedBy(); 205 ruleSet.addRuleProvider(AnnotationUtils.getAnnotationPattern(annotation), 206 providerType, 207 annotation, 208 element); 209 } else { 210 Class<? extends DigesterLoaderHandler<Annotation, AnnotatedElement>> handlerType = 211 (Class<? extends DigesterLoaderHandler<Annotation, AnnotatedElement>>) digesterRule.handledBy(); 212 DigesterLoaderHandler<Annotation, AnnotatedElement> handler = 213 this.digesterLoaderHandlerFactory.newInstance(handlerType); 214 215 // run! 216 handler.handle(annotation, element, ruleSet); 217 } 218 } 219 } 220 221 }