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