001 /*
002 * Copyright 2002-2006 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016
017 package org.springframework.binding.support;
018
019 import java.lang.annotation.Annotation;
020 import java.lang.reflect.Method;
021 import java.lang.reflect.Modifier;
022 import java.lang.reflect.InvocationTargetException;
023 import java.util.Map;
024
025 /**
026 * Uses reflection to translate annotations into property level user metadata.
027 * This implementation will translate annotations in the following manner:
028 * <ul>
029 * <li>The Annotation instance itself will be placed into a key in the form of
030 * the '@' character followed by the annotation class name. For example:
031 * <code><pre>
032 * import com.mypkg.SomeAnnotation;
033 * ...
034 * @SomeAnnotation("This is a test")
035 * public String getMyProperty()
036 * ...
037 * </pre></code>
038 * Will store the "SomeAnnotation" instance in the key
039 * <code>@com.mypkg.SomeAnnotation</code>.
040 * This allows 1.5 code easier access to annotation data.
041 * <li>If the Annotation does not define any value or properties, then
042 * <code>Boolean.TRUE</code> will be stored against the fully qualified
043 * classname of the Annotation as the key. For example:
044 * <code><pre>
045 * import com.mypkg.MyAnnotation;
046 * ...
047 * @MyAnnotation
048 * public String getMyProperty()
049 * ...
050 * </pre></code>
051 * Will store <code>Boolean.TRUE</code> in the key
052 * <code>com.mypkg.MyAnnotation</code>.
053 * <li>If the Annotation defines a single "value", then the annotation value
054 * will be stored using the fully qualified classname of the Annotation as the
055 * key and single value of the annotation as the key's value. For example:
056 * <code><pre>
057 * import com.mypkg.SomeAnnotation;
058 * ...
059 * @SomeAnnotation("Hello, World!")
060 * public String getMyProperty()
061 * ...
062 * </pre></code>
063 * Will store the value <code>"Hello, World!"</code> in the key
064 * <code>com.mypkg.SomeAnnotation</code>.
065 * <li>If the Annotation has multiple values, then <code>Boolean.TRUE</code>
066 * will be stored against the fully qualified annotation class name and each
067 * value will be stored using the fully qualified classname of the Annotation
068 * with a "." and the property name of the annotation as the key value. For
069 * example:
070 * <code><pre>
071 * import com.mypkg.OtherAnnotation;
072 * ...
073 * @OtherAnnotation(aprop1="Something", aprop2=100)
074 * public String getMyProperty()
075 * ...
076 * </pre></code>
077 * Will create three key + value pairs:
078 * <table>
079 * <tr><td><b>Key</b></td><td/><td><b>Value</b></td></tr>
080 * <tr><td>com.mypkg.OtherAnnotation</td><td>=</td><td>Boolean.TRUE</td></tr>
081 * <tr><td>com.mypkg.OtherAnnotation.aprop1</td><td>=</td><td>"Something"</td></tr>
082 * <tr><td>com.mypkg.OtherAnnotation.aprop2</td><td>=</td><td>100</td></tr>
083 * </table>
084 * </ul>
085 *
086 * @author andy
087 * @since May 8, 2006 4:08:53 PM
088 */
089 public class ReflectionAnnotationTranslator implements AnnotationTranslator {
090 public final static String SINGLE_VALUE_METHOD_NAME = "value";
091
092 public void translate(final Annotation annotation, final Map<String, Object> result) {
093 try {
094 final Class<? extends Annotation> type = annotation.annotationType();
095 final Method[] methods = type.getDeclaredMethods();
096 final String name = type.getName();
097
098 result.put("@" + name, annotation);
099
100 if(methods.length == 0) {
101 result.put(name, Boolean.TRUE);
102 } else if(methods.length == 1 && SINGLE_VALUE_METHOD_NAME.equals(methods[0].getName())) {
103 result.put(name, methods[0].invoke(annotation));
104 } else {
105 result.put(name, Boolean.TRUE);
106 for(final Method method : methods) {
107 if(Modifier.isPublic(method.getModifiers())) {
108 result.put(name + "." + method.getName(), method.invoke(annotation));
109 }
110 }
111 }
112 } catch(IllegalAccessException e) {
113 throw new RuntimeException(e);
114 } catch(InvocationTargetException e) {
115 throw new RuntimeException(e);
116 }
117 }
118 }