package ftbsc.lll.tools; import jdk.internal.org.objectweb.asm.Type; import java.util.ArrayList; /** * Builds a method descriptor for you. * Parameters must be given in a specific order. * Return type should always be specified for clarity, but defaults to void. */ public class DescriptorBuilder { /** * The descriptor of the return type. */ private String returnType; /** * The descriptors of the parameters. */ private final ArrayList params; /** * Public constructor. * Initialises default values. */ public DescriptorBuilder() { this.returnType = Type.getDescriptor(void.class); this.params = new ArrayList<>(); } /** * Sets the return type to the given type. * WARNING: will most likely cause problems if used with objects outside the * Java SDK. Pass the fully qualified name as a String rather than the Class * object for non-standard types (such as Minecraft classes). * @param returnType the Class object corresponding to the return type * @return the builder's state after the change */ public DescriptorBuilder setReturnType(Class returnType) { this.returnType = Type.getDescriptor(returnType); return this; } /** * Sets the return type to the Object specified here as a fully * qualified name. Example: java.lang.String. * No validity checks are performed: it's up to the user to ensure the name is correct. * @param returnType the fully qualified name of the desired Object. * @return the builder's state after the change */ public DescriptorBuilder setReturnType(String returnType) { return this.setReturnType(returnType, 0); } /** * Sets the return type to the Object specified here as a fully * qualified name (example: java.lang.String), with the specified array level. * No validity checks are performed: it's up to the user to ensure the name is correct. * @param returnType the fully qualified name of the desired Object. * @param arrayLevel how many levels of array are there * (example: String is 0, String[] is 1, String[][] is 2, etc.) * @return the builder's state after the change */ public DescriptorBuilder setReturnType(String returnType, int arrayLevel) { this.returnType = nameToDescriptor(returnType, arrayLevel); return this; } /** * Adds a parameter of the given class type to the method. * Parameter order matters. * WARNING: will most likely cause problems if used with objects outside the * Java SDK. Pass the fully qualified name as a String rather than the Class * object for non-standard types (such as Minecraft classes). * @param param the Class object corresponding to the parameter * @return the builder's state after the change */ public DescriptorBuilder addParameter(Class param) { this.params.add(Type.getDescriptor(param)); return this; } /** * Adds a parameter with the type specified by the given fully * qualified name to the method. Example: java.lang.String. * Parameter order matters. * No validity checks are performed: it's up to the user to ensure the name is correct. * @param param the fully qualified name of the parameter type * @return the builder's state after the change */ public DescriptorBuilder addParameter(String param) { return this.addParameter(param, 0); } /** * Adds a parameter with the type specified by the given fully * qualified name (example: java.lang.String) to the method, with * the specified array level. * Parameter order matters. * No validity checks are performed: it's up to the user to ensure the name is correct. * @param param the fully qualified name of the parameter type * @param arrayLevel how many levels of array are there * (example: String is 0, String[] is 1, String[][] is 2, etc.) * @return the builder's state after the change */ public DescriptorBuilder addParameter(String param, int arrayLevel) { this.params.add(nameToDescriptor(param, arrayLevel)); return this; } /** * Builds the descriptor into a string. * Example result: int m(Object[] o) -> ([Ljava/lang/Object;)I * @return the resulting descriptor */ public String build() { StringBuilder sb = new StringBuilder(); sb.append('('); for(String p : params) sb.append(p); sb.append(')').append(returnType); return sb.toString(); } /** * Converts a fully qualified name and array level to a descriptor. * @param name the fully qualified name of the object type * @param arrayLevel how many levels of array are there * (example: String is 0, String[] is 1, String[][] is 2, etc.) * @return object descriptor */ public static String nameToDescriptor(String name, int arrayLevel) { StringBuilder sb = new StringBuilder(); for(int i = 0; i < arrayLevel; i++) sb.append('['); sb.append('L').append(name.replace('.', '/')).append(';'); return sb.toString(); } }