1 /* 2 * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.sun.tools.javac.code; 26 27 import java.lang.annotation.Annotation; 28 import java.lang.annotation.Inherited; 29 import java.lang.reflect.InvocationTargetException; 30 import java.lang.reflect.Method; 31 32 import javax.lang.model.AnnotatedConstruct; 33 34 import com.sun.tools.javac.model.AnnotationProxyMaker; 35 import com.sun.tools.javac.util.DefinedBy; 36 import com.sun.tools.javac.util.DefinedBy.Api; 37 import com.sun.tools.javac.util.List; 38 import com.sun.tools.javac.util.ListBuffer; 39 40 /** 41 * Common super type for annotated constructs such as Types and Symbols. 42 * 43 * This class should *not* contain any fields since it would have a significant 44 * impact on the javac memory footprint. 45 * 46 * <p><b>This is NOT part of any supported API. 47 * If you write code that depends on this, you do so at your own 48 * risk. This code and its internal interfaces are subject to change 49 * or deletion without notice.</b></p> 50 */ 51 public abstract class AnnoConstruct implements AnnotatedConstruct { 52 53 54 // Override to enforce a narrower return type. 55 @Override @DefinedBy(Api.LANGUAGE_MODEL) 56 public abstract List<? extends Attribute.Compound> getAnnotationMirrors(); 57 58 59 // This method is part of the javax.lang.model API, do not use this in javac code. 60 protected <A extends Annotation> Attribute.Compound getAttribute(Class<A> annoType) { 61 String name = annoType.getName(); 62 63 for (Attribute.Compound anno : getAnnotationMirrors()) { 64 if (name.equals(anno.type.tsym.flatName().toString())) 65 return anno; 66 } 67 68 return null; 69 } 70 71 72 @SuppressWarnings("unchecked") 73 protected <A extends Annotation> A[] getInheritedAnnotations(Class<A> annoType) { 74 return (A[]) java.lang.reflect.Array.newInstance(annoType, 0); // annoType is the Class for A 75 } 76 77 78 // This method is part of the javax.lang.model API, do not use this in javac code. 79 @DefinedBy(Api.LANGUAGE_MODEL) 80 public <A extends Annotation> A[] getAnnotationsByType(Class<A> annoType) { 81 82 if (!annoType.isAnnotation()) 83 throw new IllegalArgumentException("Not an annotation type: " 84 + annoType); 85 // If annoType does not declare a container this is equivalent to wrapping 86 // getAnnotation(...) in an array. 87 Class <? extends Annotation> containerType = getContainer(annoType); 88 if (containerType == null) { 89 A res = getAnnotation(annoType); 90 int size = res == null ? 0 : 1; 91 92 @SuppressWarnings("unchecked") // annoType is the Class for A 93 A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size); 94 if (res != null) 95 arr[0] = res; 96 return arr; 97 } 98 99 // So we have a containing type 100 String annoTypeName = annoType.getName(); 101 String containerTypeName = containerType.getName(); 102 int directIndex = -1, containerIndex = -1; 103 Attribute.Compound direct = null, container = null; 104 // Find directly (explicit or implicit) present annotations 105 int index = -1; 106 for (Attribute.Compound attribute : getAnnotationMirrors()) { 107 index++; 108 if (attribute.type.tsym.flatName().contentEquals(annoTypeName)) { 109 directIndex = index; 110 direct = attribute; 111 } else if(containerTypeName != null && 112 attribute.type.tsym.flatName().contentEquals(containerTypeName)) { 113 containerIndex = index; 114 container = attribute; 115 } 116 } 117 118 // Deal with inherited annotations 119 if (direct == null && container == null && 120 annoType.isAnnotationPresent(Inherited.class)) 121 return getInheritedAnnotations(annoType); 122 123 Attribute.Compound[] contained = unpackContained(container); 124 125 // In case of an empty legacy container we might need to look for 126 // inherited annos as well 127 if (direct == null && contained.length == 0 && 128 annoType.isAnnotationPresent(Inherited.class)) 129 return getInheritedAnnotations(annoType); 130 131 int size = (direct == null ? 0 : 1) + contained.length; 132 @SuppressWarnings("unchecked") // annoType is the Class for A 133 A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size); 134 135 // if direct && container, which is first? 136 int insert = -1; 137 int length = arr.length; 138 if (directIndex >= 0 && containerIndex >= 0) { 139 if (directIndex < containerIndex) { 140 arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType); 141 insert = 1; 142 } else { 143 arr[arr.length - 1] = AnnotationProxyMaker.generateAnnotation(direct, annoType); 144 insert = 0; 145 length--; 146 } 147 } else if (directIndex >= 0) { 148 arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType); 149 return arr; 150 } else { 151 // Only container 152 insert = 0; 153 } 154 155 for (int i = 0; i + insert < length; i++) 156 arr[insert + i] = AnnotationProxyMaker.generateAnnotation(contained[i], annoType); 157 158 return arr; 159 } 160 161 private Attribute.Compound[] unpackContained(Attribute.Compound container) { 162 // Pack them in an array 163 Attribute[] contained0 = null; 164 if (container != null) 165 contained0 = unpackAttributes(container); 166 ListBuffer<Attribute.Compound> compounds = new ListBuffer<>(); 167 if (contained0 != null) { 168 for (Attribute a : contained0) 169 if (a instanceof Attribute.Compound) 170 compounds = compounds.append((Attribute.Compound)a); 171 } 172 return compounds.toArray(new Attribute.Compound[compounds.size()]); 173 } 174 175 // This method is part of the javax.lang.model API, do not use this in javac code. 176 @DefinedBy(Api.LANGUAGE_MODEL) 177 public <A extends Annotation> A getAnnotation(Class<A> annoType) { 178 179 if (!annoType.isAnnotation()) 180 throw new IllegalArgumentException("Not an annotation type: " + annoType); 181 182 Attribute.Compound c = getAttribute(annoType); 183 return c == null ? null : AnnotationProxyMaker.generateAnnotation(c, annoType); 184 } 185 186 // Needed to unpack the runtime view of containing annotations 187 private static final Class<? extends Annotation> REPEATABLE_CLASS = initRepeatable(); 188 private static final Method VALUE_ELEMENT_METHOD = initValueElementMethod(); 189 190 private static Class<? extends Annotation> initRepeatable() { 191 try { 192 // Repeatable will not be available when bootstrapping on 193 // JDK 7 so use a reflective lookup instead of a class 194 // literal for Repeatable.class. 195 return Class.forName("java.lang.annotation.Repeatable").asSubclass(Annotation.class); 196 } catch (ClassNotFoundException | SecurityException e) { 197 return null; 198 } 199 } 200 201 private static Method initValueElementMethod() { 202 if (REPEATABLE_CLASS == null) 203 return null; 204 205 Method m = null; 206 try { 207 m = REPEATABLE_CLASS.getMethod("value"); 208 if (m != null) 209 m.setAccessible(true); 210 return m; 211 } catch (NoSuchMethodException e) { 212 return null; 213 } 214 } 215 216 217 // Helper to getAnnotationsByType 218 private static Class<? extends Annotation> getContainer(Class<? extends Annotation> annoType) { 219 // Since we can not refer to java.lang.annotation.Repeatable until we are 220 // bootstrapping with java 8 we need to get the Repeatable annotation using 221 // reflective invocations instead of just using its type and element method. 222 if (REPEATABLE_CLASS != null && 223 VALUE_ELEMENT_METHOD != null) { 224 // Get the Repeatable instance on the annotations declaration 225 Annotation repeatable = (Annotation)annoType.getAnnotation(REPEATABLE_CLASS); 226 if (repeatable != null) { 227 try { 228 // Get the value element, it should be a class 229 // indicating the containing annotation type 230 @SuppressWarnings("unchecked") 231 Class<? extends Annotation> containerType = (Class)VALUE_ELEMENT_METHOD.invoke(repeatable); 232 if (containerType == null) 233 return null; 234 235 return containerType; 236 } catch (ClassCastException | IllegalAccessException | InvocationTargetException e) { 237 return null; 238 } 239 } 240 } 241 return null; 242 } 243 244 245 // Helper to getAnnotationsByType 246 private static Attribute[] unpackAttributes(Attribute.Compound container) { 247 // We now have an instance of the container, 248 // unpack it returning an instance of the 249 // contained type or null 250 return ((Attribute.Array)container.member(container.type.tsym.name.table.names.value)).values; 251 } 252 253 }