/*
* Copyright 2014 - 2020 Rafael Winterhalter
*
* 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 net.bytebuddy.implementation;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
import net.bytebuddy.implementation.bytecode.Duplication;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.implementation.bytecode.TypeCreation;
import net.bytebuddy.implementation.bytecode.constant.TextConstant;
import net.bytebuddy.implementation.bytecode.member.FieldAccess;
import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
import net.bytebuddy.implementation.bytecode.member.MethodReturn;
import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.util.ArrayList;
import java.util.List;
import static net.bytebuddy.matcher.ElementMatchers.*;
An implementation of Object.toString()
that concatenates the String
representation of all fields that are declared by a class. /**
* An implementation of {@link Object#toString()} that concatenates the {@link String} representation of all fields that are declared by a class.
*/
@HashCodeAndEqualsPlugin.Enhance
public class ToStringMethod implements Implementation {
The StringBuilder(String)
constructor. /**
* The {@link StringBuilder#StringBuilder(String)} constructor.
*/
private static final MethodDescription.InDefinedShape STRING_BUILDER_CONSTRUCTOR = TypeDescription.ForLoadedType.of(StringBuilder.class)
.getDeclaredMethods()
.filter(isConstructor().and(ElementMatchers.takesArguments(String.class)))
.getOnly();
The StringBuilder.toString()
method. /**
* The {@link StringBuilder#toString()} method.
*/
private static final MethodDescription.InDefinedShape TO_STRING = TypeDescription.ForLoadedType.of(StringBuilder.class)
.getDeclaredMethods()
.filter(isToString())
.getOnly();
A resolver for the prefix of a String
representation. /**
* A resolver for the prefix of a {@link String} representation.
*/
private final PrefixResolver prefixResolver;
A token that is added between the prefix and the first field value.
/**
* A token that is added between the prefix and the first field value.
*/
private final String start;
A token that is added after the last field value.
/**
* A token that is added after the last field value.
*/
private final String end;
A token that is added between two field values.
/**
* A token that is added between two field values.
*/
private final String separator;
A token that is added between a field's name and its value.
/**
* A token that is added between a field's name and its value.
*/
private final String definer;
A filter that determines what fields to ignore.
/**
* A filter that determines what fields to ignore.
*/
private final ElementMatcher.Junction<? super FieldDescription.InDefinedShape> ignored;
Creates a new toString
implementation. Params: - prefixResolver – A resolver for the prefix of a
String
representation.
/**
* Creates a new {@code toString} implementation.
*
* @param prefixResolver A resolver for the prefix of a {@link String} representation.
*/
protected ToStringMethod(PrefixResolver prefixResolver) {
this(prefixResolver, "{", "}", ", ", "=", none());
}
Creates a new toString
implementation. Params: - prefixResolver – A resolver for the prefix of a
String
representation. - start – A token that is added between the prefix and the first field value.
- end – A token that is added after the last field value.
- separator – A token that is added between two field values.
- definer – A token that is added between a field's name and its value.
- ignored – A filter that determines what fields to ignore.
/**
* Creates a new {@code toString} implementation.
*
* @param prefixResolver A resolver for the prefix of a {@link String} representation.
* @param start A token that is added between the prefix and the first field value.
* @param end A token that is added after the last field value.
* @param separator A token that is added between two field values.
* @param definer A token that is added between a field's name and its value.
* @param ignored A filter that determines what fields to ignore.
*/
private ToStringMethod(PrefixResolver prefixResolver,
String start,
String end,
String separator,
String definer,
ElementMatcher.Junction<? super FieldDescription.InDefinedShape> ignored) {
this.prefixResolver = prefixResolver;
this.start = start;
this.end = end;
this.separator = separator;
this.definer = definer;
this.ignored = ignored;
}
Creates a Object.toString()
implementation that is prefixed by the fully qualified class name of the instrumented type. Returns: A Object.toString()
implementation that is prefixed by the fully qualified class name of the instrumented type.
/**
* Creates a {@link Object#toString()} implementation that is prefixed by the fully qualified class name of the instrumented type.
*
* @return A {@link Object#toString()} implementation that is prefixed by the fully qualified class name of the instrumented type.
*/
public static ToStringMethod prefixedByFullyQualifiedClassName() {
return prefixedBy(PrefixResolver.Default.FULLY_QUALIFIED_CLASS_NAME);
}
Creates a Object.toString()
implementation that is prefixed by the canonical class name of the instrumented type. Returns: A Object.toString()
implementation that is prefixed by the canonical class name of the instrumented type.
/**
* Creates a {@link Object#toString()} implementation that is prefixed by the canonical class name of the instrumented type.
*
* @return A {@link Object#toString()} implementation that is prefixed by the canonical class name of the instrumented type.
*/
public static ToStringMethod prefixedByCanonicalClassName() {
return prefixedBy(PrefixResolver.Default.CANONICAL_CLASS_NAME);
}
Creates a Object.toString()
implementation that is prefixed by the simple class name of the instrumented type. Returns: A Object.toString()
implementation that is prefixed by the simple class name of the instrumented type.
/**
* Creates a {@link Object#toString()} implementation that is prefixed by the simple class name of the instrumented type.
*
* @return A {@link Object#toString()} implementation that is prefixed by the simple class name of the instrumented type.
*/
public static ToStringMethod prefixedBySimpleClassName() {
return prefixedBy(PrefixResolver.Default.SIMPLE_CLASS_NAME);
}
Creates a Object.toString()
implementation that is prefixed by the supplied string. Params: - prefix – The prefix to use.
Returns: A Object.toString()
implementation that is prefixed by the supplied string.
/**
* Creates a {@link Object#toString()} implementation that is prefixed by the supplied string.
*
* @param prefix The prefix to use.
* @return A {@link Object#toString()} implementation that is prefixed by the supplied string.
*/
public static ToStringMethod prefixedBy(String prefix) {
if (prefix == null) {
throw new IllegalArgumentException("Prefix cannot be null");
}
return prefixedBy(new PrefixResolver.ForFixedValue(prefix));
}
Creates a Object.toString()
implementation that is prefixed by the string that is supplied by the given prefix resolver. Params: - prefixResolver – The prefix resolver to use.
Returns: A Object.toString()
implementation that is prefixed by the string that is supplied by the given prefix resolver.
/**
* Creates a {@link Object#toString()} implementation that is prefixed by the string that is supplied by the given prefix resolver.
*
* @param prefixResolver The prefix resolver to use.
* @return A {@link Object#toString()} implementation that is prefixed by the string that is supplied by the given prefix resolver.
*/
public static ToStringMethod prefixedBy(PrefixResolver prefixResolver) {
return new ToStringMethod(prefixResolver);
}
Returns a new version of this toString method implementation that ignores the specified fields additionally to any
previously specified fields.
Params: - ignored – A matcher to specify any fields that should be ignored.
Returns: A new version of this toString method implementation that also ignores any fields matched by the provided matcher.
/**
* Returns a new version of this toString method implementation that ignores the specified fields additionally to any
* previously specified fields.
*
* @param ignored A matcher to specify any fields that should be ignored.
* @return A new version of this toString method implementation that also ignores any fields matched by the provided matcher.
*/
public ToStringMethod withIgnoredFields(ElementMatcher<? super FieldDescription.InDefinedShape> ignored) {
return new ToStringMethod(prefixResolver, start, end, separator, definer, this.ignored.<FieldDescription.InDefinedShape>or(ignored));
}
Changes the tokens used for the Object.toString()
implementation. Params: - start – A token that is added between the prefix and the first field value.
- end – A token that is added after the last field value.
- separator – A token that is added between two field values.
- definer – A token that is added between two field values.
Returns: A new instance of this implementation that uses the supplied tokens.
/**
* Changes the tokens used for the {@link Object#toString()} implementation.
*
* @param start A token that is added between the prefix and the first field value.
* @param end A token that is added after the last field value.
* @param separator A token that is added between two field values.
* @param definer A token that is added between two field values.
* @return A new instance of this implementation that uses the supplied tokens.
*/
public Implementation withTokens(String start, String end, String separator, String definer) {
if (start == null || end == null || separator == null || definer == null) {
throw new IllegalArgumentException("Token values cannot be null");
}
return new ToStringMethod(prefixResolver, start, end, separator, definer, ignored);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public InstrumentedType prepare(InstrumentedType instrumentedType) {
return instrumentedType;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Appender appender(Target implementationTarget) {
if (implementationTarget.getInstrumentedType().isInterface()) {
throw new IllegalStateException("Cannot implement meaningful toString method for " + implementationTarget.getInstrumentedType());
}
String prefix = prefixResolver.resolve(implementationTarget.getInstrumentedType());
if (prefix == null) {
throw new IllegalStateException("Prefix for toString method cannot be null");
}
return new Appender(prefix,
start,
end,
separator,
definer,
implementationTarget.getInstrumentedType().getDeclaredFields().filter(not(isStatic().or(ignored))));
}
An appender to implement ToStringMethod
. /**
* An appender to implement {@link ToStringMethod}.
*/
@HashCodeAndEqualsPlugin.Enhance
protected static class Appender implements ByteCodeAppender {
The prefix to use.
/**
* The prefix to use.
*/
private final String prefix;
A token that is added between the prefix and the first field value.
/**
* A token that is added between the prefix and the first field value.
*/
private final String start;
A token that is added after the last field value.
/**
* A token that is added after the last field value.
*/
private final String end;
A token that is added between two field values.
/**
* A token that is added between two field values.
*/
private final String separator;
A token that is added between a field's name and its value.
/**
* A token that is added between a field's name and its value.
*/
private final String definer;
The list of fields to include in the Object.toString()
implementation. /**
* The list of fields to include in the {@link Object#toString()} implementation.
*/
private final List<? extends FieldDescription.InDefinedShape> fieldDescriptions;
Creates a new appender.
Params: - prefix – The prefix to use.
- start – A token that is added between the prefix and the first field value.
- end – A token that is added after the last field value.
- separator – A token that is added between two field values.
- definer – A token that is added between a field's name and its value.
- fieldDescriptions – The list of fields to include in the
Object.toString()
implementation.
/**
* Creates a new appender.
*
* @param prefix The prefix to use.
* @param start A token that is added between the prefix and the first field value.
* @param end A token that is added after the last field value.
* @param separator A token that is added between two field values.
* @param definer A token that is added between a field's name and its value.
* @param fieldDescriptions The list of fields to include in the {@link Object#toString()} implementation.
*/
protected Appender(String prefix,
String start,
String end,
String separator,
String definer,
List<? extends FieldDescription.InDefinedShape> fieldDescriptions) {
this.prefix = prefix;
this.start = start;
this.end = end;
this.separator = separator;
this.definer = definer;
this.fieldDescriptions = fieldDescriptions;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) {
if (instrumentedMethod.isStatic()) {
throw new IllegalStateException("toString method must not be static: " + instrumentedMethod);
} else if (!instrumentedMethod.getReturnType().asErasure().isAssignableFrom(String.class)) {
throw new IllegalStateException("toString method does not return String-compatible type: " + instrumentedMethod);
}
List<StackManipulation> stackManipulations = new ArrayList<StackManipulation>(Math.max(0, fieldDescriptions.size() * 7 - 2) + 10);
stackManipulations.add(TypeCreation.of(TypeDescription.ForLoadedType.of(StringBuilder.class)));
stackManipulations.add(Duplication.SINGLE);
stackManipulations.add(new TextConstant(prefix));
stackManipulations.add(MethodInvocation.invoke(STRING_BUILDER_CONSTRUCTOR));
stackManipulations.add(new TextConstant(start));
stackManipulations.add(ValueConsumer.STRING);
boolean first = true;
for (FieldDescription.InDefinedShape fieldDescription : fieldDescriptions) {
if (first) {
first = false;
} else {
stackManipulations.add(new TextConstant(separator));
stackManipulations.add(ValueConsumer.STRING);
}
stackManipulations.add(new TextConstant(fieldDescription.getName() + definer));
stackManipulations.add(ValueConsumer.STRING);
stackManipulations.add(MethodVariableAccess.loadThis());
stackManipulations.add(FieldAccess.forField(fieldDescription).read());
stackManipulations.add(ValueConsumer.of(fieldDescription.getType().asErasure()));
}
stackManipulations.add(new TextConstant(end));
stackManipulations.add(ValueConsumer.STRING);
stackManipulations.add(MethodInvocation.invoke(TO_STRING));
stackManipulations.add(MethodReturn.REFERENCE);
return new Size(new StackManipulation.Compound(stackManipulations).apply(methodVisitor, implementationContext).getMaximalSize(), instrumentedMethod.getStackSize());
}
}
A prefix resolver is responsible for providing a value that is prepended to a Object.toString()
implementation. /**
* A prefix resolver is responsible for providing a value that is prepended to a {@link Object#toString()} implementation.
*/
public interface PrefixResolver {
Resolves the prefixed value.
Params: - instrumentedType – The instrumented type.
Returns: The value to be prefixed.
/**
* Resolves the prefixed value.
*
* @param instrumentedType The instrumented type.
* @return The value to be prefixed.
*/
String resolve(TypeDescription instrumentedType);
Default implementations for a prefix resolver.
/**
* Default implementations for a prefix resolver.
*/
enum Default implements PrefixResolver {
A prefix resolver for the instrumented type's fully qualified class name.
/**
* A prefix resolver for the instrumented type's fully qualified class name.
*/
FULLY_QUALIFIED_CLASS_NAME {
{@inheritDoc} /** {@inheritDoc} */
public String resolve(TypeDescription instrumentedType) {
return instrumentedType.getName();
}
},
A prefix resolver for the instrumented type's fully qualified class name.
/**
* A prefix resolver for the instrumented type's fully qualified class name.
*/
CANONICAL_CLASS_NAME {
{@inheritDoc} /** {@inheritDoc} */
public String resolve(TypeDescription instrumentedType) {
return instrumentedType.getCanonicalName();
}
},
A prefix resolver for the instrumented type's simple class name.
/**
* A prefix resolver for the instrumented type's simple class name.
*/
SIMPLE_CLASS_NAME {
{@inheritDoc} /** {@inheritDoc} */
public String resolve(TypeDescription instrumentedType) {
return instrumentedType.getSimpleName();
}
}
}
A prefix resolver that returns a fixed value.
/**
* A prefix resolver that returns a fixed value.
*/
@HashCodeAndEqualsPlugin.Enhance
class ForFixedValue implements PrefixResolver {
The prefix to prepend.
/**
* The prefix to prepend.
*/
private final String prefix;
Creates a new prefix resolver that returns a fixed value.
Params: - prefix – The prefix to prepend.
/**
* Creates a new prefix resolver that returns a fixed value.
*
* @param prefix The prefix to prepend.
*/
protected ForFixedValue(String prefix) {
this.prefix = prefix;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public String resolve(TypeDescription instrumentedType) {
return prefix;
}
}
}
A value consumer that is responsible for adding a field value to the string creating StringBuilder
. /**
* A value consumer that is responsible for adding a field value to the string creating {@link StringBuilder}.
*/
protected enum ValueConsumer implements StackManipulation {
A value consumer for a boolean
value. /**
* A value consumer for a {@code boolean} value.
*/
BOOLEAN {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Z)Ljava/lang/StringBuilder;", false);
return new Size(0, 0);
}
},
A value consumer for a char
value. /**
* A value consumer for a {@code char} value.
*/
CHARACTER {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(C)Ljava/lang/StringBuilder;", false);
return new Size(0, 0);
}
},
A value consumer for an int
value. /**
* A value consumer for an {@code int} value.
*/
INTEGER {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;", false);
return new Size(0, 0);
}
},
A value consumer for a long
value. /**
* A value consumer for a {@code long} value.
*/
LONG {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;", false);
return new Size(-1, 0);
}
},
A value consumer for a float
value. /**
* A value consumer for a {@code float} value.
*/
FLOAT {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(F)Ljava/lang/StringBuilder;", false);
return new Size(0, 0);
}
},
A value consumer for a double
value. /**
* A value consumer for a {@code double} value.
*/
DOUBLE {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(D)Ljava/lang/StringBuilder;", false);
return new Size(-1, 0);
}
},
A value consumer for a String
value. /**
* A value consumer for a {@link String} value.
*/
STRING {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
return new Size(0, 0);
}
},
A value consumer for a CharSequence
value. /**
* A value consumer for a {@link CharSequence} value.
*/
CHARACTER_SEQUENCE {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/CharSequence;)Ljava/lang/StringBuilder;", false);
return new Size(0, 0);
}
},
A value consumer for a reference type.
/**
* A value consumer for a reference type.
*/
OBJECT {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuilder;", false);
return new Size(0, 0);
}
},
A value consumer for a boolean
array type. /**
* A value consumer for a {@code boolean} array type.
*/
BOOLEAN_ARRAY {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([Z)Ljava/lang/String;", false);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
return new Size(0, 0);
}
},
A value consumer for a byte
array type. /**
* A value consumer for a {@code byte} array type.
*/
BYTE_ARRAY {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([B)Ljava/lang/String;", false);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
return new Size(0, 0);
}
},
A value consumer for a short
array type. /**
* A value consumer for a {@code short} array type.
*/
SHORT_ARRAY {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([S)Ljava/lang/String;", false);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
return new Size(0, 0);
}
},
A value consumer for a char
array type. /**
* A value consumer for a {@code char} array type.
*/
CHARACTER_ARRAY {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([C)Ljava/lang/String;", false);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
return new Size(0, 0);
}
},
A value consumer for an int
array type. /**
* A value consumer for an {@code int} array type.
*/
INTEGER_ARRAY {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([I)Ljava/lang/String;", false);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
return new Size(0, 0);
}
},
A value consumer for a long
array type. /**
* A value consumer for a {@code long} array type.
*/
LONG_ARRAY {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([J)Ljava/lang/String;", false);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
return new Size(0, 0);
}
},
A value consumer for a float
array type. /**
* A value consumer for a {@code float} array type.
*/
FLOAT_ARRAY {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([F)Ljava/lang/String;", false);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
return new Size(0, 0);
}
},
A value consumer for a double
array type. /**
* A value consumer for a {@code double} array type.
*/
DOUBLE_ARRAY {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([D)Ljava/lang/String;", false);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
return new Size(0, 0);
}
},
A value consumer for a reference array type.
/**
* A value consumer for a reference array type.
*/
REFERENCE_ARRAY {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([Ljava/lang/Object;)Ljava/lang/String;", false);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
return new Size(0, 0);
}
},
A value consumer for a nested array type.
/**
* A value consumer for a nested array type.
*/
NESTED_ARRAY {
{@inheritDoc} /** {@inheritDoc} */
public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "deepToString", "([Ljava/lang/Object;)Ljava/lang/String;", false);
methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
return new Size(0, 0);
}
};
Resolves an appropriate value resolver for a given type.
Params: - typeDescription – The type for which to resolve a value resolver.
Returns: An appropriate stack manipulation.
/**
* Resolves an appropriate value resolver for a given type.
*
* @param typeDescription The type for which to resolve a value resolver.
* @return An appropriate stack manipulation.
*/
protected static StackManipulation of(TypeDescription typeDescription) {
if (typeDescription.represents(boolean.class)) {
return BOOLEAN;
} else if (typeDescription.represents(char.class)) {
return CHARACTER;
} else if (typeDescription.represents(byte.class)
|| typeDescription.represents(short.class)
|| typeDescription.represents(int.class)) {
return INTEGER;
} else if (typeDescription.represents(long.class)) {
return LONG;
} else if (typeDescription.represents(float.class)) {
return FLOAT;
} else if (typeDescription.represents(double.class)) {
return DOUBLE;
} else if (typeDescription.represents(String.class)) {
return STRING;
} else if (typeDescription.isAssignableTo(CharSequence.class)) {
return CHARACTER_SEQUENCE;
} else if (typeDescription.represents(boolean[].class)) {
return BOOLEAN_ARRAY;
} else if (typeDescription.represents(byte[].class)) {
return BYTE_ARRAY;
} else if (typeDescription.represents(short[].class)) {
return SHORT_ARRAY;
} else if (typeDescription.represents(char[].class)) {
return CHARACTER_ARRAY;
} else if (typeDescription.represents(int[].class)) {
return INTEGER_ARRAY;
} else if (typeDescription.represents(long[].class)) {
return LONG_ARRAY;
} else if (typeDescription.represents(float[].class)) {
return FLOAT_ARRAY;
} else if (typeDescription.represents(double[].class)) {
return DOUBLE_ARRAY;
} else if (typeDescription.isArray()) {
return typeDescription.getComponentType().isArray()
? NESTED_ARRAY
: REFERENCE_ARRAY;
} else {
return OBJECT;
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isValid() {
return true;
}
}
}