2
Copyright (C) 2005-2011 Jeroen Frijters
4
This software is provided 'as-is', without any express or implied
5
warranty. In no event will the authors be held liable for any damages
6
arising from the use of this software.
8
Permission is granted to anyone to use this software for any purpose,
9
including commercial applications, and to alter it and redistribute it
10
freely, subject to the following restrictions:
12
1. The origin of this software must not be misrepresented; you must not
13
claim that you wrote the original software. If you use this software
14
in a product, an acknowledgment in the product documentation would be
15
appreciated but is not required.
16
2. Altered source versions must be plainly marked as such, and must not be
17
misrepresented as being the original software.
18
3. This notice may not be removed or altered from any source distribution.
24
package ikvm.internal;
26
import cli.System.Reflection.BindingFlags;
28
import java.io.Serializable;
29
import java.lang.annotation.Annotation;
30
import java.lang.reflect.Array;
31
import java.lang.reflect.InvocationHandler;
32
import java.lang.reflect.InvocationTargetException;
33
import java.lang.reflect.Method;
34
import java.lang.reflect.Proxy;
35
import java.util.Arrays;
36
import java.util.Iterator;
38
import java.util.HashMap;
40
public abstract class AnnotationAttributeBase
41
extends cli.System.Attribute
42
implements Annotation, Serializable
44
private final HashMap values = new HashMap();
45
private final Class annotationType;
46
private boolean frozen;
48
protected AnnotationAttributeBase(Class annotationType)
50
this.annotationType = annotationType;
53
protected final Object getValue(String name)
56
return values.get(name);
59
protected final byte getByteValue(String name)
62
return ((Byte)values.get(name)).byteValue();
65
protected final boolean getBooleanValue(String name)
68
return ((Boolean)values.get(name)).booleanValue();
71
protected final short getShortValue(String name)
74
return ((Short)values.get(name)).shortValue();
77
protected final char getCharValue(String name)
80
return ((Character)values.get(name)).charValue();
83
protected final int getIntValue(String name)
86
return ((Integer)values.get(name)).intValue();
89
protected final float getFloatValue(String name)
92
return ((Float)values.get(name)).floatValue();
95
protected final long getLongValue(String name)
98
return ((Long)values.get(name)).longValue();
101
protected final double getDoubleValue(String name)
104
return ((Double)values.get(name)).doubleValue();
107
protected final synchronized void setValue(String name, Object value)
111
throw new IllegalStateException("Annotation properties have already been defined");
115
Class type = annotationType.getMethod(name).getReturnType();
118
value = type.getMethod("valueOf", String.class).invoke(null, value.toString());
120
else if(type == Class.class)
122
value = ikvm.runtime.Util.getFriendlyClassFromType((cli.System.Type)value);
124
else if(type == boolean.class)
126
value = ikvm.lang.CIL.unbox_boolean(value);
128
else if(type == byte.class)
130
value = ikvm.lang.CIL.unbox_byte(value);
132
else if(type == short.class)
134
value = ikvm.lang.CIL.unbox_short(value);
136
else if(type == char.class)
138
value = ikvm.lang.CIL.unbox_char(value);
140
else if(type == int.class)
142
value = ikvm.lang.CIL.unbox_int(value);
144
else if(type == long.class)
146
value = ikvm.lang.CIL.unbox_long(value);
148
else if(type == float.class)
150
value = ikvm.lang.CIL.unbox_float(value);
152
else if(type == double.class)
154
value = ikvm.lang.CIL.unbox_double(value);
156
else if(type == String.class)
158
// no conversion needed
160
else if(type.isArray())
162
type = type.getComponentType();
165
Method valueOf = type.getMethod("valueOf", String.class);
166
cli.System.Array orgarray = (cli.System.Array)value;
167
Object[] array = (Object[])Array.newInstance(type, orgarray.get_Length());
168
for(int i = 0; i < array.length; i++)
170
array[i] = valueOf.invoke(null, orgarray.GetValue(i).toString());
174
else if(type == Class.class)
176
cli.System.Type[] orgarray = (cli.System.Type[])value;
177
Class[] array = new Class[orgarray.length];
178
for(int i = 0; i < array.length; i++)
180
array[i] = ikvm.runtime.Util.getFriendlyClassFromType(orgarray[i]);
186
// no conversion needed
191
throw new InternalError("Invalid annotation type: " + type);
193
values.put(name, value);
195
catch (NoSuchMethodException x)
197
throw (NoSuchMethodError)new NoSuchMethodError().initCause(x);
199
catch (IllegalAccessException x)
201
throw (IllegalAccessError)new IllegalAccessError().initCause(x);
203
catch (InvocationTargetException x)
205
throw (InternalError)new InternalError().initCause(x);
209
protected final synchronized void setDefinition(Object[] array)
213
throw new IllegalStateException("Annotation properties have already been defined");
216
// TODO consider checking that the type matches
217
// (or better yet (?), remove the first two redundant elements from the array)
218
decodeValues(values, annotationType, annotationType.getClassLoader(), array);
222
public Map getValues()
228
public synchronized void freeze()
233
setDefaults(values, annotationType);
237
private static void decodeValues(HashMap map, Class annotationClass, ClassLoader loader, Object[] array)
239
for (int i = 2; i < array.length; i += 2)
241
String name = (String)array[i];
244
Method method = annotationClass.getDeclaredMethod(name, new Class[0]);
245
map.put(name, decodeElementValue(array[i + 1], method.getReturnType(), loader));
247
catch (IllegalAccessException x)
249
// TODO this probably isn't the right exception
250
throw new IncompatibleClassChangeError();
252
catch (NoSuchMethodException x)
254
// TODO this probably isn't the right exception
255
throw new IncompatibleClassChangeError("Method " + name + " is missing in annotation " + annotationClass.getName());
258
setDefaults(map, annotationClass);
261
private static void setDefaults(HashMap map, Class annotationClass)
263
for (Method m : annotationClass.getDeclaredMethods())
265
Object defaultValue = m.getDefaultValue();
266
// TODO throw exception if default is missing for method that doesn't yet have a value
267
if (defaultValue != null && !map.containsKey(m.getName()))
269
map.put(m.getName(), defaultValue);
274
private static Class classFromSig(ClassLoader loader, String name)
276
if (name.startsWith("L") && name.endsWith(";"))
278
name = name.substring(1, name.length() - 1).replace('/', '.');
280
else if (name.startsWith("["))
282
name = name.replace('/', '.');
284
else if (name.length() == 1)
286
switch (name.charAt(0))
293
return Character.TYPE;
307
throw new TypeNotPresentException(name, null);
312
return Class.forName(name, false, loader);
314
catch (ClassNotFoundException x)
316
throw new TypeNotPresentException(name, x);
320
public static Object newAnnotation(ClassLoader loader, Object definition)
322
Object[] array = (Object[])definition;
323
byte tag = CIL.unbox_byte(array[0]);
325
throw new ClassCastException();
326
Object classNameOrClass = array[1];
327
Class annotationClass;
328
if (classNameOrClass instanceof String)
330
annotationClass = classFromSig(loader, (String)classNameOrClass);
331
array[1] = annotationClass;
335
annotationClass = (Class)classNameOrClass;
337
HashMap map = new HashMap();
338
decodeValues(map, annotationClass, loader, array);
339
return Proxy.newProxyInstance(annotationClass.getClassLoader(), new Class<?>[] { annotationClass }, newAnnotationInvocationHandler(annotationClass, map));
342
public static Object decodeElementValue(Object obj, Class type, ClassLoader loader)
343
throws IllegalAccessException
345
if (obj instanceof Object[] && CIL.unbox_byte(((Object[])obj)[0]) == '?')
350
Object[] error = (Object[])obj;
351
t = (Throwable)Class.forName((String)error[1]).getConstructor(String.class).newInstance(error[2]);
357
sun.misc.Unsafe.getUnsafe().throwException(t);
359
if (type == Byte.TYPE)
361
return new Byte(CIL.unbox_byte(obj));
363
else if (type == Boolean.TYPE)
365
return new Boolean(CIL.unbox_boolean(obj));
367
else if (type == Short.TYPE)
369
return new Short(CIL.unbox_short(obj));
371
else if (type == Character.TYPE)
373
return new Character(CIL.unbox_char(obj));
375
else if (type == Integer.TYPE)
377
return new Integer(CIL.unbox_int(obj));
379
else if (type == Float.TYPE)
381
return new Float(CIL.unbox_float(obj));
383
else if (type == Long.TYPE)
385
return new Long(CIL.unbox_long(obj));
387
else if (type == Double.TYPE)
389
return new Double(CIL.unbox_double(obj));
391
else if (type == String.class)
395
else if (type == Class.class)
397
Object[] array = (Object[])obj;
398
byte tag = CIL.unbox_byte(array[0]);
400
throw new ClassCastException();
401
return classFromSig(loader, (String)array[1]);
403
else if (type.isArray())
405
Object[] array = (Object[])obj;
406
byte tag = CIL.unbox_byte(array[0]);
408
throw new ClassCastException();
409
type = type.getComponentType();
410
Object dst = Array.newInstance(type, array.length - 1);
411
for (int i = 0; i < array.length - 1; i++)
413
Array.set(dst, i, decodeElementValue(array[i + 1], type, loader));
417
else if (type.isAnnotation())
419
return type.cast(newAnnotation(loader, obj));
421
else if (type.isEnum())
423
Object[] array = (Object[])obj;
424
byte tag = CIL.unbox_byte(array[0]);
426
throw new ClassCastException();
427
Class enumClass = classFromSig(loader, (String)array[1]);
430
return Enum.valueOf(enumClass, (String)array[2]);
432
catch (IllegalArgumentException x)
434
throw new EnumConstantNotPresentException(enumClass, (String)array[2]);
439
throw new ClassCastException();
443
protected final Object writeReplace()
445
return Proxy.newProxyInstance(annotationType.getClassLoader(),
446
new Class[] { annotationType },
447
newAnnotationInvocationHandler(annotationType, values));
450
private static cli.System.Reflection.ConstructorInfo annotationInvocationHandlerConstructor;
452
private static InvocationHandler newAnnotationInvocationHandler(Class type, Map memberValues)
454
if (annotationInvocationHandlerConstructor == null)
456
cli.System.Type typeofClass = cli.System.Type.GetType("java.lang.Class");
457
cli.System.Type typeofMap = cli.System.Type.GetType("java.util.Map");
458
annotationInvocationHandlerConstructor = cli.System.Type.GetType("sun.reflect.annotation.AnnotationInvocationHandler")
459
.GetConstructor(BindingFlags.wrap(BindingFlags.Instance | BindingFlags.NonPublic), null, new cli.System.Type[] { typeofClass, typeofMap }, null);
461
return (InvocationHandler)annotationInvocationHandlerConstructor.Invoke(new Object[] { type, memberValues });
464
public final Class<? extends Annotation> annotationType()
466
return annotationType;
469
public final boolean Equals(Object o)
471
return equals(annotationType, values, o);
474
public final int GetHashCode()
476
return hashCode(annotationType, values);
479
public final String ToString()
481
return toString(annotationType, values);
484
private static boolean equals(Class type, Map memberValues, Object other)
486
if (type.isInstance(other))
490
Method[] methods = type.getDeclaredMethods();
491
if (methods.length == memberValues.size())
493
for (int i = 0; i < methods.length; i++)
495
String key = methods[i].getName();
496
Object val = methods[i].invoke(other, new Object[0]);
497
if (! deepEquals(memberValues.get(key), val))
505
catch (IllegalAccessException _)
507
// Ignore exception, like the JDK
509
catch (InvocationTargetException _)
511
// Ignore exception, like the JDK
517
private static boolean deepEquals(Object o1, Object o2)
522
if (o1 == null || o2 == null)
525
if (o1 instanceof boolean[] && o2 instanceof boolean[])
526
return Arrays.equals((boolean[]) o1, (boolean[]) o2);
528
if (o1 instanceof byte[] && o2 instanceof byte[])
529
return Arrays.equals((byte[]) o1, (byte[]) o2);
531
if (o1 instanceof char[] && o2 instanceof char[])
532
return Arrays.equals((char[]) o1, (char[]) o2);
534
if (o1 instanceof short[] && o2 instanceof short[])
535
return Arrays.equals((short[]) o1, (short[]) o2);
537
if (o1 instanceof int[] && o2 instanceof int[])
538
return Arrays.equals((int[]) o1, (int[]) o2);
540
if (o1 instanceof float[] && o2 instanceof float[])
541
return Arrays.equals((float[]) o1, (float[]) o2);
543
if (o1 instanceof long[] && o2 instanceof long[])
544
return Arrays.equals((long[]) o1, (long[]) o2);
546
if (o1 instanceof double[] && o2 instanceof double[])
547
return Arrays.equals((double[]) o1, (double[]) o2);
549
if (o1 instanceof Object[] && o2 instanceof Object[])
550
return Arrays.equals((Object[]) o1, (Object[]) o2);
552
return o1.equals(o2);
555
private static int deepHashCode(Object obj)
557
if (obj instanceof boolean[])
558
return Arrays.hashCode((boolean[]) obj);
560
if (obj instanceof byte[])
561
return Arrays.hashCode((byte[]) obj);
563
if (obj instanceof char[])
564
return Arrays.hashCode((char[]) obj);
566
if (obj instanceof short[])
567
return Arrays.hashCode((short[]) obj);
569
if (obj instanceof int[])
570
return Arrays.hashCode((int[]) obj);
572
if (obj instanceof float[])
573
return Arrays.hashCode((float[]) obj);
575
if (obj instanceof long[])
576
return Arrays.hashCode((long[]) obj);
578
if (obj instanceof double[])
579
return Arrays.hashCode((double[]) obj);
581
if (obj instanceof Object[])
582
return Arrays.hashCode((Object[]) obj);
584
return obj.hashCode();
587
private static int hashCode(Class type, Map memberValues)
590
Iterator iter = memberValues.keySet().iterator();
591
while (iter.hasNext())
593
Object key = iter.next();
594
Object val = memberValues.get(key);
595
h += deepHashCode(val) ^ 127 * key.hashCode();
600
private static String deepToString(Object obj)
602
if (obj instanceof boolean[])
603
return Arrays.toString((boolean[]) obj);
605
if (obj instanceof byte[])
606
return Arrays.toString((byte[]) obj);
608
if (obj instanceof char[])
609
return Arrays.toString((char[]) obj);
611
if (obj instanceof short[])
612
return Arrays.toString((short[]) obj);
614
if (obj instanceof int[])
615
return Arrays.toString((int[]) obj);
617
if (obj instanceof float[])
618
return Arrays.toString((float[]) obj);
620
if (obj instanceof long[])
621
return Arrays.toString((long[]) obj);
623
if (obj instanceof double[])
624
return Arrays.toString((double[]) obj);
626
if (obj instanceof Object[])
627
return Arrays.toString((Object[]) obj);
629
return obj.toString();
632
private static String toString(Class type, Map memberValues)
634
StringBuffer sb = new StringBuffer();
635
sb.append('@').append(type.getName()).append('(');
637
Iterator iter = memberValues.keySet().iterator();
638
while (iter.hasNext())
640
Object key = iter.next();
641
Object val = memberValues.get(key);
642
sb.append(sep).append(key).append('=').append(deepToString(val));
646
return sb.toString();