1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.harmony.lang.annotation; 19 20 import java.io.ByteArrayInputStream; 21 import java.io.ByteArrayOutputStream; 22 import java.io.ObjectInputStream; 23 import java.io.ObjectOutputStream; 24 import java.io.Serializable; 25 import java.lang.annotation.AnnotationTypeMismatchException; 26 import java.lang.reflect.Array; 27 import java.lang.reflect.Method; 28 import java.util.Arrays; 29 30 /** 31 * This class represents member element of an annotation. 32 * It consists of name and value, supplemented with element 33 * definition information (such as declared type of element). 34 * <br>The value may be one of the following types: 35 * <ul> 36 * <li> boxed primitive 37 * <li> Class 38 * <li> enum constant 39 * <li> annotation (nested) 40 * <li> one-dimensional array of the above 41 * <li> Throwable 42 * </ul> 43 * The last type is specific for this implementation; a Throwable value 44 * means that the error occured during parsing or resolution of corresponding 45 * class-data structures and throwing is delayed until the element 46 * is requested for value. 47 * 48 * @see android.lang.annotation.AnnotationFactory 49 * 50 * @author Alexey V. Varlamov, Serguei S. Zapreyev 51 * @version $Revision$ 52 */ 53 @SuppressWarnings({"serial"}) 54 public class AnnotationMember implements Serializable { 55 56 /** 57 * Tag description of a Throwable value type. 58 */ 59 protected static final char ERROR = '!'; 60 61 /** 62 * Tag description of an array value type. 63 */ 64 protected static final char ARRAY = '['; 65 66 /** 67 * Tag description of all value types except arrays and Throwables. 68 */ 69 protected static final char OTHER = '*'; 70 71 // public static final char INT = 'I'; 72 // public static final char CHAR = 'C'; 73 // public static final char DOUBLE = 'D'; 74 // public static final char FLOAT = 'F'; 75 // public static final char BYTE = 'B'; 76 // public static final char LONG = 'J'; 77 // public static final char SHORT = 'S'; 78 // public static final char BOOL = 'Z'; 79 // public static final char CLASS = 'c'; 80 // public static final char ENUM = 'e'; 81 // public static final char ANTN = '@'; 82 83 private enum DefaultValues {NO_VALUE} 84 85 /** 86 * Singleton representing missing element value. 87 */ 88 protected static final Object NO_VALUE = DefaultValues.NO_VALUE; 89 90 protected final String name; 91 protected final Object value; // a primitive value is wrapped to the corresponding wrapper class 92 protected final char tag; 93 // no sense to serialize definition info as it can be changed arbitrarily 94 protected transient Class<?> elementType; 95 protected transient Method definingMethod; 96 97 98 /** 99 * Creates a new element with specified name and value. 100 * Definition info will be provided later when this 101 * element becomes actual annotation member. 102 * @param name element name, must not be null 103 * @param val element value, should be of addmissible type, 104 * as specified in the description of this class 105 * 106 * @see #setDefinition(AnnotationMember) 107 */ 108 public AnnotationMember(String name, Object val) { 109 this.name = name; 110 value = val == null ? NO_VALUE : val; 111 if (value instanceof Throwable) { 112 tag = ERROR; 113 } else if (value.getClass().isArray()) { 114 tag = ARRAY; 115 } else { 116 tag = OTHER; 117 } 118 } 119 120 /** 121 * Creates the completely defined element. 122 * @param name element name, must not be null 123 * @param value element value, should be of addmissible type, 124 * as specified in the description of this class 125 * @param m element-defining method, reflected on the annotation type 126 * @param type declared type of this element 127 * (return type of the defining method) 128 */ 129 public AnnotationMember(String name, Object val, Class type, Method m) { 130 this(name, val); 131 132 definingMethod = m; 133 134 if (type == int.class) { 135 elementType = Integer.class; 136 } else if (type == boolean.class) { 137 elementType = Boolean.class; 138 } else if (type == char.class) { 139 elementType = Character.class; 140 } else if (type == float.class) { 141 elementType = Float.class; 142 } else if (type == double.class) { 143 elementType = Double.class; 144 } else if (type == long.class) { 145 elementType = Long.class; 146 } else if (type == short.class) { 147 elementType = Short.class; 148 } else if (type == byte.class) { 149 elementType = Byte.class; 150 } else { 151 elementType = type; 152 } 153 } 154 155 /** 156 * Fills in element's definition info and returns this. 157 */ 158 protected AnnotationMember setDefinition(AnnotationMember copy) { 159 definingMethod = copy.definingMethod; 160 elementType = copy.elementType; 161 return this; 162 } 163 164 /** 165 * Returns readable description of this annotation value. 166 */ 167 public String toString() { 168 if (tag == ARRAY) { 169 StringBuilder sb = new StringBuilder(80); 170 sb.append(name).append("=["); 171 int len = Array.getLength(value); 172 for (int i = 0; i < len; i++) { 173 if (i != 0) sb.append(", "); 174 sb.append(Array.get(value, i)); 175 } 176 return sb.append("]").toString(); 177 } else { 178 return name+ "=" +value; 179 } 180 } 181 182 /** 183 * Returns true if the specified object represents equal element 184 * (equivalent name-value pair). 185 * <br> A special case is the contained Throwable value; it is considered 186 * transcendent so no other element would be equal. 187 * @return true if passed object is equivalent element representation, 188 * false otherwise 189 * @see #equalArrayValue(Object) 190 * @see java.lang.annotation.Annotation#equals(Object) 191 */ 192 public boolean equals(Object obj) { 193 if (obj == this) { 194 // not a mere optimization, 195 // this is needed for consistency with hashCode() 196 return true; 197 } 198 if (obj instanceof AnnotationMember) { 199 AnnotationMember that = (AnnotationMember)obj; 200 if (name.equals(that.name) && tag == that.tag) { 201 if (tag == ARRAY) { 202 return equalArrayValue(that.value); 203 } else if (tag == ERROR) { 204 // undefined value is incomparable (transcendent) 205 return false; 206 } else { 207 return value.equals(that.value); 208 } 209 } 210 } 211 return false; 212 } 213 214 /** 215 * Returns true if the contained value and a passed object are equal arrays, 216 * false otherwise. Appropriate overloaded method of Arrays.equals() 217 * is used for equality testing. 218 * @see java.util.Arrays#equals(java.lang.Object[], java.lang.Object[]) 219 * @return true if the value is array and is equal to specified object, 220 * false otherwise 221 */ 222 public boolean equalArrayValue(Object otherValue) { 223 if (value instanceof Object[] && otherValue instanceof Object[]) { 224 return Arrays.equals((Object[])value, (Object[])otherValue); 225 } 226 Class type = value.getClass(); 227 if (type != otherValue.getClass()) { 228 return false; 229 } 230 if (type == int[].class) { 231 return Arrays.equals((int[])value, (int[])otherValue); 232 } else if (type == byte[].class) { 233 return Arrays.equals((byte[])value, (byte[])otherValue); 234 } else if (type == short[].class) { 235 return Arrays.equals((short[])value, (short[])otherValue); 236 } else if (type == long[].class) { 237 return Arrays.equals((long[])value, (long[])otherValue); 238 } else if (type == char[].class) { 239 return Arrays.equals((char[])value, (char[])otherValue); 240 } else if (type == boolean[].class) { 241 return Arrays.equals((boolean[])value, (boolean[])otherValue); 242 } else if (type == float[].class) { 243 return Arrays.equals((float[])value, (float[])otherValue); 244 } else if (type == double[].class) { 245 return Arrays.equals((double[])value, (double[])otherValue); 246 } 247 return false; 248 } 249 250 /** 251 * Computes hash code of this element. The formula is as follows: 252 * <code> (name.hashCode() * 127) ^ value.hashCode() </code> 253 * <br>If value is an array, one of overloaded Arrays.hashCode() 254 * methods is used. 255 * @return the hash code 256 * @see java.util.Arrays#hashCode(java.lang.Object[]) 257 * @see java.lang.annotation.Annotation#hashCode() 258 */ 259 public int hashCode() { 260 int hash = name.hashCode() * 127; 261 if (tag == ARRAY) { 262 Class type = value.getClass(); 263 if (type == int[].class) { 264 return hash ^ Arrays.hashCode((int[])value); 265 } else if (type == byte[].class) { 266 return hash ^ Arrays.hashCode((byte[])value); 267 } else if (type == short[].class) { 268 return hash ^ Arrays.hashCode((short[])value); 269 } else if (type == long[].class) { 270 return hash ^ Arrays.hashCode((long[])value); 271 } else if (type == char[].class) { 272 return hash ^ Arrays.hashCode((char[])value); 273 } else if (type == boolean[].class) { 274 return hash ^ Arrays.hashCode((boolean[])value); 275 } else if (type == float[].class) { 276 return hash ^ Arrays.hashCode((float[])value); 277 } else if (type == double[].class) { 278 return hash ^ Arrays.hashCode((double[])value); 279 } 280 return hash ^ Arrays.hashCode((Object[])value); 281 } else { 282 return hash ^ value.hashCode(); 283 } 284 } 285 286 /** 287 * Throws contained error (if any) with a renewed stack trace. 288 */ 289 public void rethrowError() throws Throwable { 290 if (tag == ERROR) { 291 // need to throw cloned exception for thread safety 292 // besides it is better to provide actual stack trace 293 // rather than recorded during parsing 294 295 // first check for expected types 296 if (value instanceof TypeNotPresentException) { 297 TypeNotPresentException tnpe = (TypeNotPresentException)value; 298 throw new TypeNotPresentException(tnpe.typeName(), tnpe.getCause()); 299 } else if (value instanceof EnumConstantNotPresentException) { 300 EnumConstantNotPresentException ecnpe = (EnumConstantNotPresentException)value; 301 throw new EnumConstantNotPresentException(ecnpe.enumType(), ecnpe.constantName()); 302 } else if (value instanceof ArrayStoreException) { 303 ArrayStoreException ase = (ArrayStoreException)value; 304 throw new ArrayStoreException(ase.getMessage()); 305 } 306 // got some other error, have to go with deep cloning 307 // via serialization mechanism 308 Throwable error = (Throwable)value; 309 StackTraceElement[] ste = error.getStackTrace(); 310 ByteArrayOutputStream bos = new ByteArrayOutputStream( 311 ste == null ? 512 : (ste.length + 1) * 80); 312 ObjectOutputStream oos = new ObjectOutputStream(bos); 313 oos.writeObject(error); 314 oos.flush(); 315 oos.close(); 316 ByteArrayInputStream bis = new ByteArrayInputStream(bos 317 .toByteArray()); 318 ObjectInputStream ois = new ObjectInputStream(bis); 319 error = (Throwable)ois.readObject(); 320 ois.close(); 321 322 throw error; 323 } 324 } 325 326 /** 327 * Validates contained value against its member definition 328 * and if ok returns the value. 329 * Otherwise, if the value type mismatches definition 330 * or the value itself describes an error, 331 * throws appropriate exception. 332 * <br> Note, this method may return null if this element was constructed 333 * with such value. 334 * 335 * @see #rethrowError() 336 * @see #copyValue() 337 * @return actual valid value or null if no value 338 */ 339 public Object validateValue() throws Throwable { 340 if (tag == ERROR) { 341 rethrowError(); 342 } 343 if (value == NO_VALUE) { 344 return null; 345 } 346 if (elementType == value.getClass() 347 || elementType.isInstance(value)) { // nested annotation value 348 return copyValue(); 349 } else { 350 throw new AnnotationTypeMismatchException(definingMethod, 351 value.getClass().getName()); 352 } 353 354 } 355 356 357 /** 358 * Provides mutation-safe access to contained value. That is, caller is free 359 * to modify the returned value, it will not affect the contained data value. 360 * @return cloned value if it is mutable or the original immutable value 361 */ 362 public Object copyValue() throws Throwable 363 { 364 if (tag != ARRAY || Array.getLength(value) == 0) { 365 return value; 366 } 367 Class type = value.getClass(); 368 if (type == int[].class) { 369 return ((int[])value).clone(); 370 } else if (type == byte[].class) { 371 return ((byte[])value).clone(); 372 } else if (type == short[].class) { 373 return ((short[])value).clone(); 374 } else if (type == long[].class) { 375 return ((long[])value).clone(); 376 } else if (type == char[].class) { 377 return ((char[])value).clone(); 378 } else if (type == boolean[].class) { 379 return ((boolean[])value).clone(); 380 } else if (type == float[].class) { 381 return ((float[])value).clone(); 382 } else if (type == double[].class) { 383 return ((double[])value).clone(); 384 } 385 return ((Object[])value).clone(); 386 } 387 } 388