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.Array;
018 import java.util.ArrayList;
019 import java.util.List;
020
021 import org.apache.tapestry5.internal.plastic.asm.AnnotationVisitor;
022 import org.apache.tapestry5.internal.plastic.asm.Type;
023
024 @SuppressWarnings(
025 { "rawtypes", "unchecked" })
026 public abstract class AbstractAnnotationBuilder implements AnnotationVisitor
027 {
028 protected final PlasticClassPool pool;
029
030 public AbstractAnnotationBuilder(PlasticClassPool pool)
031 {
032 this.pool = pool;
033 }
034
035 protected abstract void store(String name, Object value);
036
037 protected Class elementTypeForArrayAttribute(String name)
038 {
039 throw new IllegalStateException("elementTypeForArrayAttribute() may not be invoked here.");
040 }
041
042 public void visit(String name, Object value)
043 {
044 if (value instanceof Type)
045 {
046 Type type = (Type) value;
047
048 Class valueType = pool.loadClass(type.getClassName());
049 store(name, valueType);
050 return;
051 }
052
053 store(name, value);
054 }
055
056 public void visitEnum(String name, String desc, String value)
057 {
058
059 try
060 {
061 String enumClassName = PlasticInternalUtils.objectDescriptorToClassName(desc);
062
063 Class enumClass = pool.loader.loadClass(enumClassName);
064
065 Object enumValue = Enum.valueOf(enumClass, value);
066
067 store(name, enumValue);
068 }
069 catch (Exception ex)
070 {
071 throw new IllegalArgumentException(String.format("Unable to convert enum annotation attribute %s %s: %s",
072 value, desc, PlasticInternalUtils.toMessage(ex)), ex);
073 }
074 }
075
076 public AnnotationVisitor visitAnnotation(final String name, String desc)
077 {
078 final AbstractAnnotationBuilder outerBuilder = this;
079
080 final Class nestedAnnotationType = pool.loadClass(PlasticInternalUtils.objectDescriptorToClassName(desc));
081
082 // Return a nested builder that constructs the inner annotation and, at the end of
083 // construction, pushes the final Annotation object into this builder's attributes.
084
085 return new AnnotationBuilder(nestedAnnotationType, pool)
086 {
087 @Override
088 public void visitEnd()
089 {
090 outerBuilder.store(name, createAnnotation());
091 };
092 };
093 }
094
095 /**
096 * Because of how ASM works, this should only be invoked when the array values are not
097 * primitives and not Class/Type; i.e. the inner values will be either Class/Type, enum, or
098 * nested annotations. All the arrays of strings and primitives are handled by ASM and become
099 * a single call to {@link #visit(String, Object)}.
100 */
101 public AnnotationVisitor visitArray(final String name)
102 {
103 final List<Object> values = new ArrayList<Object>();
104
105 final Class componentType = elementTypeForArrayAttribute(name);
106
107 final AbstractAnnotationBuilder outerBuilder = this;
108
109 return new AbstractAnnotationBuilder(pool)
110 {
111 @Override
112 protected void store(String name, Object value)
113 {
114 values.add(value);
115 }
116
117 @Override
118 public void visitEnd()
119 {
120 Object array = Array.newInstance(componentType, values.size());
121
122 // Now, empty arrays may be primitive types and will not cast to Object[], but
123 // non empty arrays indicate that it was a Class/Enum/Annotation, which can cast
124 // to Object[]
125
126 if (values.size() != 0)
127 array = values.toArray((Object[]) array);
128
129 outerBuilder.store(name, array);
130 }
131 };
132 }
133
134 public void visitEnd()
135 {
136 // Nothing to do here. Subclasses use this as a chance to store a value into an outer
137 // builder.
138 }
139
140 }