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 }