AnnotatedElementUtils.java 9.3 KB
Newer Older
1
/*
2
 * Copyright 2002-2014 the original author or authors.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 *
 * Licensed 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.springframework.core.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

/**
P
Phillip Webb 已提交
30 31
 * Utility class used to collect all annotation values including those declared on
 * meta-annotations.
32 33 34
 *
 * @author Phillip Webb
 * @author Juergen Hoeller
35
 * @author Sam Brannen
36 37 38 39 40 41
 * @since 4.0
 */
public class AnnotatedElementUtils {

	public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, String annotationType) {
		final Set<String> types = new LinkedHashSet<String>();
42
		process(element, annotationType, false, new Processor<Object>() {
43
			@Override
44 45
			public Object process(Annotation annotation, int metaDepth) {
				if (metaDepth > 0) {
46 47 48 49 50 51 52 53 54 55 56 57
					types.add(annotation.annotationType().getName());
				}
				return null;
			}
			@Override
			public void postProcess(Annotation annotation, Object result) {
			}
		});
		return (types.isEmpty() ? null : types);
	}

	public static boolean hasMetaAnnotationTypes(AnnotatedElement element, String annotationType) {
58
		return Boolean.TRUE.equals(process(element, annotationType, false, new Processor<Boolean>() {
59
			@Override
60 61 62
			public Boolean process(Annotation annotation, int metaDepth) {
				if (metaDepth > 0) {
					return Boolean.TRUE;
63 64 65 66 67 68 69 70 71 72
				}
				return null;
			}
			@Override
			public void postProcess(Annotation annotation, Boolean result) {
			}
		}));
	}

	public static boolean isAnnotated(AnnotatedElement element, String annotationType) {
73
		return Boolean.TRUE.equals(process(element, annotationType, false, new Processor<Boolean>() {
74
			@Override
75 76
			public Boolean process(Annotation annotation, int metaDepth) {
				return Boolean.TRUE;
77 78 79 80 81 82 83 84 85 86 87
			}
			@Override
			public void postProcess(Annotation annotation, Boolean result) {
			}
		}));
	}

	public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement element, String annotationType) {
		return getAnnotationAttributes(element, annotationType, false, false);
	}

88
	public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement element, String annotationType,
89 90
			final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {

91
		return process(element, annotationType, false, new Processor<AnnotationAttributes>() {
92
			@Override
93
			public AnnotationAttributes process(Annotation annotation, int metaDepth) {
94 95 96 97 98
				return AnnotationUtils.getAnnotationAttributes(annotation, classValuesAsString, nestedAnnotationsAsMap);
			}
			@Override
			public void postProcess(Annotation annotation, AnnotationAttributes result) {
				for (String key : result.keySet()) {
99 100
					if (!AnnotationUtils.VALUE.equals(key)) {
						Object value = AnnotationUtils.getValue(annotation, key);
101
						if (value != null) {
102
							result.put(key, AnnotationUtils.adaptValue(value, classValuesAsString, nestedAnnotationsAsMap));
103 104 105 106 107 108 109
						}
					}
				}
			}
		});
	}

110
	public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element, String annotationType) {
111 112 113 114 115
		return getAllAnnotationAttributes(element, annotationType, false, false);
	}

	public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element,
			final String annotationType, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) {
116 117

		final MultiValueMap<String, Object> attributes = new LinkedMultiValueMap<String, Object>();
118
		process(element, annotationType, false, new Processor<Void>() {
119
			@Override
120
			public Void process(Annotation annotation, int metaDepth) {
121
				if (annotation.annotationType().getName().equals(annotationType)) {
122 123
					for (Map.Entry<String, Object> entry : AnnotationUtils.getAnnotationAttributes(
							annotation, classValuesAsString, nestedAnnotationsAsMap).entrySet()) {
124 125 126 127 128 129 130 131
						attributes.add(entry.getKey(), entry.getValue());
					}
				}
				return null;
			}
			@Override
			public void postProcess(Annotation annotation, Void result) {
				for (String key : attributes.keySet()) {
132 133
					if (!AnnotationUtils.VALUE.equals(key)) {
						Object value = AnnotationUtils.getValue(annotation, key);
134 135 136 137 138 139 140 141 142 143 144
						if (value != null) {
							attributes.add(key, value);
						}
					}
				}
			}
		});
		return (attributes.isEmpty() ? null : attributes);
	}

	/**
145 146 147 148 149 150
	 * Process all annotations of the specified {@code annotationType} and
	 * recursively all meta-annotations on the specified {@code element}.
	 * <p>If the {@code traverseClassHierarchy} flag is {@code true} and the sought
	 * annotation is neither <em>directly present</em> on the given element nor
	 * present on the given element as a meta-annotation, then the algorithm will
	 * recursively search through the class hierarchy of the given element.
151
	 * @param element the annotated element
152 153 154 155
	 * @param annotationType the annotation type to find
	 * @param traverseClassHierarchy whether or not to traverse up the class
	 * hierarchy recursively
	 * @param processor the processor to delegate to
156 157
	 * @return the result of the processor
	 */
