aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/ftbsc/lll/proxies/impl/TypeProxy.java
blob: 4be27df971dfaea759b93406a66551f4119e24b8 (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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
package ftbsc.lll.proxies.impl;

import ftbsc.lll.proxies.AbstractProxy;
import ftbsc.lll.proxies.ProxyType;
import ftbsc.lll.proxies.QualifiableProxy;
import org.objectweb.asm.Type;

import java.lang.reflect.Modifier;

import static ftbsc.lll.tools.DescriptorBuilder.nameToDescriptor;

/**
 * A container for information about classes to be used
 * in ASM patching.
 * @since 0.4.0
 */
public class TypeProxy extends QualifiableProxy {
   /**
    * Whether this proxy represents a primitive.
    */
   public final boolean primitive;

   /**
    * Protected constructor, called only from the builder.
    * @param name the name of the class
    * @param descriptor the descriptor of the class
    * @param modifiers the modifiers of the class
    * @param parent the package containing this class
    * @param primitive whether the proxy is a primitive
    */
   protected TypeProxy(String name, String descriptor, int modifiers, String parent, boolean primitive) {
      super(descriptor, modifiers, PackageProxy.from(parent), String.format("%s.%s", parent, name), ProxyType.TYPE);
      this.primitive = primitive;
   }

   /**
    * Protected constructor, called only from the builder.
    * @param name the name of the class
    * @param descriptor the descriptor of the element
    * @param modifiers the modifiers of the class
    * @param primitive whether the proxy is a primitive
    * @param containerClass the FQN of the parent class of the class
    */
   protected TypeProxy(String name, String descriptor, int modifiers, QualifiableProxy containerClass, boolean primitive) {
      super(descriptor, modifiers, containerClass, String.format("%s$%s", containerClass.fullyQualifiedName, name), ProxyType.TYPE);
      this.primitive = primitive;
   }

   /**
    * Builds a {@link TypeProxy} from a {@link Type} and modifiers.
    * @param type the {@link Type} representing this Class
    * @param modifiers the modifiers of the class
    * @return the built {@link TypeProxy}
    */
   public static TypeProxy from(Type type, int modifiers) {
      while(type.getSort() == Type.ARRAY)
         type = type.getElementType();
      boolean primitive = type.getSort() < Type.ARRAY;
      String fqn = primitive ? type.getClassName() : type.getInternalName().replace('/', '.');
      String simpleName = extractSimpleNameFromFQN(fqn);
      String parent = extractParentFromFQN(fqn);
      if(fqn.contains("$") && parent != null)
         return new TypeProxy(simpleName, type.getDescriptor(), modifiers, from(parent, 0, Modifier.PUBLIC), primitive);
      else return new TypeProxy(simpleName, type.getDescriptor(), modifiers, parent, primitive);
   }

   /**
    * Builds a {@link TypeProxy} given only the fully-qualified name and modifiers.
    * If present, parent classes will be assumed to have {@code public} as their
    * only modifier.
    * @param fqn the fully qualified name of the desired class
    * @param arrayLevel the array level for this type
    * @param modifiers the access modifiers of the desired class
    * @return the built {@link TypeProxy}
    */
   public static TypeProxy from(String fqn, int arrayLevel, int modifiers) {
      return from(Type.getType(nameToDescriptor(fqn, arrayLevel)), modifiers);
   }

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

   /**
    * Returns a new instance of {@link TypeProxy.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);
   }

   /**
    * Indicates whether the given object is a proxy for the same element as this.
    * @param obj the object to perform
    * @return true if it's equal
    */
   @Override
   public boolean equals(Object obj) {
      return obj instanceof TypeProxy && super.equals(obj);
   }

   /**
    * A builder object for {@link TypeProxy}.
    */
   public static class Builder extends AbstractProxy.Builder<TypeProxy> {

      /**
       * Whether the proxy represents a primitive.
       */
      private boolean primitive;

      /**
       * The constructor of the builder, used only internally.
       * @param name the "simple name" of the class
       */
      Builder(String name) {
         super(name);
         this.primitive = false;
      }

      /**
       * 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 setParent(Class<?> containerClass) {
         super.setParent(TypeProxy.from(containerClass));
         return this;
      }

      /**
       * Sets this class as an inner class and builds a {@link TypeProxy}
       * from the given parent and modifiers.
       * @param parentFQN the fully qualified name of the parent
       * @param modifiers the modifiers of the parent (if it's a class)
       * @param isParentPackage whether this parent should be interpreted as a package or class
       * @return the builder's state after the change
       */
      public Builder setParent(String parentFQN, int modifiers, boolean isParentPackage) {
         super.setParent(isParentPackage ? PackageProxy.from(parentFQN) : TypeProxy.from(parentFQN, 0, modifiers));
         return this;
      }

      /**
       * Sets this class as an inner class and builds a {@link TypeProxy}
       * from the given parent.
       * @param parentFQN the fully qualified name of the parent
       * @param isParentPackage whether this parent should be interpreted as a package or class
       * @return the builder's state after the change
       */
      public Builder setParent(String parentFQN, boolean isParentPackage) {
         return this.setParent(parentFQN, 0, isParentPackage);
      }

      /**
       * Sets the primitive flag to true or false, to signal that the type here specified
       * is a primitive.
       * @param primitive the new state of the primitive flag
       * @return the builder's state after the change
       */
      public Builder setPrimitive(boolean primitive) {
         this.primitive = primitive;
         return this;
      }

      /**
       * Builds a {@link TypeProxy} of the given kind.
       * @return the built {@link TypeProxy}
       */
      @Override
      public TypeProxy build() {
         return new TypeProxy(this.name, this.descriptor, this.modifiers, this.parent, this.primitive);
      }
   }
}