action) {
175 | fields.forEach(action);
176 | }
177 |
178 | @Override
179 | public int hashCode() {
180 | return classes.hashCode() ^ methods.hashCode() ^ fields.hashCode();
181 | }
182 |
183 | @Override
184 | public boolean equals(Object obj) {
185 | if (obj == this) {
186 | return true;
187 | } else if (obj == null) {
188 | return false;
189 | } else if (obj.getClass() == ImmutableMappings.class) {
190 | return this.classes.equals(((ImmutableMappings) obj).classes)
191 | && this.methods.equals(((ImmutableMappings) obj).methods)
192 | && this.fields.equals(((ImmutableMappings) obj).fields);
193 | } else if (obj instanceof Mappings) {
194 | return this.equals(((Mappings) obj).snapshot());
195 | } else {
196 | return false;
197 | }
198 | }
199 |
200 | @Override
201 | public String toString() {
202 | return MoreObjects.toStringHelper(Mappings.class)
203 | .add("classes", ImmutableMaps.joinToString(
204 | classes,
205 | (original, renamed) -> String.format(" %s = %s", original.getName(), renamed.getName()),
206 | "\n", "{", "}"
207 | ))
208 | .add("methods", ImmutableMaps.joinToString(
209 | methods,
210 | (original, renamed) -> String.format(" %s = %s", original, renamed),
211 | "\n", "{\n", "\n}"
212 | ))
213 | .add("fields", ImmutableMaps.joinToString(
214 | fields,
215 | (original, renamed) -> String.format(" %s = %s", original, renamed),
216 | "\n", "{\n", "\n}"
217 | ))
218 | .toString();
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/src/main/java/net/techcable/srglib/mappings/Mappings.java:
--------------------------------------------------------------------------------
1 | package net.techcable.srglib.mappings;
2 |
3 | import java.lang.reflect.Field;
4 | import java.util.HashMap;
5 | import java.util.Map;
6 | import java.util.Set;
7 | import java.util.function.BiConsumer;
8 | import java.util.function.Function;
9 | import java.util.function.UnaryOperator;
10 | import javax.annotation.Nullable;
11 |
12 | import com.google.common.collect.BiMap;
13 | import com.google.common.collect.HashBiMap;
14 | import com.google.common.collect.ImmutableBiMap;
15 | import com.google.common.collect.ImmutableList;
16 | import com.google.common.collect.ImmutableMap;
17 |
18 | import net.techcable.srglib.FieldData;
19 | import net.techcable.srglib.JavaType;
20 | import net.techcable.srglib.MethodData;
21 |
22 | import static java.util.Objects.*;
23 |
24 | /**
25 | * A mapping from one set of source names to another.
26 | */
27 | public interface Mappings {
28 | /**
29 | * Get the remapped class name, given the original class name.
30 | *
31 | * Returns the original class if no mapping is found.
32 | *
33 | *
34 | * @param original the original class name
35 | * @return the new class name
36 | */
37 | default JavaType getNewClass(String original) {
38 | return getNewClass(JavaType.fromName(original));
39 | }
40 |
41 |
42 | /**
43 | * Get the remapped class, given the original class, or the original if no class is found.
44 | *
45 | * @param original the original class
46 | * @return the new class
47 | * @throws IllegalArgumentException if the class isn't a reference type
48 | */
49 | JavaType getNewClass(JavaType original);
50 |
51 |
52 | /**
53 | * Get the remapped type, given the original type, or the original if the type is found.
54 | *
55 | * If type is an array, it remaps the innermost element type.
56 | * If the type is a class, the result is the same as invoking {@link #getNewClass(JavaType)}
57 | * If the type is a primitive, it returns the same element type.
58 | *
59 | *
60 | * @param original the original type
61 | * @return the new type
62 | */
63 | default JavaType getNewType(JavaType original) {
64 | return requireNonNull(original, "Null type").mapClass(this::getNewClass);
65 | }
66 |
67 | /**
68 | * Get the remapped method data, given the original data.
69 | *
70 | * Automatically remaps class names in the signature as needed,
71 | * even if the method name remains the same.
72 | *
73 | *
74 | * @param original the original method data
75 | * @return the remapped method data
76 | */
77 | MethodData getNewMethod(MethodData original);
78 |
79 | /**
80 | * Get the remapped field data, given the original data.
81 | *
82 | * Automatically remaps class names in the signature as needed,
83 | * even if the field name remains the same.
84 | *
85 | *
86 | * @param original the original field data
87 | * @return the remapped field data
88 | */
89 | FieldData getNewField(FieldData original);
90 |
91 | /**
92 | * Return an immutable snapshot of these mappings.
93 | *
94 | * @return an immutable snapshot
95 | */
96 | default ImmutableMappings snapshot() {
97 | return ImmutableMappings.copyOf(this);
98 | }
99 |
100 | /**
101 | * Return an inverted copy of the mappings, switching the original and renamed.
102 | *
103 | * Even if this mapping's underlying source is mutable,
104 | * changes in this mapping will not be reflected in the resulting view.
105 | *
106 | *
107 | * @return an inverted copy
108 | */
109 | default Mappings inverted() {
110 | return snapshot().inverted();
111 | }
112 |
113 | /**
114 | * Return the original classes known to these mappings.
115 | *
116 | * @return the original classes.
117 | */
118 | Set classes();
119 |
120 |
121 | /**
122 | * Return the original methods known to these mappings.
123 | *
124 | * @return the original methods.
125 | */
126 | Set methods();
127 |
128 | /**
129 | * Return the original fields known to these mappings.
130 | *
131 | * @return the original fields.
132 | */
133 | Set fields();
134 |
135 | default boolean contains(JavaType type) {
136 | return classes().contains(type);
137 | }
138 |
139 | default boolean contains(MethodData methodData) {
140 | return methods().contains(methodData);
141 | }
142 |
143 | default boolean contains(FieldData fieldData) {
144 | return fields().contains(fieldData);
145 | }
146 |
147 | default void forEachClass(BiConsumer action) {
148 | classes().forEach((original) -> action.accept(original, getNewType(original)));
149 | }
150 |
151 | default void forEachMethod(BiConsumer action) {
152 | methods().forEach((original) -> action.accept(original, getNewMethod(original)));
153 | }
154 |
155 | default void forEachField(BiConsumer action) {
156 | fields().forEach((original) -> action.accept(original, getNewField(original)));
157 | }
158 |
159 | /**
160 | * Transform all the original data in the specified mapping, using this mapping.
161 | *
162 | * This is useful for {@link #createRenamingMappings(UnaryOperator, Function, Function)},
163 | * since renaming mappings have no 'original' data of their own, and so can't be directly output to a file.
164 | * The returned mapping data is guaranteed to have the same originals as the data of the old mapping data.
165 | *
166 | * @return the transformed data
167 | */
168 | default Mappings transform(Mappings original) {
169 | ImmutableBiMap.Builder types = ImmutableBiMap.builder();
170 | ImmutableBiMap.Builder methods = ImmutableBiMap.builder();
171 | ImmutableBiMap.Builder fields = ImmutableBiMap.builder();
172 | original.classes().forEach(originalType -> {
173 | JavaType newType = this.getNewType(originalType);
174 | types.put(originalType, newType);
175 | });
176 | original.methods().forEach(originalMethodData -> {
177 | MethodData newMethodData = this.getNewMethod(originalMethodData);
178 | methods.put(originalMethodData, newMethodData);
179 | });
180 | original.fields().forEach(originalFieldData -> {
181 | FieldData newFieldData = this.getNewField(originalFieldData);
182 | fields.put(originalFieldData, newFieldData);
183 | });
184 | return ImmutableMappings.create(types.build(), methods.build(), fields.build());
185 | }
186 |
187 | /**
188 | * Return an immutable empty mappings instance.
189 | *
190 | * @return an immutable empty mappings object.
191 | */
192 | static ImmutableMappings empty() {
193 | return ImmutableMappings.EMPTY;
194 | }
195 |
196 | /**
197 | * Chain the specified mappings together, using the renamed result of each mapping as the original for the next
198 | *
199 | * @param mappings the mappings to chain together
200 | */
201 | static Mappings chain(Mappings... mappings) {
202 | return chain(ImmutableList.copyOf(mappings));
203 | }
204 |
205 | /**
206 | * Chain the specified mappings together, using the renamed result of each mapping as the original for the next
207 | *
208 | * @param mappings the mappings to chain together
209 | */
210 | static Mappings chain(ImmutableList extends Mappings> mappings) {
211 | ImmutableMappings chained = empty();
212 | for (int i = 0; i < mappings.size(); i++) {
213 | Mappings mapping = mappings.get(i);
214 | ImmutableBiMap.Builder classes = ImmutableBiMap.builder();
215 | ImmutableBiMap.Builder methods = ImmutableBiMap.builder();
216 | ImmutableBiMap.Builder fields = ImmutableBiMap.builder();
217 | ImmutableMappings inverted = chained.inverted();
218 |
219 | // If we encounter a new name, add it to the set
220 | mapping.forEachClass((original, renamed) -> {
221 | if (!inverted.contains(original)) {
222 | classes.put(original, renamed);
223 | }
224 | });
225 | mapping.forEachField((original, renamed) -> {
226 | if (!inverted.contains(original)) {
227 | // We need to make sure the originals we put in the map have the oldest possible type name to remain consistent
228 | // Since inverted is a map of new->old, use the old type name if we've ever seen this class before
229 | fields.put(original.mapTypes(inverted::getNewType), renamed);
230 | }
231 | });
232 | mapping.forEachMethod((original, renamed) -> {
233 | if (!inverted.contains(original)) {
234 | methods.put(original.mapTypes(inverted::getNewType), renamed);
235 | }
236 | });
237 | // Now run all our current chain through the mapping to get our new result
238 | chained.forEachClass((original, renamed) -> {
239 | renamed = mapping.getNewType(renamed);
240 | classes.put(original, renamed);
241 | });
242 | chained.forEachField((original, renamed) -> {
243 | renamed = mapping.getNewField(renamed);
244 | fields.put(original, renamed);
245 | });
246 | chained.forEachMethod((original, renamed) -> {
247 | renamed = mapping.getNewMethod(renamed);
248 | methods.put(original, renamed);
249 | });
250 | chained = ImmutableMappings.create(classes.build(), methods.build(), fields.build());
251 | }
252 | return chained;
253 | }
254 |
255 |
256 | /**
257 | * Mappings which rename classes/methods/fields dynamically, based entirely on transformer functions.
258 | *
259 | * Unlike most other mappings, these mappings have no fields, methods, or classes of their own,
260 | * and just rename whatever they are given.
261 | * In order to use them with class data, use them to {@link #transform(Mappings)} some other set of mappings.
262 | *
263 | *
264 | * The functions are expected to be 'pure', and always give the same output for any input.
265 | * The type transformer is only called for references, and can't remap primitives.
266 | * A 'null' transformer does nothing, and simply returns the existing name.
267 | * Method and field signatures are automatically remapped.
268 | *
269 | *
270 | * @param typeTransformer the function to transform/rename the classes
271 | * @param methodRenamer the function to rename the methods
272 | * @param fieldRenamer the function to rename the fields
273 | * @return a mapping which remaps members using the specified methods
274 | */
275 | static Mappings createRenamingMappings(
276 | @Nullable UnaryOperator typeTransformer,
277 | @Nullable Function methodRenamer,
278 | @Nullable Function fieldRenamer
279 | ) {
280 | return new RenamingMappings(typeTransformer, methodRenamer, fieldRenamer);
281 | }
282 |
283 | /**
284 | * Mappings which dynamically remap classes from one package into another.
285 | *
286 | * Unlike other mappings, these mappings have no fields, methods, or classes of their own,
287 | * and just rename whatever they are given, similar to {@link #createRenamingMappings(UnaryOperator, Function, Function)}.
288 | * Method and field signatures are automatically remapped.
289 | *
290 | *
291 | * @param packages the packages to remap
292 | * @return a package mapping
293 | */
294 | static Mappings createPackageMappings(ImmutableMap packages) {
295 | return createRenamingMappings((original) -> {
296 | String originalPackage = original.getPackageName();
297 | String newPackage = packages.get(originalPackage);
298 | if (newPackage != null) {
299 | return JavaType.fromName(newPackage + "." + original.getSimpleName());
300 | } else {
301 | return original;
302 | }
303 | }, null, null);
304 | }
305 | }
306 |
--------------------------------------------------------------------------------
/src/main/java/net/techcable/srglib/mappings/MutableMappings.java:
--------------------------------------------------------------------------------
1 | package net.techcable.srglib.mappings;
2 |
3 | import java.util.HashMap;
4 |
5 | import com.google.common.collect.HashBiMap;
6 |
7 | import net.techcable.srglib.FieldData;
8 | import net.techcable.srglib.JavaType;
9 | import net.techcable.srglib.MethodData;
10 |
11 | import static com.google.common.base.Preconditions.*;
12 |
13 | /**
14 | * Mappings that can be modified
15 | */
16 | public interface MutableMappings extends Mappings {
17 |
18 | /**
19 | * Set a class's new name.
20 | *
21 | * @param original the original name
22 | * @param renamed the class's new name
23 | * @throws IllegalArgumentException if the class isn't a reference type
24 | */
25 | void putClass(JavaType original, JavaType renamed);
26 |
27 | /**
28 | * Set a method's new name, ensuring the signatures match.
29 | *
30 | * After mapping the method's signature to the new type names the signatures must match,
31 | * so that {@code original.mapTypes(mappings::getNewType)} equals the new types.
32 | *
33 | *
34 | * @param original the original method data
35 | * @param renamed the new method data
36 | * @throws IllegalArgumentException if the signatures mismatch
37 | */
38 | default void putMethod(MethodData original, MethodData renamed) {
39 | checkArgument(
40 | original.mapTypes(this::getNewType).hasSameTypes(renamed),
41 | "Remapped method data types (%s) don't correspond to original types (%s)",
42 | renamed,
43 | original
44 | );
45 | putMethod(original, renamed.getName());
46 | }
47 |
48 | /**
49 | * Set the method's new name.
50 | *
51 | * @param original the original method data
52 | * @param newName the new method name
53 | */
54 | void putMethod(MethodData original, String newName);
55 |
56 | /**
57 | * Set a fields's new name, ensuring the signatures match.
58 | *
59 | * After mapping the method's signature to the new type names the signatures must match,
60 | * so that {@code original.mapTypes(mappings::getNewType)} equals the new types.
61 | *
62 | *
63 | * @param original the original method data
64 | * @param renamed the new method data
65 | * @throws IllegalArgumentException if the signatures mismatch
66 | */
67 | default void putField(FieldData original, FieldData renamed) {
68 | checkArgument(
69 | original.mapTypes(this::getNewType).hasSameTypes(renamed),
70 | "Remapped field data (%s) doesn't correspond to original types (%s)",
71 | renamed,
72 | original
73 | );
74 | putField(original, renamed.getName());
75 | }
76 |
77 | /**
78 | * Set a fields's new name.
79 | *
80 | * @param original the original method data
81 | * @param newName the new name
82 | */
83 | void putField(FieldData original, String newName);
84 |
85 | /**
86 | * Return an inverted copy of the mappings, switching the original and renamed.
87 | *
88 | * Changes in this mapping will not be reflected in the resulting view
89 | *
90 | *
91 | * @return an inverted copy
92 | */
93 | @Override
94 | Mappings inverted();
95 |
96 | /**
97 | * Create a new mutable mappings object, with no contents.
98 | *
99 | * @return a new mutable mappings
100 | */
101 | static MutableMappings create() {
102 | return new SimpleMappings(HashBiMap.create(), new HashMap<>(), new HashMap<>());
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/main/java/net/techcable/srglib/mappings/RenamingMappings.java:
--------------------------------------------------------------------------------
1 | package net.techcable.srglib.mappings;
2 |
3 | import java.util.Set;
4 | import java.util.function.Function;
5 | import java.util.function.UnaryOperator;
6 | import javax.annotation.Nullable;
7 |
8 | import com.google.common.collect.ImmutableSet;
9 |
10 | import net.techcable.srglib.FieldData;
11 | import net.techcable.srglib.JavaType;
12 | import net.techcable.srglib.MethodData;
13 |
14 | import static com.google.common.base.Preconditions.checkArgument;
15 |
16 | /* package */ final class RenamingMappings implements Mappings {
17 | private final UnaryOperator typeTransformer;
18 | private final Function methodRenamer;
19 | private final Function fieldRenamer;
20 | public RenamingMappings(
21 | @Nullable UnaryOperator typeTransformer,
22 | @Nullable Function methodRenamer,
23 | @Nullable Function fieldRenamer
24 | ) {
25 | this.typeTransformer = typeTransformer != null ? typeTransformer : UnaryOperator.identity();
26 | this.methodRenamer = methodRenamer != null ? methodRenamer : MethodData::getName;
27 | this.fieldRenamer = methodRenamer != null ? fieldRenamer : FieldData::getName;
28 | }
29 |
30 | @Override
31 | public JavaType getNewClass(JavaType original) {
32 | checkArgument(original.isReferenceType(), "Type isn't a reference type: %s", original);
33 | JavaType result = typeTransformer.apply(original);
34 | return result == null ? original : result;
35 | }
36 |
37 | @Override
38 | public MethodData getNewMethod(MethodData original) {
39 | return original
40 | .mapTypes(this::getNewType)
41 | .withName(methodRenamer.apply(original));
42 | }
43 |
44 | @Override
45 | public FieldData getNewField(FieldData original) {
46 | return original
47 | .mapTypes(this::getNewType)
48 | .withName(fieldRenamer.apply(original));
49 | }
50 |
51 | @Override
52 | public Set classes() {
53 | return ImmutableSet.of();
54 | }
55 |
56 | @Override
57 | public Set methods() {
58 | return ImmutableSet.of();
59 | }
60 |
61 | @Override
62 | public Set fields() {
63 | return ImmutableSet.of();
64 | }
65 |
66 | @Override
67 | public int hashCode() {
68 | return typeTransformer.hashCode() ^ methodRenamer.hashCode() ^ fieldRenamer.hashCode();
69 | }
70 |
71 | @Override
72 | public Mappings inverted() {
73 | throw new UnsupportedOperationException(); // Doesn't make much sense
74 | }
75 |
76 | @Override
77 | public ImmutableMappings snapshot() {
78 | throw new UnsupportedOperationException(); // Doesn't make much sense
79 | }
80 |
81 | @Override
82 | public boolean equals(Object obj) {
83 | return this == obj || obj.getClass() == RenamingMappings.class
84 | && ((RenamingMappings) obj).typeTransformer.equals(this.typeTransformer)
85 | && ((RenamingMappings) obj).methodRenamer.equals(this.methodRenamer)
86 | && ((RenamingMappings) obj).fieldRenamer.equals(this.fieldRenamer);
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/main/java/net/techcable/srglib/mappings/SimpleMappings.java:
--------------------------------------------------------------------------------
1 | package net.techcable.srglib.mappings;
2 |
3 | import java.util.Map;
4 | import java.util.Set;
5 | import java.util.function.BiConsumer;
6 |
7 | import com.google.common.collect.BiMap;
8 |
9 | import net.techcable.srglib.FieldData;
10 | import net.techcable.srglib.JavaType;
11 | import net.techcable.srglib.MethodData;
12 |
13 | import static com.google.common.base.Preconditions.*;
14 | import static java.util.Objects.*;
15 |
16 | /* package */ class SimpleMappings implements MutableMappings {
17 | private final BiMap classes;
18 | private final Map methodNames;
19 | private final Map fieldNames;
20 |
21 | /* package */ SimpleMappings(
22 | BiMap classes,
23 | Map methodNames,
24 | Map fieldNames
25 | ) {
26 | this.classes = requireNonNull(classes, "Null types");
27 | this.methodNames = requireNonNull(methodNames, "Null methods");
28 | this.fieldNames = requireNonNull(fieldNames, "Null fields");
29 | }
30 |
31 | @Override
32 | public void putClass(JavaType original, JavaType renamed) {
33 | checkArgument(original.isReferenceType(), "Original type isn't a reference type: %s", original);
34 | checkArgument(renamed.isReferenceType(), "Renamed type isn't a reference type: %s", renamed);
35 | if (original.equals(renamed)) {
36 | classes.remove(original);
37 | } else {
38 | classes.put(original, renamed);
39 | }
40 | }
41 |
42 | @Override
43 | public void putMethod(MethodData original, String newName) {
44 | methodNames.put(checkNotNull(original, "Null original"), checkNotNull(newName, "Null newName"));
45 | }
46 |
47 | @Override
48 | public void putField(FieldData original, String newName) {
49 | fieldNames.put(checkNotNull(original, "Null original"), checkNotNull(newName, "Null newName"));
50 | }
51 |
52 | @Override
53 | public JavaType getNewClass(JavaType original) {
54 | checkArgument(original.isReferenceType(), "Type isn't a reference type: %s", original);
55 | return classes.getOrDefault(requireNonNull(original), original);
56 | }
57 |
58 | @Override
59 | public MethodData getNewMethod(MethodData original) {
60 | String newName = methodNames.getOrDefault(original, original.getName());
61 | return original.mapTypes(this::getNewType).withName(newName);
62 | }
63 |
64 | @Override
65 | public FieldData getNewField(FieldData original) {
66 | String newName = fieldNames.getOrDefault(original, original.getName());
67 | return FieldData.create(getNewType(original.getDeclaringType()), newName);
68 | }
69 |
70 | @Override
71 | public ImmutableMappings snapshot() {
72 | return ImmutableMappings.copyOf(
73 | this.classes,
74 | this.methodNames,
75 | this.fieldNames
76 | );
77 | }
78 |
79 | @Override
80 | public Set classes() {
81 | return classes.keySet();
82 | }
83 |
84 | @Override
85 | public Set methods() {
86 | return methodNames.keySet();
87 | }
88 |
89 | @Override
90 | public Set fields() {
91 | return fieldNames.keySet();
92 | }
93 |
94 | @Override
95 | public Mappings inverted() {
96 | return snapshot().inverted();
97 | }
98 |
99 | @Override
100 | public void forEachClass(BiConsumer action) {
101 | classes.forEach(action);
102 | }
103 |
104 | @Override
105 | public void forEachMethod(BiConsumer action) {
106 | methodNames.forEach((originalData, newName) -> {
107 | MethodData newData = originalData.mapTypes(this::getNewType).withName(newName);
108 | action.accept(originalData, newData);
109 | });
110 | }
111 |
112 | @Override
113 | public void forEachField(BiConsumer action) {
114 | fieldNames.forEach((originalData, newName) -> {
115 | FieldData newData = FieldData.create(getNewType(originalData.getDeclaringType()), newName);
116 | action.accept(originalData, newData);
117 | });
118 | }
119 |
120 | @Override
121 | public boolean equals(Object otherObj) {
122 | if (this == otherObj) return true;
123 | if (otherObj == null) return false;
124 | if (otherObj.getClass() == SimpleMappings.class) {
125 | SimpleMappings other = (SimpleMappings) otherObj;
126 | return classes.equals(other.classes) && methodNames.equals(other.methodNames) && fieldNames.equals(other.fieldNames);
127 | } else if (otherObj instanceof Mappings) {
128 | return this.snapshot().equals(((Mappings) otherObj).snapshot());
129 | } else {
130 | return false;
131 | }
132 | }
133 |
134 | @Override
135 | public int hashCode() {
136 | int result = classes.hashCode();
137 | result = 31 * result + methodNames.hashCode();
138 | result = 31 * result + fieldNames.hashCode();
139 | return result;
140 | }
141 |
142 | @Override
143 | public String toString() {
144 | return snapshot().toString();
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/src/main/java/net/techcable/srglib/utils/CheckedBiConsumer.java:
--------------------------------------------------------------------------------
1 | package net.techcable.srglib.utils;
2 |
3 | public interface CheckedBiConsumer {
4 | void accept(T first, U second) throws E;
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/net/techcable/srglib/utils/CheckedConsumer.java:
--------------------------------------------------------------------------------
1 | package net.techcable.srglib.utils;
2 |
3 | public interface CheckedConsumer {
4 | void accept(T obj) throws E;
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/net/techcable/srglib/utils/CheckedRunnable.java:
--------------------------------------------------------------------------------
1 | package net.techcable.srglib.utils;
2 |
3 | @FunctionalInterface
4 | public interface CheckedRunnable {
5 | void run() throws E;
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/net/techcable/srglib/utils/CheckedSupplier.java:
--------------------------------------------------------------------------------
1 | package net.techcable.srglib.utils;
2 |
3 | public interface CheckedSupplier {
4 | T get() throws E;
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/net/techcable/srglib/utils/Exceptions.java:
--------------------------------------------------------------------------------
1 | package net.techcable.srglib.utils;
2 |
3 | import java.util.function.BiConsumer;
4 | import java.util.function.Consumer;
5 | import java.util.function.Supplier;
6 |
7 | public final class Exceptions {
8 | private Exceptions() {}
9 |
10 | public static BiConsumer sneakyThrowing(CheckedBiConsumer consumer) {
11 | return (first, second) -> {
12 | try {
13 | consumer.accept(first, second);
14 | } catch (Throwable e) {
15 | throw sneakyThrow(e);
16 | }
17 | };
18 | }
19 |
20 | public static Consumer sneakyThrowing(CheckedConsumer consumer) {
21 | return t -> {
22 | try {
23 | consumer.accept(t);
24 | } catch (Throwable e) {
25 | throw sneakyThrow(e);
26 | }
27 | };
28 | }
29 |
30 | public static Runnable sneakyThrowing(CheckedRunnable r) {
31 | return () -> {
32 | try {
33 | r.run();
34 | } catch (Throwable t) {
35 | throw sneakyThrow(t);
36 | }
37 | };
38 | }
39 |
40 | public static Supplier sneakyThrowing(CheckedSupplier r) {
41 | return () -> {
42 | try {
43 | return r.get();
44 | } catch (Throwable t) {
45 | throw sneakyThrow(t);
46 | }
47 | };
48 | }
49 |
50 | public static AssertionError sneakyThrow(Throwable t) {
51 | throw sneakyThrow0(t);
52 | }
53 |
54 | @SuppressWarnings("unchecked") // This is intentional :p
55 | private static AssertionError sneakyThrow0(Throwable t) throws T {
56 | throw (T) t;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/net/techcable/srglib/utils/ImmutableLists.java:
--------------------------------------------------------------------------------
1 | package net.techcable.srglib.utils;
2 |
3 | import java.util.Set;
4 | import java.util.function.BiConsumer;
5 | import java.util.function.BinaryOperator;
6 | import java.util.function.Function;
7 | import java.util.function.Supplier;
8 | import java.util.stream.Collector;
9 |
10 | import com.google.common.collect.ImmutableList;
11 | import com.google.common.collect.ImmutableSet;
12 |
13 | import static com.google.common.base.Preconditions.checkNotNull;
14 | import static java.util.Objects.requireNonNull;
15 |
16 | public final class ImmutableLists {
17 | private ImmutableLists() {}
18 |
19 |
20 | private static class ImmutableListCollector implements Collector, ImmutableList> {
21 | private ImmutableListCollector() {}
22 | public static ImmutableListCollector INSTANCE = new ImmutableListCollector();
23 | @Override
24 | public Supplier> supplier() {
25 | return ImmutableList::builder;
26 | }
27 |
28 | @Override
29 | public BiConsumer, E> accumulator() {
30 | return ImmutableList.Builder::add;
31 | }
32 |
33 | private static ImmutableList.Builder combine(ImmutableList.Builder first, ImmutableList.Builder second) {
34 | return first.addAll(second.build());
35 | }
36 |
37 | @Override
38 | public BinaryOperator> combiner() {
39 | return ImmutableListCollector::combine;
40 | }
41 |
42 | @Override
43 | public Function, ImmutableList> finisher() {
44 | return ImmutableList.Builder::build;
45 | }
46 |
47 | @Override
48 | public Set characteristics() {
49 | return ImmutableSet.of();
50 | }
51 | }
52 |
53 | @SuppressWarnings("unchecked")
54 | public static Collector> collector() {
55 | return ImmutableListCollector.INSTANCE;
56 | }
57 |
58 | @SuppressWarnings("unchecked")
59 | public static ImmutableList transform(ImmutableList original, Function transformer) {
60 | int size = requireNonNull(original, "Null original list").size();
61 | Object[] result = new Object[size];
62 | for (int i = 0; i < size; i++) {
63 | T originalElement = original.get(i);
64 | U newElement = transformer.apply(originalElement);
65 | checkNotNull(newElement, "Transformer produced null value for input: %s", originalElement);
66 | result[i] = newElement;
67 | }
68 | return (ImmutableList) ImmutableList.copyOf(result);
69 | }
70 |
71 |
72 | public static String joinToString(ImmutableList list, Function asString, String delimiter) {
73 | return joinToString(list, asString, delimiter, "", "");
74 | }
75 |
76 | public static String joinToString(
77 | ImmutableList list,
78 | Function asString,
79 | String delimiter,
80 | String prefix,
81 | String suffix
82 | ) {
83 | int size = requireNonNull(list, "Null list").size();
84 | int delimiterLength = requireNonNull(delimiter, "Null delimiter").length();
85 | int prefixLength = requireNonNull(prefix, "Null prefix").length();
86 | int suffixLength = requireNonNull(suffix, "Null suffix").length();
87 | String[] strings = new String[size];
88 | int neededChars = prefixLength + suffixLength + (Math.max(0, size - 1)) * delimiterLength;
89 | for (int i = 0; i < size; i++) {
90 | T element = list.get(i);
91 | String str = asString.apply(element);
92 | strings[i] = str;
93 | neededChars += str.length();
94 | }
95 | char[] result = new char[neededChars];
96 | int resultSize = 0;
97 | prefix.getChars(0, prefixLength, result, resultSize);
98 | resultSize += prefixLength;
99 | for (int i = 0; i < size; i++) {
100 | String str = strings[i];
101 | if (i > 0) {
102 | // Prefix it with the delimiter
103 | delimiter.getChars(0, delimiterLength, result, resultSize);
104 | resultSize += delimiterLength;
105 | }
106 | int length = str.length();
107 | str.getChars(0, length, result, resultSize);
108 | resultSize += length;
109 | }
110 | suffix.getChars(0, suffixLength, result, resultSize);
111 | resultSize += suffixLength;
112 | assert result.length == resultSize;
113 | return String.valueOf(result);
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/main/java/net/techcable/srglib/utils/ImmutableMaps.java:
--------------------------------------------------------------------------------
1 | package net.techcable.srglib.utils;
2 |
3 | import java.util.Map;
4 | import java.util.Set;
5 | import java.util.function.BiFunction;
6 | import java.util.function.Function;
7 |
8 | import com.google.common.collect.ImmutableBiMap;
9 | import com.google.common.collect.ImmutableMap;
10 |
11 | import static java.util.Objects.*;
12 |
13 | public final class ImmutableMaps {
14 | private ImmutableMaps() {
15 | }
16 |
17 | public static ImmutableBiMap createBiMap(Set keys, Function valueFunction) {
18 | ImmutableBiMap.Builder builder = ImmutableBiMap.builder();
19 | keys.forEach((key) -> builder.put(key, valueFunction.apply(key)));
20 | return builder.build();
21 | }
22 |
23 | public static ImmutableMap createMap(Set keys, Function valueFunction) {
24 | ImmutableMap.Builder builder = ImmutableMap.builder();
25 | keys.forEach((key) -> builder.put(key, valueFunction.apply(key)));
26 | return builder.build();
27 | }
28 |
29 | public static ImmutableBiMap inverse(Map input) {
30 | return ImmutableBiMap.copyOf(input).inverse();
31 | }
32 |
33 | public static String joinToString(
34 | ImmutableMap map,
35 | BiFunction asString,
36 | String delimiter,
37 | String prefix,
38 | String suffix
39 | ) {
40 | int size = requireNonNull(map, "Null list").size();
41 | int delimiterLength = requireNonNull(delimiter, "Null delimiter").length();
42 | int prefixLength = requireNonNull(prefix, "Null prefix").length();
43 | int suffixLength = requireNonNull(suffix, "Null suffix").length();
44 | String[] strings = new String[size];
45 | int neededChars = prefixLength + suffixLength + (size - 1) * delimiterLength;
46 | int index = 0;
47 | for (Map.Entry entry : map.entrySet()) {
48 | K key = entry.getKey();
49 | V value = entry.getValue();
50 | String str = asString.apply(key, value);
51 | strings[index++] = str;
52 | neededChars += str.length();
53 | }
54 | char[] result = new char[neededChars];
55 | int resultSize = 0;
56 | prefix.getChars(0, prefixLength, result, resultSize);
57 | resultSize += prefixLength;
58 | for (int i = 0; i < size; i++) {
59 | String str = strings[i];
60 | if (i > 0) {
61 | // Prefix it with the delimiter
62 | delimiter.getChars(0, delimiterLength, result, resultSize);
63 | resultSize += delimiterLength;
64 | }
65 | int length = str.length();
66 | str.getChars(0, length, result, resultSize);
67 | resultSize += length;
68 | }
69 | suffix.getChars(0, suffixLength, result, resultSize);
70 | resultSize += suffixLength;
71 | assert result.length == resultSize;
72 | return String.valueOf(result);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/test/java/net/techcable/srglib/MappingsChainTest.java:
--------------------------------------------------------------------------------
1 | package net.techcable.srglib;
2 |
3 | import java.util.Arrays;
4 |
5 | import com.google.common.collect.ImmutableList;
6 | import com.google.common.collect.ImmutableMap;
7 |
8 | import net.techcable.srglib.format.MappingsFormat;
9 | import net.techcable.srglib.mappings.ImmutableMappings;
10 | import net.techcable.srglib.mappings.Mappings;
11 | import net.techcable.srglib.utils.ImmutableLists;
12 |
13 | import org.junit.Test;
14 | import org.junit.runner.RunWith;
15 | import org.junit.runners.Parameterized;
16 |
17 | import static org.junit.Assert.*;
18 |
19 | @RunWith(Parameterized.class)
20 | public class MappingsChainTest {
21 | @Parameterized.Parameters
22 | public static Object[][] testData() {
23 | return new Object[][] {
24 | {
25 | new Mappings[]{
26 | MappingsFormat.SEARGE_FORMAT.parseLines(
27 | "CL: aa Entity",
28 | "CL: ab Cow",
29 | "CL: ac EntityPlayer",
30 | "CL: ad World",
31 | "CL: ae Server"
32 | ),
33 | MappingsFormat.SEARGE_FORMAT.parseLines(
34 | "CL: af ForgetfulClass",
35 | "FD: Entity/a Entity/dead",
36 | "MD: Cow/a (LCow;)V Cow/love (LCow;)V",
37 | "MD: EntityPlayer/a (Ljava/lang/String;)V EntityPlayer/disconnect (Ljava/lang/String;)V",
38 | "FD: World/a World/time",
39 | "MD: World/a ()V World/tick ()V",
40 | "FD: Server/a Server/ticks",
41 | "MD: Server/a ()V Server/tick ()V"
42 | ),
43 | MappingsFormat.SEARGE_FORMAT.parseLines(
44 | "CL: ForgetfulClass me/stupid/ChangedMind",
45 | "FD: World/time World/numTicks",
46 | "MD: World/tick ()V World/pulse ()V"
47 | ),
48 | Mappings.createPackageMappings(ImmutableMap.of("", "net.minecraft.server"))
49 | },
50 | MappingsFormat.SEARGE_FORMAT.parseLines(
51 | "CL: aa net/minecraft/server/Entity",
52 | "CL: ab net/minecraft/server/Cow",
53 | "CL: ac net/minecraft/server/EntityPlayer",
54 | "CL: ad net/minecraft/server/World",
55 | "CL: ae net/minecraft/server/Server",
56 | "CL: af me/stupid/ChangedMind",
57 | "FD: aa/a net/minecraft/server/Entity/dead",
58 | "MD: ab/a (Lab;)V net/minecraft/server/Cow/love (Lnet/minecraft/server/Cow;)V",
59 | "MD: ac/a (Ljava/lang/String;)V net/minecraft/server/EntityPlayer/disconnect (Ljava/lang/String;)V",
60 | "FD: ad/a net/minecraft/server/World/numTicks",
61 | "MD: ad/a ()V net/minecraft/server/World/pulse ()V",
62 | "FD: ae/a net/minecraft/server/Server/ticks",
63 | "MD: ae/a ()V net/minecraft/server/Server/tick ()V"
64 | )
65 | }
66 | };
67 | }
68 |
69 | private final ImmutableList mappings;
70 | private final ImmutableMappings expectedOutput;
71 |
72 | public MappingsChainTest(Mappings[] mappings, Mappings expectedOutput) {
73 | this.mappings = ImmutableList.copyOf(mappings);
74 | this.expectedOutput = expectedOutput.snapshot();
75 | }
76 |
77 | @Test
78 | public void testChaining() {
79 | ImmutableMappings chained = Mappings.chain(mappings).snapshot();
80 | assertEquals(expectedOutput, chained);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/test/java/net/techcable/srglib/MappingsFormatTest.java:
--------------------------------------------------------------------------------
1 | package net.techcable.srglib;
2 |
3 | import java.util.List;
4 |
5 | import com.google.common.collect.ImmutableList;
6 |
7 | import net.techcable.srglib.format.MappingsFormat;
8 | import net.techcable.srglib.mappings.Mappings;
9 |
10 | import org.junit.Test;
11 | import org.junit.runner.RunWith;
12 | import org.junit.runners.Parameterized;
13 |
14 | import static org.junit.Assert.*;
15 |
16 | @RunWith(Parameterized.class)
17 | public class MappingsFormatTest {
18 | private static final ImmutableList TEST_LINES = ImmutableList.of(
19 | "CL: org/spigotmc/XRay net/techcable/xray/XRay",
20 | "CL: org/spigotmc/XRay$Manager net/techcable/xray/XRayManager",
21 | "CL: org/spigotmc/XRay$Injector net/techcable/xray/injector/Injector",
22 | "CL: org/spigotmc/XRay$Injector$Manager net/techcable/xray/injector/InjectorManager",
23 | "CL: obfs net/techcable/minecraft/NoHax",
24 | "CL: obf4 net/techcable/minecraft/Player",
25 | "FD: obf4/a net/techcable/minecraft/Player/dead",
26 | "FD: obf4/b net/techcable/minecraft/Player/blood",
27 | "FD: obf4/c net/techcable/minecraft/Player/health",
28 | "FD: obf4/d net/techcable/minecraft/Player/speed",
29 | "FD: org/spigotmc/XRay$Injector$Manager/taco net/techcable/xray/injector/InjectorManager/seriousVariableName",
30 | "MD: obfs/a (Lobf4;ID)Z net/techcable/minecraft/NoHax/isHacking (Lnet/techcable/minecraft/Player;ID)Z",
31 | "MD: org/spigotmc/XRay/deobfuscate ([BLjava/util/Set;)I net/techcable/xray/XRay/doAFunkyDance ([BLjava/util/Set;)I",
32 | "MD: org/spigotmc/XRay$Manager/aquire ()Lorg/spigotmc/XRay; net/techcable/xray/XRayManager/get ()Lnet/techcable/xray/XRay;"
33 | );
34 | private static final ImmutableList COMPACT_TEST_LINES = ImmutableList.of(
35 | "org/spigotmc/XRay net/techcable/xray/XRay",
36 | "org/spigotmc/XRay$Manager net/techcable/xray/XRayManager",
37 | "org/spigotmc/XRay$Injector net/techcable/xray/injector/Injector",
38 | "org/spigotmc/XRay$Injector$Manager net/techcable/xray/injector/InjectorManager",
39 | "obfs net/techcable/minecraft/NoHax",
40 | "obf4 net/techcable/minecraft/Player",
41 | "obf4 a dead",
42 | "obf4 b blood",
43 | "obf4 c health",
44 | "obf4 d speed",
45 | "org/spigotmc/XRay$Injector$Manager taco seriousVariableName",
46 | "obfs a (Lobf4;ID)Z isHacking",
47 | "org/spigotmc/XRay deobfuscate ([BLjava/util/Set;)I doAFunkyDance",
48 | "org/spigotmc/XRay$Manager aquire ()Lorg/spigotmc/XRay; get"
49 | );
50 | @Parameterized.Parameters
51 | public static Object[][] mappingFormats() {
52 | return new Object[][] {
53 | new Object[] { MappingsFormat.SEARGE_FORMAT, TEST_LINES },
54 | new Object[] { MappingsFormat.COMPACT_SEARGE_FORMAT, COMPACT_TEST_LINES }
55 | };
56 | }
57 | private final MappingsFormat mappingsFormat;
58 | private final ImmutableList testLines;
59 | public MappingsFormatTest(MappingsFormat mappingsFormat, ImmutableList testLines) {
60 | this.mappingsFormat = mappingsFormat;
61 | this.testLines = testLines;
62 | }
63 |
64 | @Test
65 | public void testParse() {
66 | Mappings result = mappingsFormat.parseLines(testLines);
67 | SrgLib.checkConsistency(result.snapshot());
68 | assertEquals("net.techcable.xray.XRay", result.getNewClass("org.spigotmc.XRay").getName());
69 | assertEquals("net.techcable.minecraft.Player", result.getNewClass("obf4").getName());
70 | assertEquals(
71 | MethodData.create(
72 | JavaType.fromName("net.techcable.minecraft.NoHax"),
73 | "isHacking",
74 | ImmutableList.of(
75 | JavaType.fromName("net.techcable.minecraft.Player"),
76 | PrimitiveType.INT,
77 | PrimitiveType.DOUBLE
78 | ),
79 | PrimitiveType.BOOLEAN
80 | ),
81 | result.getNewMethod(MethodData.create(
82 | JavaType.fromName("obfs"),
83 | "a",
84 | ImmutableList.of(
85 | JavaType.fromName("obf4"),
86 | PrimitiveType.INT,
87 | PrimitiveType.DOUBLE
88 | ),
89 | PrimitiveType.BOOLEAN
90 | ))
91 | );
92 | assertEquals(
93 | FieldData.create(JavaType.fromName("net.techcable.minecraft.Player"), "dead"),
94 | result.getNewField(FieldData.create(JavaType.fromName("obf4"), "a"))
95 | );
96 | }
97 |
98 | @Test
99 | public void testSerialize() {
100 | Mappings expected = mappingsFormat.parseLines(testLines);
101 | List serialized = mappingsFormat.toLines(expected);
102 | Mappings actual = mappingsFormat.parseLines(serialized);
103 | assertEquals(expected, actual);
104 | }
105 | }
106 |
--------------------------------------------------------------------------------