158 159
	private static <T> T process(AnnotatedElement element, String annotationType, boolean traverseClassHierarchy,
			Processor<T> processor) {
160 161

		try {
162
			return doProcess(element, annotationType, traverseClassHierarchy, processor,
163
					new HashSet<AnnotatedElement>(), 0);
164 165 166 167
		}
		catch (Throwable ex) {
			throw new IllegalStateException("Failed to introspect annotations: " + element, ex);
		}
168 169
	}

170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
	/**
	 * Perform the search algorithm for the {@link #process} method, avoiding
	 * endless recursion by tracking which annotated elements have already been
	 * <em>visited</em>.
	 * <p>The {@code metaDepth} parameter represents the depth of the annotation
	 * relative to the initial element. For example, an annotation that is
	 * <em>present</em> on the element will have a depth of 0; a meta-annotation
	 * will have a depth of 1; and a meta-meta-annotation will have a depth of 2.
	 * @param element the annotated element
	 * @param annotationType the annotation type to find
	 * @param traverseClassHierarchy whether or not to traverse up the class
	 * hierarchy recursively
	 * @param processor the processor to delegate to
	 * @param visited the set of annotated elements that have already been visited
	 * @param metaDepth the depth of the annotation relative to the initial element
	 * @return the result of the processor
	 */
	private static <T> T doProcess(AnnotatedElement element, String annotationType, boolean traverseClassHierarchy,
			Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) {
189 190

		if (visited.add(element)) {
J
Juergen Hoeller 已提交
191 192
			Annotation[] annotations =
					(traverseClassHierarchy ? element.getDeclaredAnnotations() : element.getAnnotations());
193 194 195
			for (Annotation annotation : annotations) {
				if (annotation.annotationType().getName().equals(annotationType) || metaDepth > 0) {
					T result = processor.process(annotation, metaDepth);
196
					if (result != null) {
197 198
						return result;
					}
199 200
					result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy,
							processor, visited, metaDepth + 1);
201 202 203 204 205 206
					if (result != null) {
						processor.postProcess(annotation, result);
						return result;
					}
				}
			}
207
			for (Annotation annotation : annotations) {
208
				if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
209
					T result = doProcess(annotation.annotationType(), annotationType, traverseClassHierarchy,
210
							processor, visited, metaDepth);
211 212 213 214
					if (result != null) {
						processor.postProcess(annotation, result);
						return result;
					}
215 216
				}
			}
217 218 219
			if (traverseClassHierarchy && element instanceof Class) {
				Class<?> superclass = ((Class<?>) element).getSuperclass();
				if (superclass != null && !superclass.equals(Object.class)) {
220
					T result = doProcess(superclass, annotationType, true, processor, visited, metaDepth);
221 222 223 224 225
					if (result != null) {
						return result;
					}
				}
			}
226 227 228 229 230 231 232 233 234 235 236 237 238
		}
		return null;
	}


	/**
	 * Callback interface used to process an annotation.
	 * @param <T> the result type
	 */
	private static interface Processor<T> {

		/**
		 * Called to process the annotation.
239 240 241 242 243
		 * <p>The {@code metaDepth} parameter represents the depth of the
		 * annotation relative to the initial element. For example, an annotation
		 * that is <em>present</em> on the element will have a depth of 0; a
		 * meta-annotation will have a depth of 1; and a meta-meta-annotation
		 * will have a depth of 2.
244
		 * @param annotation the annotation to process
245
		 * @param metaDepth the depth of the annotation relative to the initial element
246
		 * @return the result of the processing, or {@code null} to continue
247
		 */
248
		T process(Annotation annotation, int metaDepth);
249 250 251 252 253

		void postProcess(Annotation annotation, T result);
	}

}