All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.jandex.ClassInfo Maven / Gradle / Ivy

There is a newer version: 1.3.3
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2013 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jboss.jandex;

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Represents a class entry in an index. A ClassInfo is only a partial view of a
 * Java class, it is not intended as a complete replacement for Java reflection.
 * Only the methods and fields which are references by an annotation are stored.
 *
 * 

Global information including the parent class, implemented methodParameters, and * access flags are also provided since this information is often necessary. * *

Note that a parent class and interface may exist outside of the scope of the * index (e.g. classes in a different jar) so the references are stored as names * instead of direct references. It is expected that multiple indexes may need * to be queried to assemble a full hierarchy in a complex multi-jar environment * (e.g. an application server). * *

Thread-Safety

* This class is immutable and can be shared between threads without safe publication. * * @author Jason T. Greene * */ public final class ClassInfo implements AnnotationTarget { private final DotName name; private final short flags; private final Map> annotations; // Not final to allow lazy initialization, immutable once published private Type[] interfaceTypes; private Type superClassType; private Type[] typeParameters; private MethodInternal[] methods; private FieldInternal[] fields; private boolean hasNoArgsConstructor; private NestingInfo nestingInfo; /** Describes the form of nesting used by a class */ public enum NestingType { /** A standard class declared within its own source unit */ TOP_LEVEL, /** A named class enclosed by another class */ INNER, /** A named class enclosed within a code block */ LOCAL, /** An unnamed class enclosed within a code block */ ANONYMOUS} private static final class NestingInfo { private DotName enclosingClass; private String simpleName; private EnclosingMethodInfo enclosingMethod; } /** * Provides information on the enclosing method or constructor for a local or anonymous class, * if available. */ public static final class EnclosingMethodInfo { private String name; private Type returnType; private Type[] parameters; private DotName enclosingClass; /** * The name of the method or constructor * * @return the name of the method or constructor */ public String name() { return name; } /** * Returns the return type of the method. * * @return the return type */ public Type returnType() { return returnType; } /** * Returns the list of parameters declared by this method or constructor. * This may be empty, but never null. * * @return the list of parameters. */ public List parameters() { return Collections.unmodifiableList(Arrays.asList(parameters)); } Type[] parametersArray() { return parameters; } /** * Returns the class name which declares this method or constructor. * * @return the name of the class which declared this method or constructor */ public DotName enclosingClass() { return enclosingClass; } EnclosingMethodInfo(String name, Type returnType, Type[] parameters, DotName enclosingClass) { this.name = name; this.returnType = returnType; this.parameters = parameters; this.enclosingClass = enclosingClass; } public String toString() { StringBuilder builder = new StringBuilder(); builder.append(returnType).append(' ').append(enclosingClass).append('.').append(name).append('('); for (int i = 0; i < parameters.length; i++) { builder.append(parameters[i]); if (i + 1 < parameters.length) builder.append(", "); } builder.append(')'); return builder.toString(); } } ClassInfo(DotName name, Type superClassType, short flags, Type[] interfaceTypes, Map> annotations) { this(name, superClassType, flags, interfaceTypes, annotations, false); } ClassInfo(DotName name, Type superClassType, short flags, Type[] interfaceTypes, Map> annotations, boolean hasNoArgsConstructor) { this.name = name; this.superClassType = superClassType; this.flags = flags; this.interfaceTypes = interfaceTypes.length == 0 ? Type.EMPTY_ARRAY : interfaceTypes; this.annotations = Collections.unmodifiableMap(annotations); // FIXME this.hasNoArgsConstructor = hasNoArgsConstructor; this.typeParameters = Type.EMPTY_ARRAY; this.methods = MethodInternal.EMPTY_ARRAY; this.fields = FieldInternal.EMPTY_ARRAY; } /** * Constructs a "mock" ClassInfo using the passed values. All passed values MUST NOT BE MODIFIED AFTER THIS CALL. * Otherwise the resulting object would not conform to the contract outlined above. * * @param name the name of this class * @param superName the name of the parent class * @param flags the class attributes * @param interfaces the methodParameters this class implements * @param annotations the annotations on this class * @param hasNoArgsConstructor whether this class has a no arg constructor * @return a new mock class representation */ @Deprecated public static ClassInfo create(DotName name, DotName superName, short flags, DotName[] interfaces, Map> annotations, boolean hasNoArgsConstructor) { Type[] interfaceTypes = new Type[interfaces.length]; for (int i = 0; i < interfaces.length; i++) { interfaceTypes[i] = new ClassType(interfaces[i]); } ClassType superClassType = superName == null ? null : new ClassType(superName); return new ClassInfo(name, superClassType, flags, interfaceTypes, annotations, hasNoArgsConstructor); } @Override public final Kind kind() { return Kind.CLASS; } public String toString() { return name.toString(); } /** * Returns the name of the class * * @return the name of the class */ public final DotName name() { return name; } /** * Returns the access flags for this class. The standard {@link java.lang.reflect.Modifier} * can be used to decode the value. * * @return the access flags */ public final short flags() { return flags; } /** * Returns the name of the super class declared by the extends clause of this class. This * information is also available from the {@link #superClassType} method. For all classes, * with the one exception of java.lang.Object, which is the one class in the * Java language without a super-type, this method will always return a non-null value. * * @return the name of the super class of this class, or null if this class is java.lang.Object */ public final DotName superName() { return superClassType == null ? null : superClassType.name(); } /** * Returns an array of interface names implemented by this class. Every call to this method * performs a defensive copy, so {@link #interfaceNames()} should be used instead. * * @return an array of interface names implemented by this class */ @Deprecated public final DotName[] interfaces() { DotName[] interfaces = new DotName[interfaceTypes.length]; for (int i = 0; i < interfaceTypes.length; i++) { interfaces[i] = interfaceTypes[i].name(); } return interfaces; } /** * Returns a map indexed by annotation name, with a value list of annotation instances. * The annotation instances in this map correspond to both annotations on the class, * and every nested element of the class (fields, types, methods, etc). * *

The target of the annotation instance can be used to determine the location of * the annotation usage.

* * @return the annotations specified on this class and its elements */ public final Map> annotations() { return annotations; } /** * Returns a list of all annotations directly declared on this class. * * @return the list of annotations declared on this class */ public final Collection classAnnotations() { return new AnnotationTargetFilterCollection(annotations, ClassInfo.class); } /** * Returns a list of all methods declared in this class. This includes constructors * and static initializer blocks which have the special JVM assigned names of "<init>" * and "<clinit>", respectively. It does not, however, include inherited methods. * These must be discovered by traversing the class hierarchy. * *

This list may be empty, but never null.

* * @return the list of methods declared in this class */ public final List methods() { return new MethodInfoGenerator(this, methods); } final MethodInternal[] methodArray() { return methods; } /** * Retrieves a method based on its signature, which includes a method name and an argument list. * The argument list is compared based on the underlying raw type of the type arguments. As an example, * a generic type parameter "T" is equivalent to java.lang.Object, since the raw form * of a type parameter is its upper bound. * *

Eligible methods include constructors and static initializer blocks which have the special JVM * assigned names of "<init>" and "<clinit>", respectively. This does not, however, include * inherited methods. These must be discovered by traversing the class hierarchy.

* * @param name the name of the method to find * @param parameters the type parameters of the method * @return the located method or null if not found */ public final MethodInfo method(String name, Type... parameters) { MethodInternal key = new MethodInternal(Utils.toUTF8(name), parameters, null, (short) 0); int i = Arrays.binarySearch(methods, key, MethodInternal.NAME_AND_PARAMETER_COMPONENT_COMPARATOR); return i >= 0 ? new MethodInfo(this, methods[i]) : null; } /** * Retrieves the "first" occurrence of a method by the given name. Note that the order of methods * is not defined, and may change in the future. Therefore, this method should not be used when * overloading is possible. It's merely intended to provide a handy shortcut for throw away or test * code. * * @param name the name of the method * @return the first discovered method matching this name, or null if no match is found */ public final MethodInfo firstMethod(String name) { MethodInternal key = new MethodInternal(Utils.toUTF8(name), Type.EMPTY_ARRAY, null, (short) 0); int i = Arrays.binarySearch(methods, key, MethodInternal.NAME_AND_PARAMETER_COMPONENT_COMPARATOR); if (i < -methods.length) { return null; } MethodInfo method = new MethodInfo(this,i >= 0 ? methods[i] : methods[++i * -1]); return method.name().equals(name) ? method : null; } /** * Retrieves a field by the given name. Only fields declared in this class are available. * Locating inherited fields requires traversing the class hierarchy. * * @param name the name of the field * @return the field */ public final FieldInfo field(String name) { FieldInternal key = new FieldInternal(Utils.toUTF8(name), VoidType.VOID, (short)0); int i = Arrays.binarySearch(fields, key, FieldInternal.NAME_COMPARATOR); if (i < 0) { return null; } return new FieldInfo(this, fields[i]); } /** * Returns a list of all available fields. Only fields declared in this class are available. * Locating inherited fields requires traversing the class hierarchy. This list may be * empty, but never null. * * @return a list of fields */ public final List fields() { return new FieldInfoGenerator(this, fields); } final FieldInternal[] fieldArray() { return fields; } /** * Returns a list of names for all interfaces this class implements. This list may be empty, but never null. * *

Note that this information is also available on the Type instances returned by * {@link #interfaceTypes}

* * @return the list of names implemented by this class */ public final List interfaceNames() { return new AbstractList() { @Override public DotName get(int i) { return interfaceTypes[i].name(); } @Override public int size() { return interfaceTypes.length; } }; } /** * Returns the list of types in the implements clause of this class. These types may be generic types. * This list may be empty, but never null * * @return the list of types declared in the implements clause of this class */ public final List interfaceTypes() { return Collections.unmodifiableList(Arrays.asList(interfaceTypes)); } final Type[] interfaceTypeArray() { return interfaceTypes; } final Type[] copyInterfaceTypes() { return interfaceTypes.clone(); } /** * Returns a super type represented by the extends clause of this class. This type might be a generic type. * * @return the super class type definition in the extends clause */ public final Type superClassType() { return superClassType; } /** * Returns the generic type parameters of this class, if any. These will be returned as resolved type variables, * so if a parameter has a bound on another parameter, that information will be available. * * @return the generic type parameters of this class */ public final List typeParameters() { @SuppressWarnings("unchecked") List list = (List) Arrays.asList(typeParameters); return Collections.unmodifiableList(list); } final Type[] typeParameterArray() { return typeParameters; } /** * Returns a boolean indicating the presence of a no-arg constructor, if supported by the underlying index store. * This information is available in indexes produced by Jandex 1.2.0 and later. * * @return true in case of the Java class has a no-copyParameters constructor, false * if it does not, or it is not known * @since 1.2.0 */ public final boolean hasNoArgsConstructor() { return hasNoArgsConstructor; } /** * Returns the nesting type of this class, which could either be a standard top level class, an inner class, * an anonymous class, or a local class. * * @return the nesting type of this class */ public NestingType nestingType() { if (nestingInfo == null) { return NestingType.TOP_LEVEL; } else if (nestingInfo.enclosingClass != null) { return NestingType.INNER; } else if (nestingInfo.simpleName != null) { return NestingType.LOCAL; } return NestingType.ANONYMOUS; } /** * Returns the source declared name of this class if it is an inner class, or a local class. Otherwise * this method will return null. * * @return the simple name of a local or inner class, or null if this is a top level or anonymous class */ public String simpleName() { return nestingInfo != null ? nestingInfo.simpleName : null; } /** * Returns the enclosing class if this is an inner class, or null if this is an anonymous, a local, or * a top level class. * * @return the enclosing class if this class is an inner class */ public DotName enclosingClass() { return nestingInfo != null ? nestingInfo.enclosingClass : null; } /** * Returns the enclosing method of this class if it is a local, or anonymous class, and it is declared * within the body of a method or constructor. It will return null if this class is a top level, or an inner class. * It will also return null if the local or anonymous class is on an initializer. * * @return the enclosing method/constructor, if this class is local or anonymous, and it is within a * method/constructor */ public EnclosingMethodInfo enclosingMethod() { return nestingInfo != null ? nestingInfo.enclosingMethod : null; } @Override public ClassInfo asClass() { return this; } @Override public FieldInfo asField() { throw new IllegalArgumentException("Not a field"); } @Override public MethodInfo asMethod() { throw new IllegalArgumentException("Not a method"); } @Override public MethodParameterInfo asMethodParameter() { throw new IllegalArgumentException("Not a method parameter"); } @Override public TypeTarget asType() { throw new IllegalArgumentException("Not a type"); } void setHasNoArgsConstructor(boolean hasNoArgsConstructor) { this.hasNoArgsConstructor = hasNoArgsConstructor; } void setFields(List fields, NameTable names) { if (fields.size() == 0) { this.fields = FieldInternal.EMPTY_ARRAY; return; } this.fields = new FieldInternal[fields.size()]; for (int i = 0; i < fields.size(); i++) { FieldInfo fieldInfo = fields.get(i); FieldInternal internal = names.intern(fieldInfo.fieldInternal()); fieldInfo.setFieldInternal(internal); this.fields[i] = internal; } Arrays.sort(this.fields, FieldInternal.NAME_COMPARATOR); } void setFieldArray(FieldInternal[] fields) { this.fields = fields; } void setMethodArray(MethodInternal[] methods) { this.methods = methods; } void setMethods(List methods, NameTable names) { if (methods.size() == 0) { this.methods = MethodInternal.EMPTY_ARRAY; return; } this.methods = new MethodInternal[methods.size()]; for (int i = 0; i < methods.size(); i++) { MethodInfo methodInfo = methods.get(i); MethodInternal internal = names.intern(methodInfo.methodInternal()); methodInfo.setMethodInternal(internal); this.methods[i] = internal; } Arrays.sort(this.methods, MethodInternal.NAME_AND_PARAMETER_COMPONENT_COMPARATOR); } void setSuperClassType(Type superClassType) { this.superClassType = superClassType; } void setInterfaceTypes(Type[] interfaceTypes) { this.interfaceTypes = interfaceTypes.length == 0 ? Type.EMPTY_ARRAY : interfaceTypes; } void setTypeParameters(Type[] typeParameters) { this.typeParameters = typeParameters.length == 0 ? Type.EMPTY_ARRAY : typeParameters; } void setInnerClassInfo(DotName enclosingClass, String simpleName) { if (enclosingClass == null && simpleName == null) { return; } if (nestingInfo == null) { nestingInfo = new NestingInfo(); } nestingInfo.enclosingClass = enclosingClass; nestingInfo.simpleName = simpleName; } void setEnclosingMethod(EnclosingMethodInfo enclosingMethod) { if (enclosingMethod == null) { return; } if (nestingInfo == null) { nestingInfo = new NestingInfo(); } nestingInfo.enclosingMethod = enclosingMethod; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy