/*
 * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package sun.reflect.annotation;

import java.lang.annotation.Annotation;
import java.lang.annotation.AnnotationFormatError;
import java.lang.reflect.AnnotatedElement;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

A TypeAnnotation contains all the information needed to transform type annotations on declarations in the class file to actual Annotations in AnnotatedType instances. TypeAnnotaions contain a base Annotation, location info (which lets you distinguish between '@A Inner.@B Outer' in for example nested types), target info and the declaration the TypeAnnotaiton was parsed from.
/** * A TypeAnnotation contains all the information needed to transform type * annotations on declarations in the class file to actual Annotations in * AnnotatedType instances. * * TypeAnnotaions contain a base Annotation, location info (which lets you * distinguish between '@A Inner.@B Outer' in for example nested types), * target info and the declaration the TypeAnnotaiton was parsed from. */
public final class TypeAnnotation { private final TypeAnnotationTargetInfo targetInfo; private final LocationInfo loc; private final Annotation annotation; private final AnnotatedElement baseDeclaration; public TypeAnnotation(TypeAnnotationTargetInfo targetInfo, LocationInfo loc, Annotation annotation, AnnotatedElement baseDeclaration) { this.targetInfo = targetInfo; this.loc = loc; this.annotation = annotation; this.baseDeclaration = baseDeclaration; } public TypeAnnotationTargetInfo getTargetInfo() { return targetInfo; } public Annotation getAnnotation() { return annotation; } public AnnotatedElement getBaseDeclaration() { return baseDeclaration; } public LocationInfo getLocationInfo() { return loc; } public static List<TypeAnnotation> filter(TypeAnnotation[] typeAnnotations, TypeAnnotationTarget predicate) { ArrayList<TypeAnnotation> typeAnnos = new ArrayList<>(typeAnnotations.length); for (TypeAnnotation t : typeAnnotations) if (t.getTargetInfo().getTarget() == predicate) typeAnnos.add(t); typeAnnos.trimToSize(); return typeAnnos; } public static enum TypeAnnotationTarget { CLASS_TYPE_PARAMETER, METHOD_TYPE_PARAMETER, CLASS_EXTENDS, CLASS_IMPLEMENTS, // Not in the spec CLASS_TYPE_PARAMETER_BOUND, METHOD_TYPE_PARAMETER_BOUND, FIELD, METHOD_RETURN, METHOD_RECEIVER, METHOD_FORMAL_PARAMETER, THROWS; } public static final class TypeAnnotationTargetInfo { private final TypeAnnotationTarget target; private final int count; private final int secondaryIndex; private static final int UNUSED_INDEX = -2; // this is not a valid index in the 308 spec public TypeAnnotationTargetInfo(TypeAnnotationTarget target) { this(target, UNUSED_INDEX, UNUSED_INDEX); } public TypeAnnotationTargetInfo(TypeAnnotationTarget target, int count) { this(target, count, UNUSED_INDEX); } public TypeAnnotationTargetInfo(TypeAnnotationTarget target, int count, int secondaryIndex) { this.target = target; this.count = count; this.secondaryIndex = secondaryIndex; } public TypeAnnotationTarget getTarget() { return target; } public int getCount() { return count; } public int getSecondaryIndex() { return secondaryIndex; } @Override public String toString() { return "" + target + ": " + count + ", " + secondaryIndex; } } public static final class LocationInfo { private final int depth; private final Location[] locations; private LocationInfo() { this(0, new Location[0]); } private LocationInfo(int depth, Location[] locations) { this.depth = depth; this.locations = locations; } public static final LocationInfo BASE_LOCATION = new LocationInfo(); public static LocationInfo parseLocationInfo(ByteBuffer buf) { int depth = buf.get() & 0xFF; if (depth == 0) return BASE_LOCATION; Location[] locations = new Location[depth]; for (int i = 0; i < depth; i++) { byte tag = buf.get(); short index = (short)(buf.get() & 0xFF); if (!(tag == 0 || tag == 1 | tag == 2 || tag == 3)) throw new AnnotationFormatError("Bad Location encoding in Type Annotation"); if (tag != 3 && index != 0) throw new AnnotationFormatError("Bad Location encoding in Type Annotation"); locations[i] = new Location(tag, index); } return new LocationInfo(depth, locations); } public LocationInfo pushArray() { return pushLocation((byte)0, (short)0); } public LocationInfo pushInner() { return pushLocation((byte)1, (short)0); } public LocationInfo pushWildcard() { return pushLocation((byte) 2, (short) 0); } public LocationInfo pushTypeArg(short index) { return pushLocation((byte) 3, index); } public LocationInfo pushLocation(byte tag, short index) { int newDepth = this.depth + 1; Location[] res = new Location[newDepth]; System.arraycopy(this.locations, 0, res, 0, depth); res[newDepth - 1] = new Location(tag, (short)(index & 0xFF)); return new LocationInfo(newDepth, res); }
Pop a series of locations matching tag. Stop poping as soon as a non-matching tag is found.
/** Pop a series of locations matching {@code tag}. Stop poping as soon as a non-matching tag is found. */
public LocationInfo popAllLocations(byte tag) { LocationInfo l = this; int newDepth = l.depth; while(newDepth > 0 && l.locations[newDepth - 1].tag == tag) { newDepth--; } if (newDepth != l.depth) { Location[] res = new Location[newDepth]; System.arraycopy(this.locations, 0, res, 0, newDepth); return new LocationInfo(newDepth, res); } else return l; } public TypeAnnotation[] filter(TypeAnnotation[] ta) { ArrayList<TypeAnnotation> l = new ArrayList<>(ta.length); for (TypeAnnotation t : ta) { if (isSameLocationInfo(t.getLocationInfo())) l.add(t); } return l.toArray(AnnotatedTypeFactory.EMPTY_TYPE_ANNOTATION_ARRAY); } boolean isSameLocationInfo(LocationInfo other) { if (depth != other.depth) return false; for (int i = 0; i < depth; i++) if (!locations[i].isSameLocation(other.locations[i])) return false; return true; } public static final class Location { public final byte tag; public final short index; boolean isSameLocation(Location other) { return tag == other.tag && index == other.index; } public Location(byte tag, short index) { this.tag = tag; this.index = index; } } } @Override public String toString() { return annotation.toString() + " with Targetnfo: " + targetInfo.toString() + " on base declaration: " + baseDeclaration.toString(); } }