aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/ftbsc/lll/utils/DescriptorBuilder.java
blob: 541497b48427e97790e663842704b2e4404ce2fe (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package ftbsc.lll.utils;

import org.objectweb.asm.Type;

import java.util.ArrayList;

/**
 * Builds a method descriptor for you.
 * See the <a href="https://asm.ow2.io/asm4-guide.pdf">documentation</a> to better understand what this is.
 * 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<String> 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.
    * Passing a {@link Class} may cause problems if used with objects outside the Java
    * SDK. Pass the fully qualified name as a {@link String} rather than the {@link Class}
    * object for non-standard types.
    * @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.
    * Passing a {@link Class} may cause problems if used with objects outside the Java
    * SDK. Pass the fully qualified name as a {@link String} rather than the {@link Class}
    * object for non-standard types.
    * @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: {@code int m(Object[] o)} becomes {@code ([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();
   }
}