001package org.apache.commons.digester3.annotations; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import static org.apache.commons.digester3.annotations.utils.AnnotationUtils.getAnnotationsArrayValue; 023 024import java.lang.annotation.Annotation; 025import java.lang.reflect.AnnotatedElement; 026import java.lang.reflect.Constructor; 027import java.lang.reflect.Field; 028import java.lang.reflect.Method; 029import java.security.AccessController; 030import java.security.PrivilegedAction; 031 032import org.apache.commons.digester3.Rule; 033import org.apache.commons.digester3.annotations.reflect.MethodArgument; 034import org.apache.commons.digester3.binder.AbstractRulesModule; 035 036/** 037 * {@link org.apache.commons.digester3.binder.RulesModule} implementation that allows loading rules from 038 * annotated classes. 039 * 040 * @since 3.0 041 */ 042public abstract class FromAnnotationsRuleModule 043 extends AbstractRulesModule 044{ 045 046 private static final String JAVA_PACKAGE = "java"; 047 048 private static final AnnotationHandlerFactory DEFAULT_HANDLER_FACTORY = new DefaultAnnotationHandlerFactory(); 049 050 private AnnotationHandlerFactory annotationHandlerFactory = DEFAULT_HANDLER_FACTORY; 051 052 private WithMemoryRulesBinder rulesBinder; 053 054 /** 055 * {@inheritDoc} 056 */ 057 @Override 058 protected final void configure() 059 { 060 if ( rulesBinder == null ) 061 { 062 rulesBinder = new WithMemoryRulesBinder( rulesBinder() ); 063 } 064 065 try 066 { 067 configureRules(); 068 } 069 finally 070 { 071 rulesBinder = null; 072 } 073 } 074 075 /** 076 * Configures a {@link org.apache.commons.digester3.binder.RulesBinder} via the exposed methods. 077 */ 078 protected abstract void configureRules(); 079 080 /** 081 * Allows users plug a different {@link AnnotationHandlerFactory} to create {@link AnnotationHandler} instances. 082 * 083 * @param annotationHandlerFactory A custom {@link AnnotationHandlerFactory} to create 084 * {@link AnnotationHandler} instances 085 */ 086 protected final void useAnnotationHandlerFactory( AnnotationHandlerFactory annotationHandlerFactory ) 087 { 088 if ( annotationHandlerFactory == null ) 089 { 090 throw new IllegalArgumentException( "Argument 'annotationHandlerFactory' must be not null" ); 091 } 092 093 this.annotationHandlerFactory = annotationHandlerFactory; 094 } 095 096 /** 097 * Allows users to switch back to the default {@link AnnotationHandlerFactory} implementation. 098 */ 099 protected final void useDefaultAnnotationHandlerFactory() 100 { 101 useAnnotationHandlerFactory( DEFAULT_HANDLER_FACTORY ); 102 } 103 104 /** 105 * Scan the input Class, looking for Digester rules expressed via annotations, and binds them. 106 * 107 * @param type the type has to be analyzed 108 * @see DigesterRule 109 */ 110 protected final void bindRulesFrom( final Class<?> type ) 111 { 112 if ( type == null || type.getPackage().getName().startsWith( JAVA_PACKAGE ) 113 || rulesBinder.isAlreadyBound( type ) ) 114 { 115 return; 116 } 117 118 // TYPE 119 visitElements( type ); 120 121 if ( !type.isInterface() ) 122 { 123 // CONSTRUCTOR 124 visitElements( new PrivilegedAction<Constructor<?>[]>() 125 { 126 public Constructor<?>[] run() 127 { 128 return type.getDeclaredConstructors(); 129 } 130 } ); 131 132 // FIELD 133 visitElements( new PrivilegedAction<Field[]>() 134 { 135 public Field[] run() 136 { 137 return type.getDeclaredFields(); 138 } 139 } ); 140 } 141 142 // METHOD 143 visitElements( new PrivilegedAction<Method[]>() 144 { 145 public Method[] run() 146 { 147 return type.getDeclaredMethods(); 148 } 149 } ); 150 151 rulesBinder.markAsBound( type ); 152 bindRulesFrom( type.getSuperclass() ); 153 } 154 155 /** 156 * 157 * 158 * @param <AE> 159 * @param action 160 */ 161 private <AE extends AnnotatedElement> void visitElements( PrivilegedAction<AE[]> action ) 162 { 163 AE[] annotatedElements = null; 164 if ( System.getSecurityManager() != null ) 165 { 166 annotatedElements = AccessController.doPrivileged( action ); 167 } 168 else 169 { 170 annotatedElements = action.run(); 171 } 172 visitElements( annotatedElements ); 173 } 174 175 /** 176 * 177 * 178 * @param annotatedElements 179 */ 180 private void visitElements( AnnotatedElement... annotatedElements ) 181 { 182 for ( AnnotatedElement element : annotatedElements ) 183 { 184 for ( Annotation annotation : element.getAnnotations() ) 185 { 186 handle( annotation, element ); 187 } 188 189 if ( element instanceof Constructor || element instanceof Method ) 190 { 191 Annotation[][] parameterAnnotations; 192 Class<?>[] parameterTypes; 193 194 if ( element instanceof Constructor ) 195 { 196 // constructor args 197 Constructor<?> construcotr = (Constructor<?>) element; 198 parameterAnnotations = construcotr.getParameterAnnotations(); 199 parameterTypes = construcotr.getParameterTypes(); 200 } 201 else 202 { 203 // method args 204 Method method = (Method) element; 205 parameterAnnotations = method.getParameterAnnotations(); 206 parameterTypes = method.getParameterTypes(); 207 } 208 209 for ( int i = 0; i < parameterTypes.length; i++ ) 210 { 211 visitElements( new MethodArgument( i, parameterTypes[i], parameterAnnotations[i] ) ); 212 } 213 } 214 } 215 } 216 217 /** 218 * Handles the current visited element and related annotation, invoking the 219 * right handler putting the rule provider in the rule set. 220 * 221 * @param annotation the current visited annotation. 222 * @param element the current visited element. 223 */ 224 @SuppressWarnings( "unchecked" ) 225 private <A extends Annotation, E extends AnnotatedElement, R extends Rule> void handle( A annotation, E element ) 226 { 227 Class<?> annotationType = annotation.annotationType(); 228 229 // check if it is one of the @*.List annotation 230 if ( annotationType.isAnnotationPresent( DigesterRuleList.class ) ) 231 { 232 Annotation[] annotations = getAnnotationsArrayValue( annotation ); 233 if ( annotations != null && annotations.length > 0 ) 234 { 235 // if it is an annotations array, process them 236 for ( Annotation ptr : annotations ) 237 { 238 handle( ptr, element ); 239 } 240 } 241 } 242 else if ( annotationType.isAnnotationPresent( DigesterRule.class ) ) 243 { 244 DigesterRule digesterRule = annotationType.getAnnotation( DigesterRule.class ); 245 246 // the default behavior if the handler is not specified 247 Class<? extends AnnotationHandler<Annotation, AnnotatedElement>> handlerType = 248 (Class<? extends AnnotationHandler<Annotation, AnnotatedElement>>) digesterRule.handledBy(); 249 try 250 { 251 AnnotationHandler<Annotation, AnnotatedElement> handler = 252 annotationHandlerFactory.newInstance( handlerType ); 253 254 // run! 255 handler.handle( annotation, element, this.rulesBinder ); 256 } 257 catch ( Exception e ) 258 { 259 rulesBinder.addError( e ); 260 } 261 } 262 } 263 264}