Author: simonetripodi Date: Sat Jan 9 12:15:52 2010 New Revision: 897428 URL: http://svn.apache.org/viewvc?rev=897428&view=rev Log: first checkin of DigesterLoader class
Added: commons/sandbox/at-digester/trunk/src/java/org/apache/commons/digester/annotations/DigesterLoader.java (with props) Added: commons/sandbox/at-digester/trunk/src/java/org/apache/commons/digester/annotations/DigesterLoader.java URL: http://svn.apache.org/viewvc/commons/sandbox/at-digester/trunk/src/java/org/apache/commons/digester/annotations/DigesterLoader.java?rev=897428&view=auto ============================================================================== --- commons/sandbox/at-digester/trunk/src/java/org/apache/commons/digester/annotations/DigesterLoader.java (added) +++ commons/sandbox/at-digester/trunk/src/java/org/apache/commons/digester/annotations/DigesterLoader.java Sat Jan 9 12:15:52 2010 @@ -0,0 +1,203 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.digester.annotations; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.commons.digester.Digester; +import org.apache.commons.digester.RuleSet; +import org.apache.commons.digester.annotations.reflect.MethodArgument; +import org.apache.commons.digester.annotations.utils.AnnotationUtils; + +/** + * This class manages the creation of Digester instances analyzing target classes + * annotated with digester annotations. + * + * It avoids iterate the annotations analysis for already analyzed classes, + * using an in-memory LRU cache. + * + * @author Simone Tripodi (simonetripodi) + * @version $Id$ + */ +public final class DigesterLoader { + + /** + * The fixed cache size. + */ + private static final int CACHE_SIZE = 255; + + /** + * The fixed cache load facor. + */ + private static final float LOAD_FACTOR = 0.75f; + + /** + * The fixed cache capacity. + */ + private static final int CACHE_CAPACITY = (int) Math.ceil(CACHE_SIZE / LOAD_FACTOR) + 1; + + /** + * In-memory LRU cache that stores already analyzed classes and relative + * {...@link RuleSet}. + */ + private static final Map<Class<?>, FromAnnotationsRuleSet> CACHED_RULESET = + new LinkedHashMap<Class<?>, FromAnnotationsRuleSet>(CACHE_CAPACITY, LOAD_FACTOR) { + + private static final long serialVersionUID = 1L; + + protected boolean removeEldestEntry(Map.Entry<Class<?>,FromAnnotationsRuleSet> eldest) { + return size() > CACHE_SIZE; + }; + }; + + /** + * This class can't be instantiated. + */ + private DigesterLoader() { + // do nothing + } + + /** + * Creates a new digester which rules are defined by analyzing the digester + * annotations in the target class. + * + * @param target the class has to be analyzed. + * @return a new Digester instance. + */ + public static Digester createDigester(final Class<?> target) { + Digester digester = new Digester(); + digester.setClassLoader(target.getClassLoader()); + addRules(target, digester); + return digester; + } + + /** + * Add rules to an already created Digester instance, analyzing the digester + * annotations in the target class. + * + * @param target the class has to be analyzed. + * @param digester the Digester instance reference. + */ + public static void addRules(final Class<?> target, final Digester digester) { + RuleSet ruleSet = getRuleSet(target); + ruleSet.addRuleInstances(digester); + } + + /** + * Builds a new {...@link RuleSet} analyzing the digester annotations in the + * target class. + * + * It avoids iterate the annotations analysis for already analyzed classes, + * using an in-memory LRU cache. + * + * @param target the class has to be analyzed. + * @return a new {...@link RuleSet}. + */ + public static RuleSet getRuleSet(final Class<?> target) { + if (CACHED_RULESET.containsKey(target)) { + return CACHED_RULESET.get(target); + } + + FromAnnotationsRuleSet ruleSet = new FromAnnotationsRuleSet(); + addRules(target, ruleSet); + CACHED_RULESET.put(target, ruleSet); + + return ruleSet; + } + + /** + * Analyzes the target class and adds the {...@link AnnotationRuleProvider}s to + * the existing {...@link FromAnnotationsRuleSet}. + * + * @param target the class has to be analyzed. + * @param ruleSet the RuleSet where adding the providers. + */ + public static void addRules(final Class<?> target, FromAnnotationsRuleSet ruleSet) { + if (target == Object.class + || target.isInterface() + || Modifier.isAbstract(target.getModifiers())) { + return; + } + + if (CACHED_RULESET.containsKey(target)) { + ruleSet.addRulesProviderFrom(CACHED_RULESET.get(target)); + return; + } + + // current analyzed class + handle(target, ruleSet); + + // class fields + for (Field field : target.getDeclaredFields()) { + handle(field, ruleSet); + } + + // class methods + for (Method method : target.getDeclaredMethods()) { + handle(method, ruleSet); + + // method args + Annotation[][] parameterAnnotations = method.getParameterAnnotations(); + Class<?>[] parameterTypes = method.getParameterTypes(); + for (int i = 0; i < parameterTypes.length; i++) { + handle(new MethodArgument(i, parameterTypes[i], parameterAnnotations[i]), ruleSet); + } + } + + addRules(target.getSuperclass(), ruleSet); + } + + /** + * + * @param element + */ + private static void handle(AnnotatedElement element, FromAnnotationsRuleSet ruleSet) { + for (Annotation annotation : element.getAnnotations()) { + handle(annotation, element, ruleSet); + } + } + + /** + * + * @param annotation + * @param element + */ + private static void handle(Annotation annotation, AnnotatedElement element, FromAnnotationsRuleSet ruleSet) { + Class<?> annotationType = annotation.annotationType(); + + // check if it is one of the @*.List annotation + if (annotationType.isAnnotationPresent(DigesterRuleList.class)) { + Annotation[] annotations = AnnotationUtils.getAnnotationsArrayValue(annotation); + if (annotations != null && annotations.length > 0) { + // if it is an annotations array, process them + for (Annotation ptr : annotations) { + handle(ptr, element, ruleSet); + } + } + } else if (annotationType.isAnnotationPresent(DigesterRule.class)) { + DigesterRule digesterRule = annotationType.getAnnotation(DigesterRule.class); + // TODO add missing code + } + } + +} Propchange: commons/sandbox/at-digester/trunk/src/java/org/apache/commons/digester/annotations/DigesterLoader.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: commons/sandbox/at-digester/trunk/src/java/org/apache/commons/digester/annotations/DigesterLoader.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Propchange: commons/sandbox/at-digester/trunk/src/java/org/apache/commons/digester/annotations/DigesterLoader.java ------------------------------------------------------------------------------ svn:mime-type = text/plain