package scouter.bytebuddy.description.field;
import scouter.bytebuddy.description.ByteCodeElement;
import scouter.bytebuddy.description.ModifierReviewable;
import scouter.bytebuddy.description.NamedElement;
import scouter.bytebuddy.description.annotation.AnnotationDescription;
import scouter.bytebuddy.description.annotation.AnnotationList;
import scouter.bytebuddy.description.type.TypeDescription;
import scouter.bytebuddy.matcher.ElementMatcher;
import scouter.bytebuddy.jar.asm.Opcodes;
import scouter.bytebuddy.jar.asm.signature.SignatureWriter;
import java.lang.reflect.Field;
import java.lang.reflect.GenericSignatureFormatError;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.List;
/**
* Implementations of this interface describe a Java field. Implementations of this interface must provide meaningful
* {@code equal(Object)} and {@code hashCode()} implementations.
*/
public interface FieldDescription extends ByteCodeElement,
ModifierReviewable.ForFieldDescription,
NamedElement.WithGenericName,
ByteCodeElement.TypeDependant<FieldDescription.InDefinedShape, FieldDescription.Token> {
/**
* A representative of a field's non-set default value.
*/
Object NO_DEFAULT_VALUE = null;
/**
* Returns the type of the described field.
*
* @return The type of the described field.
*/
TypeDescription.Generic getType();
/**
* Returns the field's actual modifiers as it is present in a class file, i.e. its modifiers including
* a flag if this field is deprecated.
*
* @return The field's actual modifiers.
*/
int getActualModifiers();
/**
* Returns a signature token representing this field.
*
* @return A signature token representing this field.
*/
SignatureToken asSignatureToken();
/**
* Represents a field description in its generic shape, i.e. in the shape it is defined by a generic or raw type.
*/
interface InGenericShape extends FieldDescription {
@Override
TypeDescription.Generic getDeclaringType();
}
/**
* Represents a field in its defined shape, i.e. in the form it is defined by a class without its type variables being resolved.
*/
interface InDefinedShape extends FieldDescription {
@Override
TypeDescription getDeclaringType();
/**
* An abstract base implementation of a field description in its defined shape.
*/
abstract class AbstractBase extends FieldDescription.AbstractBase implements InDefinedShape {
@Override
public InDefinedShape asDefined() {
return this;
}
}
}
/**
* An abstract base implementation of a field description.
*/
abstract class AbstractBase extends ModifierReviewable.AbstractBase implements FieldDescription {
@Override
public String getInternalName() {
return getName();
}
@Override
public String getActualName() {
return getName();
}
@Override
public String getDescriptor() {
return getType().asErasure().getDescriptor();
}
@Override
public String getGenericSignature() {
TypeDescription.Generic fieldType = getType();
try {
return fieldType.getSort().isNonGeneric()
? NON_GENERIC_SIGNATURE
: fieldType.accept(new TypeDescription.Generic.Visitor.ForSignatureVisitor(new SignatureWriter())).toString();
} catch (GenericSignatureFormatError ignored) {
return NON_GENERIC_SIGNATURE;
}
}
@Override
public boolean isVisibleTo(TypeDescription typeDescription) {
return getDeclaringType().asErasure().isVisibleTo(typeDescription)
&& (isPublic()
|| typeDescription.equals(getDeclaringType().asErasure())
|| (isProtected() && getDeclaringType().asErasure().isAssignableFrom(typeDescription))
|| (!isPrivate() && typeDescription.isSamePackage(getDeclaringType().asErasure())));
}
@Override
public boolean isAccessibleTo(TypeDescription typeDescription) {
return isPublic()
|| typeDescription.equals(getDeclaringType().asErasure())
|| (!isPrivate() && typeDescription.isSamePackage(getDeclaringType().asErasure()));
}
@Override
public int getActualModifiers() {
return getModifiers() | (getDeclaredAnnotations().isAnnotationPresent(Deprecated.class)
? Opcodes.ACC_DEPRECATED
: EMPTY_MASK);
}
@Override
public FieldDescription.Token asToken(ElementMatcher<? super TypeDescription> matcher) {
return new FieldDescription.Token(getName(),
getModifiers(),
getType().accept(new TypeDescription.Generic.Visitor.Substitutor.ForDetachment(matcher)),
getDeclaredAnnotations());
}
@Override
public SignatureToken asSignatureToken() {
return new SignatureToken(getInternalName(), getType().asErasure());
}
@Override
public boolean equals(Object other) {
return other == this || other instanceof FieldDescription
&& getName().equals(((FieldDescription) other).getName())
&& getDeclaringType().equals(((FieldDescription) other).getDeclaringType());
}
@Override
public int hashCode() {
return getDeclaringType().hashCode() + 31 * getName().hashCode();
}
@Override
public String toGenericString() {
StringBuilder stringBuilder = new StringBuilder();
if (getModifiers() != EMPTY_MASK) {
stringBuilder.append(Modifier.toString(getModifiers())).append(" ");
}
stringBuilder.append(getType().getActualName()).append(" ");
stringBuilder.append(getDeclaringType().asErasure().getActualName()).append(".");
return stringBuilder.append(getName()).toString();
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
if (getModifiers() != EMPTY_MASK) {
stringBuilder.append(Modifier.toString(getModifiers())).append(" ");
}
stringBuilder.append(getType().asErasure().getActualName()).append(" ");
stringBuilder.append(getDeclaringType().asErasure().getActualName()).append(".");
return stringBuilder.append(getName()).toString();
}
}
/**
* An implementation of a field description for a loaded field.
*/
class ForLoadedField extends InDefinedShape.AbstractBase {
/**
* The represented loaded field.
*/
private final Field field;
/**
* Creates an immutable field description for a loaded field.
*
* @param field The represented field.
*/
public ForLoadedField(Field field) {
this.field = field;
}
@Override
public TypeDescription.Generic getType() {
return new TypeDescription.Generic.LazyProjection.ForLoadedFieldType(field);
}
@Override
public AnnotationList getDeclaredAnnotations() {
return new AnnotationList.ForLoadedAnnotations(field.getDeclaredAnnotations());
}
@Override
public String getName() {
return field.getName();
}
@Override
public TypeDescription getDeclaringType() {
return new TypeDescription.ForLoadedType(field.getDeclaringClass());
}
@Override
public int getModifiers() {
return field.getModifiers();
}
@Override
public boolean isSynthetic() {
return field.isSynthetic();
}
}
/**
* A latent field description describes a field that is not attached to a declaring
* {@link TypeDescription}.
*/
class Latent extends InDefinedShape.AbstractBase {
/**
* The type for which this field is defined.
*/
private final TypeDescription declaringType;
/**
* The name of the field.
*/
private final String fieldName;
/**
* The field's modifiers.
*/
private final int modifiers;
/**
* The type of the field.
*/
private final TypeDescription.Generic fieldType;
/**
* The annotations of this field.
*/
private final List<? extends AnnotationDescription> declaredAnnotations;
/**
* Creates a new latent field description. All provided types are attached to this instance before they are returned.
*
* @param declaringType The declaring type of the field.
* @param token A token representing the field's shape.
*/
public Latent(TypeDescription declaringType, FieldDescription.Token token) {
this(declaringType,
token.getName(),
token.getModifiers(),
token.getType(),
token.getAnnotations());
}
/**
* Creates a new latent field description. All provided types are attached to this instance before they are returned.
*
* @param declaringType The declaring type of the field.
* @param fieldName The name of the field.
* @param fieldType The field's modifiers.
* @param modifiers The type of the field.
* @param declaredAnnotations The annotations of this field.
*/
public Latent(TypeDescription declaringType,
String fieldName,
int modifiers,
TypeDescription.Generic fieldType,
List<? extends AnnotationDescription> declaredAnnotations) {
this.declaringType = declaringType;
this.fieldName = fieldName;
this.modifiers = modifiers;
this.fieldType = fieldType;
this.declaredAnnotations = declaredAnnotations;
}
@Override
public TypeDescription.Generic getType() {
return fieldType.accept(TypeDescription.Generic.Visitor.Substitutor.ForAttachment.of(this));
}
@Override
public AnnotationList getDeclaredAnnotations() {
return new AnnotationList.Explicit(declaredAnnotations);
}
@Override
public String getName() {
return fieldName;
}
@Override
public TypeDescription getDeclaringType() {
return declaringType;
}
@Override
public int getModifiers() {
return modifiers;
}
}
/**
* A field description that represents a given field but with a substituted field type.
*/
class TypeSubstituting extends AbstractBase implements InGenericShape {
/**
* The declaring type of the field.
*/
private final TypeDescription.Generic declaringType;
/**
* The represented field.
*/
private final FieldDescription fieldDescription;
/**
* A visitor that is applied to the field type.
*/
private final TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor;
/**
* Creates a field description with a substituted field type.
*
* @param declaringType The declaring type of the field.
* @param fieldDescription The represented field.
* @param visitor A visitor that is applied to the field type.
*/
public TypeSubstituting(TypeDescription.Generic declaringType,
FieldDescription fieldDescription,
TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor) {
this.declaringType = declaringType;
this.fieldDescription = fieldDescription;
this.visitor = visitor;
}
@Override
public TypeDescription.Generic getType() {
return fieldDescription.getType().accept(visitor);
}
@Override
public AnnotationList getDeclaredAnnotations() {
return fieldDescription.getDeclaredAnnotations();
}
@Override
public TypeDescription.Generic getDeclaringType() {
return declaringType;
}
@Override
public int getModifiers() {
return fieldDescription.getModifiers();
}
@Override
public String getName() {
return fieldDescription.getName();
}
@Override
public InDefinedShape asDefined() {
return fieldDescription.asDefined();
}
}
/**
* A token representing a field's properties detached from a type.
*/
class Token implements ByteCodeElement.Token<Token> {
/**
* The name of the represented field.
*/
private final String name;
/**
* The modifiers of the represented field.
*/
private final int modifiers;
/**
* The type of the represented field.
*/
private final TypeDescription.Generic type;
/**
* The annotations of the represented field.
*/
private final List<? extends AnnotationDescription> annotations;
/**
* Creates a new field token without annotations. The field type must be represented in its detached form.
*
* @param name The name of the represented field.
* @param modifiers The modifiers of the represented field.
* @param type The type of the represented field.
*/
public Token(String name, int modifiers, TypeDescription.Generic type) {
this(name, modifiers, type, Collections.<AnnotationDescription>emptyList());
}
/**
* Creates a new field token. The field type must be represented in its detached form.
*
* @param name The name of the represented field.
* @param modifiers The modifiers of the represented field.
* @param type The type of the represented field.
* @param annotations The annotations of the represented field.
*/
public Token(String name, int modifiers, TypeDescription.Generic type, List<? extends AnnotationDescription> annotations) {
this.name = name;
this.modifiers = modifiers;
this.type = type;
this.annotations = annotations;
}
/**
* Returns the name of the represented field.
*
* @return The name of the represented field.
*/
public String getName() {
return name;
}
/**
* Returns the type of the represented field.
*
* @return The type of the represented field.
*/
public TypeDescription.Generic getType() {
return type;
}
/**
* Returns the modifiers of the represented field.
*
* @return The modifiers of the represented field.
*/
public int getModifiers() {
return modifiers;
}
/**
* Returns the annotations of the represented field.
*
* @return The annotations of the represented field.
*/
public AnnotationList getAnnotations() {
return new AnnotationList.Explicit(annotations);
}
@Override
public Token accept(TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor) {
return new Token(name,
modifiers,
type.accept(visitor),
annotations);
}
/**
* Creates a signature token that represents the method that is represented by this token.
*
* @param declaringType The declaring type of the field that this token represents.
* @return A signature token representing this token.
*/
public SignatureToken asSignatureToken(TypeDescription declaringType) {
return new SignatureToken(name, type.accept(new TypeDescription.Generic.Visitor.Reducing(declaringType)));
}
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other == null || getClass() != other.getClass()) return false;
Token token = (Token) other;
return modifiers == token.modifiers
&& name.equals(token.name)
&& type.equals(token.type)
&& annotations.equals(token.annotations);
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + modifiers;
result = 31 * result + type.hashCode();
result = 31 * result + annotations.hashCode();
return result;
}
}
/**
* A token that uniquely identifies a field by its name and type erasure.
*/
class SignatureToken {
/**
* The field's name.
*/
private final String name;
/**
* The field's raw type.
*/
private final TypeDescription type;
/**
* Creates a new signature token.
*
* @param name The field's name.
* @param type The field's raw type.
*/
public SignatureToken(String name, TypeDescription type) {
this.name = name;
this.type = type;
}
/**
* Returns the name of the represented field.
*
* @return The name of the represented field.
*/
public String getName() {
return name;
}
/**
* Returns the type of the represented field.
*
* @return The type of the represented field.
*/
public TypeDescription getType() {
return type;
}
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (!(other instanceof SignatureToken)) return false;
SignatureToken that = (SignatureToken) other;
return name.equals(that.name) && type.equals(that.type);
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + type.hashCode();
return result;
}
@Override
public String toString() {
return type + " " + name;
}
}
}