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

import ftbsc.lll.exceptions.MappingNotFoundException;
import ftbsc.lll.mapper.utils.Mapper;
import ftbsc.lll.mapper.utils.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));
   }

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

   /**
    * 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
    * @return a ClassData representing the inverted mappings
    */
   public ClassData generateReverseMappings(Mapper mapper) {
      ClassData reverse = new ClassData(this.nameMapped, this.name);
      this.methods.forEach((signature, data) -> reverse.addMethod(nameMapped, signature.name,
         MappingUtils.mapMethodDescriptor(signature.descriptor, mapper, false)));
      this.fields.forEach((name, data) -> reverse.addField(data.nameMapped, name, data.descriptor));
      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;
   }

   /**
    * Gets the underlying {@link Map} for {@link MethodData}.
    * @return a {@link Map} tying each {@link MethodSignature} to its {@link MethodData}
    */
   public Map<MethodSignature, MethodData> getMethods() {
      return this.methods;
   }

   /**
    * Gets the underlying {@link Map} for {@link FieldData}.
    * @return a {@link Map} tying each field name to its {@link FieldData}
    */
   public Map<String, FieldData> getFields() {
      return this.fields;
   }
}