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 }