aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/ftbsc/lll/proxies/ClassProxy.java
blob: c3692314daebbbfab05fc9b29ae3289a857a38b2 (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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package ftbsc.lll.proxies;

import org.objectweb.asm.Type;

/**
 * A container for information about classes to be used
 * in ASM patching.
 * @since 0.4.0
 */
public class ClassProxy extends AbstractProxy {

   /**
    * The fully-qualified name of the class represented by this proxy.
    */
   public final String fqn;

   /**
    * The {@link ClassProxy} representing the class which contains the
    * class represented by this proxy. May be null if the class represented
    * by this proxy is not an inner class.
    */
   public final ClassProxy containerClass;

   /**
    * Protected constructor, called only from the builder.
    * @param name the name of the class
    * @param type the {@link Type} of the class
    * @param modifiers the modifiers of the class
    * @param parent the FQN of the parent class of the class
    */
   protected ClassProxy(String name, Type type, int modifiers, String parent) {
      super(name, type, modifiers, parent);
      this.fqn = String.format("%s.%s", name, parent);
      this.containerClass = null;
   }

   /**
    * Protected constructor, called only from the builder.
    * @param name the name of the class
    * @param type the {@link Type} of the class
    * @param modifiers the modifiers of the class
    * @param containerClass the FQN of the parent class of the class
    */
   protected ClassProxy(String name, Type type, int modifiers, ClassProxy containerClass) {
      super(name, type, modifiers, containerClass.fqn);
      this.fqn = String.format("%s$%s", name, parent);
      this.containerClass = containerClass;
   }

   /**
    * Builds a {@link ClassProxy} given only the fully-qualified name and modifiers.
    * @param fqn the fully qualified name of the desired class
    * @param modifiers the access modifiers of the desired class
    * @return the built {@link ClassProxy}
    */
   protected static ClassProxy from(String fqn, int modifiers) {
      Type type = Type.getObjectType(fqn.replace('.', '/'));
      if(fqn.contains("$")) {
         String[] split = fqn.split("\\$");
         String simpleName = split[split.length - 1];
         ClassProxy parentClass = from(fqn.replace("$" + simpleName, ""), 0);
         return new ClassProxy(simpleName, type, modifiers, parentClass);
      } else {
         String[] split = fqn.split("\\.");
         String simpleName = split[split.length - 1];
         String parent = fqn.replace("." + simpleName, "");
         return new ClassProxy(simpleName, type, modifiers, parent);
      }
   }

   /**
    * Builds a {@link ClassProxy} from a {@link Class} object.
    * @param clazz the {@link Class} object representing the target class
    * @return the built {@link ClassProxy}
    */
   protected static ClassProxy from(Class<?> clazz) {
      if(clazz.getEnclosingClass() == null)
         return new ClassProxy(
            clazz.getSimpleName(),
            Type.getType(clazz),
            clazz.getModifiers(),
            clazz.getPackage().getName()
         );
      else
         return new ClassProxy(
            clazz.getSimpleName(),
            Type.getType(clazz),
            clazz.getModifiers(),
            from(clazz.getEnclosingClass())
         );
   }

   /**
    * Returns a new instance of {@link ClassProxy.Builder}.
    * @param name the name of the class
    * @return the builder object for class proxies
    */
   public static Builder builder(String name) {
      return new Builder(name);
   }

   /**
    * A builder object for {@link ClassProxy}.
    */
   public static class Builder extends AbstractProxy.Builder<ClassProxy> {
      
      private ClassProxy containerClass;
      
      /**
       * The constructor of the builder, used only internally.
       * @param name the "simple name" of the class
       */
      Builder(String name) {
         super(name);
         this.containerClass = null;
      }

      /**
       * Sets this class as an inner class and sets the containing
       * class to the given class object.
       * @param containerClass the {@link Class} representing the
       *                       container class
       * @return the builder's state after the change
       */
      public Builder setContainerClass(Class<?> containerClass) {
         this.containerClass = ClassProxy.from(containerClass);
         return this;
      }

      /**
       * Sets this class as an inner class and sets the containing
       * class to the given proxy.
       * @param containerClass the {@link ClassProxy} representing
       *                         the container class
       * @return the builder's state after the change
       */
      public Builder setContainerClass(ClassProxy containerClass) {
         this.containerClass = containerClass;
         return this;
      }

      /**
       * Sets this class as an inner class and builds a {@link ClassProxy}
       * from the given parent and modifiers.
       * @param parentFQN  the fully qualified name of the parent
       * @return the builder's state after the change
       */
      public Builder setParent(String parentFQN, int modifiers) {
         return this.setContainerClass(ClassProxy.from(parentFQN, modifiers));
      }

      /**
       * Sets this class as an inner class and builds a {@link ClassProxy}
       * from the given parent.
       * @param parentFQN  the fully qualified name of the parent
       * @return the builder's state after the change
       */
      @Override
      public Builder setParent(String parentFQN) {
         return this.setParent(parentFQN, 0);
      }

      /**
       * Builds a {@link ClassProxy} of the given kind.
       * @return the built {@link ClassProxy}
       */
      @Override
      public ClassProxy build() {
         if(this.containerClass == null)
            return new ClassProxy(this.name, this.type, this.modifiers, this.parent);
         else return new ClassProxy(this.name, this.type, this.modifiers, this.containerClass);
      }
   }
}