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 }