001 // Copyright 2011 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry5.internal.plastic; 016 017 import java.lang.reflect.InvocationHandler; 018 import java.lang.reflect.Method; 019 import java.lang.reflect.Proxy; 020 import java.util.Map; 021 022 @SuppressWarnings( 023 { "rawtypes", "unchecked" }) 024 public class AnnotationBuilder extends AbstractAnnotationBuilder 025 { 026 private static final class AnnotationValueHandler implements InvocationHandler 027 { 028 private final Class annotationType; 029 030 private final Map<String, Object> attributes; 031 032 public AnnotationValueHandler(final Class annotationType, Map<String, Object> attributes) 033 { 034 this.annotationType = annotationType; 035 this.attributes = attributes; 036 } 037 038 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 039 { 040 // args is null for no-arguments methods 041 if (args == null) 042 { 043 String attributeName = method.getName(); 044 045 if (attributes.containsKey(attributeName)) { return attributes.get(attributeName); } 046 } 047 048 // TODO: Handling of equals() and hashCode() and toString(), plus other methods 049 // inherited from Object 050 051 throw new RuntimeException(String.format("Annotation proxy for class %s does not handle method %s.", 052 annotationType.getName(), method)); 053 } 054 } 055 056 private final Class annotationType; 057 058 final Map<String, Object> attributes = PlasticInternalUtils.newMap(); 059 060 public AnnotationBuilder(Class annotationType, PlasticClassPool pool) 061 { 062 super(pool); 063 064 this.annotationType = annotationType; 065 066 attributes.put("annotationType", annotationType); 067 068 // Annotation attributes are represented as methods, and for each method there may be a 069 // default value. Preload the default values, which may be overwritten by explicit 070 // values. 071 072 for (Method m : annotationType.getMethods()) 073 { 074 Object defaultValue = m.getDefaultValue(); 075 076 if (defaultValue != null) 077 { 078 attributes.put(m.getName(), defaultValue); 079 } 080 } 081 082 if (!attributes.containsKey("toString")) 083 { 084 attributes.put("toString", "@" + annotationType.getName()); 085 } 086 087 } 088 089 protected void store(String name, Object value) 090 { 091 attributes.put(name, value); 092 } 093 094 protected Class elementTypeForArrayAttribute(String name) 095 { 096 try 097 { 098 return annotationType.getMethod(name).getReturnType().getComponentType(); 099 } 100 catch (Exception ex) 101 { 102 throw new RuntimeException(String.format( 103 "Unable to determine element type for attribute '%s' of annotation %s: %s", name, 104 annotationType.getName(), PlasticInternalUtils.toMessage(ex)), ex); 105 } 106 } 107 108 public Object createAnnotation() 109 { 110 // Use a static inner class to keep the AnnotationBuilder from being retained 111 112 InvocationHandler handler = new AnnotationValueHandler(annotationType, attributes); 113 114 try 115 { 116 return Proxy.newProxyInstance(pool.loader, new Class[] 117 { annotationType }, handler); 118 } 119 catch (IllegalArgumentException ex) 120 { 121 throw new IllegalArgumentException(String.format("Unable to create instance of annotation type %s: %s", 122 annotationType.getName(), PlasticInternalUtils.toMessage(ex)), ex); 123 } 124 } 125 126 }