001    package edu.rice.cs.cunit.subAnnot;
002    
003    import edu.rice.cs.cunit.classFile.ClassFile;
004    import edu.rice.cs.cunit.classFile.ClassFileTools;
005    import edu.rice.cs.cunit.classFile.MethodInfo;
006    import edu.rice.cs.cunit.classFile.attributes.AAnnotationsAttributeInfo;
007    import edu.rice.cs.cunit.classFile.constantPool.*;
008    import edu.rice.cs.cunit.classFile.constantPool.visitors.ADefaultPoolInfoVisitor;
009    
010    import java.io.File;
011    import java.io.IOException;
012    import java.lang.annotation.Annotation;
013    import java.lang.reflect.Array;
014    import java.lang.reflect.InvocationHandler;
015    import java.lang.reflect.Method;
016    import java.lang.reflect.Proxy;
017    import java.util.*;
018    
019    /**
020     * Proxy handler for annotations.
021     *
022     * @author Mathias Ricken
023     */
024    public class AnnotationDynamicProxyHandler implements InvocationHandler {
025        /**
026         * Annotation.
027         */
028        protected Class<? extends Annotation> _annotClass;
029    
030        /**
031         * Map with key-value pairs.
032         */
033        protected Map<String, Object> _values;
034    
035        /**
036         * List of class path entries.
037         */
038        protected static final ArrayList<String> _classPath = new ArrayList<String>();
039        static {
040            _classPath.addAll(Arrays.asList(System.getProperty("sun.boot.class.path").split(File.pathSeparator)));
041            _classPath.addAll(Arrays.asList(System.getProperty("java.class.path").split(File.pathSeparator)));
042        }
043    
044        /**
045         * Constructor for the proxy handler.
046         * @param annotClass annotation class
047         * @param annot annotation information from the class file
048         */
049        public AnnotationDynamicProxyHandler(Class<? extends Annotation> annotClass, AAnnotationsAttributeInfo.Annotation annot) {
050            _annotClass = annotClass;
051            getDefaultValues();
052            for(AAnnotationsAttributeInfo.Annotation.NameValuePair nvp: annot.getPairs()) {
053                _values.put(nvp.getName().toString(), getMemberValue(nvp, _annotClass));
054            }
055        }
056    
057        /**
058         * Load default values for members not specified.
059         */
060        protected void getDefaultValues() {
061            HashMap<String, Object> defaults = new HashMap<String, Object>();
062            Queue<Class> classesToProcess = new LinkedList<Class>();
063            classesToProcess.add(_annotClass);
064            while(classesToProcess.size()>0) {
065                Class nextClass = classesToProcess.remove();
066                Method[] methods = nextClass.getDeclaredMethods();
067                for(Method m: methods) {
068                    if (m.getParameterTypes().length==0) {
069                        Object v = m.getDefaultValue();
070                        if (v!=null) {
071                            defaults.put(m.getName(), v);
072                        }
073                    }
074                }
075                classesToProcess.addAll(Arrays.asList(nextClass.getInterfaces()));
076            }
077            _values = new LinkedHashMap<String, Object>(defaults);
078        }
079    
080        /**
081         * Return the value specified in the
082         * @param nvp name-value pair
083         * @param annotClass the class of the annotation
084         * @return member value as an object
085         */
086        protected Object getMemberValue(AAnnotationsAttributeInfo.Annotation.NameValuePair nvp,
087                                        final Class<? extends Annotation> annotClass) {
088            return nvp.getValue().execute(
089                    new AAnnotationsAttributeInfo.Annotation.IMemberValueVisitor<Object, AAnnotationsAttributeInfo.Annotation.NameValuePair>() {
090                public Object constantMemberCase(final AAnnotationsAttributeInfo.Annotation.ConstantMemberValue outerHost,
091                                                 AAnnotationsAttributeInfo.Annotation.NameValuePair param) {
092                    return outerHost.getConstValue().execute(new ADefaultPoolInfoVisitor<Object,Object>() {
093                        public Object defaultCase(APoolInfo host, Object param) {
094                            throw new ClassFormatError(
095                                "Unexpected constant pool entry (" + host.getClass().getName() + "): " + host);
096                        }
097    
098                        public Object stringCase(StringPoolInfo host, Object param) {
099                            return host.getUtf().toString();
100                        }
101    
102                        public Object intCase(IntegerPoolInfo host, Object param) {
103                            switch(outerHost.getTag()) {
104                                case 'B':
105                                    return (byte)host.getIntValue();
106                                case 'C':
107                                    return (char)host.getIntValue();
108                                case 'I':
109                                    return host.getIntValue();
110                                case 'Z':
111                                    return (host.getIntValue() != 0);
112                                default:
113                                    throw new ClassFormatError(
114                                        "Unexpected tag for integer constant pool entry (" + outerHost.getTag() + "): " + host);
115                            }
116                        }
117    
118                        public Object floatCase(FloatPoolInfo host, Object param) {
119                            return host.getFloatValue();
120                        }
121    
122                        public Object longCase(LongPoolInfo host, Object param) {
123                            return host.getLongValue();
124                        }
125    
126                        public Object doubleCase(DoublePoolInfo host, Object param) {
127                            return host.getDoubleValue();
128                        }
129    
130                        public Object asciizCase(ASCIIPoolInfo host, Object param) {
131                            return host.toString();
132                        }
133    
134                        public Object unicodeCase(UnicodePoolInfo host, Object param) {
135                            return host.toString();
136                        }
137                    }, null);
138                }
139                public Object enumMemberCase(AAnnotationsAttributeInfo.Annotation.EnumMemberValue host,
140                                             AAnnotationsAttributeInfo.Annotation.NameValuePair param) {
141                    String className = host.getTypeName().toString().replace('/','.').replace('$','.');
142                    if ((className.length()==0) || (className.charAt(0)!='L')) {
143                        throw new ClassFormatError("Unexpected type name for enum constant: "+host.getTypeName().toString());
144                    }
145                    className = className.substring(1,className.length()-1);
146                    try {
147                        Class enumClass = Class.forName(className, true, annotClass.getClassLoader());
148                        String enumName = host.getConstValue().toString();
149                        for(Object o: enumClass.getEnumConstants()) {
150                            if (o.toString().equals(enumName)) { return o; }
151                        }
152                        throw new ClassFormatError("Unknown enum constant "+ enumName +" for class "+className);
153                    }
154                    catch(ClassNotFoundException e) {
155                        throw new ClassFormatError("Could not find class for "+className);
156                    }
157                }
158                public Object classMemberCase(AAnnotationsAttributeInfo.Annotation.ClassMemberValue host,
159                                              AAnnotationsAttributeInfo.Annotation.NameValuePair param) {
160                    final String name = host.getClassName().toString();
161                    try {
162                        return ClassFileTools.getClassFromType(name, true, _annotClass.getClassLoader());
163                    }
164                    catch(ClassNotFoundException e) {
165                        throw new ClassFormatError("Could not find class instance for "+ClassFileTools.getClassNameFromType(name));
166                    }
167                }
168                @SuppressWarnings("unchecked")
169                public Object annotationMemberCase(AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue host,
170                                                   AAnnotationsAttributeInfo.Annotation.NameValuePair param) {
171                    final String name = host.getAnnotation().getType();
172                    try {
173                        if ((name.length()==0) || (name.charAt(0)!='L')) {
174                            throw new ClassFormatError("Unexpected type name for annotation: "+host.getAnnotation().getType());
175                        }
176                        Class a = Class.forName(name.substring(1, name.length() - 1).replace('/', '.').replace('$', '.'), true, annotClass.getClassLoader());
177                        return Proxy.newProxyInstance(
178                                a.getClassLoader(),
179                                new Class[]{a},
180                                new AnnotationDynamicProxyHandler(a, host.getAnnotation()));
181                    }
182                    catch (ClassNotFoundException e) {
183                        throw new ClassFormatError("Could not find class instance for " + ClassFileTools.getClassNameFromType(name));
184                    }
185                }
186                public Object arrayMemberCase(final AAnnotationsAttributeInfo.Annotation.ArrayMemberValue arrHost,
187                                              final AAnnotationsAttributeInfo.Annotation.NameValuePair arrParam) {
188                    return arrHost.getEntries()[0].execute(new AAnnotationsAttributeInfo.Annotation.IMemberValueVisitor<Object,Object>() {
189                        public Object constantMemberCase(final AAnnotationsAttributeInfo.Annotation.ConstantMemberValue outerHost, Object param) {
190                            return outerHost.getConstValue().execute(new ADefaultPoolInfoVisitor<Object, Object>() {
191                                public Object defaultCase(APoolInfo host, Object param) {
192                                    throw new ClassFormatError("Unexpected constant pool entry (" +host.getClass().getName() + "): " + host);
193                                }
194                                public Object stringCase(StringPoolInfo host, Object param) {
195                                    String[] arr = new String[arrHost.getEntries().length];
196                                    int i = 0;
197                                    for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
198                                        arr[i++] = mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<String, Object>() {
199                                            public String defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
200                                                throw new ClassFormatError("Expected String as array element (was "+host.getClass().getName()+")");
201                                            }
202                                            public String constantMemberCase(AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host, Object param) {
203                                                return host.getConstValue().execute(new ADefaultPoolInfoVisitor<String,Object>() {
204                                                    public String defaultCase(APoolInfo host, Object param) {
205                                                        throw new ClassFormatError("Expected String as array element (was "+host.getClass().getName()+")");
206                                                    }
207                                                    public String unicodeCase(UnicodePoolInfo host, Object param) {
208                                                        return host.toString();
209                                                    }
210                                                    public String asciizCase(ASCIIPoolInfo host, Object param) {
211                                                        return host.toString();
212                                                    }
213                                                    public String stringCase(StringPoolInfo host, Object param) {
214                                                        return host.toString();
215                                                    }
216                                                }, host);
217                                            }
218                                        }, null);
219                                    }
220                                    return arr;
221                                }
222                                public Object intCase(IntegerPoolInfo host, Object param) {
223                                    Object temp;
224                                    switch (outerHost.getTag()) {
225                                        case 'B':
226                                            temp = new byte[arrHost.getEntries().length];
227                                            break;
228                                        case 'C':
229                                            temp = new char[arrHost.getEntries().length];
230                                            break;
231                                        case 'I':
232                                            temp = new int[arrHost.getEntries().length];
233                                            break;
234                                        case 'Z':
235                                            temp = new boolean[arrHost.getEntries().length];
236                                            break;
237                                        default:
238                                            throw new ClassFormatError("Unexpected tag for integer constant pool entry ("+outerHost.getTag() + "): " + host);
239                                    }
240                                    final Object arr = temp;
241                                    int i = 0;
242                                    for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
243                                        mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Object, Integer>() {
244                                            public Object defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Integer param) {
245                                                throw new ClassFormatError("Expected integer constant as array element (was "+host.getClass().getName()+")");
246                                            }
247                                            public Object constantMemberCase(AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host, final Integer index) {
248                                                return host.getConstValue().execute(new ADefaultPoolInfoVisitor<Object, Object>() {
249                                                    public Object defaultCase(APoolInfo host, Object param) {
250                                                        throw new ClassFormatError("Expected integer constant as array element (was "+host.getClass().getName()+")");
251                                                    }
252                                                    public Object intCase(IntegerPoolInfo host, Object param) {
253                                                        switch (outerHost.getTag()) {
254                                                            case 'B':
255                                                                Array.setByte(arr, index, (byte)host.getIntValue());
256                                                                return null;
257                                                            case 'C':
258                                                                Array.setChar(arr, index, (char)host.getIntValue());
259                                                                return null;
260                                                            case 'I':
261                                                                Array.setInt(arr, index, host.getIntValue());
262                                                                return null;
263                                                            case 'Z':
264                                                                Array.setBoolean(arr, index, (host.getIntValue() != 0));
265                                                                return null;
266                                                            default:
267                                                                throw new ClassFormatError("Unexpected tag for integer constant pool entry (" +
268                                                                        outerHost.getTag() + "): " + host);
269                                                        }
270                                                    }
271                                                }, null);
272                                            }
273                                        }, i++);
274                                    }
275                                    return arr;
276                                }
277                                public Object floatCase(FloatPoolInfo host, Object param) {
278                                    float[] arr = new float[arrHost.getEntries().length];
279                                    int i = 0;
280                                    for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
281                                        arr[i++] = mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Float, Object>() {
282                                            public Float defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
283                                                throw new ClassFormatError("Expected float as array element (was "+host.getClass().getName()+")");
284                                            }
285                                            public Float constantMemberCase(AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host, Object param) {
286                                                return host.getConstValue().execute(new ADefaultPoolInfoVisitor<Float,Object>() {
287                                                    public Float defaultCase(APoolInfo host, Object param) {
288                                                        throw new ClassFormatError("Expected float as array element (was "+host.getClass().getName()+")");
289                                                    }
290                                                    public Float floatCase(FloatPoolInfo host, Object param) {
291                                                        return host.getFloatValue();
292                                                    }
293                                                }, host);
294                                            }
295                                        }, null);
296                                    }
297                                    return arr;
298                                }
299                                public Object longCase(LongPoolInfo host, Object param) {
300                                    long[] arr = new long[arrHost.getEntries().length];
301                                    int i = 0;
302                                    for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
303                                        arr[i++] = mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Long, Object>() {
304                                            public Long defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
305                                                throw new ClassFormatError("Expected long as array element (was "+host.getClass().getName()+")");
306                                            }
307                                            public Long constantMemberCase(AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host, Object param) {
308                                                return host.getConstValue().execute(new ADefaultPoolInfoVisitor<Long,Object>() {
309                                                    public Long defaultCase(APoolInfo host, Object param) {
310                                                        throw new ClassFormatError("Expected long as array element (was "+host.getClass().getName()+")");
311                                                    }
312                                                    public Long longCase(LongPoolInfo host, Object param) {
313                                                        return host.getLongValue();
314                                                    }
315                                                }, host);
316                                            }
317                                        }, null);
318                                    }
319                                    return arr;
320                                }
321                                public Object doubleCase(DoublePoolInfo host, Object param) {
322                                    double[] arr = new double[arrHost.getEntries().length];
323                                    int i = 0;
324                                    for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
325                                        arr[i++] = mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Double, Object>() {
326                                            public Double defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
327                                                throw new ClassFormatError("Expected double as array element (was "+host.getClass().getName()+")");
328                                            }
329                                            public Double constantMemberCase(AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host, Object param) {
330                                                return host.getConstValue().execute(new ADefaultPoolInfoVisitor<Double,Object>() {
331                                                    public Double defaultCase(APoolInfo host, Object param) {
332                                                        throw new ClassFormatError("Expected double as array element (was "+host.getClass().getName()+")");
333                                                    }
334                                                    public Double doubleCase(DoublePoolInfo host, Object param) {
335                                                        return host.getDoubleValue();
336                                                    }
337                                                }, host);
338                                            }
339                                        }, null);
340                                    }
341                                    return arr;
342                                }
343                                public Object asciizCase(ASCIIPoolInfo host, Object param) {
344                                    String[] arr = new String[arrHost.getEntries().length];
345                                    int i = 0;
346                                    for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
347                                        arr[i++] = mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<String, Object>() {
348                                            public String defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
349                                                throw new ClassFormatError("Expected String as array element (was "+host.getClass().getName()+")");
350                                            }
351                                            public String constantMemberCase(AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host, Object param) {
352                                                return host.getConstValue().execute(new ADefaultPoolInfoVisitor<String,Object>() {
353                                                    public String defaultCase(APoolInfo host, Object param) {
354                                                        throw new ClassFormatError("Expected String as array element (was "+host.getClass().getName()+")");
355                                                    }
356                                                    public String unicodeCase(UnicodePoolInfo host, Object param) {
357                                                        return host.toString();
358                                                    }
359                                                    public String asciizCase(ASCIIPoolInfo host, Object param) {
360                                                        return host.toString();
361                                                    }
362                                                    public String stringCase(StringPoolInfo host, Object param) {
363                                                        return host.toString();
364                                                    }
365                                                }, host);
366                                            }
367                                        }, null);
368                                    }
369                                    return arr;
370                                }
371                                public Object unicodeCase(UnicodePoolInfo host, Object param) {
372                                    String[] arr = new String[arrHost.getEntries().length];
373                                    int i = 0;
374                                    for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
375                                        arr[i++] = mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<String, Object>() {
376                                            public String defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
377                                                throw new ClassFormatError("Expected String as array element (was "+host.getClass().getName()+")");
378                                            }
379                                            public String constantMemberCase(AAnnotationsAttributeInfo.Annotation.ConstantMemberValue host, Object param) {
380                                                return host.getConstValue().execute(new ADefaultPoolInfoVisitor<String,Object>() {
381                                                    public String defaultCase(APoolInfo host, Object param) {
382                                                        throw new ClassFormatError("Expected String as array element (was "+host.getClass().getName()+")");
383                                                    }
384                                                    public String unicodeCase(UnicodePoolInfo host, Object param) {
385                                                        return host.toString();
386                                                    }
387                                                    public String asciizCase(ASCIIPoolInfo host, Object param) {
388                                                        return host.toString();
389                                                    }
390                                                    public String stringCase(StringPoolInfo host, Object param) {
391                                                        return host.toString();
392                                                    }
393                                                }, host);
394                                            }
395                                        }, null);
396                                    }
397                                    return arr;
398                                }
399                            },null);
400                        }
401    
402                        public Object enumMemberCase(AAnnotationsAttributeInfo.Annotation.EnumMemberValue host, Object param) {
403                            String tn = host.getTypeName().toString();
404                            if ((tn.length()==0) || (tn.charAt(0)!='L')) {
405                                throw new ClassFormatError("Unexpected type name for enum constant: "+host.getTypeName().toString());
406                            }
407                            final String className = tn.replace('/', '.').replace('$', '.').substring(1, tn.length() - 1);
408                            try {
409                                final Class enumClass = Class.forName(className, true, annotClass.getClassLoader());
410                                Object arr = Array.newInstance(enumClass, arrHost.getEntries().length);
411                                int i = 0;
412                                for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
413                                    Array.set(arr, i++, mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Object, Object>() {
414                                        public Object defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
415                                            throw new ClassFormatError("Expected enum of type " + enumClass.getName() + " as array element");
416                                        }
417                                        public Object enumMemberCase(AAnnotationsAttributeInfo.Annotation.EnumMemberValue host, Object param) {
418                                            String enumName = host.getConstValue().toString();
419                                            for (Object o : enumClass.getEnumConstants()) {
420                                                if (o.toString().equals(enumName)) {
421                                                    return o;
422                                                }
423                                            }
424                                            throw new ClassFormatError("Unknown enum constant " + enumName + " for class " + className);
425                                        }
426                                    }, null));
427                                }
428                                return arr;
429                            }
430                            catch (ClassNotFoundException e) {
431                                throw new ClassFormatError("Could not find class for " + className);
432                            }
433                        }
434    
435                        public Object classMemberCase(AAnnotationsAttributeInfo.Annotation.ClassMemberValue host, Object param) {
436                            Class[] arr = new Class[arrHost.getEntries().length];
437                            int i = 0;
438                            for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
439                                arr[i++] = mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Class, Object>() {
440                                    public Class defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
441                                        throw new ClassFormatError("Expected Class object as array element");
442                                    }
443    
444                                    public Class classMemberCase(AAnnotationsAttributeInfo.Annotation.ClassMemberValue host, Object param) {
445                                        String name = host.getClassName().toString();
446                                        try {
447                                            return ClassFileTools.getClassFromType(name);
448                                        } catch (ClassNotFoundException e) {
449                                            throw new ClassFormatError("Could not find class instance for "+name);
450                                        }
451                                    }
452                                }, null);
453                            }
454                            return arr;
455                        }
456    
457                        @SuppressWarnings("unchecked")
458                        public Object annotationMemberCase(AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue host, Object param) {
459                            // create array based on member type in annotation definition, NOT based on first element
460                            String cn = annotClass.getName();
461                            ClassFileTools.ClassLocation cl = ClassFileTools.findClassFile(cn, _classPath);
462                            if (cl==null) {
463                                throw new ClassFormatError("Could not find class file for "+cn);
464                            }
465                            ClassFile cf = cl.getClassFile();
466                            try {
467                                cl.close();
468                            }
469                            catch (IOException e) { /* ignore */ }
470                            String memberName = arrParam.getName().toString();
471                            String retType = null;
472                            for(MethodInfo mi: cf.getMethods()) {
473                                if (mi.getName().toString().equals(memberName)) {
474                                    retType = mi.getDescriptor().toString();
475                                    retType = retType.substring(retType.lastIndexOf(')')+1);
476                                }
477                            }
478                            if (retType==null) {
479                                throw new ClassFormatError("Could not find member "+memberName+" in class file for "+cn);
480                            }
481                            if ((!retType.startsWith("[L")) || (!retType.endsWith(";"))) {
482                                throw new ClassFormatError("Array of annotations return type expected for member "+memberName+" in class file for "+cn);
483                            }
484    
485                            final String arrName = retType.replace('/', '.').replace('$', '.').substring(2, retType.length() - 1);
486                            try {
487                                // this is the array element class
488                                final Class arrClass = Class.forName(arrName, true, annotClass.getClassLoader());
489                                Object arr = Array.newInstance(arrClass, arrHost.getEntries().length);
490                                int i = 0;
491                                for (AAnnotationsAttributeInfo.Annotation.AMemberValue mv : arrHost.getEntries()) {
492                                    Array.set(arr, i++, mv.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Object, Object>() {
493                                        public Object defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Object param) {
494                                            throw new ClassFormatError("Expected annotation of type " + arrName + " as array element");
495                                        }
496                                        @SuppressWarnings("unchecked")
497                                        public Object annotationMemberCase(AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue host, Object param) {
498                                            // create elements based on their own type, provided they are subtypes of the array element type
499                                            String tn = host.getAnnotation().getType();
500                                            if ((tn.length()==0) || (tn.charAt(0)!='L')) {
501                                               throw new ClassFormatError("Unexpected type name for enum constant: "+host.getAnnotation().toString());
502                                            }
503                                            final String eltName = tn.replace('/', '.').replace('$', '.').substring(1, tn.length() - 1);
504                                            try {
505                                                final Class eltClass = Class.forName(eltName, true, arrClass.getClassLoader());
506                                                return Proxy.newProxyInstance(
507                                                        eltClass.getClassLoader(),
508                                                        new Class[]{eltClass},
509                                                        new AnnotationDynamicProxyHandler(eltClass, host.getAnnotation()));
510                                            } catch (ClassNotFoundException e) {
511                                                throw new ClassFormatError("Could not find class for " + eltName);
512                                            }
513    
514                                        }
515                                    }, null));
516                                }
517                                return arr;
518                            }
519                            catch (ClassNotFoundException e) {
520                                throw new ClassFormatError("Could not find class for " + arrName);
521                            }
522                        }
523    
524                        public Object arrayMemberCase(AAnnotationsAttributeInfo.Annotation.ArrayMemberValue host, Object param) {
525                            throw new ClassFormatError("Array of arrays not supported as annotation member");
526                        }
527                    }, null);
528                }
529            }, nvp);
530        }
531    
532        /**
533         * Processes a method invocation on a proxy instance and returns the result.  This method will be invoked on an
534         * invocation handler when a method is invoked on a proxy instance that it is associated with.
535         *
536         * @param    proxy the proxy instance that the method was invoked on
537         * @param    m the <code>Method</code> instance corresponding to the interface method invoked on the proxy
538         * instance.
539         * @param    args an array of objects containing the values of the arguments passed in the method invocation on the
540         * proxy instance, or <code>null</code> if interface method takes no arguments. Arguments of primitive types are
541         * wrapped in instances of the appropriate primitive wrapper class, such as <code>java.lang.Integer</code> or
542         * <code>java.lang.Boolean</code>.
543         * @return the value to return from the method invocation on the proxy instance.  If the declared return type of the
544         * interface method is a primitive type, then the value returned by this method must be an instance of the
545         * corresponding primitive wrapper class; otherwise, it must be a type assignable to the declared return type.  If
546         * the value returned by this method is <code>null</code> and the interface method's return type is primitive, then
547         * a <code>NullPointerException</code> will be thrown by the method invocation on the proxy instance.  If the value
548         * returned by this method is otherwise not compatible with the interface method's declared return type as described
549         * above, a <code>ClassCastException</code> will be thrown by the method invocation on the proxy instance.
550         * @throws Throwable the exception to throw from the method invocation on the proxy instance.  The exception's type
551         * must be assignable either to any of the exception types declared in the <code>throws</code> clause of the
552         * interface method or to the unchecked exception types <code>java.lang.RuntimeException</code> or
553         * <code>java.lang.Error</code>.  If a checked exception is thrown by this method that is not assignable to any of
554         * the exception types declared in the <code>throws</code> clause of the interface method, then an {@link
555         * java.lang.reflect.UndeclaredThrowableException} containing the exception that was thrown by this method will be
556         * thrown by the method invocation on the proxy instance.
557         * @see    java.lang.reflect.UndeclaredThrowableException
558         */
559        public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
560            Class<?>[] parTypes = m.getParameterTypes();
561            if ((m.getName().equals("toString")) &&
562                (parTypes.length==0)) {
563                // handle toString
564                // TODO: figure out sort order for toString of annotations
565                StringBuilder sb = new StringBuilder();
566                sb.append('@');
567                sb.append(_annotClass.getName());
568                LinkedHashMap<String,Object> kvPairs = new LinkedHashMap<String,Object>();
569                for(Method method: _annotClass.getMethods()) {
570                    if ((!method.getDeclaringClass().equals(Object.class)) &&
571                        (!method.getDeclaringClass().equals(Annotation.class)) &&
572                        (method.getParameterTypes().length==0)) {
573                        kvPairs.put(method.getName(), _values.get(method.getName()));
574                    }
575                }
576                if (kvPairs.size()>0) {
577                    sb.append('(');
578                    boolean comma = false;
579                    for(Map.Entry<String,Object> entry: kvPairs.entrySet()) {
580                        if (comma) { sb.append(", "); } else { comma = true; }
581                        sb.append(entry.getKey());
582                        sb.append('=');
583                        if ((entry.getValue()!=null) && (entry.getValue().getClass().isArray())) {
584                            sb.append('[');
585                            for(int i=0; i<Array.getLength(entry.getValue()); ++i) {
586                                if (i>0) { sb.append(", "); }
587                                sb.append(Array.get(entry.getValue(), i));
588                            }
589                            sb.append(']');
590                        }
591                        else {
592                            sb.append(entry.getValue());
593                        }
594                    }
595                    sb.append(')');
596                }
597                return sb.toString();
598            }
599            else if ((m.getName().equals("equals")) &&
600                     (parTypes.length==1)) {
601                // handle equals
602                if (args[0]==null) { return false; }
603                if (!(args[0] instanceof Annotation)) { return false; }
604                Annotation ours = (Annotation)proxy;
605                Annotation other = (Annotation)args[0];
606                if (!other.annotationType().equals(ours.annotationType())) { return false; }
607                for(Method method: ours.annotationType().getMethods()) {
608                    if ((!method.getDeclaringClass().equals(Object.class)) &&
609                        (!method.getDeclaringClass().equals(Annotation.class)) &&
610                        (method.getParameterTypes().length==0)) {
611                        Method otherMethod;
612                        try {
613                            otherMethod = other.annotationType().getMethod(method.getName(), method.getParameterTypes());
614                        }
615                        catch(Exception e) {
616                            return false;
617                        }
618                        Object value = method.invoke(proxy);
619                        Object otherValue = otherMethod.invoke(other);
620                        if ((value==null) && (otherValue!=null) ||
621                            (value!=null) && (otherValue==null)) { return false; }
622                        if ((value!=null) && (!value.equals(otherValue))) { return false; }
623                    }
624                }
625                return true;
626            }
627            else if ((m.getName().equals("hashCode")) &&
628                     (parTypes.length==0)) {
629                // handle hashCode
630                int result = 0;
631                for(Object value: _values.values()) {
632                    result = 31 * result + (value != null ? value.hashCode() : 0);
633                }
634                return result;
635            }
636            else if ((m.getName().equals("annotationType")) &&
637                     (parTypes.length==0)) {
638                // handle annotationType
639                return _annotClass;
640            }
641            else {
642                // handle annotation member methods
643                if ((!m.getDeclaringClass().equals(Object.class)) &&
644                    (!m.getDeclaringClass().equals(Annotation.class)) &&
645                    (m.getParameterTypes().length==0)) {
646                    return _values.get(m.getName());
647                }
648                throw new NoSuchMethodException("Unexpected method "+m);
649            }
650        }
651    }