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