AnnoConstruct.java revision 2571:10fc81ac75b4
1321936Shselasky/* 2321936Shselasky * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. 3321936Shselasky * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4321936Shselasky * 5321936Shselasky * This code is free software; you can redistribute it and/or modify it 6321936Shselasky * under the terms of the GNU General Public License version 2 only, as 7321936Shselasky * published by the Free Software Foundation. Oracle designates this 8321936Shselasky * particular file as subject to the "Classpath" exception as provided 9321936Shselasky * by Oracle in the LICENSE file that accompanied this code. 10321936Shselasky * 11321936Shselasky * This code is distributed in the hope that it will be useful, but WITHOUT 12321936Shselasky * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13321936Shselasky * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14321936Shselasky * version 2 for more details (a copy is included in the LICENSE file that 15321936Shselasky * accompanied this code). 16321936Shselasky * 17321936Shselasky * You should have received a copy of the GNU General Public License version 18321936Shselasky * 2 along with this work; if not, write to the Free Software Foundation, 19321936Shselasky * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20321936Shselasky * 21321936Shselasky * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22321936Shselasky * or visit www.oracle.com if you need additional information or have any 23321936Shselasky * questions. 24321936Shselasky */ 25321936Shselaskypackage com.sun.tools.javac.code; 26321936Shselasky 27321936Shselaskyimport java.lang.annotation.Annotation; 28321936Shselaskyimport java.lang.annotation.Inherited; 29321936Shselaskyimport java.lang.reflect.InvocationTargetException; 30321936Shselaskyimport java.lang.reflect.Method; 31321936Shselasky 32321936Shselaskyimport javax.lang.model.AnnotatedConstruct; 33321936Shselasky 34321936Shselaskyimport com.sun.tools.javac.model.AnnotationProxyMaker; 35321936Shselaskyimport com.sun.tools.javac.util.List; 36321936Shselaskyimport com.sun.tools.javac.util.ListBuffer; 37321936Shselasky 38321936Shselasky/** 39321936Shselasky * Common super type for annotated constructs such as Types and Symbols. 40321936Shselasky * 41321936Shselasky * This class should *not* contain any fields since it would have a significant 42321936Shselasky * impact on the javac memory footprint. 43321936Shselasky * 44321936Shselasky * <p><b>This is NOT part of any supported API. 45321936Shselasky * If you write code that depends on this, you do so at your own 46321936Shselasky * risk. This code and its internal interfaces are subject to change 47321936Shselasky * or deletion without notice.</b></p> 48321936Shselasky */ 49321936Shselaskypublic abstract class AnnoConstruct implements AnnotatedConstruct { 50321936Shselasky 51321936Shselasky 52321936Shselasky // Override to enforce a narrower return type. 53321936Shselasky @Override 54321936Shselasky public abstract List<? extends Attribute.Compound> getAnnotationMirrors(); 55321936Shselasky 56321936Shselasky 57321936Shselasky // This method is part of the javax.lang.model API, do not use this in javac code. 58321936Shselasky protected <A extends Annotation> Attribute.Compound getAttribute(Class<A> annoType) { 59321936Shselasky String name = annoType.getName(); 60321936Shselasky 61321936Shselasky for (Attribute.Compound anno : getAnnotationMirrors()) { 62321936Shselasky if (name.equals(anno.type.tsym.flatName().toString())) 63321936Shselasky return anno; 64321936Shselasky } 65321936Shselasky 66321936Shselasky return null; 67321936Shselasky } 68321936Shselasky 69321936Shselasky 70321936Shselasky @SuppressWarnings("unchecked") 71321936Shselasky protected <A extends Annotation> A[] getInheritedAnnotations(Class<A> annoType) { 72321936Shselasky return (A[]) java.lang.reflect.Array.newInstance(annoType, 0); // annoType is the Class for A 73321936Shselasky } 74321936Shselasky 75321936Shselasky 76321936Shselasky // This method is part of the javax.lang.model API, do not use this in javac code. 77321936Shselasky public <A extends Annotation> A[] getAnnotationsByType(Class<A> annoType) { 78321936Shselasky 79321936Shselasky if (!annoType.isAnnotation()) 80321936Shselasky throw new IllegalArgumentException("Not an annotation type: " 81321936Shselasky + annoType); 82321936Shselasky // If annoType does not declare a container this is equivalent to wrapping 83321936Shselasky // getAnnotation(...) in an array. 84321936Shselasky Class <? extends Annotation> containerType = getContainer(annoType); 85321936Shselasky if (containerType == null) { 86321936Shselasky A res = getAnnotation(annoType); 87321936Shselasky int size = res == null ? 0 : 1; 88321936Shselasky 89321936Shselasky @SuppressWarnings("unchecked") // annoType is the Class for A 90321936Shselasky A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size); 91321936Shselasky if (res != null) 92321936Shselasky arr[0] = res; 93321936Shselasky return arr; 94321936Shselasky } 95321936Shselasky 96321936Shselasky // So we have a containing type 97321936Shselasky String annoTypeName = annoType.getName(); 98321936Shselasky String containerTypeName = containerType.getName(); 99321936Shselasky int directIndex = -1, containerIndex = -1; 100321936Shselasky Attribute.Compound direct = null, container = null; 101321936Shselasky // Find directly (explicit or implicit) present annotations 102321936Shselasky int index = -1; 103321936Shselasky for (Attribute.Compound attribute : getAnnotationMirrors()) { 104321936Shselasky index++; 105321936Shselasky if (attribute.type.tsym.flatName().contentEquals(annoTypeName)) { 106321936Shselasky directIndex = index; 107321936Shselasky direct = attribute; 108321936Shselasky } else if(containerTypeName != null && 109321936Shselasky attribute.type.tsym.flatName().contentEquals(containerTypeName)) { 110321936Shselasky containerIndex = index; 111321936Shselasky container = attribute; 112321936Shselasky } 113321936Shselasky } 114321936Shselasky 115321936Shselasky // Deal with inherited annotations 116321936Shselasky if (direct == null && container == null && 117321936Shselasky annoType.isAnnotationPresent(Inherited.class)) 118321936Shselasky return getInheritedAnnotations(annoType); 119321936Shselasky 120321936Shselasky Attribute.Compound[] contained = unpackContained(container); 121321936Shselasky 122321936Shselasky // In case of an empty legacy container we might need to look for 123321936Shselasky // inherited annos as well 124321936Shselasky if (direct == null && contained.length == 0 && 125321936Shselasky annoType.isAnnotationPresent(Inherited.class)) 126321936Shselasky return getInheritedAnnotations(annoType); 127321936Shselasky 128321936Shselasky int size = (direct == null ? 0 : 1) + contained.length; 129321936Shselasky @SuppressWarnings("unchecked") // annoType is the Class for A 130321936Shselasky A[] arr = (A[])java.lang.reflect.Array.newInstance(annoType, size); 131321936Shselasky 132321936Shselasky // if direct && container, which is first? 133321936Shselasky int insert = -1; 134321936Shselasky int length = arr.length; 135321936Shselasky if (directIndex >= 0 && containerIndex >= 0) { 136321936Shselasky if (directIndex < containerIndex) { 137321936Shselasky arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType); 138321936Shselasky insert = 1; 139321936Shselasky } else { 140321936Shselasky arr[arr.length - 1] = AnnotationProxyMaker.generateAnnotation(direct, annoType); 141321936Shselasky insert = 0; 142321936Shselasky length--; 143321936Shselasky } 144321936Shselasky } else if (directIndex >= 0) { 145321936Shselasky arr[0] = AnnotationProxyMaker.generateAnnotation(direct, annoType); 146321936Shselasky return arr; 147321936Shselasky } else { 148321936Shselasky // Only container 149321936Shselasky insert = 0; 150321936Shselasky } 151321936Shselasky 152321936Shselasky for (int i = 0; i + insert < length; i++) 153321936Shselasky arr[insert + i] = AnnotationProxyMaker.generateAnnotation(contained[i], annoType); 154321936Shselasky 155321936Shselasky return arr; 156321936Shselasky } 157321936Shselasky 158321936Shselasky private Attribute.Compound[] unpackContained(Attribute.Compound container) { 159321936Shselasky // Pack them in an array 160321936Shselasky Attribute[] contained0 = null; 161321936Shselasky if (container != null) 162321936Shselasky contained0 = unpackAttributes(container); 163321936Shselasky ListBuffer<Attribute.Compound> compounds = new ListBuffer<>(); 164321936Shselasky if (contained0 != null) { 165321936Shselasky for (Attribute a : contained0) 166321936Shselasky if (a instanceof Attribute.Compound) 167321936Shselasky compounds = compounds.append((Attribute.Compound)a); 168321936Shselasky } 169321936Shselasky return compounds.toArray(new Attribute.Compound[compounds.size()]); 170321936Shselasky } 171321936Shselasky 172321936Shselasky // This method is part of the javax.lang.model API, do not use this in javac code. 173321936Shselasky public <A extends Annotation> A getAnnotation(Class<A> annoType) { 174321936Shselasky 175321936Shselasky if (!annoType.isAnnotation()) 176321936Shselasky throw new IllegalArgumentException("Not an annotation type: " + annoType); 177321936Shselasky 178321936Shselasky Attribute.Compound c = getAttribute(annoType); 179321936Shselasky return c == null ? null : AnnotationProxyMaker.generateAnnotation(c, annoType); 180321936Shselasky } 181321936Shselasky 182321936Shselasky // Needed to unpack the runtime view of containing annotations 183321936Shselasky private static final Class<? extends Annotation> REPEATABLE_CLASS = initRepeatable(); 184321936Shselasky private static final Method VALUE_ELEMENT_METHOD = initValueElementMethod(); 185321936Shselasky 186321936Shselasky private static Class<? extends Annotation> initRepeatable() { 187321936Shselasky try { 188321936Shselasky // Repeatable will not be available when bootstrapping on 189321936Shselasky // JDK 7 so use a reflective lookup instead of a class 190321936Shselasky // literal for Repeatable.class. 191321936Shselasky return Class.forName("java.lang.annotation.Repeatable").asSubclass(Annotation.class); 192321936Shselasky } catch (ClassNotFoundException | SecurityException e) { 193321936Shselasky return null; 194321936Shselasky } 195321936Shselasky } 196321936Shselasky 197321936Shselasky private static Method initValueElementMethod() { 198321936Shselasky if (REPEATABLE_CLASS == null) 199321936Shselasky return null; 200321936Shselasky 201321936Shselasky Method m = null; 202321936Shselasky try { 203321936Shselasky m = REPEATABLE_CLASS.getMethod("value"); 204321936Shselasky if (m != null) 205321936Shselasky m.setAccessible(true); 206321936Shselasky return m; 207321936Shselasky } catch (NoSuchMethodException e) { 208321936Shselasky return null; 209321936Shselasky } 210321936Shselasky } 211321936Shselasky 212321936Shselasky 213321936Shselasky // Helper to getAnnotationsByType 214321936Shselasky private static Class<? extends Annotation> getContainer(Class<? extends Annotation> annoType) { 215321936Shselasky // Since we can not refer to java.lang.annotation.Repeatable until we are 216321936Shselasky // bootstrapping with java 8 we need to get the Repeatable annotation using 217321936Shselasky // reflective invocations instead of just using its type and element method. 218321936Shselasky if (REPEATABLE_CLASS != null && 219321936Shselasky VALUE_ELEMENT_METHOD != null) { 220321936Shselasky // Get the Repeatable instance on the annotations declaration 221321936Shselasky Annotation repeatable = (Annotation)annoType.getAnnotation(REPEATABLE_CLASS); 222321936Shselasky if (repeatable != null) { 223321936Shselasky try { 224321936Shselasky // Get the value element, it should be a class 225321936Shselasky // indicating the containing annotation type 226321936Shselasky @SuppressWarnings("unchecked") 227321936Shselasky Class<? extends Annotation> containerType = (Class)VALUE_ELEMENT_METHOD.invoke(repeatable); 228321936Shselasky if (containerType == null) 229321936Shselasky return null; 230321936Shselasky 231321936Shselasky return containerType; 232321936Shselasky } catch (ClassCastException | IllegalAccessException | InvocationTargetException e) { 233321936Shselasky return null; 234321936Shselasky } 235321936Shselasky } 236321936Shselasky } 237321936Shselasky return null; 238321936Shselasky } 239321936Shselasky 240321936Shselasky 241321936Shselasky // Helper to getAnnotationsByType 242321936Shselasky private static Attribute[] unpackAttributes(Attribute.Compound container) { 243321936Shselasky // We now have an instance of the container, 244321936Shselasky // unpack it returning an instance of the 245321936Shselasky // contained type or null 246321936Shselasky return ((Attribute.Array)container.member(container.type.tsym.name.table.names.value)).values; 247321936Shselasky } 248321936Shselasky 249321936Shselasky} 250321936Shselasky