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