    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}