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 libcore.reflect; 19 20 import java.io.IOException; 21 import java.io.ObjectInputStream; 22 import java.io.Serializable; 23 import java.lang.annotation.Annotation; 24 import java.lang.annotation.IncompleteAnnotationException; 25 import java.lang.reflect.InvocationHandler; 26 import java.lang.reflect.Method; 27 import java.lang.reflect.Proxy; 28 import java.util.ArrayList; 29 import java.util.List; 30 import java.util.Map; 31 import java.util.WeakHashMap; 32 33 /** 34 * The annotation implementation based on dynamically generated proxy instances. 35 * It conforms to all requirements stated in public APIs, see in particular 36 * {@link java.lang.reflect.AnnotatedElement java.lang.reflect.AnnotatedElement} 37 * and {@link java.lang.annotation.Annotation java.lang.annotation.Annotation}. 38 * Namely, annotation instances are immutable and serializable; they provide 39 * conforming access to annotation member values and required implementations of 40 * methods declared in Annotation interface. 41 * 42 * @see AnnotationMember 43 * @see java.lang.annotation.Annotation 44 * 45 * @author Alexey V. Varlamov, Serguei S. Zapreyev 46 * @version $Revision$ 47 */ 48 @SuppressWarnings({"serial"}) 49 public final class AnnotationFactory implements InvocationHandler, Serializable { 50 51 private static final transient Map<Class<? extends Annotation>, AnnotationMember[]> cache = 52 new WeakHashMap<Class<? extends Annotation>, AnnotationMember[]>(); 53 54 /** 55 * Reflects specified annotation type and returns an array 56 * of member element definitions with default values. 57 */ getElementsDescription(Class<? extends Annotation> annotationType)58 public static AnnotationMember[] getElementsDescription(Class<? extends Annotation> annotationType) { 59 synchronized (cache) { 60 AnnotationMember[] desc = cache.get(annotationType); 61 if (desc != null) { 62 return desc; 63 } 64 } 65 if (!annotationType.isAnnotation()) { 66 throw new IllegalArgumentException("Type is not annotation: " + annotationType.getName()); 67 } 68 Method[] declaredMethods = annotationType.getDeclaredMethods(); 69 AnnotationMember[] desc = new AnnotationMember[declaredMethods.length]; 70 for (int i = 0; i < declaredMethods.length; ++i) { 71 Method element = declaredMethods[i]; 72 String name = element.getName(); 73 Class<?> type = element.getReturnType(); 74 try { 75 desc[i] = new AnnotationMember(name, element.getDefaultValue(), type, element); 76 } catch (Throwable t) { 77 desc[i] = new AnnotationMember(name, t, type, element); 78 } 79 } 80 synchronized (cache) { 81 cache.put(annotationType, desc); 82 } 83 return desc; 84 } 85 86 /** 87 * Provides a new annotation instance. 88 * @param annotationType the annotation type definition 89 * @param elements name-value pairs representing elements of the annotation 90 * @return a new annotation instance 91 */ createAnnotation(Class<? extends Annotation> annotationType, AnnotationMember[] elements)92 public static <A extends Annotation> A createAnnotation(Class<? extends Annotation> annotationType, 93 AnnotationMember[] elements) { 94 AnnotationFactory factory = new AnnotationFactory(annotationType, elements); 95 return (A) Proxy.newProxyInstance(annotationType.getClassLoader(), 96 new Class[]{annotationType}, factory); 97 } 98 99 private final Class<? extends Annotation> klazz; 100 private AnnotationMember[] elements; 101 102 /** 103 * New instances should not be created directly, use factory method 104 * {@link #createAnnotation(Class, AnnotationMember[]) createAnnotation()} 105 * instead. 106 * 107 * @param klzz class defining the annotation type 108 * @param values actual element values 109 */ AnnotationFactory(Class<? extends Annotation> klzz, AnnotationMember[] values)110 private AnnotationFactory(Class<? extends Annotation> klzz, AnnotationMember[] values) { 111 klazz = klzz; 112 AnnotationMember[] defs = getElementsDescription(klazz); 113 if (values == null) { 114 elements = defs; 115 } else { 116 //merge default and actual values 117 elements = new AnnotationMember[defs.length]; 118 next: for (int i = elements.length - 1; i >= 0; i--) { 119 for (AnnotationMember val : values) { 120 if (val.name.equals(defs[i].name)) { 121 elements[i] = val.setDefinition(defs[i]); 122 continue next; 123 } 124 } 125 elements[i] = defs[i]; 126 } 127 } 128 } 129 130 /** 131 * Reads the object, obtains actual member definitions for the annotation type, 132 * and merges deserialized values with the new definitions. 133 */ readObject(ObjectInputStream os)134 private void readObject(ObjectInputStream os) throws IOException, ClassNotFoundException { 135 os.defaultReadObject(); 136 // Annotation type members can be changed arbitrarily 137 // So there may be zombi elements from the previous life; 138 // they hardly fit into this new annotation's incarnation, 139 // as we have no defining methods for them. 140 // Reasonably just drop such elements, 141 // but seems better to keep them for compatibility 142 AnnotationMember[] defs = getElementsDescription(klazz); 143 AnnotationMember[] old = elements; 144 List<AnnotationMember> merged = new ArrayList<AnnotationMember>(defs.length + old.length); 145 nextOld: for (AnnotationMember el1 : old) { 146 for (AnnotationMember el2 : defs) { 147 if (el2.name.equals(el1.name)) { 148 continue nextOld; 149 } 150 } 151 merged.add(el1); //phantom element 152 } 153 nextNew: for (AnnotationMember def : defs) { 154 for (AnnotationMember val : old) { 155 if (val.name.equals(def.name)) { 156 // nothing to do about cached errors (if any) 157 // anyway they remain relevant to values 158 merged.add(val.setDefinition(def)); 159 continue nextNew; 160 } 161 } 162 merged.add(def); // brand new element 163 } 164 elements = merged.toArray(new AnnotationMember[merged.size()]); 165 } 166 167 /** 168 * Returns true if the specified object represents the same annotation instance. 169 * That is, if it implements the same annotation type and 170 * returns the same element values. 171 * <br>Note, actual underlying implementation mechanism does not matter - it may 172 * differ completely from this class. 173 * @return true if the passed object is equivalent annotation instance, 174 * false otherwise. 175 * @see AnnotationMember#equals(Object) 176 */ equals(Object obj)177 public boolean equals(Object obj) { 178 if (obj == this) { 179 return true; 180 } 181 if (!klazz.isInstance(obj)) { 182 return false; 183 } 184 Object handler = null; 185 if (Proxy.isProxyClass(obj.getClass()) 186 && (handler = Proxy.getInvocationHandler(obj)) instanceof AnnotationFactory) { 187 AnnotationFactory other = (AnnotationFactory) handler; 188 if (elements.length != other.elements.length) { 189 return false; 190 } 191 next: for (AnnotationMember el1 : elements) { 192 for (AnnotationMember el2 : other.elements) { 193 if (el1.equals(el2)) { 194 continue next; 195 } 196 } 197 return false; 198 } 199 return true; 200 } else { 201 // encountered foreign annotation implementation 202 // so have to obtain element values via invocation 203 // of corresponding methods 204 for (final AnnotationMember el : elements) { 205 if (el.tag == AnnotationMember.ERROR) { 206 // undefined value is incomparable (transcendent) 207 return false; 208 } 209 try { 210 if (!el.definingMethod.isAccessible()) { 211 el.definingMethod.setAccessible(true); 212 } 213 Object otherValue = el.definingMethod.invoke(obj); 214 if (otherValue != null) { 215 if (el.tag == AnnotationMember.ARRAY) { 216 if (!el.equalArrayValue(otherValue)) { 217 return false; 218 } 219 } else { 220 if (!el.value.equals(otherValue)) { 221 return false; 222 } 223 } 224 } else if (el.value != AnnotationMember.NO_VALUE) { 225 return false; 226 } 227 } catch (Throwable e) { 228 return false; 229 } 230 } 231 return true; 232 } 233 } 234 235 /** 236 * Returns a hash code composed as a sum of hash codes of member elements, 237 * including elements with default values. 238 * @see AnnotationMember#hashCode() 239 */ hashCode()240 public int hashCode() { 241 int hash = 0; 242 for (AnnotationMember element : elements) { 243 hash += element.hashCode(); 244 } 245 return hash; 246 } 247 248 /** 249 * Provides detailed description of this annotation instance, 250 * including all member name-values pairs. 251 * @return string representation of this annotation 252 */ toString()253 public String toString() { 254 StringBuilder result = new StringBuilder(); 255 result.append('@'); 256 result.append(klazz.getName()); 257 result.append('('); 258 for (int i = 0; i < elements.length; ++i) { 259 if (i != 0) { 260 result.append(", "); 261 } 262 result.append(elements[i]); 263 } 264 result.append(')'); 265 return result.toString(); 266 } 267 268 /** 269 * Processes a method invocation request to this annotation instance. 270 * Recognizes the methods declared in the 271 * {@link java.lang.annotation.Annotation java.lang.annotation.Annotation} 272 * interface, and member-defining methods of the implemented annotation type. 273 * @throws IllegalArgumentException If the specified method is none of the above 274 * @return the invocation result 275 */ invoke(Object proxy, Method method, Object[] args)276 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 277 String name = method.getName(); 278 Class[] params = method.getParameterTypes(); 279 if (params.length == 0) { 280 if ("annotationType".equals(name)) { 281 return klazz; 282 } else if ("toString".equals(name)) { 283 return toString(); 284 } else if ("hashCode".equals(name)) { 285 return hashCode(); 286 } 287 288 // this must be element value request 289 AnnotationMember element = null; 290 for (AnnotationMember el : elements) { 291 if (name.equals(el.name)) { 292 element = el; 293 break; 294 } 295 } 296 if (element == null || !method.equals(element.definingMethod)) { 297 throw new IllegalArgumentException(method.toString()); 298 } else { 299 Object value = element.validateValue(); 300 if (value == null) { 301 throw new IncompleteAnnotationException(klazz, name); 302 } 303 return value; 304 } 305 } else if (params.length == 1 && params[0] == Object.class && "equals".equals(name)) { 306 return Boolean.valueOf(equals(args[0])); 307 } 308 throw new IllegalArgumentException("Invalid method for annotation type: " + method); 309 } 310 } 311