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