aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/ftbsc/lll/mapper/tools/data/ClassData.java
blob: 5ae4124c365b2a2fa75414b213cffd82351920d1 (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
package ftbsc.lll.mapper.tools.data;

import ftbsc.lll.exceptions.MappingNotFoundException;
import ftbsc.lll.mapper.IMapper;
import ftbsc.lll.mapper.tools.MappingUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Container class used to store information about classes.
 */
public class ClassData {

   /**
    * The internal (like the fully-qualified name, but with '/' instead
    * of '.') of the class.
    */
   public final String name;

   /**
    * The mapped internal (like the fully-qualified name, but with '/'
    * instead of '.') of the class.
    */
   public final String nameMapped;

   /**
    * A {@link Map} tying each method's signature to its data class.
    */
   private final Map<MethodSignature, MethodData> methods;

   /**
    * A {@link Map} tying each field's name to its data class.
    */
   private final Map<String, FieldData> fields;

   /**
    * The constructor. It takes in the names (plain and mapped) of a class.
    * @param name the plain name
    * @param nameMapped the mapped name
    */
   public ClassData(String name, String nameMapped) {
      this.name = name;
      this.nameMapped = nameMapped;
      this.methods = new HashMap<>();
      this.fields = new HashMap<>();
   }

   /**
    * Adds a method to the target class.
    * @param name the method name
    * @param nameMapped the mapped method name
    * @param descriptor the descriptor of the method
    */
   public void addMethod(String name, String nameMapped, String descriptor) {
      MethodData data = new MethodData(this, name, nameMapped, descriptor);
      this.methods.put(data.signature, data);
   }

   /**
    * Adds a field to the target class.
    * @param plain the name of the field
    * @param mapped the mapped name of the field
    */
   public void addField(String plain, String mapped) {
      this.fields.put(plain, new FieldData(this, plain, mapped));
   }

   /**
    * Generates the reverse mappings for this class.
    * Should always be called only after the given mapper has finished
    * processing all classes.
    * @param mapper the mapper that generated this data
    */
   public ClassData generateReverseMappings(IMapper mapper) {
      ClassData reverse = new ClassData(this.nameMapped, this.name);
      this.methods.forEach((signature, data) -> reverse.addMethod(nameMapped, signature.name,
         MappingUtils.obfuscateMethodDescriptor(signature.descriptor, mapper)));
      this.fields.forEach((name, data) -> reverse.addField(data.nameMapped, name));
      return reverse;
   }

   /**
    * Gets the {@link MethodData} from its name and descriptor, which may be partial
    * (i.e. not include the return type).
    * @param methodName the method name
    * @param methodDescriptor the method descriptor, which may be partial
    * @return the requested {@link MethodData}
    * @throws MappingNotFoundException if the mapping wasn't found
    */
   public MethodData mapMethod(String methodName, String methodDescriptor) {
      List<MethodSignature> signatures = this.methods.keySet().stream().filter(
         s -> s.name.equals(methodName) && s.descriptor.startsWith(methodDescriptor)
      ).collect(Collectors.toList());
      if(signatures.size() > 1)
         throw new RuntimeException(); //should never happen unless something goes horribly wrong
      else if(signatures.isEmpty())
         throw new MappingNotFoundException("method",
            String.format("%s::%s%s", this.name, methodName, methodDescriptor));
      return this.methods.get(signatures.get(0));
   }

   /**
    * Gets the {@link FieldData} its name.
    * @param fieldName the field name
    * @return the requested {@link FieldData}
    * @throws MappingNotFoundException if the mapping wasn't found
    */
   public FieldData mapField(String fieldName) {
      FieldData data = this.fields.get(fieldName);
      if(data == null)
         throw new MappingNotFoundException("field", String.format("%s.%s", this.name, fieldName));
      else return data;
   }
}