001/***
002 * ASM: a very small and fast Java bytecode manipulation framework
003 * Copyright (c) 2000-2011 INRIA, France Telecom
004 * All rights reserved.
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions
008 * are met:
009 * 1. Redistributions of source code must retain the above copyright
010 *    notice, this list of conditions and the following disclaimer.
011 * 2. Redistributions in binary form must reproduce the above copyright
012 *    notice, this list of conditions and the following disclaimer in the
013 *    documentation and/or other materials provided with the distribution.
014 * 3. Neither the name of the copyright holders nor the names of its
015 *    contributors may be used to endorse or promote products derived from
016 *    this software without specific prior written permission.
017 *
018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
028 * THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package io.ebean.enhance.asm.tree;
031
032import io.ebean.enhance.asm.AnnotationVisitor;
033import io.ebean.enhance.asm.Opcodes;
034import io.ebean.enhance.asm.Type;
035
036import java.util.ArrayList;
037import java.util.List;
038
039/**
040 * A node that represents an annotation.
041 * 
042 * @author Eric Bruneton
043 */
044public class AnnotationNode extends AnnotationVisitor {
045
046    /**
047     * The class descriptor of the annotation class.
048     */
049    public String desc;
050
051    /**
052     * The name value pairs of this annotation. Each name value pair is stored
053     * as two consecutive elements in the list. The name is a {@link String},
054     * and the value may be a {@link Byte}, {@link Boolean}, {@link Character},
055     * {@link Short}, {@link Integer}, {@link Long}, {@link Float},
056     * {@link Double}, {@link String} or {@link Type}, or a
057     * two elements String array (for enumeration values), an
058     * {@link AnnotationNode}, or a {@link List} of values of one of the
059     * preceding types. The list may be <tt>null</tt> if there is no name value
060     * pair.
061     */
062    public List<Object> values;
063
064    /**
065     * Constructs a new {@link AnnotationNode}. <i>Subclasses must not use this
066     * constructor</i>. Instead, they must use the
067     * {@link #AnnotationNode(int, String)} version.
068     * 
069     * @param desc
070     *            the class descriptor of the annotation class.
071     * @throws IllegalStateException
072     *             If a subclass calls this constructor.
073     */
074    public AnnotationNode(final String desc) {
075        this(Opcodes.ASM6, desc);
076        if (getClass() != AnnotationNode.class) {
077            throw new IllegalStateException();
078        }
079    }
080
081    /**
082     * Constructs a new {@link AnnotationNode}.
083     * 
084     * @param api
085     *            the ASM API version implemented by this visitor. Must be one
086     *            of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
087     * @param desc
088     *            the class descriptor of the annotation class.
089     */
090    public AnnotationNode(final int api, final String desc) {
091        super(api);
092        this.desc = desc;
093    }
094
095    /**
096     * Constructs a new {@link AnnotationNode} to visit an array value.
097     * 
098     * @param values
099     *            where the visited values must be stored.
100     */
101    AnnotationNode(final List<Object> values) {
102        super(Opcodes.ASM6);
103        this.values = values;
104    }
105
106    // ------------------------------------------------------------------------
107    // Implementation of the AnnotationVisitor abstract class
108    // ------------------------------------------------------------------------
109
110    @Override
111    public void visit(final String name, final Object value) {
112        if (values == null) {
113            values = new ArrayList<Object>(this.desc != null ? 2 : 1);
114        }
115        if (this.desc != null) {
116            values.add(name);
117        }
118        if (value instanceof byte[]) {
119            byte[] v = (byte[]) value;
120            ArrayList<Byte> l = new ArrayList<Byte>(v.length);
121            for (byte b : v) {
122                l.add(b);
123            }
124            values.add(l);
125        } else if (value instanceof boolean[]) {
126            boolean[] v = (boolean[]) value;
127            ArrayList<Boolean> l = new ArrayList<Boolean>(v.length);
128            for (boolean b : v) {
129                l.add(b);
130            }
131            values.add(l);
132        } else if (value instanceof short[]) {
133            short[] v = (short[]) value;
134            ArrayList<Short> l = new ArrayList<Short>(v.length);
135            for (short s : v) {
136                l.add(s);
137            }
138            values.add(l);
139        } else if (value instanceof char[]) {
140            char[] v = (char[]) value;
141            ArrayList<Character> l = new ArrayList<Character>(v.length);
142            for (char c : v) {
143                l.add(c);
144            }
145            values.add(l);
146        } else if (value instanceof int[]) {
147            int[] v = (int[]) value;
148            ArrayList<Integer> l = new ArrayList<Integer>(v.length);
149            for (int i : v) {
150                l.add(i);
151            }
152            values.add(l);
153        } else if (value instanceof long[]) {
154            long[] v = (long[]) value;
155            ArrayList<Long> l = new ArrayList<Long>(v.length);
156            for (long lng : v) {
157                l.add(lng);
158            }
159            values.add(l);
160        } else if (value instanceof float[]) {
161            float[] v = (float[]) value;
162            ArrayList<Float> l = new ArrayList<Float>(v.length);
163            for (float f : v) {
164                l.add(f);
165            }
166            values.add(l);
167        } else if (value instanceof double[]) {
168            double[] v = (double[]) value;
169            ArrayList<Double> l = new ArrayList<Double>(v.length);
170            for (double d : v) {
171                l.add(d);
172            }
173            values.add(l);
174        } else {
175            values.add(value);
176        }
177    }
178
179    @Override
180    public void visitEnum(final String name, final String desc,
181            final String value) {
182        if (values == null) {
183            values = new ArrayList<Object>(this.desc != null ? 2 : 1);
184        }
185        if (this.desc != null) {
186            values.add(name);
187        }
188        values.add(new String[] { desc, value });
189    }
190
191    @Override
192    public AnnotationVisitor visitAnnotation(final String name,
193                                             final String desc) {
194        if (values == null) {
195            values = new ArrayList<Object>(this.desc != null ? 2 : 1);
196        }
197        if (this.desc != null) {
198            values.add(name);
199        }
200        AnnotationNode annotation = new AnnotationNode(desc);
201        values.add(annotation);
202        return annotation;
203    }
204
205    @Override
206    public AnnotationVisitor visitArray(final String name) {
207        if (values == null) {
208            values = new ArrayList<Object>(this.desc != null ? 2 : 1);
209        }
210        if (this.desc != null) {
211            values.add(name);
212        }
213        List<Object> array = new ArrayList<Object>();
214        values.add(array);
215        return new AnnotationNode(array);
216    }
217
218    @Override
219    public void visitEnd() {
220    }
221
222    // ------------------------------------------------------------------------
223    // Accept methods
224    // ------------------------------------------------------------------------
225
226    /**
227     * Checks that this annotation node is compatible with the given ASM API
228     * version. This methods checks that this node, and all its nodes
229     * recursively, do not contain elements that were introduced in more recent
230     * versions of the ASM API than the given version.
231     * 
232     * @param api
233     *            an ASM API version. Must be one of {@link Opcodes#ASM4},
234     *            {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
235     */
236    public void check(final int api) {
237        // nothing to do
238    }
239
240    /**
241     * Makes the given visitor visit this annotation.
242     * 
243     * @param av
244     *            an annotation visitor. Maybe <tt>null</tt>.
245     */
246    public void accept(final AnnotationVisitor av) {
247        if (av != null) {
248            if (values != null) {
249                for (int i = 0; i < values.size(); i += 2) {
250                    String name = (String) values.get(i);
251                    Object value = values.get(i + 1);
252                    accept(av, name, value);
253                }
254            }
255            av.visitEnd();
256        }
257    }
258
259    /**
260     * Makes the given visitor visit a given annotation value.
261     * 
262     * @param av
263     *            an annotation visitor. Maybe <tt>null</tt>.
264     * @param name
265     *            the value name.
266     * @param value
267     *            the actual value.
268     */
269    static void accept(final AnnotationVisitor av, final String name,
270                       final Object value) {
271        if (av != null) {
272            if (value instanceof String[]) {
273                String[] typeconst = (String[]) value;
274                av.visitEnum(name, typeconst[0], typeconst[1]);
275            } else if (value instanceof AnnotationNode) {
276                AnnotationNode an = (AnnotationNode) value;
277                an.accept(av.visitAnnotation(name, an.desc));
278            } else if (value instanceof List) {
279                AnnotationVisitor v = av.visitArray(name);
280                if (v != null) {
281                    List<?> array = (List<?>) value;
282                    for (int j = 0; j < array.size(); ++j) {
283                        accept(v, null, array.get(j));
284                    }
285                    v.visitEnd();
286                }
287            } else {
288                av.visit(name, value);
289            }
290        }
291    }
292}