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 }