src/share/classes/com/sun/tools/javac/model/JavacAnnoConstructs.java

Mon, 18 Mar 2013 18:33:13 -0700

author
jjg
date
Mon, 18 Mar 2013 18:33:13 -0700
changeset 1645
97f6839673d6
child 1709
bae8387d16aa
permissions
-rw-r--r--

8007803: Implement javax.lang.model API for Type Annotations
Reviewed-by: darcy

jjg@1645 1 /*
jjg@1645 2 * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
jjg@1645 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jjg@1645 4 *
jjg@1645 5 * This code is free software; you can redistribute it and/or modify it
jjg@1645 6 * under the terms of the GNU General Public License version 2 only, as
jjg@1645 7 * published by the Free Software Foundation. Oracle designates this
jjg@1645 8 * particular file as subject to the "Classpath" exception as provided
jjg@1645 9 * by Oracle in the LICENSE file that accompanied this code.
jjg@1645 10 *
jjg@1645 11 * This code is distributed in the hope that it will be useful, but WITHOUT
jjg@1645 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jjg@1645 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
jjg@1645 14 * version 2 for more details (a copy is included in the LICENSE file that
jjg@1645 15 * accompanied this code).
jjg@1645 16 *
jjg@1645 17 * You should have received a copy of the GNU General Public License version
jjg@1645 18 * 2 along with this work; if not, write to the Free Software Foundation,
jjg@1645 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jjg@1645 20 *
jjg@1645 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jjg@1645 22 * or visit www.oracle.com if you need additional information or have any
jjg@1645 23 * questions.
jjg@1645 24 */
jjg@1645 25 package com.sun.tools.javac.model;
jjg@1645 26
jjg@1645 27 import java.lang.annotation.Annotation;
jjg@1645 28 import java.lang.annotation.Inherited;
jjg@1645 29 import java.lang.reflect.InvocationTargetException;
jjg@1645 30 import java.lang.reflect.Method;
jjg@1645 31
jjg@1645 32 import com.sun.tools.javac.code.Attribute;
jjg@1645 33 import com.sun.tools.javac.code.Kinds;
jjg@1645 34 import com.sun.tools.javac.code.Symbol;
jjg@1645 35 import com.sun.tools.javac.code.Symbol.ClassSymbol;
jjg@1645 36 import com.sun.tools.javac.code.Type;
jjg@1645 37 import com.sun.tools.javac.code.Type.AnnotatedType;
jjg@1645 38 import com.sun.tools.javac.util.ListBuffer;
jjg@1645 39 import static com.sun.tools.javac.code.TypeTag.CLASS;
jjg@1645 40
jjg@1645 41 /**
jjg@1645 42 * Utility methods for operating on annotated constructs.
jjg@1645 43 *
jjg@1645 44 * <p><b>This is NOT part of any supported API.
jjg@1645 45 * If you write code that depends on this, you do so at your own
jjg@1645 46 * risk. This code and its internal interfaces are subject to change
jjg@1645 47 * or deletion without notice.</b></p>
jjg@1645 48 */
jjg@1645 49 public class JavacAnnoConstructs {
jjg@1645 50
jjg@1645 51 // <editor-fold defaultstate="collapsed" desc="Symbols">
jjg@1645 52
jjg@1645 53 /**
jjg@1645 54 * An internal-use utility that creates a runtime view of an
jjg@1645 55 * annotation. This is the implementation of
jjg@1645 56 * Element.getAnnotation(Class).
jjg@1645 57 */
jjg@1645 58 public static <A extends Annotation> A getAnnotation(Symbol annotated,
jjg@1645 59 Class<A> annoType) {
jjg@1645 60 if (!annoType.isAnnotation())
jjg@1645 61 throw new IllegalArgumentException("Not an annotation type: "
jjg@1645 62 + annoType);
jjg@1645 63 Attribute.Compound c;
jjg@1645 64 if (annotated.kind == Kinds.TYP && annotated instanceof ClassSymbol) {
jjg@1645 65 c = getAttributeOnClass((ClassSymbol)annotated, annoType);
jjg@1645 66 } else {
jjg@1645 67 c = getAttribute(annotated, annoType);
jjg@1645 68 }
jjg@1645 69 return c == null ? null : AnnotationProxyMaker.generateAnnotation(c, annoType);
jjg@1645 70 }
jjg@1645 71
jjg@1645 72 // Helper to getAnnotation[s]
jjg@1645 73 private static <A extends Annotation> Attribute.Compound getAttribute(Symbol annotated,
jjg@1645 74 Class<A> annoType) {
jjg@1645 75 String name = annoType.getName();
jjg@1645 76
jjg@1645 77 for (Attribute.Compound anno : annotated.getRawAttributes()) {
jjg@1645 78 if (name.equals(anno.type.tsym.flatName().toString()))
jjg@1645 79 return anno;
jjg@1645 80 }
jjg@1645 81
jjg@1645 82 return null;
jjg@1645 83 }
jjg@1645 84
jjg@1645 85 // Helper to getAnnotation[s]
jjg@1645 86 private static <A extends Annotation> Attribute.Compound getAttributeOnClass(ClassSymbol annotated,
jjg@1645 87 Class<A> annoType) {
jjg@1645 88 boolean inherited = annoType.isAnnotationPresent(Inherited.class);
jjg@1645 89 Attribute.Compound result = null;
jjg@1645 90 while (annotated.name != annotated.name.table.names.java_lang_Object) {
jjg@1645 91 result = getAttribute(annotated, annoType);
jjg@1645 92 if (result != null || !inherited)
jjg@1645 93 break;
jjg@1645 94 Type sup = annotated.getSuperclass();
jjg@1645 95 if (!sup.hasTag(CLASS) || sup.isErroneous())
jjg@1645 96 break;
jjg@1645 97 annotated = (ClassSymbol) sup.tsym;
jjg@1645 98 }
jjg@1645 99 return result;
jjg@1645 100 }
jjg@1645 101
jjg@1645 102 /**
jjg@1645 103 * An internal-use utility that creates a runtime view of
jjg@1645 104 * annotations. This is the implementation of
jjg@1645 105 * Element.getAnnotations(Class).
jjg@1645 106 */
jjg@1645 107 public static <A extends Annotation> A[] getAnnotations(Symbol annotated,
jjg@1645 108 Class<A> annoType) {
jjg@1645 109 if (!annoType.isAnnotation())
jjg@1645 110 throw new IllegalArgumentException("Not an annotation type: "
jjg@1645 111 + annoType);
jjg@1645 112 // If annoType does not declare a container this is equivalent to wrapping
jjg@1645 113 // getAnnotation(...) in an array.
jjg@1645 114 Class <? extends Annotation> containerType = getContainer(annoType);
jjg@1645 115 if (containerType == null) {
jjg@1645 116 A res = getAnnotation(annotated, annoType);
jjg@1645 117 int size;
jjg@1645 118 if (res == null) {
jjg@1645 119 size = 0;
jjg@1645 120 } else {
jjg@1645 121 size = 1;
jjg@1645 122 }
jjg@1645 123 @SuppressWarnings("unchecked") // annoType is the Class for A
jjg@1645 124 A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size);
jjg@1645 125 if (res != null)
jjg@1645 126 arr[0] = res;
jjg@1645 127 return arr;
jjg@1645 128 }
jjg@1645 129
jjg@1645 130 // So we have a containing type
jjg@1645 131 String name = annoType.getName();
jjg@1645 132 String annoTypeName = annoType.getSimpleName();
jjg@1645 133 String containerTypeName = containerType.getSimpleName();
jjg@1645 134 int directIndex = -1, containerIndex = -1;
jjg@1645 135 Attribute.Compound direct = null, container = null;
jjg@1645 136 Attribute.Compound[] rawAttributes = annotated.getRawAttributes().toArray(new Attribute.Compound[0]);
jjg@1645 137
jjg@1645 138 // Find directly present annotations
jjg@1645 139 for (int i = 0; i < rawAttributes.length; i++) {
jjg@1645 140 if (annoTypeName.equals(rawAttributes[i].type.tsym.flatName().toString())) {
jjg@1645 141 directIndex = i;
jjg@1645 142 direct = rawAttributes[i];
jjg@1645 143 } else if(containerTypeName != null &&
jjg@1645 144 containerTypeName.equals(rawAttributes[i].type.tsym.flatName().toString())) {
jjg@1645 145 containerIndex = i;
jjg@1645 146 container = rawAttributes[i];
jjg@1645 147 }
jjg@1645 148 }
jjg@1645 149
jjg@1645 150 // Deal with inherited annotations
jjg@1645 151 if (annotated.kind == Kinds.TYP &&
jjg@1645 152 (annotated instanceof ClassSymbol)) {
jjg@1645 153 ClassSymbol s = (ClassSymbol)annotated;
jjg@1645 154 if (direct == null && container == null) {
jjg@1645 155 direct = getAttributeOnClass(s, annoType);
jjg@1645 156 container = getAttributeOnClass(s, containerType);
jjg@1645 157
jjg@1645 158 // both are inherited and found, put container last
jjg@1645 159 if (direct != null && container != null) {
jjg@1645 160 directIndex = 0;
jjg@1645 161 containerIndex = 1;
jjg@1645 162 } else if (direct != null) {
jjg@1645 163 directIndex = 0;
jjg@1645 164 } else {
jjg@1645 165 containerIndex = 0;
jjg@1645 166 }
jjg@1645 167 } else if (direct == null) {
jjg@1645 168 direct = getAttributeOnClass(s, annoType);
jjg@1645 169 if (direct != null)
jjg@1645 170 directIndex = containerIndex + 1;
jjg@1645 171 } else if (container == null) {
jjg@1645 172 container = getAttributeOnClass(s, containerType);
jjg@1645 173 if (container != null)
jjg@1645 174 containerIndex = directIndex + 1;
jjg@1645 175 }
jjg@1645 176 }
jjg@1645 177
jjg@1645 178 // Pack them in an array
jjg@1645 179 Attribute[] contained0 = new Attribute[0];
jjg@1645 180 if (container != null)
jjg@1645 181 contained0 = unpackAttributes(container);
jjg@1645 182 ListBuffer<Attribute.Compound> compounds = ListBuffer.lb();
jjg@1645 183 for (Attribute a : contained0)
jjg@1645 184 if (a instanceof Attribute.Compound)
jjg@1645 185 compounds = compounds.append((Attribute.Compound)a);
jjg@1645 186 Attribute.Compound[] contained = compounds.toArray(new Attribute.Compound[0]);
jjg@1645 187
jjg@1645 188 int size = (direct == null ? 0 : 1) + contained.length;
jjg@1645 189 @SuppressWarnings("unchecked") // annoType is the Class for A
jjg@1645 190 A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size);
jjg@1645 191
jjg@1645 192 // if direct && container, which is first?
jjg@1645 193 int insert = -1;
jjg@1645 194 int length = arr.length;
jjg@1645 195 if (directIndex >= 0 && containerIndex >= 0) {
jjg@1645 196 if (directIndex < containerIndex) {
jjg@1645 197 arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
jjg@1645 198 insert = 1;
jjg@1645 199 } else {
jjg@1645 200 arr[arr.length - 1] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
jjg@1645 201 insert = 0;
jjg@1645 202 length--;
jjg@1645 203 }
jjg@1645 204 } else if (directIndex >= 0) {
jjg@1645 205 arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
jjg@1645 206 return arr;
jjg@1645 207 } else {
jjg@1645 208 // Only container
jjg@1645 209 insert = 0;
jjg@1645 210 }
jjg@1645 211
jjg@1645 212 for (int i = 0; i + insert < length; i++)
jjg@1645 213 arr[insert + i] = AnnotationProxyMaker.generateAnnotation(contained[i], annoType);
jjg@1645 214
jjg@1645 215 return arr;
jjg@1645 216 }
jjg@1645 217
jjg@1645 218 // </editor-fold>
jjg@1645 219
jjg@1645 220 // <editor-fold defaultstate="collapsed" desc="Types">
jjg@1645 221
jjg@1645 222 /**
jjg@1645 223 * An internal-use utility that creates a runtime view of an
jjg@1645 224 * annotation. This is the implementation of
jjg@1645 225 * TypeMirror.getAnnotation(Class).
jjg@1645 226 */
jjg@1645 227 public static <A extends Annotation> A getAnnotation(AnnotatedType annotated, Class<A> annoType) {
jjg@1645 228 if (!annoType.isAnnotation())
jjg@1645 229 throw new IllegalArgumentException("Not an annotation type: "
jjg@1645 230 + annoType);
jjg@1645 231 Attribute.Compound c = getAttribute(annotated, annoType);
jjg@1645 232 return c == null ? null : AnnotationProxyMaker.generateAnnotation(c, annoType);
jjg@1645 233 }
jjg@1645 234
jjg@1645 235 // Helper to getAnnotation[s]
jjg@1645 236 private static <A extends Annotation> Attribute.Compound getAttribute(Type annotated,
jjg@1645 237 Class<A> annoType) {
jjg@1645 238 String name = annoType.getName();
jjg@1645 239
jjg@1645 240 for (Attribute.Compound anno : annotated.getAnnotationMirrors()) {
jjg@1645 241 if (name.equals(anno.type.tsym.flatName().toString()))
jjg@1645 242 return anno;
jjg@1645 243 }
jjg@1645 244
jjg@1645 245 return null;
jjg@1645 246 }
jjg@1645 247
jjg@1645 248 /**
jjg@1645 249 * An internal-use utility that creates a runtime view of
jjg@1645 250 * annotations. This is the implementation of
jjg@1645 251 * TypeMirror.getAnnotationsByType(Class).
jjg@1645 252 */
jjg@1645 253 public static <A extends Annotation> A[] getAnnotationsByType(AnnotatedType annotated, Class<A> annoType) {
jjg@1645 254 if (!annoType.isAnnotation())
jjg@1645 255 throw new IllegalArgumentException("Not an annotation type: "
jjg@1645 256 + annoType);
jjg@1645 257 // If annoType does not declare a container this is equivalent to wrapping
jjg@1645 258 // getAnnotation(...) in an array.
jjg@1645 259 Class <? extends Annotation> containerType = getContainer(annoType);
jjg@1645 260 if (containerType == null) {
jjg@1645 261 A res = getAnnotation(annotated, annoType);
jjg@1645 262 int size;
jjg@1645 263 if (res == null) {
jjg@1645 264 size = 0;
jjg@1645 265 } else {
jjg@1645 266 size = 1;
jjg@1645 267 }
jjg@1645 268 @SuppressWarnings("unchecked") // annoType is the Class for A
jjg@1645 269 A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size);
jjg@1645 270 if (res != null)
jjg@1645 271 arr[0] = res;
jjg@1645 272 return arr;
jjg@1645 273 }
jjg@1645 274
jjg@1645 275 // So we have a containing type
jjg@1645 276 String name = annoType.getName();
jjg@1645 277 String annoTypeName = annoType.getSimpleName();
jjg@1645 278 String containerTypeName = containerType.getSimpleName();
jjg@1645 279 int directIndex = -1, containerIndex = -1;
jjg@1645 280 Attribute.Compound direct = null, container = null;
jjg@1645 281 Attribute.Compound[] rawAttributes = annotated.getAnnotationMirrors().toArray(new Attribute.Compound[0]);
jjg@1645 282
jjg@1645 283 // Find directly present annotations
jjg@1645 284 for (int i = 0; i < rawAttributes.length; i++) {
jjg@1645 285 if (annoTypeName.equals(rawAttributes[i].type.tsym.flatName().toString())) {
jjg@1645 286 directIndex = i;
jjg@1645 287 direct = rawAttributes[i];
jjg@1645 288 } else if(containerTypeName != null &&
jjg@1645 289 containerTypeName.equals(rawAttributes[i].type.tsym.flatName().toString())) {
jjg@1645 290 containerIndex = i;
jjg@1645 291 container = rawAttributes[i];
jjg@1645 292 }
jjg@1645 293 }
jjg@1645 294
jjg@1645 295 // Pack them in an array
jjg@1645 296 Attribute[] contained0 = new Attribute[0];
jjg@1645 297 if (container != null)
jjg@1645 298 contained0 = unpackAttributes(container);
jjg@1645 299 ListBuffer<Attribute.Compound> compounds = ListBuffer.lb();
jjg@1645 300 for (Attribute a : contained0) {
jjg@1645 301 if (a instanceof Attribute.Compound)
jjg@1645 302 compounds = compounds.append((Attribute.Compound)a);
jjg@1645 303 }
jjg@1645 304 Attribute.Compound[] contained = compounds.toArray(new Attribute.Compound[0]);
jjg@1645 305
jjg@1645 306 int size = (direct == null ? 0 : 1) + contained.length;
jjg@1645 307 @SuppressWarnings("unchecked") // annoType is the Class for A
jjg@1645 308 A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size);
jjg@1645 309
jjg@1645 310 // if direct && container, which is first?
jjg@1645 311 int insert = -1;
jjg@1645 312 int length = arr.length;
jjg@1645 313 if (directIndex >= 0 && containerIndex >= 0) {
jjg@1645 314 if (directIndex < containerIndex) {
jjg@1645 315 arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
jjg@1645 316 insert = 1;
jjg@1645 317 } else {
jjg@1645 318 arr[arr.length - 1] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
jjg@1645 319 insert = 0;
jjg@1645 320 length--;
jjg@1645 321 }
jjg@1645 322 } else if (directIndex >= 0) {
jjg@1645 323 arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType);
jjg@1645 324 return arr;
jjg@1645 325 } else {
jjg@1645 326 // Only container
jjg@1645 327 insert = 0;
jjg@1645 328 }
jjg@1645 329
jjg@1645 330 for (int i = 0; i + insert < length; i++)
jjg@1645 331 arr[insert + i] = AnnotationProxyMaker.generateAnnotation(contained[i], annoType);
jjg@1645 332
jjg@1645 333 return arr;
jjg@1645 334 }
jjg@1645 335
jjg@1645 336 // </editor-fold>
jjg@1645 337
jjg@1645 338 // <editor-fold defaultstate="collapsed" desc="Container support">
jjg@1645 339
jjg@1645 340 // Needed to unpack the runtime view of containing annotations
jjg@1645 341 private static final Class<? extends Annotation> REPEATABLE_CLASS = initRepeatable();
jjg@1645 342 private static final Method VALUE_ELEMENT_METHOD = initValueElementMethod();
jjg@1645 343
jjg@1645 344 private static Class<? extends Annotation> initRepeatable() {
jjg@1645 345 try {
jjg@1645 346 // Repeatable will not be available when bootstrapping on
jjg@1645 347 // JDK 7 so use a reflective lookup instead of a class
jjg@1645 348 // literal for Repeatable.class.
jjg@1645 349 return Class.forName("java.lang.annotation.Repeatable").asSubclass(Annotation.class);
jjg@1645 350 } catch (ClassNotFoundException e) {
jjg@1645 351 return null;
jjg@1645 352 } catch (SecurityException e) {
jjg@1645 353 return null;
jjg@1645 354 }
jjg@1645 355 }
jjg@1645 356
jjg@1645 357 private static Method initValueElementMethod() {
jjg@1645 358 if (REPEATABLE_CLASS == null)
jjg@1645 359 return null;
jjg@1645 360
jjg@1645 361 Method m = null;
jjg@1645 362 try {
jjg@1645 363 m = REPEATABLE_CLASS.getMethod("value");
jjg@1645 364 if (m != null)
jjg@1645 365 m.setAccessible(true);
jjg@1645 366 return m;
jjg@1645 367 } catch (NoSuchMethodException e) {
jjg@1645 368 return null;
jjg@1645 369 }
jjg@1645 370 }
jjg@1645 371
jjg@1645 372 // Helper to getAnnotations
jjg@1645 373 private static Class<? extends Annotation> getContainer(Class<? extends Annotation> annoType) {
jjg@1645 374 // Since we can not refer to java.lang.annotation.Repeatable until we are
jjg@1645 375 // bootstrapping with java 8 we need to get the Repeatable annotation using
jjg@1645 376 // reflective invocations instead of just using its type and element method.
jjg@1645 377 if (REPEATABLE_CLASS != null &&
jjg@1645 378 VALUE_ELEMENT_METHOD != null) {
jjg@1645 379 // Get the Repeatable instance on the annotations declaration
jjg@1645 380 Annotation repeatable = (Annotation)annoType.getAnnotation(REPEATABLE_CLASS);
jjg@1645 381 if (repeatable != null) {
jjg@1645 382 try {
jjg@1645 383 // Get the value element, it should be a class
jjg@1645 384 // indicating the containing annotation type
jjg@1645 385 @SuppressWarnings("unchecked")
jjg@1645 386 Class<? extends Annotation> containerType = (Class)VALUE_ELEMENT_METHOD.invoke(repeatable);
jjg@1645 387 if (containerType == null)
jjg@1645 388 return null;
jjg@1645 389
jjg@1645 390 return containerType;
jjg@1645 391 } catch (ClassCastException e) {
jjg@1645 392 return null;
jjg@1645 393 } catch (IllegalAccessException e) {
jjg@1645 394 return null;
jjg@1645 395 } catch (InvocationTargetException e ) {
jjg@1645 396 return null;
jjg@1645 397 }
jjg@1645 398 }
jjg@1645 399 }
jjg@1645 400 return null;
jjg@1645 401 }
jjg@1645 402
jjg@1645 403 // Helper to getAnnotations
jjg@1645 404 private static Attribute[] unpackAttributes(Attribute.Compound container) {
jjg@1645 405 // We now have an instance of the container,
jjg@1645 406 // unpack it returning an instance of the
jjg@1645 407 // contained type or null
jjg@1645 408 return ((Attribute.Array)container.member(container.type.tsym.name.table.names.value)).values;
jjg@1645 409 }
jjg@1645 410
jjg@1645 411 // </editor-fold>
jjg@1645 412 }

mercurial