summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/translation
diff options
context:
space:
mode:
authorGravatar Runemoro2020-06-03 13:39:42 -0400
committerGravatar GitHub2020-06-03 18:39:42 +0100
commit0f47403d0220757fed189b76e2071e25b1025cb8 (patch)
tree879bf72c4476f0a5e0d82da99d7ff2b2276bcaca /src/main/java/cuchaz/enigma/translation
parentFix search dialog hanging for a short time sometimes (#250) (diff)
downloadenigma-fork-0f47403d0220757fed189b76e2071e25b1025cb8.tar.gz
enigma-fork-0f47403d0220757fed189b76e2071e25b1025cb8.tar.xz
enigma-fork-0f47403d0220757fed189b76e2071e25b1025cb8.zip
Split GUI code to separate module (#242)
* Split into modules * Post merge compile fixes Co-authored-by: modmuss50 <modmuss50@gmail.com>
Diffstat (limited to 'src/main/java/cuchaz/enigma/translation')
-rw-r--r--src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java44
-rw-r--r--src/main/java/cuchaz/enigma/translation/MappingTranslator.java24
-rw-r--r--src/main/java/cuchaz/enigma/translation/SignatureUpdater.java92
-rw-r--r--src/main/java/cuchaz/enigma/translation/Translatable.java9
-rw-r--r--src/main/java/cuchaz/enigma/translation/TranslationDirection.java36
-rw-r--r--src/main/java/cuchaz/enigma/translation/Translator.java61
-rw-r--r--src/main/java/cuchaz/enigma/translation/VoidTranslator.java10
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/AccessModifier.java25
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/EntryMap.java24
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java75
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java105
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/EntryResolver.java41
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/IndexEntryResolver.java227
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java54
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/MappingFileNameFormat.java10
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/MappingPair.java32
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/MappingSaveParameters.java16
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java76
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java99
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/NameValidator.java53
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/ResolutionStrategy.java6
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/VoidEntryResolver.java27
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaFormat.java9
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsReader.java319
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java316
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java59
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/MappingHelper.java51
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsReader.java14
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java17
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/ProguardMappingsReader.java134
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/RawEntryMapping.java30
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java118
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/TinyMappingsReader.java115
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/TinyMappingsWriter.java148
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/TinyV2Reader.java295
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/serde/TinyV2Writer.java169
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java110
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java26
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTreeNode.java40
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java188
-rw-r--r--src/main/java/cuchaz/enigma/translation/mapping/tree/HashTreeNode.java75
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java116
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/Lambda.java105
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/MethodDescriptor.java132
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/Signature.java98
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java268
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java93
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java214
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/DefEntry.java7
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java107
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java71
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java96
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java51
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java93
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java71
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java105
-rw-r--r--src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java82
57 files changed, 0 insertions, 5088 deletions
diff --git a/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java b/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java
deleted file mode 100644
index 18c966c..0000000
--- a/src/main/java/cuchaz/enigma/translation/LocalNameGenerator.java
+++ /dev/null
@@ -1,44 +0,0 @@
1package cuchaz.enigma.translation;
2
3import cuchaz.enigma.translation.mapping.NameValidator;
4import cuchaz.enigma.translation.representation.TypeDescriptor;
5
6import java.util.Collection;
7import java.util.Locale;
8
9public class LocalNameGenerator {
10 public static String generateArgumentName(int index, TypeDescriptor desc, Collection<TypeDescriptor> arguments) {
11 boolean uniqueType = arguments.stream().filter(desc::equals).count() <= 1;
12 String translatedName;
13 int nameIndex = index + 1;
14 StringBuilder nameBuilder = new StringBuilder(getTypeName(desc));
15 if (!uniqueType || NameValidator.isReserved(nameBuilder.toString())) {
16 nameBuilder.append(nameIndex);
17 }
18 translatedName = nameBuilder.toString();
19 return translatedName;
20 }
21
22 public static String generateLocalVariableName(int index, TypeDescriptor desc) {
23 int nameIndex = index + 1;
24 return getTypeName(desc) + nameIndex;
25 }
26
27 private static String getTypeName(TypeDescriptor desc) {
28 // Unfortunately each of these have different name getters, so they have different code paths
29 if (desc.isPrimitive()) {
30 TypeDescriptor.Primitive argCls = desc.getPrimitive();
31 return argCls.name().toLowerCase(Locale.ROOT);
32 } else if (desc.isArray()) {
33 // List types would require this whole block again, so just go with aListx
34 return "arr";
35 } else if (desc.isType()) {
36 String typeName = desc.getTypeEntry().getSimpleName().replace("$", "");
37 typeName = typeName.substring(0, 1).toLowerCase(Locale.ROOT) + typeName.substring(1);
38 return typeName;
39 } else {
40 System.err.println("Encountered invalid argument type descriptor " + desc.toString());
41 return "var";
42 }
43 }
44}
diff --git a/src/main/java/cuchaz/enigma/translation/MappingTranslator.java b/src/main/java/cuchaz/enigma/translation/MappingTranslator.java
deleted file mode 100644
index 529d0ed..0000000
--- a/src/main/java/cuchaz/enigma/translation/MappingTranslator.java
+++ /dev/null
@@ -1,24 +0,0 @@
1package cuchaz.enigma.translation;
2
3import cuchaz.enigma.translation.mapping.EntryMapping;
4import cuchaz.enigma.translation.mapping.EntryResolver;
5import cuchaz.enigma.translation.mapping.EntryMap;
6
7public class MappingTranslator implements Translator {
8 private final EntryMap<EntryMapping> mappings;
9 private final EntryResolver resolver;
10
11 public MappingTranslator(EntryMap<EntryMapping> mappings, EntryResolver resolver) {
12 this.mappings = mappings;
13 this.resolver = resolver;
14 }
15
16 @SuppressWarnings("unchecked")
17 @Override
18 public <T extends Translatable> T translate(T translatable) {
19 if (translatable == null) {
20 return null;
21 }
22 return (T) translatable.translate(this, resolver, mappings);
23 }
24}
diff --git a/src/main/java/cuchaz/enigma/translation/SignatureUpdater.java b/src/main/java/cuchaz/enigma/translation/SignatureUpdater.java
deleted file mode 100644
index 3783053..0000000
--- a/src/main/java/cuchaz/enigma/translation/SignatureUpdater.java
+++ /dev/null
@@ -1,92 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation;
13
14import com.google.common.collect.Lists;
15
16import java.io.IOException;
17import java.io.StringReader;
18import java.util.List;
19
20public class SignatureUpdater {
21
22 public static String update(String signature, ClassNameUpdater updater) {
23 try {
24 StringBuilder buf = new StringBuilder();
25
26 // read the signature character-by-character
27 StringReader reader = new StringReader(signature);
28 int i;
29 while ((i = reader.read()) != -1) {
30 char c = (char) i;
31
32 // does this character start a class name?
33 if (c == 'L') {
34 // update the class name and add it to the buffer
35 buf.append('L');
36 String className = readClass(reader);
37 if (className == null) {
38 throw new IllegalArgumentException("Malformed signature: " + signature);
39 }
40 buf.append(updater.update(className));
41 buf.append(';');
42 } else {
43 // copy the character into the buffer
44 buf.append(c);
45 }
46 }
47
48 return buf.toString();
49 } catch (IOException ex) {
50 // I'm pretty sure a StringReader will never throw one of these
51 throw new Error(ex);
52 }
53 }
54
55 private static String readClass(StringReader reader) throws IOException {
56 // read all the characters in the buffer until we hit a ';'
57 // remember to treat generics correctly
58 StringBuilder buf = new StringBuilder();
59 int depth = 0;
60 int i;
61 while ((i = reader.read()) != -1) {
62 char c = (char) i;
63
64 if (c == '<') {
65 depth++;
66 } else if (c == '>') {
67 depth--;
68 } else if (depth == 0) {
69 if (c == ';') {
70 return buf.toString();
71 } else {
72 buf.append(c);
73 }
74 }
75 }
76
77 return null;
78 }
79
80 public static List<String> getClasses(String signature) {
81 final List<String> classNames = Lists.newArrayList();
82 update(signature, className -> {
83 classNames.add(className);
84 return className;
85 });
86 return classNames;
87 }
88
89 public interface ClassNameUpdater {
90 String update(String className);
91 }
92}
diff --git a/src/main/java/cuchaz/enigma/translation/Translatable.java b/src/main/java/cuchaz/enigma/translation/Translatable.java
deleted file mode 100644
index 0370ef1..0000000
--- a/src/main/java/cuchaz/enigma/translation/Translatable.java
+++ /dev/null
@@ -1,9 +0,0 @@
1package cuchaz.enigma.translation;
2
3import cuchaz.enigma.translation.mapping.EntryMapping;
4import cuchaz.enigma.translation.mapping.EntryResolver;
5import cuchaz.enigma.translation.mapping.EntryMap;
6
7public interface Translatable {
8 Translatable translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings);
9}
diff --git a/src/main/java/cuchaz/enigma/translation/TranslationDirection.java b/src/main/java/cuchaz/enigma/translation/TranslationDirection.java
deleted file mode 100644
index 2ecb30b..0000000
--- a/src/main/java/cuchaz/enigma/translation/TranslationDirection.java
+++ /dev/null
@@ -1,36 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation;
13
14public enum TranslationDirection {
15
16 DEOBFUSCATING {
17 @Override
18 public <T> T choose(T deobfChoice, T obfChoice) {
19 if (deobfChoice == null) {
20 return obfChoice;
21 }
22 return deobfChoice;
23 }
24 },
25 OBFUSCATING {
26 @Override
27 public <T> T choose(T deobfChoice, T obfChoice) {
28 if (obfChoice == null) {
29 return deobfChoice;
30 }
31 return obfChoice;
32 }
33 };
34
35 public abstract <T> T choose(T deobfChoice, T obfChoice);
36}
diff --git a/src/main/java/cuchaz/enigma/translation/Translator.java b/src/main/java/cuchaz/enigma/translation/Translator.java
deleted file mode 100644
index c70141f..0000000
--- a/src/main/java/cuchaz/enigma/translation/Translator.java
+++ /dev/null
@@ -1,61 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation;
13
14import com.google.common.collect.HashMultimap;
15import com.google.common.collect.Multimap;
16
17import java.util.Collection;
18import java.util.HashMap;
19import java.util.Map;
20import java.util.Set;
21import java.util.stream.Collectors;
22
23public interface Translator {
24 <T extends Translatable> T translate(T translatable);
25
26 default <T extends Translatable> Collection<T> translate(Collection<T> translatable) {
27 return translatable.stream()
28 .map(this::translate)
29 .collect(Collectors.toList());
30 }
31
32 default <T extends Translatable> Set<T> translate(Set<T> translatable) {
33 return translatable.stream()
34 .map(this::translate)
35 .collect(Collectors.toSet());
36 }
37
38 default <T extends Translatable, V> Map<T, V> translateKeys(Map<T, V> translatable) {
39 Map<T, V> result = new HashMap<>(translatable.size());
40 for (Map.Entry<T, V> entry : translatable.entrySet()) {
41 result.put(translate(entry.getKey()), entry.getValue());
42 }
43 return result;
44 }
45
46 default <K extends Translatable, V extends Translatable> Map<K, V> translate(Map<K, V> translatable) {
47 Map<K, V> result = new HashMap<>(translatable.size());
48 for (Map.Entry<K, V> entry : translatable.entrySet()) {
49 result.put(translate(entry.getKey()), translate(entry.getValue()));
50 }
51 return result;
52 }
53
54 default <K extends Translatable, V extends Translatable> Multimap<K, V> translate(Multimap<K, V> translatable) {
55 Multimap<K, V> result = HashMultimap.create(translatable.size(), 1);
56 for (Map.Entry<K, Collection<V>> entry : translatable.asMap().entrySet()) {
57 result.putAll(translate(entry.getKey()), translate(entry.getValue()));
58 }
59 return result;
60 }
61}
diff --git a/src/main/java/cuchaz/enigma/translation/VoidTranslator.java b/src/main/java/cuchaz/enigma/translation/VoidTranslator.java
deleted file mode 100644
index c010833..0000000
--- a/src/main/java/cuchaz/enigma/translation/VoidTranslator.java
+++ /dev/null
@@ -1,10 +0,0 @@
1package cuchaz.enigma.translation;
2
3public enum VoidTranslator implements Translator {
4 INSTANCE;
5
6 @Override
7 public <T extends Translatable> T translate(T translatable) {
8 return translatable;
9 }
10}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/AccessModifier.java b/src/main/java/cuchaz/enigma/translation/mapping/AccessModifier.java
deleted file mode 100644
index 5b79b79..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/AccessModifier.java
+++ /dev/null
@@ -1,25 +0,0 @@
1package cuchaz.enigma.translation.mapping;
2
3import cuchaz.enigma.translation.representation.AccessFlags;
4
5public enum AccessModifier {
6 UNCHANGED, PUBLIC, PROTECTED, PRIVATE;
7
8 public String getFormattedName() {
9 return "ACC:" + super.toString();
10 }
11
12 public AccessFlags transform(AccessFlags access) {
13 switch (this) {
14 case PUBLIC:
15 return access.setPublic();
16 case PROTECTED:
17 return access.setProtected();
18 case PRIVATE:
19 return access.setPrivate();
20 case UNCHANGED:
21 default:
22 return access;
23 }
24 }
25}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/EntryMap.java b/src/main/java/cuchaz/enigma/translation/mapping/EntryMap.java
deleted file mode 100644
index e1a3253..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/EntryMap.java
+++ /dev/null
@@ -1,24 +0,0 @@
1package cuchaz.enigma.translation.mapping;
2
3import cuchaz.enigma.translation.representation.entry.Entry;
4
5import javax.annotation.Nullable;
6import java.util.stream.Stream;
7
8public interface EntryMap<T> {
9 void insert(Entry<?> entry, T value);
10
11 @Nullable
12 T remove(Entry<?> entry);
13
14 @Nullable
15 T get(Entry<?> entry);
16
17 default boolean contains(Entry<?> entry) {
18 return get(entry) != null;
19 }
20
21 Stream<Entry<?>> getAllEntries();
22
23 boolean isEmpty();
24}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java b/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java
deleted file mode 100644
index c607817..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java
+++ /dev/null
@@ -1,75 +0,0 @@
1package cuchaz.enigma.translation.mapping;
2
3import javax.annotation.Nonnull;
4import javax.annotation.Nullable;
5
6public class EntryMapping {
7 private final String targetName;
8 private final AccessModifier accessModifier;
9 private final @Nullable String javadoc;
10
11 public EntryMapping(@Nonnull String targetName) {
12 this(targetName, AccessModifier.UNCHANGED);
13 }
14
15 public EntryMapping(@Nonnull String targetName, @Nullable String javadoc) {
16 this(targetName, AccessModifier.UNCHANGED, javadoc);
17 }
18
19 public EntryMapping(@Nonnull String targetName, AccessModifier accessModifier) {
20 this(targetName, accessModifier, null);
21 }
22
23 public EntryMapping(@Nonnull String targetName, AccessModifier accessModifier, @Nullable String javadoc) {
24 this.targetName = targetName;
25 this.accessModifier = accessModifier;
26 this.javadoc = javadoc;
27 }
28
29 @Nonnull
30 public String getTargetName() {
31 return targetName;
32 }
33
34 @Nonnull
35 public AccessModifier getAccessModifier() {
36 if (accessModifier == null) {
37 return AccessModifier.UNCHANGED;
38 }
39 return accessModifier;
40 }
41
42 @Nullable
43 public String getJavadoc() {
44 return javadoc;
45 }
46
47 public EntryMapping withName(String newName) {
48 return new EntryMapping(newName, accessModifier, javadoc);
49 }
50
51 public EntryMapping withModifier(AccessModifier newModifier) {
52 return new EntryMapping(targetName, newModifier, javadoc);
53 }
54
55 public EntryMapping withDocs(String newDocs) {
56 return new EntryMapping(targetName, accessModifier, newDocs);
57 }
58
59 @Override
60 public boolean equals(Object obj) {
61 if (obj == this) return true;
62
63 if (obj instanceof EntryMapping) {
64 EntryMapping mapping = (EntryMapping) obj;
65 return mapping.targetName.equals(targetName) && mapping.accessModifier.equals(accessModifier);
66 }
67
68 return false;
69 }
70
71 @Override
72 public int hashCode() {
73 return targetName.hashCode() + accessModifier.hashCode() * 31;
74 }
75}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java b/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java
deleted file mode 100644
index ad36c97..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java
+++ /dev/null
@@ -1,105 +0,0 @@
1package cuchaz.enigma.translation.mapping;
2
3import cuchaz.enigma.analysis.index.JarIndex;
4import cuchaz.enigma.translation.MappingTranslator;
5import cuchaz.enigma.translation.Translatable;
6import cuchaz.enigma.translation.Translator;
7import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree;
8import cuchaz.enigma.translation.mapping.tree.EntryTree;
9import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
10import cuchaz.enigma.translation.representation.entry.Entry;
11
12import javax.annotation.Nullable;
13import java.util.Collection;
14import java.util.function.UnaryOperator;
15import java.util.stream.Stream;
16
17public class EntryRemapper {
18 private final DeltaTrackingTree<EntryMapping> obfToDeobf;
19
20 private final EntryResolver obfResolver;
21 private final Translator deobfuscator;
22
23 private final MappingValidator validator;
24
25 private EntryRemapper(JarIndex jarIndex, EntryTree<EntryMapping> obfToDeobf) {
26 this.obfToDeobf = new DeltaTrackingTree<>(obfToDeobf);
27
28 this.obfResolver = jarIndex.getEntryResolver();
29
30 this.deobfuscator = new MappingTranslator(obfToDeobf, obfResolver);
31
32 this.validator = new MappingValidator(obfToDeobf, deobfuscator, jarIndex);
33 }
34
35 public static EntryRemapper mapped(JarIndex index, EntryTree<EntryMapping> obfToDeobf) {
36 return new EntryRemapper(index, obfToDeobf);
37 }
38
39 public static EntryRemapper empty(JarIndex index) {
40 return new EntryRemapper(index, new HashEntryTree<>());
41 }
42
43 public <E extends Entry<?>> void mapFromObf(E obfuscatedEntry, @Nullable EntryMapping deobfMapping) {
44 mapFromObf(obfuscatedEntry, deobfMapping, true);
45 }
46
47 public <E extends Entry<?>> void mapFromObf(E obfuscatedEntry, @Nullable EntryMapping deobfMapping, boolean renaming) {
48 Collection<E> resolvedEntries = obfResolver.resolveEntry(obfuscatedEntry, renaming ? ResolutionStrategy.RESOLVE_ROOT : ResolutionStrategy.RESOLVE_CLOSEST);
49
50 if (renaming && deobfMapping != null) {
51 for (E resolvedEntry : resolvedEntries) {
52 validator.validateRename(resolvedEntry, deobfMapping.getTargetName());
53 }
54 }
55
56 for (E resolvedEntry : resolvedEntries) {
57 obfToDeobf.insert(resolvedEntry, deobfMapping);
58 }
59 }
60
61 public void removeByObf(Entry<?> obfuscatedEntry) {
62 mapFromObf(obfuscatedEntry, null);
63 }
64
65 @Nullable
66 public EntryMapping getDeobfMapping(Entry<?> entry) {
67 return obfToDeobf.get(entry);
68 }
69
70 public boolean hasDeobfMapping(Entry<?> obfEntry) {
71 return obfToDeobf.contains(obfEntry);
72 }
73
74 public <T extends Translatable> T deobfuscate(T translatable) {
75 return deobfuscator.translate(translatable);
76 }
77
78 public Translator getDeobfuscator() {
79 return deobfuscator;
80 }
81
82 public Stream<Entry<?>> getObfEntries() {
83 return obfToDeobf.getAllEntries();
84 }
85
86 public Collection<Entry<?>> getObfChildren(Entry<?> obfuscatedEntry) {
87 return obfToDeobf.getChildren(obfuscatedEntry);
88 }
89
90 public DeltaTrackingTree<EntryMapping> getObfToDeobf() {
91 return obfToDeobf;
92 }
93
94 public MappingDelta<EntryMapping> takeMappingDelta() {
95 return obfToDeobf.takeDelta();
96 }
97
98 public boolean isDirty() {
99 return obfToDeobf.isDirty();
100 }
101
102 public EntryResolver getObfResolver() {
103 return obfResolver;
104 }
105}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/EntryResolver.java b/src/main/java/cuchaz/enigma/translation/mapping/EntryResolver.java
deleted file mode 100644
index 521f72d..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/EntryResolver.java
+++ /dev/null
@@ -1,41 +0,0 @@
1package cuchaz.enigma.translation.mapping;
2
3import com.google.common.collect.Streams;
4import cuchaz.enigma.analysis.EntryReference;
5import cuchaz.enigma.translation.representation.entry.Entry;
6import cuchaz.enigma.translation.representation.entry.MethodEntry;
7
8import java.util.Collection;
9import java.util.Set;
10import java.util.stream.Collectors;
11
12public interface EntryResolver {
13 <E extends Entry<?>> Collection<E> resolveEntry(E entry, ResolutionStrategy strategy);
14
15 default <E extends Entry<?>> E resolveFirstEntry(E entry, ResolutionStrategy strategy) {
16 return resolveEntry(entry, strategy).stream().findFirst().orElse(entry);
17 }
18
19 default <E extends Entry<?>, C extends Entry<?>> Collection<EntryReference<E, C>> resolveReference(EntryReference<E, C> reference, ResolutionStrategy strategy) {
20 Collection<E> entry = resolveEntry(reference.entry, strategy);
21 if (reference.context != null) {
22 Collection<C> context = resolveEntry(reference.context, strategy);
23 return Streams.zip(entry.stream(), context.stream(), (e, c) -> new EntryReference<>(e, c, reference))
24 .collect(Collectors.toList());
25 } else {
26 return entry.stream()
27 .map(e -> new EntryReference<>(e, null, reference))
28 .collect(Collectors.toList());
29 }
30 }
31
32 default <E extends Entry<?>, C extends Entry<?>> EntryReference<E, C> resolveFirstReference(EntryReference<E, C> reference, ResolutionStrategy strategy) {
33 E entry = resolveFirstEntry(reference.entry, strategy);
34 C context = resolveFirstEntry(reference.context, strategy);
35 return new EntryReference<>(entry, context, reference);
36 }
37
38 Set<Entry<?>> resolveEquivalentEntries(Entry<?> entry);
39
40 Set<MethodEntry> resolveEquivalentMethods(MethodEntry methodEntry);
41}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/IndexEntryResolver.java b/src/main/java/cuchaz/enigma/translation/mapping/IndexEntryResolver.java
deleted file mode 100644
index 78231dd..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/IndexEntryResolver.java
+++ /dev/null
@@ -1,227 +0,0 @@
1package cuchaz.enigma.translation.mapping;
2
3import com.google.common.collect.Sets;
4import cuchaz.enigma.analysis.IndexTreeBuilder;
5import cuchaz.enigma.analysis.MethodImplementationsTreeNode;
6import cuchaz.enigma.analysis.MethodInheritanceTreeNode;
7import cuchaz.enigma.analysis.index.BridgeMethodIndex;
8import cuchaz.enigma.analysis.index.EntryIndex;
9import cuchaz.enigma.analysis.index.InheritanceIndex;
10import cuchaz.enigma.analysis.index.JarIndex;
11import cuchaz.enigma.translation.VoidTranslator;
12import cuchaz.enigma.translation.representation.AccessFlags;
13import cuchaz.enigma.translation.representation.entry.ClassEntry;
14import cuchaz.enigma.translation.representation.entry.Entry;
15import cuchaz.enigma.translation.representation.entry.MethodEntry;
16
17import javax.annotation.Nullable;
18import java.util.*;
19import java.util.stream.Collectors;
20
21public class IndexEntryResolver implements EntryResolver {
22 private final EntryIndex entryIndex;
23 private final InheritanceIndex inheritanceIndex;
24 private final BridgeMethodIndex bridgeMethodIndex;
25
26 private final IndexTreeBuilder treeBuilder;
27
28 public IndexEntryResolver(JarIndex index) {
29 this.entryIndex = index.getEntryIndex();
30 this.inheritanceIndex = index.getInheritanceIndex();
31 this.bridgeMethodIndex = index.getBridgeMethodIndex();
32
33 this.treeBuilder = new IndexTreeBuilder(index);
34 }
35
36 @Override
37 @SuppressWarnings("unchecked")
38 public <E extends Entry<?>> Collection<E> resolveEntry(E entry, ResolutionStrategy strategy) {
39 if (entry == null) {
40 return Collections.emptySet();
41 }
42
43 Entry<ClassEntry> classChild = getClassChild(entry);
44 if (classChild != null && !(classChild instanceof ClassEntry)) {
45 AccessFlags access = entryIndex.getEntryAccess(classChild);
46
47 // If we're looking for the closest and this entry exists, we're done looking
48 if (strategy == ResolutionStrategy.RESOLVE_CLOSEST && access != null) {
49 return Collections.singleton(entry);
50 }
51
52 if (access == null || !access.isPrivate()) {
53 Collection<Entry<ClassEntry>> resolvedChildren = resolveChildEntry(classChild, strategy);
54 if (!resolvedChildren.isEmpty()) {
55 return resolvedChildren.stream()
56 .map(resolvedChild -> (E) entry.replaceAncestor(classChild, resolvedChild))
57 .collect(Collectors.toList());
58 }
59 }
60 }
61
62 return Collections.singleton(entry);
63 }
64
65 @Nullable
66 private Entry<ClassEntry> getClassChild(Entry<?> entry) {
67 if (entry instanceof ClassEntry) {
68 return null;
69 }
70
71 // get the entry in the hierarchy that is the child of a class
72 List<Entry<?>> ancestry = entry.getAncestry();
73 for (int i = ancestry.size() - 1; i > 0; i--) {
74 Entry<?> child = ancestry.get(i);
75 Entry<ClassEntry> cast = child.castParent(ClassEntry.class);
76 if (cast != null && !(cast instanceof ClassEntry)) {
77 // we found the entry which is a child of a class, we are now able to resolve the owner of this entry
78 return cast;
79 }
80 }
81
82 return null;
83 }
84
85 private Set<Entry<ClassEntry>> resolveChildEntry(Entry<ClassEntry> entry, ResolutionStrategy strategy) {
86 ClassEntry ownerClass = entry.getParent();
87
88 if (entry instanceof MethodEntry) {
89 MethodEntry bridgeMethod = bridgeMethodIndex.getBridgeFromSpecialized((MethodEntry) entry);
90 if (bridgeMethod != null && ownerClass.equals(bridgeMethod.getParent())) {
91 Set<Entry<ClassEntry>> resolvedBridge = resolveChildEntry(bridgeMethod, strategy);
92 if (!resolvedBridge.isEmpty()) {
93 return resolvedBridge;
94 } else {
95 return Collections.singleton(bridgeMethod);
96 }
97 }
98 }
99
100 Set<Entry<ClassEntry>> resolvedEntries = new HashSet<>();
101
102 for (ClassEntry parentClass : inheritanceIndex.getParents(ownerClass)) {
103 Entry<ClassEntry> parentEntry = entry.withParent(parentClass);
104
105 if (strategy == ResolutionStrategy.RESOLVE_ROOT) {
106 resolvedEntries.addAll(resolveRoot(parentEntry, strategy));
107 } else {
108 resolvedEntries.addAll(resolveClosest(parentEntry, strategy));
109 }
110 }
111
112 return resolvedEntries;
113 }
114
115 private Collection<Entry<ClassEntry>> resolveRoot(Entry<ClassEntry> entry, ResolutionStrategy strategy) {
116 // When resolving root, we want to first look for the lowest entry before returning ourselves
117 Set<Entry<ClassEntry>> parentResolution = resolveChildEntry(entry, strategy);
118
119 if (parentResolution.isEmpty()) {
120 AccessFlags parentAccess = entryIndex.getEntryAccess(entry);
121 if (parentAccess != null && !parentAccess.isPrivate()) {
122 return Collections.singleton(entry);
123 }
124 }
125
126 return parentResolution;
127 }
128
129 private Collection<Entry<ClassEntry>> resolveClosest(Entry<ClassEntry> entry, ResolutionStrategy strategy) {
130 // When resolving closest, we want to first check if we exist before looking further down
131 AccessFlags parentAccess = entryIndex.getEntryAccess(entry);
132 if (parentAccess != null && !parentAccess.isPrivate()) {
133 return Collections.singleton(entry);
134 } else {
135 return resolveChildEntry(entry, strategy);
136 }
137 }
138
139 @Override
140 public Set<Entry<?>> resolveEquivalentEntries(Entry<?> entry) {
141 MethodEntry relevantMethod = entry.findAncestor(MethodEntry.class);
142 if (relevantMethod == null || !entryIndex.hasMethod(relevantMethod)) {
143 return Collections.singleton(entry);
144 }
145
146 Set<MethodEntry> equivalentMethods = resolveEquivalentMethods(relevantMethod);
147 Set<Entry<?>> equivalentEntries = new HashSet<>(equivalentMethods.size());
148
149 for (MethodEntry equivalentMethod : equivalentMethods) {
150 Entry<?> equivalentEntry = entry.replaceAncestor(relevantMethod, equivalentMethod);
151 equivalentEntries.add(equivalentEntry);
152 }
153
154 return equivalentEntries;
155 }
156
157 @Override
158 public Set<MethodEntry> resolveEquivalentMethods(MethodEntry methodEntry) {
159 AccessFlags access = entryIndex.getMethodAccess(methodEntry);
160 if (access == null) {
161 throw new IllegalArgumentException("Could not find method " + methodEntry);
162 }
163
164 if (!canInherit(methodEntry, access)) {
165 return Collections.singleton(methodEntry);
166 }
167
168 Set<MethodEntry> methodEntries = Sets.newHashSet();
169 resolveEquivalentMethods(methodEntries, treeBuilder.buildMethodInheritance(VoidTranslator.INSTANCE, methodEntry));
170 return methodEntries;
171 }
172
173 private void resolveEquivalentMethods(Set<MethodEntry> methodEntries, MethodInheritanceTreeNode node) {
174 MethodEntry methodEntry = node.getMethodEntry();
175 if (methodEntries.contains(methodEntry)) {
176 return;
177 }
178
179 AccessFlags flags = entryIndex.getMethodAccess(methodEntry);
180 if (flags != null && canInherit(methodEntry, flags)) {
181 // collect the entry
182 methodEntries.add(methodEntry);
183 }
184
185 // look at bridge methods!
186 MethodEntry bridgedMethod = bridgeMethodIndex.getBridgeFromSpecialized(methodEntry);
187 while (bridgedMethod != null) {
188 methodEntries.addAll(resolveEquivalentMethods(bridgedMethod));
189 bridgedMethod = bridgeMethodIndex.getBridgeFromSpecialized(bridgedMethod);
190 }
191
192 // look at interface methods too
193 for (MethodImplementationsTreeNode implementationsNode : treeBuilder.buildMethodImplementations(VoidTranslator.INSTANCE, methodEntry)) {
194 resolveEquivalentMethods(methodEntries, implementationsNode);
195 }
196
197 // recurse
198 for (int i = 0; i < node.getChildCount(); i++) {
199 resolveEquivalentMethods(methodEntries, (MethodInheritanceTreeNode) node.getChildAt(i));
200 }
201 }
202
203 private void resolveEquivalentMethods(Set<MethodEntry> methodEntries, MethodImplementationsTreeNode node) {
204 MethodEntry methodEntry = node.getMethodEntry();
205 AccessFlags flags = entryIndex.getMethodAccess(methodEntry);
206 if (flags != null && !flags.isPrivate() && !flags.isStatic()) {
207 // collect the entry
208 methodEntries.add(methodEntry);
209 }
210
211 // look at bridge methods!
212 MethodEntry bridgedMethod = bridgeMethodIndex.getBridgeFromSpecialized(methodEntry);
213 while (bridgedMethod != null) {
214 methodEntries.addAll(resolveEquivalentMethods(bridgedMethod));
215 bridgedMethod = bridgeMethodIndex.getBridgeFromSpecialized(bridgedMethod);
216 }
217
218 // recurse
219 for (int i = 0; i < node.getChildCount(); i++) {
220 resolveEquivalentMethods(methodEntries, (MethodImplementationsTreeNode) node.getChildAt(i));
221 }
222 }
223
224 private boolean canInherit(MethodEntry entry, AccessFlags access) {
225 return !entry.isConstructor() && !access.isPrivate() && !access.isStatic() && !access.isFinal();
226 }
227}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java b/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java
deleted file mode 100644
index 1407bb6..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/MappingDelta.java
+++ /dev/null
@@ -1,54 +0,0 @@
1package cuchaz.enigma.translation.mapping;
2
3import cuchaz.enigma.translation.Translatable;
4import cuchaz.enigma.translation.Translator;
5import cuchaz.enigma.translation.mapping.tree.EntryTree;
6import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
7import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
8import cuchaz.enigma.translation.representation.entry.Entry;
9
10import java.util.stream.Stream;
11
12public class MappingDelta<T> implements Translatable {
13 public static final Object PLACEHOLDER = new Object();
14
15 private final EntryTree<T> baseMappings;
16
17 private final EntryTree<Object> changes;
18
19 public MappingDelta(EntryTree<T> baseMappings, EntryTree<Object> changes) {
20 this.baseMappings = baseMappings;
21 this.changes = changes;
22 }
23
24 public MappingDelta(EntryTree<T> baseMappings) {
25 this(baseMappings, new HashEntryTree<>());
26 }
27
28 public static <T> MappingDelta<T> added(EntryTree<T> mappings) {
29 EntryTree<Object> changes = new HashEntryTree<>();
30 mappings.getAllEntries().forEach(entry -> changes.insert(entry, PLACEHOLDER));
31
32 return new MappingDelta<>(new HashEntryTree<>(), changes);
33 }
34
35 public EntryTree<T> getBaseMappings() {
36 return baseMappings;
37 }
38
39 public EntryTree<?> getChanges() {
40 return changes;
41 }
42
43 public Stream<Entry<?>> getChangedRoots() {
44 return changes.getRootNodes().map(EntryTreeNode::getEntry);
45 }
46
47 @Override
48 public MappingDelta<T> translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
49 return new MappingDelta<>(
50 translator.translate(baseMappings),
51 translator.translate(changes)
52 );
53 }
54}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/MappingFileNameFormat.java b/src/main/java/cuchaz/enigma/translation/mapping/MappingFileNameFormat.java
deleted file mode 100644
index e40bfe7..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/MappingFileNameFormat.java
+++ /dev/null
@@ -1,10 +0,0 @@
1package cuchaz.enigma.translation.mapping;
2
3import com.google.gson.annotations.SerializedName;
4
5public enum MappingFileNameFormat {
6 @SerializedName("by_obf")
7 BY_OBF,
8 @SerializedName("by_deobf")
9 BY_DEOBF
10}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/MappingPair.java b/src/main/java/cuchaz/enigma/translation/mapping/MappingPair.java
deleted file mode 100644
index 5d39e3d..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/MappingPair.java
+++ /dev/null
@@ -1,32 +0,0 @@
1package cuchaz.enigma.translation.mapping;
2
3import cuchaz.enigma.translation.representation.entry.Entry;
4
5import javax.annotation.Nullable;
6
7public class MappingPair<E extends Entry<?>, M> {
8 private final E entry;
9 private M mapping;
10
11 public MappingPair(E entry, @Nullable M mapping) {
12 this.entry = entry;
13 this.mapping = mapping;
14 }
15
16 public MappingPair(E entry) {
17 this(entry, null);
18 }
19
20 public E getEntry() {
21 return entry;
22 }
23
24 @Nullable
25 public M getMapping() {
26 return mapping;
27 }
28
29 public void setMapping(M mapping) {
30 this.mapping = mapping;
31 }
32}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/MappingSaveParameters.java b/src/main/java/cuchaz/enigma/translation/mapping/MappingSaveParameters.java
deleted file mode 100644
index 07065d6..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/MappingSaveParameters.java
+++ /dev/null
@@ -1,16 +0,0 @@
1package cuchaz.enigma.translation.mapping;
2
3import com.google.gson.annotations.SerializedName;
4
5public class MappingSaveParameters {
6 @SerializedName("file_name_format")
7 private final MappingFileNameFormat fileNameFormat;
8
9 public MappingSaveParameters(MappingFileNameFormat fileNameFormat) {
10 this.fileNameFormat = fileNameFormat;
11 }
12
13 public MappingFileNameFormat getFileNameFormat() {
14 return fileNameFormat;
15 }
16}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java b/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java
deleted file mode 100644
index dffcb0c..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java
+++ /dev/null
@@ -1,76 +0,0 @@
1package cuchaz.enigma.translation.mapping;
2
3import cuchaz.enigma.analysis.index.InheritanceIndex;
4import cuchaz.enigma.analysis.index.JarIndex;
5import cuchaz.enigma.throwables.IllegalNameException;
6import cuchaz.enigma.translation.Translator;
7import cuchaz.enigma.translation.mapping.tree.EntryTree;
8import cuchaz.enigma.translation.representation.entry.ClassEntry;
9import cuchaz.enigma.translation.representation.entry.Entry;
10
11import java.util.Collection;
12import java.util.HashSet;
13import java.util.stream.Collectors;
14
15public class MappingValidator {
16 private final EntryTree<EntryMapping> obfToDeobf;
17 private final Translator deobfuscator;
18 private final JarIndex index;
19
20 public MappingValidator(EntryTree<EntryMapping> obfToDeobf, Translator deobfuscator, JarIndex index) {
21 this.obfToDeobf = obfToDeobf;
22 this.deobfuscator = deobfuscator;
23 this.index = index;
24 }
25
26 public void validateRename(Entry<?> entry, String name) throws IllegalNameException {
27 Collection<Entry<?>> equivalentEntries = index.getEntryResolver().resolveEquivalentEntries(entry);
28 for (Entry<?> equivalentEntry : equivalentEntries) {
29 equivalentEntry.validateName(name);
30 validateUnique(equivalentEntry, name);
31 }
32 }
33
34 private void validateUnique(Entry<?> entry, String name) {
35 ClassEntry containingClass = entry.getContainingClass();
36 Collection<ClassEntry> relatedClasses = getRelatedClasses(containingClass);
37
38 for (ClassEntry relatedClass : relatedClasses) {
39 Entry<?> relatedEntry = entry.replaceAncestor(containingClass, relatedClass);
40 Entry<?> translatedEntry = deobfuscator.translate(relatedEntry);
41
42 Collection<Entry<?>> translatedSiblings = obfToDeobf.getSiblings(relatedEntry).stream()
43 .map(deobfuscator::translate)
44 .collect(Collectors.toList());
45
46 if (!isUnique(translatedEntry, translatedSiblings, name)) {
47 Entry<?> parent = translatedEntry.getParent();
48 if (parent != null) {
49 throw new IllegalNameException(name, "Name is not unique in " + parent + "!");
50 } else {
51 throw new IllegalNameException(name, "Name is not unique!");
52 }
53 }
54 }
55 }
56
57 private Collection<ClassEntry> getRelatedClasses(ClassEntry classEntry) {
58 InheritanceIndex inheritanceIndex = index.getInheritanceIndex();
59
60 Collection<ClassEntry> relatedClasses = new HashSet<>();
61 relatedClasses.add(classEntry);
62 relatedClasses.addAll(inheritanceIndex.getChildren(classEntry));
63 relatedClasses.addAll(inheritanceIndex.getAncestors(classEntry));
64
65 return relatedClasses;
66 }
67
68 private boolean isUnique(Entry<?> entry, Collection<Entry<?>> siblings, String name) {
69 for (Entry<?> sibling : siblings) {
70 if (entry.canConflictWith(sibling) && sibling.getName().equals(name)) {
71 return false;
72 }
73 }
74 return true;
75 }
76}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java b/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java
deleted file mode 100644
index 5d9794f..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java
+++ /dev/null
@@ -1,99 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation.mapping;
13
14import cuchaz.enigma.ProgressListener;
15import cuchaz.enigma.analysis.index.JarIndex;
16import cuchaz.enigma.translation.mapping.tree.EntryTree;
17import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
18import cuchaz.enigma.translation.representation.entry.ClassEntry;
19import cuchaz.enigma.translation.representation.entry.Entry;
20import cuchaz.enigma.translation.representation.entry.FieldEntry;
21import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
22import cuchaz.enigma.translation.representation.entry.MethodEntry;
23
24import java.util.Collection;
25import java.util.HashMap;
26import java.util.Map;
27import java.util.stream.Collectors;
28
29public class MappingsChecker {
30 private final JarIndex index;
31 private final EntryTree<EntryMapping> mappings;
32
33 public MappingsChecker(JarIndex index, EntryTree<EntryMapping> mappings) {
34 this.index = index;
35 this.mappings = mappings;
36 }
37
38 public Dropped dropBrokenMappings(ProgressListener progress) {
39 Dropped dropped = new Dropped();
40
41 Collection<Entry<?>> obfEntries = mappings.getAllEntries()
42 .filter(e -> e instanceof ClassEntry || e instanceof MethodEntry || e instanceof FieldEntry || e instanceof LocalVariableEntry)
43 .collect(Collectors.toList());
44
45 progress.init(obfEntries.size(), "Checking for dropped mappings");
46
47 int steps = 0;
48 for (Entry<?> entry : obfEntries) {
49 progress.step(steps++, entry.toString());
50 tryDropEntry(dropped, entry);
51 }
52
53 dropped.apply(mappings);
54
55 return dropped;
56 }
57
58 private void tryDropEntry(Dropped dropped, Entry<?> entry) {
59 if (shouldDropEntry(entry)) {
60 EntryMapping mapping = mappings.get(entry);
61 if (mapping != null) {
62 dropped.drop(entry, mapping);
63 }
64 }
65 }
66
67 private boolean shouldDropEntry(Entry<?> entry) {
68 if (!index.getEntryIndex().hasEntry(entry)) {
69 return true;
70 }
71 Collection<Entry<?>> resolvedEntries = index.getEntryResolver().resolveEntry(entry, ResolutionStrategy.RESOLVE_ROOT);
72 return !resolvedEntries.contains(entry);
73 }
74
75 public static class Dropped {
76 private final Map<Entry<?>, String> droppedMappings = new HashMap<>();
77
78 public void drop(Entry<?> entry, EntryMapping mapping) {
79 droppedMappings.put(entry, mapping.getTargetName());
80 }
81
82 void apply(EntryTree<EntryMapping> mappings) {
83 for (Entry<?> entry : droppedMappings.keySet()) {
84 EntryTreeNode<EntryMapping> node = mappings.findNode(entry);
85 if (node == null) {
86 continue;
87 }
88
89 for (Entry<?> childEntry : node.getChildrenRecursively()) {
90 mappings.remove(childEntry);
91 }
92 }
93 }
94
95 public Map<Entry<?>, String> getDroppedMappings() {
96 return droppedMappings;
97 }
98 }
99}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/NameValidator.java b/src/main/java/cuchaz/enigma/translation/mapping/NameValidator.java
deleted file mode 100644
index 5bc2f67..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/NameValidator.java
+++ /dev/null
@@ -1,53 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation.mapping;
13
14import cuchaz.enigma.throwables.IllegalNameException;
15import cuchaz.enigma.translation.representation.entry.ClassEntry;
16
17import java.util.Arrays;
18import java.util.List;
19import java.util.regex.Pattern;
20
21public class NameValidator {
22 private static final Pattern IDENTIFIER_PATTERN;
23 private static final Pattern CLASS_PATTERN;
24 private static final List<String> ILLEGAL_IDENTIFIERS = Arrays.asList(
25 "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized",
26 "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte",
27 "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch",
28 "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally",
29 "long", "strictfp", "volatile", "const", "float", "native", "super", "while", "_"
30 );
31
32 static {
33 String identifierRegex = "[A-Za-z_<][A-Za-z0-9_>]*";
34 IDENTIFIER_PATTERN = Pattern.compile(identifierRegex);
35 CLASS_PATTERN = Pattern.compile(String.format("^(%s(\\.|/))*(%s)$", identifierRegex, identifierRegex));
36 }
37
38 public static void validateClassName(String name) {
39 if (!CLASS_PATTERN.matcher(name).matches() || ILLEGAL_IDENTIFIERS.contains(name)) {
40 throw new IllegalNameException(name, "This doesn't look like a legal class name");
41 }
42 }
43
44 public static void validateIdentifier(String name) {
45 if (!IDENTIFIER_PATTERN.matcher(name).matches() || ILLEGAL_IDENTIFIERS.contains(name)) {
46 throw new IllegalNameException(name, "This doesn't look like a legal identifier");
47 }
48 }
49
50 public static boolean isReserved(String name) {
51 return ILLEGAL_IDENTIFIERS.contains(name);
52 }
53}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/ResolutionStrategy.java b/src/main/java/cuchaz/enigma/translation/mapping/ResolutionStrategy.java
deleted file mode 100644
index 1c28e02..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/ResolutionStrategy.java
+++ /dev/null
@@ -1,6 +0,0 @@
1package cuchaz.enigma.translation.mapping;
2
3public enum ResolutionStrategy {
4 RESOLVE_ROOT,
5 RESOLVE_CLOSEST
6}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/VoidEntryResolver.java b/src/main/java/cuchaz/enigma/translation/mapping/VoidEntryResolver.java
deleted file mode 100644
index 2eab55f..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/VoidEntryResolver.java
+++ /dev/null
@@ -1,27 +0,0 @@
1package cuchaz.enigma.translation.mapping;
2
3import cuchaz.enigma.translation.representation.entry.Entry;
4import cuchaz.enigma.translation.representation.entry.MethodEntry;
5
6import java.util.Collection;
7import java.util.Collections;
8import java.util.Set;
9
10public enum VoidEntryResolver implements EntryResolver {
11 INSTANCE;
12
13 @Override
14 public <E extends Entry<?>> Collection<E> resolveEntry(E entry, ResolutionStrategy strategy) {
15 return Collections.singleton(entry);
16 }
17
18 @Override
19 public Set<Entry<?>> resolveEquivalentEntries(Entry<?> entry) {
20 return Collections.singleton(entry);
21 }
22
23 @Override
24 public Set<MethodEntry> resolveEquivalentMethods(MethodEntry methodEntry) {
25 return Collections.singleton(methodEntry);
26 }
27}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaFormat.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaFormat.java
deleted file mode 100644
index af92ffb..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaFormat.java
+++ /dev/null
@@ -1,9 +0,0 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3public class EnigmaFormat {
4 public static final String COMMENT = "COMMENT";
5 public static final String CLASS = "CLASS";
6 public static final String FIELD = "FIELD";
7 public static final String METHOD = "METHOD";
8 public static final String PARAMETER = "ARG";
9}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsReader.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsReader.java
deleted file mode 100644
index 53bbaa3..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsReader.java
+++ /dev/null
@@ -1,319 +0,0 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import com.google.common.base.Charsets;
4import cuchaz.enigma.ProgressListener;
5import cuchaz.enigma.throwables.MappingParseException;
6import cuchaz.enigma.translation.mapping.AccessModifier;
7import cuchaz.enigma.translation.mapping.EntryMapping;
8import cuchaz.enigma.translation.mapping.MappingPair;
9import cuchaz.enigma.translation.mapping.MappingSaveParameters;
10import cuchaz.enigma.translation.mapping.tree.EntryTree;
11import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
12import cuchaz.enigma.translation.representation.MethodDescriptor;
13import cuchaz.enigma.translation.representation.TypeDescriptor;
14import cuchaz.enigma.translation.representation.entry.*;
15import cuchaz.enigma.utils.I18n;
16
17import javax.annotation.Nullable;
18import java.io.IOException;
19import java.nio.file.FileSystem;
20import java.nio.file.FileSystems;
21import java.nio.file.Files;
22import java.nio.file.Path;
23import java.util.ArrayDeque;
24import java.util.Arrays;
25import java.util.Deque;
26import java.util.List;
27import java.util.Locale;
28import java.util.stream.Collectors;
29
30public enum EnigmaMappingsReader implements MappingsReader {
31 FILE {
32 @Override
33 public EntryTree<EntryMapping> read(Path path, ProgressListener progress, MappingSaveParameters saveParameters) throws IOException, MappingParseException {
34 progress.init(1, I18n.translate("progress.mappings.enigma_file.loading"));
35
36 EntryTree<EntryMapping> mappings = new HashEntryTree<>();
37 readFile(path, mappings);
38
39 progress.step(1, I18n.translate("progress.mappings.enigma_file.done"));
40
41 return mappings;
42 }
43 },
44 DIRECTORY {
45 @Override
46 public EntryTree<EntryMapping> read(Path root, ProgressListener progress, MappingSaveParameters saveParameters) throws IOException, MappingParseException {
47 EntryTree<EntryMapping> mappings = new HashEntryTree<>();
48
49 List<Path> files = Files.walk(root)
50 .filter(f -> !Files.isDirectory(f))
51 .filter(f -> f.toString().endsWith(".mapping"))
52 .collect(Collectors.toList());
53
54 progress.init(files.size(), I18n.translate("progress.mappings.enigma_directory.loading"));
55 int step = 0;
56
57 for (Path file : files) {
58 progress.step(step++, root.relativize(file).toString());
59 if (Files.isHidden(file)) {
60 continue;
61 }
62 readFile(file, mappings);
63 }
64
65 return mappings;
66 }
67 },
68 ZIP {
69 @Override
70 public EntryTree<EntryMapping> read(Path zip, ProgressListener progress, MappingSaveParameters saveParameters) throws MappingParseException, IOException {
71 try (FileSystem fs = FileSystems.newFileSystem(zip, (ClassLoader) null)) {
72 return DIRECTORY.read(fs.getPath("/"), progress, saveParameters);
73 }
74 }
75 };
76
77 protected void readFile(Path path, EntryTree<EntryMapping> mappings) throws IOException, MappingParseException {
78 List<String> lines = Files.readAllLines(path, Charsets.UTF_8);
79 Deque<MappingPair<?, RawEntryMapping>> mappingStack = new ArrayDeque<>();
80
81 for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) {
82 String line = lines.get(lineNumber);
83 int indentation = countIndentation(line);
84
85 line = formatLine(line);
86 if (line == null) {
87 continue;
88 }
89
90 cleanMappingStack(indentation, mappingStack, mappings);
91
92 try {
93 MappingPair<?, RawEntryMapping> pair = parseLine(mappingStack.peek(), line);
94 if (pair != null) {
95 mappingStack.push(pair);
96 if (pair.getMapping() != null) {
97
98 }
99 }
100 } catch (Throwable t) {
101 t.printStackTrace();
102 throw new MappingParseException(path::toString, lineNumber, t.toString());
103 }
104 }
105
106 // Clean up rest
107 cleanMappingStack(0, mappingStack, mappings);
108 }
109
110 private void cleanMappingStack(int indentation, Deque<MappingPair<?, RawEntryMapping>> mappingStack, EntryTree<EntryMapping> mappings) {
111 while (indentation < mappingStack.size()) {
112 MappingPair<?, RawEntryMapping> pair = mappingStack.pop();
113 if (pair.getMapping() != null) {
114 mappings.insert(pair.getEntry(), pair.getMapping().bake());
115 }
116 }
117 }
118
119 @Nullable
120 private String formatLine(String line) {
121 line = stripComment(line);
122 line = line.trim();
123
124 if (line.isEmpty()) {
125 return null;
126 }
127
128 return line;
129 }
130
131 private String stripComment(String line) {
132 //Dont support comments on javadoc lines
133 if (line.trim().startsWith(EnigmaFormat.COMMENT)) {
134 return line;
135 }
136
137 int commentPos = line.indexOf('#');
138 if (commentPos >= 0) {
139 return line.substring(0, commentPos);
140 }
141 return line;
142 }
143
144 private int countIndentation(String line) {
145 int indent = 0;
146 for (int i = 0; i < line.length(); i++) {
147 if (line.charAt(i) != '\t') {
148 break;
149 }
150 indent++;
151 }
152 return indent;
153 }
154
155 private MappingPair<?, RawEntryMapping> parseLine(@Nullable MappingPair<?, RawEntryMapping> parent, String line) {
156 String[] tokens = line.trim().split("\\s");
157 String keyToken = tokens[0].toUpperCase(Locale.ROOT);
158 Entry<?> parentEntry = parent == null ? null : parent.getEntry();
159
160 switch (keyToken) {
161 case EnigmaFormat.CLASS:
162 return parseClass(parentEntry, tokens);
163 case EnigmaFormat.FIELD:
164 return parseField(parentEntry, tokens);
165 case EnigmaFormat.METHOD:
166 return parseMethod(parentEntry, tokens);
167 case EnigmaFormat.PARAMETER:
168 return parseArgument(parentEntry, tokens);
169 case EnigmaFormat.COMMENT:
170 readJavadoc(parent, tokens);
171 return null;
172 default:
173 throw new RuntimeException("Unknown token '" + keyToken + "'");
174 }
175 }
176
177 private void readJavadoc(MappingPair<?, RawEntryMapping> parent, String[] tokens) {
178 if (parent == null)
179 throw new IllegalStateException("Javadoc has no parent!");
180 // Empty string to concat
181 String jdLine = tokens.length > 1 ? String.join(" ", Arrays.copyOfRange(tokens,1,tokens.length)) : "";
182 if (parent.getMapping() == null) {
183 parent.setMapping(new RawEntryMapping(parent.getEntry().getName(), AccessModifier.UNCHANGED));
184 }
185 parent.getMapping().addJavadocLine(MappingHelper.unescape(jdLine));
186 }
187
188 private MappingPair<ClassEntry, RawEntryMapping> parseClass(@Nullable Entry<?> parent, String[] tokens) {
189 String obfuscatedName = ClassEntry.getInnerName(tokens[1]);
190 ClassEntry obfuscatedEntry;
191 if (parent instanceof ClassEntry) {
192 obfuscatedEntry = new ClassEntry((ClassEntry) parent, obfuscatedName);
193 } else {
194 obfuscatedEntry = new ClassEntry(obfuscatedName);
195 }
196
197 String mapping = null;
198 AccessModifier modifier = AccessModifier.UNCHANGED;
199
200 if (tokens.length == 3) {
201 AccessModifier parsedModifier = parseModifier(tokens[2]);
202 if (parsedModifier != null) {
203 modifier = parsedModifier;
204 mapping = obfuscatedName;
205 } else {
206 mapping = tokens[2];
207 }
208 } else if (tokens.length == 4) {
209 mapping = tokens[2];
210 modifier = parseModifier(tokens[3]);
211 }
212
213 if (mapping != null) {
214 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping, modifier));
215 } else {
216 return new MappingPair<>(obfuscatedEntry);
217 }
218 }
219
220 private MappingPair<FieldEntry, RawEntryMapping> parseField(@Nullable Entry<?> parent, String[] tokens) {
221 if (!(parent instanceof ClassEntry)) {
222 throw new RuntimeException("Field must be a child of a class!");
223 }
224
225 ClassEntry ownerEntry = (ClassEntry) parent;
226
227 String obfuscatedName = tokens[1];
228 String mapping = obfuscatedName;
229 AccessModifier modifier = AccessModifier.UNCHANGED;
230 TypeDescriptor descriptor;
231
232 if (tokens.length == 3) {
233 mapping = tokens[1];
234 descriptor = new TypeDescriptor(tokens[2]);
235 } else if (tokens.length == 4) {
236 AccessModifier parsedModifier = parseModifier(tokens[3]);
237 if (parsedModifier != null) {
238 descriptor = new TypeDescriptor(tokens[2]);
239 modifier = parsedModifier;
240 } else {
241 mapping = tokens[2];
242 descriptor = new TypeDescriptor(tokens[3]);
243 }
244 } else if (tokens.length == 5) {
245 descriptor = new TypeDescriptor(tokens[3]);
246 mapping = tokens[2];
247 modifier = parseModifier(tokens[4]);
248 } else {
249 throw new RuntimeException("Invalid field declaration");
250 }
251
252 FieldEntry obfuscatedEntry = new FieldEntry(ownerEntry, obfuscatedName, descriptor);
253 if (mapping != null) {
254 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping, modifier));
255 } else {
256 return new MappingPair<>(obfuscatedEntry);
257 }
258 }
259
260 private MappingPair<MethodEntry, RawEntryMapping> parseMethod(@Nullable Entry<?> parent, String[] tokens) {
261 if (!(parent instanceof ClassEntry)) {
262 throw new RuntimeException("Method must be a child of a class!");
263 }
264
265 ClassEntry ownerEntry = (ClassEntry) parent;
266
267 String obfuscatedName = tokens[1];
268 String mapping = null;
269 AccessModifier modifier = AccessModifier.UNCHANGED;
270 MethodDescriptor descriptor;
271
272 if (tokens.length == 3) {
273 descriptor = new MethodDescriptor(tokens[2]);
274 } else if (tokens.length == 4) {
275 AccessModifier parsedModifier = parseModifier(tokens[3]);
276 if (parsedModifier != null) {
277 modifier = parsedModifier;
278 mapping = obfuscatedName;
279 descriptor = new MethodDescriptor(tokens[2]);
280 } else {
281 mapping = tokens[2];
282 descriptor = new MethodDescriptor(tokens[3]);
283 }
284 } else if (tokens.length == 5) {
285 mapping = tokens[2];
286 modifier = parseModifier(tokens[4]);
287 descriptor = new MethodDescriptor(tokens[3]);
288 } else {
289 throw new RuntimeException("Invalid method declaration");
290 }
291
292 MethodEntry obfuscatedEntry = new MethodEntry(ownerEntry, obfuscatedName, descriptor);
293 if (mapping != null) {
294 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping, modifier));
295 } else {
296 return new MappingPair<>(obfuscatedEntry);
297 }
298 }
299
300 private MappingPair<LocalVariableEntry, RawEntryMapping> parseArgument(@Nullable Entry<?> parent, String[] tokens) {
301 if (!(parent instanceof MethodEntry)) {
302 throw new RuntimeException("Method arg must be a child of a method!");
303 }
304
305 MethodEntry ownerEntry = (MethodEntry) parent;
306 LocalVariableEntry obfuscatedEntry = new LocalVariableEntry(ownerEntry, Integer.parseInt(tokens[1]), "", true, null);
307 String mapping = tokens[2];
308
309 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping));
310 }
311
312 @Nullable
313 private AccessModifier parseModifier(String token) {
314 if (token.startsWith("ACC:")) {
315 return AccessModifier.valueOf(token.substring(4));
316 }
317 return null;
318 }
319}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java
deleted file mode 100644
index be0fceb..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/EnigmaMappingsWriter.java
+++ /dev/null
@@ -1,316 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation.mapping.serde;
13
14import java.io.IOException;
15import java.io.PrintWriter;
16import java.net.URI;
17import java.net.URISyntaxException;
18import java.nio.file.DirectoryStream;
19import java.nio.file.FileSystem;
20import java.nio.file.FileSystems;
21import java.nio.file.Files;
22import java.nio.file.Path;
23import java.nio.file.Paths;
24import java.util.ArrayList;
25import java.util.Collection;
26import java.util.Collections;
27import java.util.Objects;
28import java.util.concurrent.atomic.AtomicInteger;
29import java.util.stream.Collectors;
30import java.util.stream.Stream;
31
32import cuchaz.enigma.ProgressListener;
33import cuchaz.enigma.translation.MappingTranslator;
34import cuchaz.enigma.translation.Translator;
35import cuchaz.enigma.translation.mapping.AccessModifier;
36import cuchaz.enigma.translation.mapping.EntryMapping;
37import cuchaz.enigma.translation.mapping.MappingDelta;
38import cuchaz.enigma.translation.mapping.MappingFileNameFormat;
39import cuchaz.enigma.translation.mapping.MappingSaveParameters;
40import cuchaz.enigma.translation.mapping.VoidEntryResolver;
41import cuchaz.enigma.translation.mapping.tree.EntryTree;
42import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
43import cuchaz.enigma.translation.representation.entry.ClassEntry;
44import cuchaz.enigma.translation.representation.entry.Entry;
45import cuchaz.enigma.translation.representation.entry.FieldEntry;
46import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
47import cuchaz.enigma.translation.representation.entry.MethodEntry;
48import cuchaz.enigma.utils.I18n;
49import cuchaz.enigma.utils.LFPrintWriter;
50
51public enum EnigmaMappingsWriter implements MappingsWriter {
52 FILE {
53 @Override
54 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters) {
55 Collection<ClassEntry> classes = mappings.getRootNodes()
56 .filter(entry -> entry instanceof ClassEntry)
57 .map(entry -> (ClassEntry) entry)
58 .collect(Collectors.toList());
59
60 progress.init(classes.size(), I18n.translate("progress.mappings.enigma_file.writing"));
61
62 int steps = 0;
63 try (PrintWriter writer = new LFPrintWriter(Files.newBufferedWriter(path))) {
64 for (ClassEntry classEntry : classes) {
65 progress.step(steps++, classEntry.getFullName());
66 writeRoot(writer, mappings, classEntry);
67 }
68 } catch (IOException e) {
69 e.printStackTrace();
70 }
71 }
72 },
73 DIRECTORY {
74 @Override
75 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters) {
76 Collection<ClassEntry> changedClasses = delta.getChangedRoots()
77 .filter(entry -> entry instanceof ClassEntry)
78 .map(entry -> (ClassEntry) entry)
79 .collect(Collectors.toList());
80
81 applyDeletions(path, changedClasses, mappings, delta.getBaseMappings(), saveParameters.getFileNameFormat());
82
83 progress.init(changedClasses.size(), I18n.translate("progress.mappings.enigma_directory.writing"));
84
85 AtomicInteger steps = new AtomicInteger();
86
87 Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE);
88 changedClasses.parallelStream().forEach(classEntry -> {
89 progress.step(steps.getAndIncrement(), classEntry.getFullName());
90
91 try {
92 ClassEntry fileEntry = classEntry;
93 if (saveParameters.getFileNameFormat() == MappingFileNameFormat.BY_DEOBF) {
94 fileEntry = translator.translate(fileEntry);
95 }
96
97 Path classPath = resolve(path, fileEntry);
98 Files.createDirectories(classPath.getParent());
99 Files.deleteIfExists(classPath);
100
101 try (PrintWriter writer = new LFPrintWriter(Files.newBufferedWriter(classPath))) {
102 writeRoot(writer, mappings, classEntry);
103 }
104 } catch (Throwable t) {
105 System.err.println("Failed to write class '" + classEntry.getFullName() + "'");
106 t.printStackTrace();
107 }
108 });
109 }
110
111 private void applyDeletions(Path root, Collection<ClassEntry> changedClasses, EntryTree<EntryMapping> mappings, EntryTree<EntryMapping> oldMappings, MappingFileNameFormat fileNameFormat) {
112 Translator oldMappingTranslator = new MappingTranslator(oldMappings, VoidEntryResolver.INSTANCE);
113
114 Stream<ClassEntry> deletedClassStream = changedClasses.stream()
115 .filter(e -> !Objects.equals(oldMappings.get(e), mappings.get(e)));
116
117 if (fileNameFormat == MappingFileNameFormat.BY_DEOBF) {
118 deletedClassStream = deletedClassStream.map(oldMappingTranslator::translate);
119 }
120
121 Collection<ClassEntry> deletedClasses = deletedClassStream.collect(Collectors.toList());
122
123 for (ClassEntry classEntry : deletedClasses) {
124 try {
125 Files.deleteIfExists(resolve(root, classEntry));
126 } catch (IOException e) {
127 System.err.println("Failed to delete deleted class '" + classEntry + "'");
128 e.printStackTrace();
129 }
130 }
131
132 for (ClassEntry classEntry : deletedClasses) {
133 String packageName = classEntry.getPackageName();
134 if (packageName != null) {
135 Path packagePath = Paths.get(packageName);
136 try {
137 deleteDeadPackages(root, packagePath);
138 } catch (IOException e) {
139 System.err.println("Failed to delete dead package '" + packageName + "'");
140 e.printStackTrace();
141 }
142 }
143 }
144 }
145
146 private void deleteDeadPackages(Path root, Path packagePath) throws IOException {
147 for (int i = packagePath.getNameCount() - 1; i >= 0; i--) {
148 Path subPath = packagePath.subpath(0, i + 1);
149 Path packagePart = root.resolve(subPath);
150 if (isEmpty(packagePart)) {
151 Files.deleteIfExists(packagePart);
152 }
153 }
154 }
155
156 private boolean isEmpty(Path path) {
157 try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
158 return !stream.iterator().hasNext();
159 } catch (IOException e) {
160 return false;
161 }
162 }
163
164 private Path resolve(Path root, ClassEntry classEntry) {
165 return root.resolve(classEntry.getFullName() + ".mapping");
166 }
167 },
168 ZIP {
169 @Override
170 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path zip, ProgressListener progress, MappingSaveParameters saveParameters) {
171 try (FileSystem fs = FileSystems.newFileSystem(new URI("jar:file", null, zip.toUri().getPath(), ""), Collections.singletonMap("create", "true"))) {
172 DIRECTORY.write(mappings, delta, fs.getPath("/"), progress, saveParameters);
173 } catch (IOException e) {
174 e.printStackTrace();
175 } catch (URISyntaxException e) {
176 throw new RuntimeException("Unexpected error creating URI for " + zip, e);
177 }
178 }
179 };
180
181 protected void writeRoot(PrintWriter writer, EntryTree<EntryMapping> mappings, ClassEntry classEntry) {
182 Collection<Entry<?>> children = groupChildren(mappings.getChildren(classEntry));
183
184 EntryMapping classEntryMapping = mappings.get(classEntry);
185
186 writer.println(writeClass(classEntry, classEntryMapping).trim());
187 if (classEntryMapping != null && classEntryMapping.getJavadoc() != null) {
188 writeDocs(writer, classEntryMapping, 0);
189 }
190
191 for (Entry<?> child : children) {
192 writeEntry(writer, mappings, child, 1);
193 }
194
195 }
196
197 private void writeDocs(PrintWriter writer, EntryMapping mapping, int depth) {
198 String jd = mapping.getJavadoc();
199 if (jd != null) {
200 for (String line : jd.split("\\R")) {
201 writer.println(indent(EnigmaFormat.COMMENT + " " + MappingHelper.escape(line), depth + 1));
202 }
203 }
204 }
205
206 protected void writeEntry(PrintWriter writer, EntryTree<EntryMapping> mappings, Entry<?> entry, int depth) {
207 EntryTreeNode<EntryMapping> node = mappings.findNode(entry);
208 if (node == null) {
209 return;
210 }
211
212 EntryMapping mapping = node.getValue();
213
214 if (entry instanceof ClassEntry) {
215 String line = writeClass((ClassEntry) entry, mapping);
216 writer.println(indent(line, depth));
217 } else if (entry instanceof MethodEntry) {
218 String line = writeMethod((MethodEntry) entry, mapping);
219 writer.println(indent(line, depth));
220 } else if (entry instanceof FieldEntry) {
221 String line = writeField((FieldEntry) entry, mapping);
222 writer.println(indent(line, depth));
223 } else if (entry instanceof LocalVariableEntry && mapping != null) {
224 String line = writeArgument((LocalVariableEntry) entry, mapping);
225 writer.println(indent(line, depth));
226 }
227 if (mapping != null && mapping.getJavadoc() != null) {
228 writeDocs(writer, mapping, depth);
229 }
230
231 Collection<Entry<?>> children = groupChildren(node.getChildren());
232 for (Entry<?> child : children) {
233 writeEntry(writer, mappings, child, depth + 1);
234 }
235 }
236
237 private Collection<Entry<?>> groupChildren(Collection<Entry<?>> children) {
238 Collection<Entry<?>> result = new ArrayList<>(children.size());
239
240 children.stream().filter(e -> e instanceof FieldEntry)
241 .map(e -> (FieldEntry) e)
242 .sorted()
243 .forEach(result::add);
244
245 children.stream().filter(e -> e instanceof MethodEntry)
246 .map(e -> (MethodEntry) e)
247 .sorted()
248 .forEach(result::add);
249
250 children.stream().filter(e -> e instanceof LocalVariableEntry)
251 .map(e -> (LocalVariableEntry) e)
252 .sorted()
253 .forEach(result::add);
254
255 children.stream().filter(e -> e instanceof ClassEntry)
256 .map(e -> (ClassEntry) e)
257 .sorted()
258 .forEach(result::add);
259
260 return result;
261 }
262
263 protected String writeClass(ClassEntry entry, EntryMapping mapping) {
264 StringBuilder builder = new StringBuilder(EnigmaFormat.CLASS +" ");
265 builder.append(entry.getName()).append(' ');
266 writeMapping(builder, mapping);
267
268 return builder.toString();
269 }
270
271 protected String writeMethod(MethodEntry entry, EntryMapping mapping) {
272 StringBuilder builder = new StringBuilder(EnigmaFormat.METHOD + " ");
273 builder.append(entry.getName()).append(' ');
274 if (mapping != null && !mapping.getTargetName().equals(entry.getName())) {
275 writeMapping(builder, mapping);
276 }
277
278 builder.append(entry.getDesc().toString());
279
280 return builder.toString();
281 }
282
283 protected String writeField(FieldEntry entry, EntryMapping mapping) {
284 StringBuilder builder = new StringBuilder(EnigmaFormat.FIELD + " ");
285 builder.append(entry.getName()).append(' ');
286 if (mapping != null && !mapping.getTargetName().equals(entry.getName())) {
287 writeMapping(builder, mapping);
288 }
289
290 builder.append(entry.getDesc().toString());
291
292 return builder.toString();
293 }
294
295 protected String writeArgument(LocalVariableEntry entry, EntryMapping mapping) {
296 return EnigmaFormat.PARAMETER + " " + entry.getIndex() + ' ' + mapping.getTargetName();
297 }
298
299 private void writeMapping(StringBuilder builder, EntryMapping mapping) {
300 if (mapping != null) {
301 builder.append(mapping.getTargetName()).append(' ');
302 if (mapping.getAccessModifier() != AccessModifier.UNCHANGED) {
303 builder.append(mapping.getAccessModifier().getFormattedName()).append(' ');
304 }
305 }
306 }
307
308 private String indent(String line, int depth) {
309 StringBuilder builder = new StringBuilder();
310 for (int i = 0; i < depth; i++) {
311 builder.append("\t");
312 }
313 builder.append(line.trim());
314 return builder.toString();
315 }
316}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java
deleted file mode 100644
index 6c8c343..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingFormat.java
+++ /dev/null
@@ -1,59 +0,0 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import cuchaz.enigma.ProgressListener;
4import cuchaz.enigma.throwables.MappingParseException;
5import cuchaz.enigma.translation.mapping.EntryMapping;
6import cuchaz.enigma.translation.mapping.MappingDelta;
7import cuchaz.enigma.translation.mapping.MappingSaveParameters;
8import cuchaz.enigma.translation.mapping.tree.EntryTree;
9
10import javax.annotation.Nullable;
11import java.io.IOException;
12import java.nio.file.Path;
13
14public enum MappingFormat {
15 ENIGMA_FILE(EnigmaMappingsWriter.FILE, EnigmaMappingsReader.FILE),
16 ENIGMA_DIRECTORY(EnigmaMappingsWriter.DIRECTORY, EnigmaMappingsReader.DIRECTORY),
17 ENIGMA_ZIP(EnigmaMappingsWriter.ZIP, EnigmaMappingsReader.ZIP),
18 TINY_V2(new TinyV2Writer("intermediary", "named"), new TinyV2Reader()),
19 TINY_FILE(TinyMappingsWriter.INSTANCE, TinyMappingsReader.INSTANCE),
20 SRG_FILE(SrgMappingsWriter.INSTANCE, null),
21 PROGUARD(null, ProguardMappingsReader.INSTANCE);
22
23
24 private final MappingsWriter writer;
25 private final MappingsReader reader;
26
27 MappingFormat(MappingsWriter writer, MappingsReader reader) {
28 this.writer = writer;
29 this.reader = reader;
30 }
31
32 public void write(EntryTree<EntryMapping> mappings, Path path, ProgressListener progressListener, MappingSaveParameters saveParameters) {
33 write(mappings, MappingDelta.added(mappings), path, progressListener, saveParameters);
34 }
35
36 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progressListener, MappingSaveParameters saveParameters) {
37 if (writer == null) {
38 throw new IllegalStateException(name() + " does not support writing");
39 }
40 writer.write(mappings, delta, path, progressListener, saveParameters);
41 }
42
43 public EntryTree<EntryMapping> read(Path path, ProgressListener progressListener, MappingSaveParameters saveParameters) throws IOException, MappingParseException {
44 if (reader == null) {
45 throw new IllegalStateException(name() + " does not support reading");
46 }
47 return reader.read(path, progressListener, saveParameters);
48 }
49
50 @Nullable
51 public MappingsWriter getWriter() {
52 return writer;
53 }
54
55 @Nullable
56 public MappingsReader getReader() {
57 return reader;
58 }
59}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingHelper.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingHelper.java
deleted file mode 100644
index 7c8f6cc..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingHelper.java
+++ /dev/null
@@ -1,51 +0,0 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3public final class MappingHelper {
4 private static final String TO_ESCAPE = "\\\n\r\0\t";
5 private static final String ESCAPED = "\\nr0t";
6
7 public static String escape(String raw) {
8 StringBuilder builder = new StringBuilder(raw.length() + 1);
9 for (int i = 0; i < raw.length(); i++) {
10 final char c = raw.charAt(i);
11 final int r = TO_ESCAPE.indexOf(c);
12 if (r < 0) {
13 builder.append(c);
14 } else {
15 builder.append('\\').append(ESCAPED.charAt(r));
16 }
17 }
18 return builder.toString();
19 }
20
21 public static String unescape(String str) {
22 int pos = str.indexOf('\\');
23 if (pos < 0) return str;
24
25 StringBuilder ret = new StringBuilder(str.length() - 1);
26 int start = 0;
27
28 do {
29 ret.append(str, start, pos);
30 pos++;
31 int type;
32
33 if (pos >= str.length()) {
34 throw new RuntimeException("incomplete escape sequence at the end");
35 } else if ((type = ESCAPED.indexOf(str.charAt(pos))) < 0) {
36 throw new RuntimeException("invalid escape character: \\" + str.charAt(pos));
37 } else {
38 ret.append(TO_ESCAPE.charAt(type));
39 }
40
41 start = pos + 1;
42 } while ((pos = str.indexOf('\\', start)) >= 0);
43
44 ret.append(str, start, str.length());
45
46 return ret.toString();
47 }
48
49 private MappingHelper() {
50 }
51}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsReader.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsReader.java
deleted file mode 100644
index 4c60787..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsReader.java
+++ /dev/null
@@ -1,14 +0,0 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import cuchaz.enigma.ProgressListener;
4import cuchaz.enigma.throwables.MappingParseException;
5import cuchaz.enigma.translation.mapping.EntryMapping;
6import cuchaz.enigma.translation.mapping.MappingSaveParameters;
7import cuchaz.enigma.translation.mapping.tree.EntryTree;
8
9import java.io.IOException;
10import java.nio.file.Path;
11
12public interface MappingsReader {
13 EntryTree<EntryMapping> read(Path path, ProgressListener progress, MappingSaveParameters saveParameters) throws MappingParseException, IOException;
14}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java
deleted file mode 100644
index 8815986..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/MappingsWriter.java
+++ /dev/null
@@ -1,17 +0,0 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import cuchaz.enigma.ProgressListener;
4import cuchaz.enigma.translation.mapping.EntryMapping;
5import cuchaz.enigma.translation.mapping.MappingDelta;
6import cuchaz.enigma.translation.mapping.MappingSaveParameters;
7import cuchaz.enigma.translation.mapping.tree.EntryTree;
8
9import java.nio.file.Path;
10
11public interface MappingsWriter {
12 void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters);
13
14 default void write(EntryTree<EntryMapping> mappings, Path path, ProgressListener progress, MappingSaveParameters saveParameters) {
15 write(mappings, MappingDelta.added(mappings), path, progress, saveParameters);
16 }
17}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/ProguardMappingsReader.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/ProguardMappingsReader.java
deleted file mode 100644
index b5ede39..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/ProguardMappingsReader.java
+++ /dev/null
@@ -1,134 +0,0 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import cuchaz.enigma.ProgressListener;
4import cuchaz.enigma.command.MappingCommandsUtil;
5import cuchaz.enigma.throwables.MappingParseException;
6import cuchaz.enigma.translation.mapping.EntryMapping;
7import cuchaz.enigma.translation.mapping.MappingSaveParameters;
8import cuchaz.enigma.translation.mapping.tree.EntryTree;
9import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
10import cuchaz.enigma.translation.representation.MethodDescriptor;
11import cuchaz.enigma.translation.representation.TypeDescriptor;
12import cuchaz.enigma.translation.representation.entry.ClassEntry;
13import cuchaz.enigma.translation.representation.entry.FieldEntry;
14import cuchaz.enigma.translation.representation.entry.MethodEntry;
15
16import java.io.IOException;
17import java.nio.charset.StandardCharsets;
18import java.nio.file.Files;
19import java.nio.file.Path;
20import java.util.regex.Matcher;
21import java.util.regex.Pattern;
22
23public class ProguardMappingsReader implements MappingsReader {
24 public static final ProguardMappingsReader INSTANCE = new ProguardMappingsReader();
25 private static final String NAME = "[a-zA-Z0-9_\\-.$<>]+";
26 private static final String TYPE = NAME + "(?:\\[])*";
27 private static final String TYPE_LIST = "|(?:(?:" + TYPE + ",)*" + TYPE + ")";
28 private static final Pattern CLASS = Pattern.compile("(" + NAME + ") -> (" + NAME + "):");
29 private static final Pattern FIELD = Pattern.compile(" {4}(" + TYPE + ") (" + NAME + ") -> (" + NAME + ")");
30 private static final Pattern METHOD = Pattern.compile(" {4}(?:[0-9]+:[0-9]+:)?(" + TYPE + ") (" + NAME + ")\\((" + TYPE_LIST + ")\\) -> (" + NAME + ")");
31
32 public ProguardMappingsReader() {}
33
34 @Override
35 public EntryTree<EntryMapping> read(Path path, ProgressListener progress, MappingSaveParameters saveParameters) throws MappingParseException, IOException {
36 EntryTree<EntryMapping> mappings = new HashEntryTree<>();
37
38 int lineNumber = 0;
39 ClassEntry currentClass = null;
40 for (String line : Files.readAllLines(path, StandardCharsets.UTF_8)) {
41 lineNumber++;
42
43 if (line.startsWith("#") || line.isEmpty()) {
44 continue;
45 }
46
47 Matcher classMatcher = CLASS.matcher(line);
48 Matcher fieldMatcher = FIELD.matcher(line);
49 Matcher methodMatcher = METHOD.matcher(line);
50
51 if (classMatcher.matches()) {
52 String name = classMatcher.group(1);
53 String targetName = classMatcher.group(2);
54
55 mappings.insert(currentClass = new ClassEntry(name.replace('.', '/')), new EntryMapping(ClassEntry.getInnerName(targetName.replace('.', '/'))));
56 } else if (fieldMatcher.matches()) {
57 String type = fieldMatcher.group(1);
58 String name = fieldMatcher.group(2);
59 String targetName = fieldMatcher.group(3);
60
61 if (currentClass == null) {
62 throw new MappingParseException(path::toString, lineNumber, "field mapping not inside class: " + line);
63 }
64
65 mappings.insert(new FieldEntry(currentClass, name, new TypeDescriptor(getDescriptor(type))), new EntryMapping(targetName));
66 } else if (methodMatcher.matches()) {
67 String returnType = methodMatcher.group(1);
68 String name = methodMatcher.group(2);
69 String[] parameterTypes = methodMatcher.group(3).isEmpty() ? new String[0] : methodMatcher.group(3).split(",");
70 String targetName = methodMatcher.group(4);
71
72 if (currentClass == null) {
73 throw new MappingParseException(path::toString, lineNumber, "method mapping not inside class: " + line);
74 }
75
76 mappings.insert(new MethodEntry(currentClass, name, new MethodDescriptor(getDescriptor(returnType, parameterTypes))), new EntryMapping(targetName));
77 } else {
78 throw new MappingParseException(path::toString, lineNumber, "invalid mapping line: " + line);
79 }
80 }
81
82 return MappingCommandsUtil.invert(mappings);
83 }
84
85 private String getDescriptor(String type) {
86 StringBuilder descriptor = new StringBuilder();
87
88 while (type.endsWith("[]")) {
89 descriptor.append("[");
90 type = type.substring(0, type.length() - 2);
91 }
92
93 switch (type) {
94 case "byte":
95 return descriptor + "B";
96 case "char":
97 return descriptor + "C";
98 case "short":
99 return descriptor + "S";
100 case "int":
101 return descriptor + "I";
102 case "long":
103 return descriptor + "J";
104 case "float":
105 return descriptor + "F";
106 case "double":
107 return descriptor + "D";
108 case "boolean":
109 return descriptor + "Z";
110 case "void":
111 return descriptor + "V";
112 }
113
114 descriptor.append("L");
115 descriptor.append(type.replace('.', '/'));
116 descriptor.append(";");
117
118 return descriptor.toString();
119 }
120
121 private String getDescriptor(String returnType, String[] parameterTypes) {
122 StringBuilder descriptor = new StringBuilder();
123 descriptor.append('(');
124
125 for (String parameterType : parameterTypes) {
126 descriptor.append(getDescriptor(parameterType));
127 }
128
129 descriptor.append(')');
130 descriptor.append(getDescriptor(returnType));
131
132 return descriptor.toString();
133 }
134}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/RawEntryMapping.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/RawEntryMapping.java
deleted file mode 100644
index afb40e9..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/RawEntryMapping.java
+++ /dev/null
@@ -1,30 +0,0 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import cuchaz.enigma.translation.mapping.AccessModifier;
4import cuchaz.enigma.translation.mapping.EntryMapping;
5
6import java.util.ArrayList;
7import java.util.List;
8
9final class RawEntryMapping {
10 private final String targetName;
11 private final AccessModifier access;
12 private List<String> javadocs = new ArrayList<>();
13
14 RawEntryMapping(String targetName) {
15 this(targetName, null);
16 }
17
18 RawEntryMapping(String targetName, AccessModifier access) {
19 this.access = access;
20 this.targetName = targetName;
21 }
22
23 void addJavadocLine(String line) {
24 javadocs.add(line);
25 }
26
27 EntryMapping bake() {
28 return new EntryMapping(targetName, access, javadocs.isEmpty() ? null : String.join("\n", javadocs));
29 }
30}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java
deleted file mode 100644
index f67f8fc..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/SrgMappingsWriter.java
+++ /dev/null
@@ -1,118 +0,0 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import com.google.common.collect.Lists;
4import cuchaz.enigma.ProgressListener;
5import cuchaz.enigma.translation.MappingTranslator;
6import cuchaz.enigma.translation.Translator;
7import cuchaz.enigma.translation.mapping.EntryMapping;
8import cuchaz.enigma.translation.mapping.MappingDelta;
9import cuchaz.enigma.translation.mapping.MappingSaveParameters;
10import cuchaz.enigma.translation.mapping.VoidEntryResolver;
11import cuchaz.enigma.translation.mapping.tree.EntryTree;
12import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
13import cuchaz.enigma.translation.representation.entry.ClassEntry;
14import cuchaz.enigma.translation.representation.entry.Entry;
15import cuchaz.enigma.translation.representation.entry.FieldEntry;
16import cuchaz.enigma.translation.representation.entry.MethodEntry;
17import cuchaz.enigma.utils.I18n;
18import cuchaz.enigma.utils.LFPrintWriter;
19
20import java.io.IOException;
21import java.io.PrintWriter;
22import java.nio.file.Files;
23import java.nio.file.Path;
24import java.util.ArrayList;
25import java.util.Collection;
26import java.util.Comparator;
27import java.util.List;
28import java.util.stream.Collectors;
29
30public enum SrgMappingsWriter implements MappingsWriter {
31 INSTANCE;
32
33 @Override
34 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters) {
35 try {
36 Files.deleteIfExists(path);
37 Files.createFile(path);
38 } catch (IOException e) {
39 e.printStackTrace();
40 }
41
42 List<String> classLines = new ArrayList<>();
43 List<String> fieldLines = new ArrayList<>();
44 List<String> methodLines = new ArrayList<>();
45
46 Collection<Entry<?>> rootEntries = Lists.newArrayList(mappings).stream()
47 .map(EntryTreeNode::getEntry)
48 .collect(Collectors.toList());
49 progress.init(rootEntries.size(), I18n.translate("progress.mappings.srg_file.generating"));
50
51 int steps = 0;
52 for (Entry<?> entry : sorted(rootEntries)) {
53 progress.step(steps++, entry.getName());
54 writeEntry(classLines, fieldLines, methodLines, mappings, entry);
55 }
56
57 progress.init(3, I18n.translate("progress.mappings.srg_file.writing"));
58 try (PrintWriter writer = new LFPrintWriter(Files.newBufferedWriter(path))) {
59 progress.step(0, I18n.translate("type.classes"));
60 classLines.forEach(writer::println);
61 progress.step(1, I18n.translate("type.fields"));
62 fieldLines.forEach(writer::println);
63 progress.step(2, I18n.translate("type.methods"));
64 methodLines.forEach(writer::println);
65 } catch (IOException e) {
66 e.printStackTrace();
67 }
68 }
69
70 private void writeEntry(List<String> classes, List<String> fields, List<String> methods, EntryTree<EntryMapping> mappings, Entry<?> entry) {
71 EntryTreeNode<EntryMapping> node = mappings.findNode(entry);
72 if (node == null) {
73 return;
74 }
75
76 Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE);
77 if (entry instanceof ClassEntry) {
78 classes.add(generateClassLine((ClassEntry) entry, translator));
79 } else if (entry instanceof FieldEntry) {
80 fields.add(generateFieldLine((FieldEntry) entry, translator));
81 } else if (entry instanceof MethodEntry) {
82 methods.add(generateMethodLine((MethodEntry) entry, translator));
83 }
84
85 for (Entry<?> child : sorted(node.getChildren())) {
86 writeEntry(classes, fields, methods, mappings, child);
87 }
88 }
89
90 private String generateClassLine(ClassEntry sourceEntry, Translator translator) {
91 ClassEntry targetEntry = translator.translate(sourceEntry);
92 return "CL: " + sourceEntry.getFullName() + " " + targetEntry.getFullName();
93 }
94
95 private String generateMethodLine(MethodEntry sourceEntry, Translator translator) {
96 MethodEntry targetEntry = translator.translate(sourceEntry);
97 return "MD: " + describeMethod(sourceEntry) + " " + describeMethod(targetEntry);
98 }
99
100 private String describeMethod(MethodEntry entry) {
101 return entry.getParent().getFullName() + "/" + entry.getName() + " " + entry.getDesc();
102 }
103
104 private String generateFieldLine(FieldEntry sourceEntry, Translator translator) {
105 FieldEntry targetEntry = translator.translate(sourceEntry);
106 return "FD: " + describeField(sourceEntry) + " " + describeField(targetEntry);
107 }
108
109 private String describeField(FieldEntry entry) {
110 return entry.getParent().getFullName() + "/" + entry.getName();
111 }
112
113 private Collection<Entry<?>> sorted(Iterable<Entry<?>> iterable) {
114 ArrayList<Entry<?>> sorted = Lists.newArrayList(iterable);
115 sorted.sort(Comparator.comparing(Entry::getName));
116 return sorted;
117 }
118}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyMappingsReader.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyMappingsReader.java
deleted file mode 100644
index 773c95e..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyMappingsReader.java
+++ /dev/null
@@ -1,115 +0,0 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import com.google.common.base.Charsets;
4import cuchaz.enigma.ProgressListener;
5import cuchaz.enigma.throwables.MappingParseException;
6import cuchaz.enigma.translation.mapping.EntryMapping;
7import cuchaz.enigma.translation.mapping.MappingPair;
8import cuchaz.enigma.translation.mapping.MappingSaveParameters;
9import cuchaz.enigma.translation.mapping.tree.EntryTree;
10import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
11import cuchaz.enigma.translation.representation.MethodDescriptor;
12import cuchaz.enigma.translation.representation.TypeDescriptor;
13import cuchaz.enigma.translation.representation.entry.ClassEntry;
14import cuchaz.enigma.translation.representation.entry.FieldEntry;
15import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
16import cuchaz.enigma.translation.representation.entry.MethodEntry;
17import cuchaz.enigma.utils.I18n;
18
19import java.io.IOException;
20import java.nio.file.Files;
21import java.nio.file.Path;
22import java.util.List;
23
24public enum TinyMappingsReader implements MappingsReader {
25 INSTANCE;
26
27 @Override
28 public EntryTree<EntryMapping> read(Path path, ProgressListener progress, MappingSaveParameters saveParameters) throws IOException, MappingParseException {
29 return read(path, Files.readAllLines(path, Charsets.UTF_8), progress);
30 }
31
32 private EntryTree<EntryMapping> read(Path path, List<String> lines, ProgressListener progress) throws MappingParseException {
33 EntryTree<EntryMapping> mappings = new HashEntryTree<>();
34 lines.remove(0);
35
36 progress.init(lines.size(), I18n.translate("progress.mappings.tiny_file.loading"));
37
38 for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) {
39 progress.step(lineNumber, "");
40
41 String line = lines.get(lineNumber);
42
43 if (line.trim().startsWith("#")) {
44 continue;
45 }
46
47 try {
48 MappingPair<?, EntryMapping> mapping = parseLine(line);
49 mappings.insert(mapping.getEntry(), mapping.getMapping());
50 } catch (Throwable t) {
51 t.printStackTrace();
52 throw new MappingParseException(path::toString, lineNumber, t.toString());
53 }
54 }
55
56 return mappings;
57 }
58
59 private MappingPair<?, EntryMapping> parseLine(String line) {
60 String[] tokens = line.split("\t");
61
62 String key = tokens[0];
63 switch (key) {
64 case "CLASS":
65 return parseClass(tokens);
66 case "FIELD":
67 return parseField(tokens);
68 case "METHOD":
69 return parseMethod(tokens);
70 case "MTH-ARG":
71 return parseArgument(tokens);
72 default:
73 throw new RuntimeException("Unknown token '" + key + "'!");
74 }
75 }
76
77 private MappingPair<ClassEntry, EntryMapping> parseClass(String[] tokens) {
78 ClassEntry obfuscatedEntry = new ClassEntry(tokens[1]);
79 String mapping = tokens[2];
80 if (mapping.indexOf('$') > 0) {
81 // inner classes should map to only the final part
82 mapping = mapping.substring(mapping.lastIndexOf('$') + 1);
83 }
84 return new MappingPair<>(obfuscatedEntry, new EntryMapping(mapping));
85 }
86
87 private MappingPair<FieldEntry, EntryMapping> parseField(String[] tokens) {
88 ClassEntry ownerClass = new ClassEntry(tokens[1]);
89 TypeDescriptor descriptor = new TypeDescriptor(tokens[2]);
90
91 FieldEntry obfuscatedEntry = new FieldEntry(ownerClass, tokens[3], descriptor);
92 String mapping = tokens[4];
93 return new MappingPair<>(obfuscatedEntry, new EntryMapping(mapping));
94 }
95
96 private MappingPair<MethodEntry, EntryMapping> parseMethod(String[] tokens) {
97 ClassEntry ownerClass = new ClassEntry(tokens[1]);
98 MethodDescriptor descriptor = new MethodDescriptor(tokens[2]);
99
100 MethodEntry obfuscatedEntry = new MethodEntry(ownerClass, tokens[3], descriptor);
101 String mapping = tokens[4];
102 return new MappingPair<>(obfuscatedEntry, new EntryMapping(mapping));
103 }
104
105 private MappingPair<LocalVariableEntry, EntryMapping> parseArgument(String[] tokens) {
106 ClassEntry ownerClass = new ClassEntry(tokens[1]);
107 MethodDescriptor ownerDescriptor = new MethodDescriptor(tokens[2]);
108 MethodEntry ownerMethod = new MethodEntry(ownerClass, tokens[3], ownerDescriptor);
109 int variableIndex = Integer.parseInt(tokens[4]);
110
111 String mapping = tokens[5];
112 LocalVariableEntry obfuscatedEntry = new LocalVariableEntry(ownerMethod, variableIndex, "", true, null);
113 return new MappingPair<>(obfuscatedEntry, new EntryMapping(mapping));
114 }
115}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyMappingsWriter.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyMappingsWriter.java
deleted file mode 100644
index c82f262..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyMappingsWriter.java
+++ /dev/null
@@ -1,148 +0,0 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import com.google.common.base.Joiner;
4import com.google.common.collect.Lists;
5import cuchaz.enigma.ProgressListener;
6import cuchaz.enigma.translation.MappingTranslator;
7import cuchaz.enigma.translation.Translator;
8import cuchaz.enigma.translation.mapping.EntryMapping;
9import cuchaz.enigma.translation.mapping.MappingDelta;
10import cuchaz.enigma.translation.mapping.MappingSaveParameters;
11import cuchaz.enigma.translation.mapping.VoidEntryResolver;
12import cuchaz.enigma.translation.mapping.tree.EntryTree;
13import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
14import cuchaz.enigma.translation.representation.entry.ClassEntry;
15import cuchaz.enigma.translation.representation.entry.Entry;
16import cuchaz.enigma.translation.representation.entry.FieldEntry;
17import cuchaz.enigma.translation.representation.entry.MethodEntry;
18
19import java.io.BufferedWriter;
20import java.io.IOException;
21import java.io.Writer;
22import java.nio.charset.StandardCharsets;
23import java.nio.file.Files;
24import java.nio.file.Path;
25import java.util.Comparator;
26import java.util.HashSet;
27import java.util.Set;
28
29public class TinyMappingsWriter implements MappingsWriter {
30 private static final String VERSION_CONSTANT = "v1";
31 private static final Joiner TAB_JOINER = Joiner.on('\t');
32
33 //Possibly add a gui or a way to select the namespaces when exporting from the gui
34 public static final TinyMappingsWriter INSTANCE = new TinyMappingsWriter("intermediary", "named");
35
36 // HACK: as of enigma 0.13.1, some fields seem to appear duplicated?
37 private final Set<String> writtenLines = new HashSet<>();
38 private final String nameObf;
39 private final String nameDeobf;
40
41 public TinyMappingsWriter(String nameObf, String nameDeobf) {
42 this.nameObf = nameObf;
43 this.nameDeobf = nameDeobf;
44 }
45
46 @Override
47 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress, MappingSaveParameters saveParameters) {
48 try {
49 Files.deleteIfExists(path);
50 Files.createFile(path);
51 } catch (IOException e) {
52 e.printStackTrace();
53 }
54
55 try (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
56 writeLine(writer, new String[]{VERSION_CONSTANT, nameObf, nameDeobf});
57
58 Lists.newArrayList(mappings).stream()
59 .map(EntryTreeNode::getEntry).sorted(Comparator.comparing(Object::toString))
60 .forEach(entry -> writeEntry(writer, mappings, entry));
61 } catch (IOException e) {
62 e.printStackTrace();
63 }
64 }
65
66 private void writeEntry(Writer writer, EntryTree<EntryMapping> mappings, Entry<?> entry) {
67 EntryTreeNode<EntryMapping> node = mappings.findNode(entry);
68 if (node == null) {
69 return;
70 }
71
72 Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE);
73
74 EntryMapping mapping = mappings.get(entry);
75 if (mapping != null && !entry.getName().equals(mapping.getTargetName())) {
76 if (entry instanceof ClassEntry) {
77 writeClass(writer, (ClassEntry) entry, translator);
78 } else if (entry instanceof FieldEntry) {
79 writeLine(writer, serializeEntry(entry, mapping.getTargetName()));
80 } else if (entry instanceof MethodEntry) {
81 writeLine(writer, serializeEntry(entry, mapping.getTargetName()));
82 }
83 }
84
85 writeChildren(writer, mappings, node);
86 }
87
88 private void writeChildren(Writer writer, EntryTree<EntryMapping> mappings, EntryTreeNode<EntryMapping> node) {
89 node.getChildren().stream()
90 .filter(e -> e instanceof FieldEntry).sorted()
91 .forEach(child -> writeEntry(writer, mappings, child));
92
93 node.getChildren().stream()
94 .filter(e -> e instanceof MethodEntry).sorted()
95 .forEach(child -> writeEntry(writer, mappings, child));
96
97 node.getChildren().stream()
98 .filter(e -> e instanceof ClassEntry).sorted()
99 .forEach(child -> writeEntry(writer, mappings, child));
100 }
101
102 private void writeClass(Writer writer, ClassEntry entry, Translator translator) {
103 ClassEntry translatedEntry = translator.translate(entry);
104
105 String obfClassName = entry.getFullName();
106 String deobfClassName = translatedEntry.getFullName();
107 writeLine(writer, new String[]{"CLASS", obfClassName, deobfClassName});
108 }
109
110 private void writeLine(Writer writer, String[] data) {
111 try {
112 String line = TAB_JOINER.join(data) + "\n";
113 if (writtenLines.add(line)) {
114 writer.write(line);
115 }
116 } catch (IOException e) {
117 throw new RuntimeException(e);
118 }
119 }
120
121 private String[] serializeEntry(Entry<?> entry, String... extraFields) {
122 String[] data = null;
123
124 if (entry instanceof FieldEntry) {
125 data = new String[4 + extraFields.length];
126 data[0] = "FIELD";
127 data[1] = entry.getContainingClass().getFullName();
128 data[2] = ((FieldEntry) entry).getDesc().toString();
129 data[3] = entry.getName();
130 } else if (entry instanceof MethodEntry) {
131 data = new String[4 + extraFields.length];
132 data[0] = "METHOD";
133 data[1] = entry.getContainingClass().getFullName();
134 data[2] = ((MethodEntry) entry).getDesc().toString();
135 data[3] = entry.getName();
136 } else if (entry instanceof ClassEntry) {
137 data = new String[2 + extraFields.length];
138 data[0] = "CLASS";
139 data[1] = ((ClassEntry) entry).getFullName();
140 }
141
142 if (data != null) {
143 System.arraycopy(extraFields, 0, data, data.length - extraFields.length, extraFields.length);
144 }
145
146 return data;
147 }
148}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyV2Reader.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyV2Reader.java
deleted file mode 100644
index d81cbdb..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyV2Reader.java
+++ /dev/null
@@ -1,295 +0,0 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import cuchaz.enigma.ProgressListener;
4import cuchaz.enigma.throwables.MappingParseException;
5import cuchaz.enigma.translation.mapping.EntryMapping;
6import cuchaz.enigma.translation.mapping.MappingPair;
7import cuchaz.enigma.translation.mapping.MappingSaveParameters;
8import cuchaz.enigma.translation.mapping.tree.EntryTree;
9import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
10import cuchaz.enigma.translation.representation.MethodDescriptor;
11import cuchaz.enigma.translation.representation.TypeDescriptor;
12import cuchaz.enigma.translation.representation.entry.ClassEntry;
13import cuchaz.enigma.translation.representation.entry.Entry;
14import cuchaz.enigma.translation.representation.entry.FieldEntry;
15import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
16import cuchaz.enigma.translation.representation.entry.MethodEntry;
17import cuchaz.enigma.utils.I18n;
18
19import java.io.IOException;
20import java.nio.charset.StandardCharsets;
21import java.nio.file.Files;
22import java.nio.file.Path;
23import java.util.BitSet;
24import java.util.List;
25
26final class TinyV2Reader implements MappingsReader {
27
28 private static final String MINOR_VERSION = "0";
29 // 0 indent
30 private static final int IN_HEADER = 0;
31 private static final int IN_CLASS = IN_HEADER + 1;
32 // 1 indent
33 private static final int IN_METHOD = IN_CLASS + 1;
34 private static final int IN_FIELD = IN_METHOD + 1;
35 // 2 indent
36 private static final int IN_PARAMETER = IN_FIELD + 1;
37 // general properties
38 private static final int STATE_SIZE = IN_PARAMETER + 1;
39 private static final int[] INDENT_CLEAR_START = {IN_HEADER, IN_METHOD, IN_PARAMETER, STATE_SIZE};
40
41 @Override
42 public EntryTree<EntryMapping> read(Path path, ProgressListener progress, MappingSaveParameters saveParameters) throws IOException, MappingParseException {
43 return read(path, Files.readAllLines(path, StandardCharsets.UTF_8), progress);
44 }
45
46 private EntryTree<EntryMapping> read(Path path, List<String> lines, ProgressListener progress) throws MappingParseException {
47 EntryTree<EntryMapping> mappings = new HashEntryTree<>();
48
49 progress.init(lines.size(), I18n.translate("progress.mappings.tiny_v2.loading"));
50
51 BitSet state = new BitSet(STATE_SIZE);
52 @SuppressWarnings({"unchecked", "rawtypes"})
53 MappingPair<? extends Entry<?>, RawEntryMapping>[] holds = new MappingPair[STATE_SIZE];
54 boolean escapeNames = false;
55
56 for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) {
57 try {
58 progress.step(lineNumber, "");
59 String line = lines.get(lineNumber);
60
61 int indent = 0;
62 while (line.charAt(indent) == '\t')
63 indent++;
64
65 String[] parts = line.substring(indent).split("\t", -1);
66 if (parts.length == 0 || indent >= INDENT_CLEAR_START.length)
67 throw new IllegalArgumentException("Invalid format");
68
69 // clean and register stuff in stack
70 for (int i = INDENT_CLEAR_START[indent]; i < STATE_SIZE; i++) {
71 state.clear(i);
72 if (holds[i] != null) {
73 RawEntryMapping mapping = holds[i].getMapping();
74 if (mapping != null) {
75 EntryMapping baked = mapping.bake();
76 if (baked != null) {
77 mappings.insert(holds[i].getEntry(), baked);
78 }
79 }
80 holds[i] = null;
81 }
82 }
83
84 switch (indent) {
85 case 0:
86 switch (parts[0]) {
87 case "tiny": // header
88 if (lineNumber != 0) {
89 throw new IllegalArgumentException("Header can only be on the first line");
90 }
91 if (parts.length < 5) {
92 throw new IllegalArgumentException("Not enough header columns, needs at least 5");
93 }
94 if (!"2".equals(parts[1]) || !MINOR_VERSION.equals(parts[2])) {
95 throw new IllegalArgumentException("Unsupported TinyV2 version, requires major " + "2" + " and minor " + MINOR_VERSION + "");
96 }
97 state.set(IN_HEADER);
98 break;
99 case "c": // class
100 state.set(IN_CLASS);
101 holds[IN_CLASS] = parseClass(parts, escapeNames);
102 break;
103 default:
104 unsupportKey(parts);
105 }
106
107 break;
108 case 1:
109 if (state.get(IN_HEADER)) {
110 if (parts[0].equals("esacpe-names")) {
111 escapeNames = true;
112 }
113
114 break;
115 }
116
117 if (state.get(IN_CLASS)) {
118 switch (parts[0]) {
119 case "m": // method
120 state.set(IN_METHOD);
121 holds[IN_METHOD] = parseMethod(holds[IN_CLASS], parts, escapeNames);
122 break;
123 case "f": // field
124 state.set(IN_FIELD);
125 holds[IN_FIELD] = parseField(holds[IN_CLASS], parts, escapeNames);
126 break;
127 case "c": // class javadoc
128 addJavadoc(holds[IN_CLASS], parts);
129 break;
130 default:
131 unsupportKey(parts);
132 }
133 break;
134 }
135
136 unsupportKey(parts);
137 case 2:
138 if (state.get(IN_METHOD)) {
139 switch (parts[0]) {
140 case "p": // parameter
141 state.set(IN_PARAMETER);
142 holds[IN_PARAMETER] = parseArgument(holds[IN_METHOD], parts, escapeNames);
143 break;
144 case "v": // local variable
145 // TODO add local var mapping
146 break;
147 case "c": // method javadoc
148 addJavadoc(holds[IN_METHOD], parts);
149 break;
150 default:
151 unsupportKey(parts);
152 }
153 break;
154 }
155
156 if (state.get(IN_FIELD)) {
157 switch (parts[0]) {
158 case "c": // field javadoc
159 addJavadoc(holds[IN_FIELD], parts);
160 break;
161 default:
162 unsupportKey(parts);
163 }
164 break;
165 }
166 unsupportKey(parts);
167 case 3:
168 if (state.get(IN_PARAMETER)) {
169 switch (parts[0]) {
170 case "c":
171 addJavadoc(holds[IN_PARAMETER], parts);
172 break;
173 default:
174 unsupportKey(parts);
175 }
176 break;
177 }
178 unsupportKey(parts);
179 default:
180 unsupportKey(parts);
181 }
182
183 } catch (Throwable t) {
184 t.printStackTrace();
185 throw new MappingParseException(path::toString, lineNumber + 1, t.toString());
186 }
187 }
188
189 return mappings;
190 }
191
192 private void unsupportKey(String[] parts) {
193 throw new IllegalArgumentException("Unsupported key " + parts[0]);
194 }
195
196 private void addJavadoc(MappingPair<? extends Entry, RawEntryMapping> pair, String[] parts) {
197 if (parts.length != 2) {
198 throw new IllegalArgumentException("Invalid javadoc declaration");
199 }
200
201 addJavadoc(pair, parts[1]);
202 }
203
204 private MappingPair<ClassEntry, RawEntryMapping> parseClass(String[] tokens, boolean escapeNames) {
205 ClassEntry obfuscatedEntry = new ClassEntry(unescapeOpt(tokens[1], escapeNames));
206 if (tokens.length <= 2)
207 return new MappingPair<>(obfuscatedEntry);
208 String token2 = unescapeOpt(tokens[2], escapeNames);
209 String mapping = token2.substring(token2.lastIndexOf('$') + 1);
210 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping));
211 }
212
213 private MappingPair<FieldEntry, RawEntryMapping> parseField(MappingPair<? extends Entry, RawEntryMapping> parent, String[] tokens, boolean escapeNames) {
214 ClassEntry ownerClass = (ClassEntry) parent.getEntry();
215 TypeDescriptor descriptor = new TypeDescriptor(unescapeOpt(tokens[1], escapeNames));
216
217 FieldEntry obfuscatedEntry = new FieldEntry(ownerClass, unescapeOpt(tokens[2], escapeNames), descriptor);
218 if (tokens.length <= 3)
219 return new MappingPair<>(obfuscatedEntry);
220 String mapping = unescapeOpt(tokens[3], escapeNames);
221 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping));
222 }
223
224 private MappingPair<MethodEntry, RawEntryMapping> parseMethod(MappingPair<? extends Entry, RawEntryMapping> parent, String[] tokens, boolean escapeNames) {
225 ClassEntry ownerClass = (ClassEntry) parent.getEntry();
226 MethodDescriptor descriptor = new MethodDescriptor(unescapeOpt(tokens[1], escapeNames));
227
228 MethodEntry obfuscatedEntry = new MethodEntry(ownerClass, unescapeOpt(tokens[2], escapeNames), descriptor);
229 if (tokens.length <= 3)
230 return new MappingPair<>(obfuscatedEntry);
231 String mapping = unescapeOpt(tokens[3], escapeNames);
232 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping));
233 }
234
235
236
237 private void addJavadoc(MappingPair<? extends Entry, RawEntryMapping> pair, String javadoc) {
238 RawEntryMapping mapping = pair.getMapping();
239 if (mapping == null) {
240 throw new IllegalArgumentException("Javadoc requires a mapping in enigma!");
241 }
242 mapping.addJavadocLine(unescape(javadoc));
243 }
244
245
246
247 private MappingPair<LocalVariableEntry, RawEntryMapping> parseArgument(MappingPair<? extends Entry, RawEntryMapping> parent, String[] tokens, boolean escapeNames) {
248 MethodEntry ownerMethod = (MethodEntry) parent.getEntry();
249 int variableIndex = Integer.parseInt(tokens[1]);
250
251 // tokens[2] is the useless obf name
252
253 LocalVariableEntry obfuscatedEntry = new LocalVariableEntry(ownerMethod, variableIndex, "", true, null);
254 if (tokens.length <= 3)
255 return new MappingPair<>(obfuscatedEntry);
256 String mapping = unescapeOpt(tokens[3], escapeNames);
257 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping));
258 }
259
260 private static final String TO_ESCAPE = "\\\n\r\0\t";
261 private static final String ESCAPED = "\\nr0t";
262
263 private static String unescapeOpt(String raw, boolean escapedStrings) {
264 return escapedStrings ? unescape(raw) : raw;
265 }
266
267 private static String unescape(String str) {
268 // copied from matcher, lazy!
269 int pos = str.indexOf('\\');
270 if (pos < 0) return str;
271
272 StringBuilder ret = new StringBuilder(str.length() - 1);
273 int start = 0;
274
275 do {
276 ret.append(str, start, pos);
277 pos++;
278 int type;
279
280 if (pos >= str.length()) {
281 throw new RuntimeException("incomplete escape sequence at the end");
282 } else if ((type = ESCAPED.indexOf(str.charAt(pos))) < 0) {
283 throw new RuntimeException("invalid escape character: \\" + str.charAt(pos));
284 } else {
285 ret.append(TO_ESCAPE.charAt(type));
286 }
287
288 start = pos + 1;
289 } while ((pos = str.indexOf('\\', start)) >= 0);
290
291 ret.append(str, start, str.length());
292
293 return ret.toString();
294 }
295}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyV2Writer.java b/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyV2Writer.java
deleted file mode 100644
index 95e04c3..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/serde/TinyV2Writer.java
+++ /dev/null
@@ -1,169 +0,0 @@
1package cuchaz.enigma.translation.mapping.serde;
2
3import com.google.common.base.Strings;
4import cuchaz.enigma.ProgressListener;
5import cuchaz.enigma.translation.mapping.EntryMap;
6import cuchaz.enigma.translation.mapping.EntryMapping;
7import cuchaz.enigma.translation.mapping.MappingDelta;
8import cuchaz.enigma.translation.mapping.MappingSaveParameters;
9import cuchaz.enigma.translation.mapping.tree.EntryTree;
10import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
11import cuchaz.enigma.translation.representation.entry.ClassEntry;
12import cuchaz.enigma.translation.representation.entry.Entry;
13import cuchaz.enigma.translation.representation.entry.FieldEntry;
14import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
15import cuchaz.enigma.translation.representation.entry.MethodEntry;
16import cuchaz.enigma.utils.LFPrintWriter;
17
18import java.io.IOException;
19import java.io.PrintWriter;
20import java.nio.file.Files;
21import java.nio.file.Path;
22import java.util.Deque;
23import java.util.LinkedList;
24import java.util.List;
25import java.util.stream.Collectors;
26import java.util.stream.StreamSupport;
27
28public final class TinyV2Writer implements MappingsWriter {
29
30 private static final String MINOR_VERSION = "0";
31 private final String obfHeader;
32 private final String deobfHeader;
33
34 public TinyV2Writer(String obfHeader, String deobfHeader) {
35 this.obfHeader = obfHeader;
36 this.deobfHeader = deobfHeader;
37 }
38
39 @Override
40 public void write(EntryTree<EntryMapping> mappings, MappingDelta<EntryMapping> delta, Path path, ProgressListener progress, MappingSaveParameters parameters) {
41 List<EntryTreeNode<EntryMapping>> classes = StreamSupport.stream(mappings.spliterator(), false).filter(node -> node.getEntry() instanceof ClassEntry).collect(Collectors.toList());
42
43 try (PrintWriter writer = new LFPrintWriter(Files.newBufferedWriter(path))) {
44 writer.println("tiny\t2\t" + MINOR_VERSION + "\t" + obfHeader + "\t" + deobfHeader);
45
46 // no escape names
47
48 for (EntryTreeNode<EntryMapping> node : classes) {
49 writeClass(writer, node, mappings);
50 }
51 } catch (IOException ex) {
52 ex.printStackTrace(); // TODO add some better logging system
53 }
54 }
55
56 private void writeClass(PrintWriter writer, EntryTreeNode<EntryMapping> node, EntryMap<EntryMapping> tree) {
57 writer.print("c\t");
58 ClassEntry classEntry = (ClassEntry) node.getEntry();
59 String fullName = classEntry.getFullName();
60 writer.print(fullName);
61 Deque<String> parts = new LinkedList<>();
62 do {
63 EntryMapping mapping = tree.get(classEntry);
64 if (mapping != null) {
65 parts.addFirst(mapping.getTargetName());
66 } else {
67 parts.addFirst(classEntry.getName());
68 }
69 classEntry = classEntry.getOuterClass();
70 } while (classEntry != null);
71
72 String mappedName = String.join("$", parts);
73
74 writer.print("\t");
75
76 writer.print(mappedName); // todo escaping when we have v2 fixed later
77
78 writer.println();
79
80 writeComment(writer, node.getValue(), 1);
81
82 for (EntryTreeNode<EntryMapping> child : node.getChildNodes()) {
83 Entry entry = child.getEntry();
84 if (entry instanceof FieldEntry) {
85 writeField(writer, child);
86 } else if (entry instanceof MethodEntry) {
87 writeMethod(writer, child);
88 }
89 }
90 }
91
92 private void writeMethod(PrintWriter writer, EntryTreeNode<EntryMapping> node) {
93 writer.print(indent(1));
94 writer.print("m\t");
95 writer.print(((MethodEntry) node.getEntry()).getDesc().toString());
96 writer.print("\t");
97 writer.print(node.getEntry().getName());
98 writer.print("\t");
99 EntryMapping mapping = node.getValue();
100 if (mapping == null) {
101 writer.println(node.getEntry().getName()); // todo fix v2 name inference
102 } else {
103 writer.println(mapping.getTargetName());
104
105 writeComment(writer, mapping, 2);
106 }
107
108 for (EntryTreeNode<EntryMapping> child : node.getChildNodes()) {
109 Entry entry = child.getEntry();
110 if (entry instanceof LocalVariableEntry) {
111 writeParameter(writer, child);
112 }
113 // TODO write actual local variables
114 }
115 }
116
117 private void writeField(PrintWriter writer, EntryTreeNode<EntryMapping> node) {
118 if (node.getValue() == null)
119 return; // Shortcut
120
121 writer.print(indent(1));
122 writer.print("f\t");
123 writer.print(((FieldEntry) node.getEntry()).getDesc().toString());
124 writer.print("\t");
125 writer.print(node.getEntry().getName());
126 writer.print("\t");
127 EntryMapping mapping = node.getValue();
128 if (mapping == null) {
129 writer.println(node.getEntry().getName()); // todo fix v2 name inference
130 } else {
131 writer.println(mapping.getTargetName());
132
133 writeComment(writer, mapping, 2);
134 }
135 }
136
137 private void writeParameter(PrintWriter writer, EntryTreeNode<EntryMapping> node) {
138 if (node.getValue() == null)
139 return; // Shortcut
140
141 writer.print(indent(2));
142 writer.print("p\t");
143 writer.print(((LocalVariableEntry) node.getEntry()).getIndex());
144 writer.print("\t");
145 writer.print(node.getEntry().getName());
146 writer.print("\t");
147 EntryMapping mapping = node.getValue();
148 if (mapping == null) {
149 writer.println(); // todo ???
150 } else {
151 writer.println(mapping.getTargetName());
152
153 writeComment(writer, mapping, 3);
154 }
155 }
156
157 private void writeComment(PrintWriter writer, EntryMapping mapping, int indent) {
158 if (mapping != null && mapping.getJavadoc() != null) {
159 writer.print(indent(indent));
160 writer.print("c\t");
161 writer.print(MappingHelper.escape(mapping.getJavadoc()));
162 writer.println();
163 }
164 }
165
166 private String indent(int level) {
167 return Strings.repeat("\t", level);
168 }
169}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java
deleted file mode 100644
index 255fa5f..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/tree/DeltaTrackingTree.java
+++ /dev/null
@@ -1,110 +0,0 @@
1package cuchaz.enigma.translation.mapping.tree;
2
3import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.mapping.EntryMap;
5import cuchaz.enigma.translation.mapping.EntryMapping;
6import cuchaz.enigma.translation.mapping.EntryResolver;
7import cuchaz.enigma.translation.mapping.MappingDelta;
8import cuchaz.enigma.translation.representation.entry.Entry;
9
10import javax.annotation.Nullable;
11import java.util.Collection;
12import java.util.Iterator;
13import java.util.stream.Stream;
14
15public class DeltaTrackingTree<T> implements EntryTree<T> {
16 private final EntryTree<T> delegate;
17
18 private EntryTree<T> deltaReference;
19 private EntryTree<Object> changes = new HashEntryTree<>();
20
21 public DeltaTrackingTree(EntryTree<T> delegate) {
22 this.delegate = delegate;
23 this.deltaReference = new HashEntryTree<>(delegate);
24 }
25
26 public DeltaTrackingTree() {
27 this(new HashEntryTree<>());
28 }
29
30 @Override
31 public void insert(Entry<?> entry, T value) {
32 trackChange(entry);
33 delegate.insert(entry, value);
34 }
35
36 @Nullable
37 @Override
38 public T remove(Entry<?> entry) {
39 trackChange(entry);
40 return delegate.remove(entry);
41 }
42
43 public void trackChange(Entry<?> entry) {
44 changes.insert(entry, MappingDelta.PLACEHOLDER);
45 }
46
47 @Nullable
48 @Override
49 public T get(Entry<?> entry) {
50 return delegate.get(entry);
51 }
52
53 @Override
54 public Collection<Entry<?>> getChildren(Entry<?> entry) {
55 return delegate.getChildren(entry);
56 }
57
58 @Override
59 public Collection<Entry<?>> getSiblings(Entry<?> entry) {
60 return delegate.getSiblings(entry);
61 }
62
63 @Nullable
64 @Override
65 public EntryTreeNode<T> findNode(Entry<?> entry) {
66 return delegate.findNode(entry);
67 }
68
69 @Override
70 public Stream<EntryTreeNode<T>> getRootNodes() {
71 return delegate.getRootNodes();
72 }
73
74 @Override
75 public DeltaTrackingTree<T> translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
76 DeltaTrackingTree<T> translatedTree = new DeltaTrackingTree<>(delegate.translate(translator, resolver, mappings));
77 translatedTree.changes = changes.translate(translator, resolver, mappings);
78 return translatedTree;
79 }
80
81 @Override
82 public Stream<Entry<?>> getAllEntries() {
83 return delegate.getAllEntries();
84 }
85
86 @Override
87 public boolean isEmpty() {
88 return delegate.isEmpty();
89 }
90
91 @Override
92 public Iterator<EntryTreeNode<T>> iterator() {
93 return delegate.iterator();
94 }
95
96 public MappingDelta<T> takeDelta() {
97 MappingDelta<T> delta = new MappingDelta<>(deltaReference, changes);
98 resetDelta();
99 return delta;
100 }
101
102 private void resetDelta() {
103 deltaReference = new HashEntryTree<>(delegate);
104 changes = new HashEntryTree<>();
105 }
106
107 public boolean isDirty() {
108 return !changes.isEmpty();
109 }
110}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java
deleted file mode 100644
index daaefcc..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTree.java
+++ /dev/null
@@ -1,26 +0,0 @@
1package cuchaz.enigma.translation.mapping.tree;
2
3import cuchaz.enigma.translation.Translatable;
4import cuchaz.enigma.translation.Translator;
5import cuchaz.enigma.translation.mapping.EntryMap;
6import cuchaz.enigma.translation.mapping.EntryMapping;
7import cuchaz.enigma.translation.mapping.EntryResolver;
8import cuchaz.enigma.translation.representation.entry.Entry;
9
10import javax.annotation.Nullable;
11import java.util.Collection;
12import java.util.stream.Stream;
13
14public interface EntryTree<T> extends EntryMap<T>, Iterable<EntryTreeNode<T>>, Translatable {
15 Collection<Entry<?>> getChildren(Entry<?> entry);
16
17 Collection<Entry<?>> getSiblings(Entry<?> entry);
18
19 @Nullable
20 EntryTreeNode<T> findNode(Entry<?> entry);
21
22 Stream<EntryTreeNode<T>> getRootNodes();
23
24 @Override
25 EntryTree<T> translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings);
26}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTreeNode.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTreeNode.java
deleted file mode 100644
index affcd50..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/tree/EntryTreeNode.java
+++ /dev/null
@@ -1,40 +0,0 @@
1package cuchaz.enigma.translation.mapping.tree;
2
3import cuchaz.enigma.translation.representation.entry.Entry;
4
5import javax.annotation.Nullable;
6import java.util.ArrayList;
7import java.util.Collection;
8import java.util.stream.Collectors;
9
10public interface EntryTreeNode<T> {
11 @Nullable
12 T getValue();
13
14 Entry<?> getEntry();
15
16 boolean isEmpty();
17
18 Collection<Entry<?>> getChildren();
19
20 Collection<? extends EntryTreeNode<T>> getChildNodes();
21
22 default Collection<? extends EntryTreeNode<T>> getNodesRecursively() {
23 Collection<EntryTreeNode<T>> nodes = new ArrayList<>();
24 nodes.add(this);
25 for (EntryTreeNode<T> node : getChildNodes()) {
26 nodes.addAll(node.getNodesRecursively());
27 }
28 return nodes;
29 }
30
31 default Collection<Entry<?>> getChildrenRecursively() {
32 return getNodesRecursively().stream()
33 .map(EntryTreeNode::getEntry)
34 .collect(Collectors.toList());
35 }
36
37 default boolean hasValue() {
38 return getValue() != null;
39 }
40}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java
deleted file mode 100644
index 570941c..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java
+++ /dev/null
@@ -1,188 +0,0 @@
1package cuchaz.enigma.translation.mapping.tree;
2
3import cuchaz.enigma.translation.Translator;
4import cuchaz.enigma.translation.mapping.EntryMap;
5import cuchaz.enigma.translation.mapping.EntryMapping;
6import cuchaz.enigma.translation.mapping.EntryResolver;
7import cuchaz.enigma.translation.representation.entry.Entry;
8
9import javax.annotation.Nullable;
10import java.util.*;
11import java.util.function.Function;
12import java.util.stream.Stream;
13import java.util.stream.StreamSupport;
14
15public class HashEntryTree<T> implements EntryTree<T> {
16 private final Map<Entry<?>, HashTreeNode<T>> root = new HashMap<>();
17
18 public HashEntryTree() {
19 }
20
21 public HashEntryTree(EntryTree<T> tree) {
22 for (EntryTreeNode<T> node : tree) {
23 insert(node.getEntry(), node.getValue());
24 }
25 }
26
27 @Override
28 public void insert(Entry<?> entry, T value) {
29 List<HashTreeNode<T>> path = computePath(entry, true);
30 path.get(path.size() - 1).putValue(value);
31 if (value == null) {
32 removeDeadAlong(path);
33 }
34 }
35
36 @Override
37 @Nullable
38 public T remove(Entry<?> entry) {
39 List<HashTreeNode<T>> path = computePath(entry, false);
40 if (path.isEmpty()) {
41 return null;
42 }
43
44 T value = path.get(path.size() - 1).removeValue();
45
46 removeDeadAlong(path);
47
48 return value;
49 }
50
51 @Override
52 @Nullable
53 public T get(Entry<?> entry) {
54 HashTreeNode<T> node = findNode(entry);
55 if (node == null) {
56 return null;
57 }
58 return node.getValue();
59 }
60
61 @Override
62 public boolean contains(Entry<?> entry) {
63 return get(entry) != null;
64 }
65
66 @Override
67 public Collection<Entry<?>> getChildren(Entry<?> entry) {
68 HashTreeNode<T> leaf = findNode(entry);
69 if (leaf == null) {
70 return Collections.emptyList();
71 }
72 return leaf.getChildren();
73 }
74
75 @Override
76 public Collection<Entry<?>> getSiblings(Entry<?> entry) {
77 Entry<?> parent = entry.getParent();
78 if (parent == null) {
79 return getSiblings(entry, root.keySet());
80 }
81 return getSiblings(entry, getChildren(parent));
82 }
83
84 private Collection<Entry<?>> getSiblings(Entry<?> entry, Collection<Entry<?>> generation) {
85 Set<Entry<?>> siblings = new HashSet<>(generation);
86 siblings.remove(entry);
87 return siblings;
88 }
89
90 @Override
91 @Nullable
92 public HashTreeNode<T> findNode(Entry<?> target) {
93 List<Entry<?>> parentChain = target.getAncestry();
94 if (parentChain.isEmpty()) {
95 return null;
96 }
97
98 HashTreeNode<T> node = root.get(parentChain.get(0));
99 for (int i = 1; i < parentChain.size(); i++) {
100 if (node == null) {
101 return null;
102 }
103 node = node.getChild(parentChain.get(i));
104 }
105
106 return node;
107 }
108
109 private List<HashTreeNode<T>> computePath(Entry<?> target, boolean make) {
110 List<Entry<?>> ancestry = target.getAncestry();
111 if (ancestry.isEmpty()) {
112 return Collections.emptyList();
113 }
114
115 List<HashTreeNode<T>> path = new ArrayList<>(ancestry.size());
116
117 Entry<?> rootEntry = ancestry.get(0);
118 HashTreeNode<T> node = make ? root.computeIfAbsent(rootEntry, HashTreeNode::new) : root.get(rootEntry);
119 if (node == null) {
120 return Collections.emptyList();
121 }
122
123 path.add(node);
124
125 for (int i = 1; i < ancestry.size(); i++) {
126 Entry<?> ancestor = ancestry.get(i);
127 node = make ? node.computeChild(ancestor) : node.getChild(ancestor);
128 if (node == null) {
129 return Collections.emptyList();
130 }
131
132 path.add(node);
133 }
134
135 return path;
136 }
137
138 private void removeDeadAlong(List<HashTreeNode<T>> path) {
139 for (int i = path.size() - 1; i >= 0; i--) {
140 HashTreeNode<T> node = path.get(i);
141 if (node.isEmpty()) {
142 if (i > 0) {
143 HashTreeNode<T> parentNode = path.get(i - 1);
144 parentNode.remove(node.getEntry());
145 } else {
146 root.remove(node.getEntry());
147 }
148 } else {
149 break;
150 }
151 }
152 }
153
154 @Override
155 public Iterator<EntryTreeNode<T>> iterator() {
156 Collection<EntryTreeNode<T>> nodes = new ArrayList<>();
157 for (EntryTreeNode<T> node : root.values()) {
158 nodes.addAll(node.getNodesRecursively());
159 }
160 return nodes.iterator();
161 }
162
163 @Override
164 public Stream<Entry<?>> getAllEntries() {
165 return StreamSupport.stream(spliterator(), false)
166 .filter(EntryTreeNode::hasValue)
167 .map(EntryTreeNode::getEntry);
168 }
169
170 @Override
171 public Stream<EntryTreeNode<T>> getRootNodes() {
172 return root.values().stream().map(Function.identity());
173 }
174
175 @Override
176 public boolean isEmpty() {
177 return root.isEmpty();
178 }
179
180 @Override
181 public HashEntryTree<T> translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
182 HashEntryTree<T> translatedTree = new HashEntryTree<>();
183 for (EntryTreeNode<T> node : this) {
184 translatedTree.insert(translator.translate(node.getEntry()), node.getValue());
185 }
186 return translatedTree;
187 }
188}
diff --git a/src/main/java/cuchaz/enigma/translation/mapping/tree/HashTreeNode.java b/src/main/java/cuchaz/enigma/translation/mapping/tree/HashTreeNode.java
deleted file mode 100644
index 0a990bd..0000000
--- a/src/main/java/cuchaz/enigma/translation/mapping/tree/HashTreeNode.java
+++ /dev/null
@@ -1,75 +0,0 @@
1package cuchaz.enigma.translation.mapping.tree;
2
3import cuchaz.enigma.translation.representation.entry.Entry;
4
5import javax.annotation.Nonnull;
6import javax.annotation.Nullable;
7import java.util.Collection;
8import java.util.HashMap;
9import java.util.Iterator;
10import java.util.Map;
11
12public class HashTreeNode<T> implements EntryTreeNode<T>, Iterable<HashTreeNode<T>> {
13 private final Entry<?> entry;
14 private final Map<Entry<?>, HashTreeNode<T>> children = new HashMap<>();
15 private T value;
16
17 HashTreeNode(Entry<?> entry) {
18 this.entry = entry;
19 }
20
21 void putValue(T value) {
22 this.value = value;
23 }
24
25 T removeValue() {
26 T value = this.value;
27 this.value = null;
28 return value;
29 }
30
31 @Nullable
32 HashTreeNode<T> getChild(Entry<?> entry) {
33 return children.get(entry);
34 }
35
36 @Nonnull
37 HashTreeNode<T> computeChild(Entry<?> entry) {
38 return children.computeIfAbsent(entry, HashTreeNode::new);
39 }
40
41 void remove(Entry<?> entry) {
42 children.remove(entry);
43 }
44
45 @Override
46 @Nullable
47 public T getValue() {
48 return value;
49 }
50
51 @Override
52 public Entry<?> getEntry() {
53 return entry;
54 }
55
56 @Override
57 public boolean isEmpty() {
58 return children.isEmpty() && value == null;
59 }
60
61 @Override
62 public Collection<Entry<?>> getChildren() {
63 return children.keySet();
64 }
65
66 @Override
67 public Collection<? extends EntryTreeNode<T>> getChildNodes() {
68 return children.values();
69 }
70
71 @Override
72 public Iterator<HashTreeNode<T>> iterator() {
73 return children.values().iterator();
74 }
75}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java b/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java
deleted file mode 100644
index b280eef..0000000
--- a/src/main/java/cuchaz/enigma/translation/representation/AccessFlags.java
+++ /dev/null
@@ -1,116 +0,0 @@
1package cuchaz.enigma.translation.representation;
2
3import cuchaz.enigma.analysis.Access;
4import org.objectweb.asm.Opcodes;
5
6import java.lang.reflect.Modifier;
7
8public class AccessFlags {
9 public static final AccessFlags PRIVATE = new AccessFlags(Opcodes.ACC_PRIVATE);
10 public static final AccessFlags PUBLIC = new AccessFlags(Opcodes.ACC_PUBLIC);
11
12 private int flags;
13
14 public AccessFlags(int flags) {
15 this.flags = flags;
16 }
17
18 public boolean isPrivate() {
19 return Modifier.isPrivate(this.flags);
20 }
21
22 public boolean isProtected() {
23 return Modifier.isProtected(this.flags);
24 }
25
26 public boolean isPublic() {
27 return Modifier.isPublic(this.flags);
28 }
29
30 public boolean isSynthetic() {
31 return (this.flags & Opcodes.ACC_SYNTHETIC) != 0;
32 }
33
34 public boolean isStatic() {
35 return Modifier.isStatic(this.flags);
36 }
37
38 public boolean isEnum() {
39 return (flags & Opcodes.ACC_ENUM) != 0;
40 }
41
42 public boolean isBridge() {
43 return (flags & Opcodes.ACC_BRIDGE) != 0;
44 }
45
46 public boolean isFinal() {
47 return (flags & Opcodes.ACC_FINAL) != 0;
48 }
49
50 public boolean isInterface() {
51 return (flags & Opcodes.ACC_INTERFACE) != 0;
52 }
53
54 public AccessFlags setPrivate() {
55 this.setVisibility(Opcodes.ACC_PRIVATE);
56 return this;
57 }
58
59 public AccessFlags setProtected() {
60 this.setVisibility(Opcodes.ACC_PROTECTED);
61 return this;
62 }
63
64 public AccessFlags setPublic() {
65 this.setVisibility(Opcodes.ACC_PUBLIC);
66 return this;
67 }
68
69 public AccessFlags setBridge() {
70 flags |= Opcodes.ACC_BRIDGE;
71 return this;
72 }
73
74 @Deprecated
75 public AccessFlags setBridged() {
76 return setBridge();
77 }
78
79 public void setVisibility(int visibility) {
80 this.resetVisibility();
81 this.flags |= visibility;
82 }
83
84 private void resetVisibility() {
85 this.flags &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC);
86 }
87
88 public int getFlags() {
89 return this.flags;
90 }
91
92 @Override
93 public boolean equals(Object obj) {
94 return obj instanceof AccessFlags && ((AccessFlags) obj).flags == flags;
95 }
96
97 @Override
98 public int hashCode() {
99 return flags;
100 }
101
102 @Override
103 public String toString() {
104 StringBuilder builder = new StringBuilder(Access.get(this).toString().toLowerCase());
105 if (isStatic()) {
106 builder.append(" static");
107 }
108 if (isSynthetic()) {
109 builder.append(" synthetic");
110 }
111 if (isBridge()) {
112 builder.append(" bridge");
113 }
114 return builder.toString();
115 }
116}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/Lambda.java b/src/main/java/cuchaz/enigma/translation/representation/Lambda.java
deleted file mode 100644
index 63eb563..0000000
--- a/src/main/java/cuchaz/enigma/translation/representation/Lambda.java
+++ /dev/null
@@ -1,105 +0,0 @@
1package cuchaz.enigma.translation.representation;
2
3import cuchaz.enigma.translation.Translatable;
4import cuchaz.enigma.translation.Translator;
5import cuchaz.enigma.translation.mapping.EntryMap;
6import cuchaz.enigma.translation.mapping.EntryMapping;
7import cuchaz.enigma.translation.mapping.EntryResolver;
8import cuchaz.enigma.translation.mapping.ResolutionStrategy;
9import cuchaz.enigma.translation.representation.entry.ClassEntry;
10import cuchaz.enigma.translation.representation.entry.MethodEntry;
11import cuchaz.enigma.translation.representation.entry.ParentedEntry;
12
13import java.util.Objects;
14
15public class Lambda implements Translatable {
16 private final String invokedName;
17 private final MethodDescriptor invokedType;
18 private final MethodDescriptor samMethodType;
19 private final ParentedEntry<?> implMethod;
20 private final MethodDescriptor instantiatedMethodType;
21
22 public Lambda(String invokedName, MethodDescriptor invokedType, MethodDescriptor samMethodType, ParentedEntry<?> implMethod, MethodDescriptor instantiatedMethodType) {
23 this.invokedName = invokedName;
24 this.invokedType = invokedType;
25 this.samMethodType = samMethodType;
26 this.implMethod = implMethod;
27 this.instantiatedMethodType = instantiatedMethodType;
28 }
29
30 @Override
31 public Lambda translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
32 MethodEntry samMethod = new MethodEntry(getInterface(), invokedName, samMethodType);
33 EntryMapping samMethodMapping = resolveMapping(resolver, mappings, samMethod);
34
35 return new Lambda(
36 samMethodMapping != null ? samMethodMapping.getTargetName() : invokedName,
37 invokedType.translate(translator, resolver, mappings),
38 samMethodType.translate(translator, resolver, mappings),
39 implMethod.translate(translator, resolver, mappings),
40 instantiatedMethodType.translate(translator, resolver, mappings)
41 );
42 }
43
44 private EntryMapping resolveMapping(EntryResolver resolver, EntryMap<EntryMapping> mappings, MethodEntry methodEntry) {
45 for (MethodEntry entry : resolver.resolveEntry(methodEntry, ResolutionStrategy.RESOLVE_ROOT)) {
46 EntryMapping mapping = mappings.get(entry);
47 if (mapping != null) {
48 return mapping;
49 }
50 }
51 return null;
52 }
53
54 public ClassEntry getInterface() {
55 return invokedType.getReturnDesc().getTypeEntry();
56 }
57
58 public String getInvokedName() {
59 return invokedName;
60 }
61
62 public MethodDescriptor getInvokedType() {
63 return invokedType;
64 }
65
66 public MethodDescriptor getSamMethodType() {
67 return samMethodType;
68 }
69
70 public ParentedEntry<?> getImplMethod() {
71 return implMethod;
72 }
73
74 public MethodDescriptor getInstantiatedMethodType() {
75 return instantiatedMethodType;
76 }
77
78 @Override
79 public boolean equals(Object o) {
80 if (this == o) return true;
81 if (o == null || getClass() != o.getClass()) return false;
82 Lambda lambda = (Lambda) o;
83 return Objects.equals(invokedName, lambda.invokedName) &&
84 Objects.equals(invokedType, lambda.invokedType) &&
85 Objects.equals(samMethodType, lambda.samMethodType) &&
86 Objects.equals(implMethod, lambda.implMethod) &&
87 Objects.equals(instantiatedMethodType, lambda.instantiatedMethodType);
88 }
89
90 @Override
91 public int hashCode() {
92 return Objects.hash(invokedName, invokedType, samMethodType, implMethod, instantiatedMethodType);
93 }
94
95 @Override
96 public String toString() {
97 return "Lambda{" +
98 "invokedName='" + invokedName + '\'' +
99 ", invokedType=" + invokedType +
100 ", samMethodType=" + samMethodType +
101 ", implMethod=" + implMethod +
102 ", instantiatedMethodType=" + instantiatedMethodType +
103 '}';
104 }
105}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/MethodDescriptor.java b/src/main/java/cuchaz/enigma/translation/representation/MethodDescriptor.java
deleted file mode 100644
index 37a7014..0000000
--- a/src/main/java/cuchaz/enigma/translation/representation/MethodDescriptor.java
+++ /dev/null
@@ -1,132 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation.representation;
13
14import com.google.common.collect.Lists;
15import cuchaz.enigma.translation.Translatable;
16import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.mapping.EntryMapping;
18import cuchaz.enigma.translation.mapping.EntryResolver;
19import cuchaz.enigma.translation.mapping.EntryMap;
20import cuchaz.enigma.translation.representation.entry.ClassEntry;
21import cuchaz.enigma.utils.Utils;
22
23import java.util.ArrayList;
24import java.util.List;
25import java.util.function.Function;
26
27public class MethodDescriptor implements Translatable {
28
29 private List<TypeDescriptor> argumentDescs;
30 private TypeDescriptor returnDesc;
31
32 public MethodDescriptor(String desc) {
33 try {
34 this.argumentDescs = Lists.newArrayList();
35 int i = 0;
36 while (i < desc.length()) {
37 char c = desc.charAt(i);
38 if (c == '(') {
39 assert (this.argumentDescs.isEmpty());
40 assert (this.returnDesc == null);
41 i++;
42 } else if (c == ')') {
43 i++;
44 break;
45 } else {
46 String type = TypeDescriptor.parseFirst(desc.substring(i));
47 this.argumentDescs.add(new TypeDescriptor(type));
48 i += type.length();
49 }
50 }
51 this.returnDesc = new TypeDescriptor(TypeDescriptor.parseFirst(desc.substring(i)));
52 } catch (Exception ex) {
53 throw new IllegalArgumentException("Unable to parse method descriptor: " + desc, ex);
54 }
55 }
56
57 public MethodDescriptor(List<TypeDescriptor> argumentDescs, TypeDescriptor returnDesc) {
58 this.argumentDescs = argumentDescs;
59 this.returnDesc = returnDesc;
60 }
61
62 public List<TypeDescriptor> getArgumentDescs() {
63 return this.argumentDescs;
64 }
65
66 public TypeDescriptor getReturnDesc() {
67 return this.returnDesc;
68 }
69
70 @Override
71 public String toString() {
72 StringBuilder buf = new StringBuilder();
73 buf.append("(");
74 for (TypeDescriptor desc : this.argumentDescs) {
75 buf.append(desc);
76 }
77 buf.append(")");
78 buf.append(this.returnDesc);
79 return buf.toString();
80 }
81
82 public Iterable<TypeDescriptor> types() {
83 List<TypeDescriptor> descs = Lists.newArrayList();
84 descs.addAll(this.argumentDescs);
85 descs.add(this.returnDesc);
86 return descs;
87 }
88
89 @Override
90 public boolean equals(Object other) {
91 return other instanceof MethodDescriptor && equals((MethodDescriptor) other);
92 }
93
94 public boolean equals(MethodDescriptor other) {
95 return this.argumentDescs.equals(other.argumentDescs) && this.returnDesc.equals(other.returnDesc);
96 }
97
98 @Override
99 public int hashCode() {
100 return Utils.combineHashesOrdered(this.argumentDescs.hashCode(), this.returnDesc.hashCode());
101 }
102
103 public boolean hasClass(ClassEntry classEntry) {
104 for (TypeDescriptor desc : types()) {
105 if (desc.containsType() && desc.getTypeEntry().equals(classEntry)) {
106 return true;
107 }
108 }
109 return false;
110 }
111
112 public MethodDescriptor remap(Function<String, String> remapper) {
113 List<TypeDescriptor> argumentDescs = new ArrayList<>(this.argumentDescs.size());
114 for (TypeDescriptor desc : this.argumentDescs) {
115 argumentDescs.add(desc.remap(remapper));
116 }
117 return new MethodDescriptor(argumentDescs, returnDesc.remap(remapper));
118 }
119
120 @Override
121 public MethodDescriptor translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
122 List<TypeDescriptor> translatedArguments = new ArrayList<>(argumentDescs.size());
123 for (TypeDescriptor argument : argumentDescs) {
124 translatedArguments.add(translator.translate(argument));
125 }
126 return new MethodDescriptor(translatedArguments, translator.translate(returnDesc));
127 }
128
129 public boolean canConflictWith(MethodDescriptor descriptor) {
130 return descriptor.argumentDescs.equals(argumentDescs);
131 }
132}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/Signature.java b/src/main/java/cuchaz/enigma/translation/representation/Signature.java
deleted file mode 100644
index 424088a..0000000
--- a/src/main/java/cuchaz/enigma/translation/representation/Signature.java
+++ /dev/null
@@ -1,98 +0,0 @@
1package cuchaz.enigma.translation.representation;
2
3import cuchaz.enigma.bytecode.translators.TranslationSignatureVisitor;
4import cuchaz.enigma.translation.Translatable;
5import cuchaz.enigma.translation.Translator;
6import cuchaz.enigma.translation.mapping.EntryMap;
7import cuchaz.enigma.translation.mapping.EntryMapping;
8import cuchaz.enigma.translation.mapping.EntryResolver;
9import cuchaz.enigma.translation.representation.entry.ClassEntry;
10import org.objectweb.asm.signature.SignatureReader;
11import org.objectweb.asm.signature.SignatureVisitor;
12import org.objectweb.asm.signature.SignatureWriter;
13
14import java.util.function.Function;
15import java.util.regex.Pattern;
16
17public class Signature implements Translatable {
18 private static final Pattern OBJECT_PATTERN = Pattern.compile(".*:Ljava/lang/Object;:.*");
19
20 private final String signature;
21 private final boolean isType;
22
23 private Signature(String signature, boolean isType) {
24 if (signature != null && OBJECT_PATTERN.matcher(signature).matches()) {
25 signature = signature.replaceAll(":Ljava/lang/Object;:", "::");
26 }
27
28 this.signature = signature;
29 this.isType = isType;
30 }
31
32 public static Signature createTypedSignature(String signature) {
33 if (signature != null && !signature.isEmpty()) {
34 return new Signature(signature, true);
35 }
36 return new Signature(null, true);
37 }
38
39 public static Signature createSignature(String signature) {
40 if (signature != null && !signature.isEmpty()) {
41 return new Signature(signature, false);
42 }
43 return new Signature(null, false);
44 }
45
46 public String getSignature() {
47 return signature;
48 }
49
50 public boolean isType() {
51 return isType;
52 }
53
54 public Signature remap(Function<String, String> remapper) {
55 if (signature == null) {
56 return this;
57 }
58 SignatureWriter writer = new SignatureWriter();
59 SignatureVisitor visitor = new TranslationSignatureVisitor(remapper, writer);
60 if (isType) {
61 new SignatureReader(signature).acceptType(visitor);
62 } else {
63 new SignatureReader(signature).accept(visitor);
64 }
65 return new Signature(writer.toString(), isType);
66 }
67
68 @Override
69 public boolean equals(Object obj) {
70 if (obj instanceof Signature) {
71 Signature other = (Signature) obj;
72 return (other.signature == null && signature == null || other.signature != null
73 && signature != null && other.signature.equals(signature))
74 && other.isType == this.isType;
75 }
76 return false;
77 }
78
79 @Override
80 public int hashCode() {
81 int hash = (isType ? 1 : 0) << 16;
82 if (signature != null) {
83 hash |= signature.hashCode();
84 }
85
86 return hash;
87 }
88
89 @Override
90 public String toString() {
91 return signature;
92 }
93
94 @Override
95 public Translatable translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
96 return remap(name -> translator.translate(new ClassEntry(name)).getFullName());
97 }
98}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java b/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java
deleted file mode 100644
index f7ba849..0000000
--- a/src/main/java/cuchaz/enigma/translation/representation/TypeDescriptor.java
+++ /dev/null
@@ -1,268 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation.representation;
13
14import com.google.common.base.Preconditions;
15import com.google.common.collect.Maps;
16import cuchaz.enigma.translation.Translatable;
17import cuchaz.enigma.translation.Translator;
18import cuchaz.enigma.translation.mapping.EntryMapping;
19import cuchaz.enigma.translation.mapping.EntryResolver;
20import cuchaz.enigma.translation.mapping.EntryMap;
21import cuchaz.enigma.translation.representation.entry.ClassEntry;
22
23import java.util.Map;
24import java.util.function.Function;
25
26public class TypeDescriptor implements Translatable {
27
28 protected final String desc;
29
30 public TypeDescriptor(String desc) {
31 Preconditions.checkNotNull(desc, "Desc cannot be null");
32
33 // don't deal with generics
34 // this is just for raw jvm types
35 if (desc.charAt(0) == 'T' || desc.indexOf('<') >= 0 || desc.indexOf('>') >= 0) {
36 throw new IllegalArgumentException("don't use with generic types or templates: " + desc);
37 }
38
39 this.desc = desc;
40 }
41
42 public static String parseFirst(String in) {
43
44 if (in == null || in.length() <= 0) {
45 throw new IllegalArgumentException("No desc to parse, input is empty!");
46 }
47
48 // read one desc from the input
49
50 char c = in.charAt(0);
51
52 // first check for void
53 if (c == 'V') {
54 return "V";
55 }
56
57 // then check for primitives
58 Primitive primitive = Primitive.get(c);
59 if (primitive != null) {
60 return in.substring(0, 1);
61 }
62
63 // then check for classes
64 if (c == 'L') {
65 return readClass(in);
66 }
67
68 // then check for templates
69 if (c == 'T') {
70 return readClass(in);
71 }
72
73 // then check for arrays
74 int dim = countArrayDimension(in);
75 if (dim > 0) {
76 String arrayType = TypeDescriptor.parseFirst(in.substring(dim));
77 return in.substring(0, dim + arrayType.length());
78 }
79
80 throw new IllegalArgumentException("don't know how to parse: " + in);
81 }
82
83 private static int countArrayDimension(String in) {
84 int i = 0;
85 while (i < in.length() && in.charAt(i) == '[')
86 i++;
87 return i;
88 }
89
90 private static String readClass(String in) {
91 // read all the characters in the buffer until we hit a ';'
92 // include the parameters too
93 StringBuilder buf = new StringBuilder();
94 int depth = 0;
95 for (int i = 0; i < in.length(); i++) {
96 char c = in.charAt(i);
97 buf.append(c);
98
99 if (c == '<') {
100 depth++;
101 } else if (c == '>') {
102 depth--;
103 } else if (depth == 0 && c == ';') {
104 return buf.toString();
105 }
106 }
107 return null;
108 }
109
110 public static TypeDescriptor of(String name) {
111 return new TypeDescriptor("L" + name + ";");
112 }
113
114 @Override
115 public String toString() {
116 return this.desc;
117 }
118
119 public boolean isVoid() {
120 return this.desc.length() == 1 && this.desc.charAt(0) == 'V';
121 }
122
123 public boolean isPrimitive() {
124 return this.desc.length() == 1 && Primitive.get(this.desc.charAt(0)) != null;
125 }
126
127 public Primitive getPrimitive() {
128 if (!isPrimitive()) {
129 throw new IllegalStateException("not a primitive");
130 }
131 return Primitive.get(this.desc.charAt(0));
132 }
133
134 public boolean isType() {
135 return this.desc.charAt(0) == 'L' && this.desc.charAt(this.desc.length() - 1) == ';';
136 }
137
138 public ClassEntry getTypeEntry() {
139 if (isType()) {
140 String name = this.desc.substring(1, this.desc.length() - 1);
141
142 int pos = name.indexOf('<');
143 if (pos >= 0) {
144 // remove the parameters from the class name
145 name = name.substring(0, pos);
146 }
147
148 return new ClassEntry(name);
149
150 } else if (isArray() && getArrayType().isType()) {
151 return getArrayType().getTypeEntry();
152 } else {
153 throw new IllegalStateException("desc doesn't have a class");
154 }
155 }
156
157 public boolean isArray() {
158 return this.desc.charAt(0) == '[';
159 }
160
161 public int getArrayDimension() {
162 if (!isArray()) {
163 throw new IllegalStateException("not an array");
164 }
165 return countArrayDimension(this.desc);
166 }
167
168 public TypeDescriptor getArrayType() {
169 if (!isArray()) {
170 throw new IllegalStateException("not an array");
171 }
172 return new TypeDescriptor(this.desc.substring(getArrayDimension()));
173 }
174
175 public boolean containsType() {
176 return isType() || (isArray() && getArrayType().containsType());
177 }
178
179 @Override
180 public boolean equals(Object other) {
181 return other instanceof TypeDescriptor && equals((TypeDescriptor) other);
182 }
183
184 public boolean equals(TypeDescriptor other) {
185 return this.desc.equals(other.desc);
186 }
187
188 @Override
189 public int hashCode() {
190 return this.desc.hashCode();
191 }
192
193 public TypeDescriptor remap(Function<String, String> remapper) {
194 String desc = this.desc;
195 if (isType() || (isArray() && containsType())) {
196 String replacedName = remapper.apply(this.getTypeEntry().getFullName());
197 if (replacedName != null) {
198 if (this.isType()) {
199 desc = "L" + replacedName + ";";
200 } else {
201 desc = getArrayPrefix(this.getArrayDimension()) + "L" + replacedName + ";";
202 }
203 }
204 }
205 return new TypeDescriptor(desc);
206 }
207
208 private static String getArrayPrefix(int dimension) {
209 StringBuilder buf = new StringBuilder();
210 for (int i = 0; i < dimension; i++) {
211 buf.append("[");
212 }
213 return buf.toString();
214 }
215
216 public int getSize() {
217 switch (desc.charAt(0)) {
218 case 'J':
219 case 'D':
220 if (desc.length() == 1) {
221 return 2;
222 } else {
223 return 1;
224 }
225 default:
226 return 1;
227 }
228 }
229
230 @Override
231 public Translatable translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
232 return remap(name -> translator.translate(new ClassEntry(name)).getFullName());
233 }
234
235 public enum Primitive {
236 BYTE('B'),
237 CHARACTER('C'),
238 SHORT('S'),
239 INTEGER('I'),
240 LONG('J'),
241 FLOAT('F'),
242 DOUBLE('D'),
243 BOOLEAN('Z');
244
245 private static final Map<Character, Primitive> lookup;
246
247 static {
248 lookup = Maps.newTreeMap();
249 for (Primitive val : values()) {
250 lookup.put(val.getCode(), val);
251 }
252 }
253
254 private char code;
255
256 Primitive(char code) {
257 this.code = code;
258 }
259
260 public static Primitive get(char code) {
261 return lookup.get(code);
262 }
263
264 public char getCode() {
265 return this.code;
266 }
267 }
268}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java
deleted file mode 100644
index 6930765..0000000
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java
+++ /dev/null
@@ -1,93 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation.representation.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.translation.Translator;
16import cuchaz.enigma.translation.mapping.EntryMapping;
17import cuchaz.enigma.translation.representation.AccessFlags;
18import cuchaz.enigma.translation.representation.Signature;
19
20import javax.annotation.Nullable;
21import java.util.Arrays;
22
23public class ClassDefEntry extends ClassEntry implements DefEntry<ClassEntry> {
24 private final AccessFlags access;
25 private final Signature signature;
26 private final ClassEntry superClass;
27 private final ClassEntry[] interfaces;
28
29 public ClassDefEntry(String className, Signature signature, AccessFlags access, @Nullable ClassEntry superClass, ClassEntry[] interfaces) {
30 this(getOuterClass(className), getInnerName(className), signature, access, superClass, interfaces, null);
31 }
32
33 public ClassDefEntry(ClassEntry parent, String className, Signature signature, AccessFlags access, @Nullable ClassEntry superClass, ClassEntry[] interfaces) {
34 this(parent, className, signature, access, superClass, interfaces, null);
35 }
36
37 public ClassDefEntry(ClassEntry parent, String className, Signature signature, AccessFlags access, @Nullable ClassEntry superClass,
38 ClassEntry[] interfaces, String javadocs) {
39 super(parent, className, javadocs);
40 Preconditions.checkNotNull(signature, "Class signature cannot be null");
41 Preconditions.checkNotNull(access, "Class access cannot be null");
42
43 this.signature = signature;
44 this.access = access;
45 this.superClass = superClass;
46 this.interfaces = interfaces != null ? interfaces : new ClassEntry[0];
47 }
48
49 public static ClassDefEntry parse(int access, String name, String signature, String superName, String[] interfaces) {
50 ClassEntry superClass = superName != null ? new ClassEntry(superName) : null;
51 ClassEntry[] interfaceClasses = Arrays.stream(interfaces).map(ClassEntry::new).toArray(ClassEntry[]::new);
52 return new ClassDefEntry(name, Signature.createSignature(signature), new AccessFlags(access), superClass, interfaceClasses);
53 }
54
55 public Signature getSignature() {
56 return signature;
57 }
58
59 @Override
60 public AccessFlags getAccess() {
61 return access;
62 }
63
64 @Nullable
65 public ClassEntry getSuperClass() {
66 return superClass;
67 }
68
69 public ClassEntry[] getInterfaces() {
70 return interfaces;
71 }
72
73 @Override
74 public ClassDefEntry translate(Translator translator, @Nullable EntryMapping mapping) {
75 Signature translatedSignature = translator.translate(signature);
76 String translatedName = mapping != null ? mapping.getTargetName() : name;
77 AccessFlags translatedAccess = mapping != null ? mapping.getAccessModifier().transform(access) : access;
78 ClassEntry translatedSuper = translator.translate(superClass);
79 ClassEntry[] translatedInterfaces = Arrays.stream(interfaces).map(translator::translate).toArray(ClassEntry[]::new);
80 String docs = mapping != null ? mapping.getJavadoc() : null;
81 return new ClassDefEntry(parent, translatedName, translatedSignature, translatedAccess, translatedSuper, translatedInterfaces, docs);
82 }
83
84 @Override
85 public ClassDefEntry withName(String name) {
86 return new ClassDefEntry(parent, name, signature, access, superClass, interfaces, javadocs);
87 }
88
89 @Override
90 public ClassDefEntry withParent(ClassEntry parent) {
91 return new ClassDefEntry(parent, name, signature, access, superClass, interfaces, javadocs);
92 }
93}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java
deleted file mode 100644
index d6171f1..0000000
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java
+++ /dev/null
@@ -1,214 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation.representation.entry;
13
14import cuchaz.enigma.throwables.IllegalNameException;
15import cuchaz.enigma.translation.Translator;
16import cuchaz.enigma.translation.mapping.EntryMapping;
17import cuchaz.enigma.translation.mapping.NameValidator;
18import cuchaz.enigma.translation.representation.TypeDescriptor;
19
20import javax.annotation.Nonnull;
21import javax.annotation.Nullable;
22import java.util.List;
23import java.util.Objects;
24
25public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<ClassEntry> {
26 private final String fullName;
27
28 public ClassEntry(String className) {
29 this(getOuterClass(className), getInnerName(className), null);
30 }
31
32 public ClassEntry(@Nullable ClassEntry parent, String className) {
33 this(parent, className, null);
34 }
35
36 public ClassEntry(@Nullable ClassEntry parent, String className, @Nullable String javadocs) {
37 super(parent, className, javadocs);
38 if (parent != null) {
39 fullName = parent.getFullName() + "$" + name;
40 } else {
41 fullName = name;
42 }
43
44 if (parent == null && className.indexOf('.') >= 0) {
45 throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className);
46 }
47 }
48
49 @Override
50 public Class<ClassEntry> getParentType() {
51 return ClassEntry.class;
52 }
53
54 @Override
55 public String getName() {
56 return this.name;
57 }
58
59 public String getFullName() {
60 return fullName;
61 }
62
63 @Override
64 public ClassEntry translate(Translator translator, @Nullable EntryMapping mapping) {
65 if (name.charAt(0) == '[') {
66 String translatedName = translator.translate(new TypeDescriptor(name)).toString();
67 return new ClassEntry(parent, translatedName);
68 }
69
70 String translatedName = mapping != null ? mapping.getTargetName() : name;
71 String docs = mapping != null ? mapping.getJavadoc() : null;
72 return new ClassEntry(parent, translatedName, docs);
73 }
74
75 @Override
76 public ClassEntry getContainingClass() {
77 return this;
78 }
79
80 @Override
81 public int hashCode() {
82 return fullName.hashCode();
83 }
84
85 @Override
86 public boolean equals(Object other) {
87 return other instanceof ClassEntry && equals((ClassEntry) other);
88 }
89
90 public boolean equals(ClassEntry other) {
91 return other != null && Objects.equals(parent, other.parent) && this.name.equals(other.name);
92 }
93
94 @Override
95 public boolean canConflictWith(Entry<?> entry) {
96 return true;
97 }
98
99 @Override
100 public void validateName(String name) throws IllegalNameException {
101 NameValidator.validateClassName(name);
102 }
103
104 @Override
105 public ClassEntry withName(String name) {
106 return new ClassEntry(parent, name, javadocs);
107 }
108
109 @Override
110 public ClassEntry withParent(ClassEntry parent) {
111 return new ClassEntry(parent, name, javadocs);
112 }
113
114 @Override
115 public String toString() {
116 return getFullName();
117 }
118
119 public String getPackageName() {
120 return getPackageName(fullName);
121 }
122
123 public String getSimpleName() {
124 int packagePos = name.lastIndexOf('/');
125 if (packagePos > 0) {
126 return name.substring(packagePos + 1);
127 }
128 return name;
129 }
130
131 public boolean isInnerClass() {
132 return parent != null;
133 }
134
135 @Nullable
136 public ClassEntry getOuterClass() {
137 return parent;
138 }
139
140 @Nonnull
141 public ClassEntry getOutermostClass() {
142 if (parent == null) {
143 return this;
144 }
145 return parent.getOutermostClass();
146 }
147
148 public ClassEntry buildClassEntry(List<ClassEntry> classChain) {
149 assert (classChain.contains(this));
150 StringBuilder buf = new StringBuilder();
151 for (ClassEntry chainEntry : classChain) {
152 if (buf.length() == 0) {
153 buf.append(chainEntry.getFullName());
154 } else {
155 buf.append("$");
156 buf.append(chainEntry.getSimpleName());
157 }
158
159 if (chainEntry == this) {
160 break;
161 }
162 }
163 return new ClassEntry(buf.toString());
164 }
165
166 public boolean isJre() {
167 String packageName = getPackageName();
168 return packageName != null && (packageName.startsWith("java") || packageName.startsWith("javax"));
169 }
170
171 public static String getPackageName(String name) {
172 int pos = name.lastIndexOf('/');
173 if (pos > 0) {
174 return name.substring(0, pos);
175 }
176 return null;
177 }
178
179 @Nullable
180 public static ClassEntry getOuterClass(String name) {
181 int index = name.lastIndexOf('$');
182 if (index >= 0) {
183 return new ClassEntry(name.substring(0, index));
184 }
185 return null;
186 }
187
188 public static String getInnerName(String name) {
189 int innerClassPos = name.lastIndexOf('$');
190 if (innerClassPos > 0) {
191 return name.substring(innerClassPos + 1);
192 }
193 return name;
194 }
195
196 @Override
197 public String getSourceRemapName() {
198 ClassEntry outerClass = getOuterClass();
199 if (outerClass != null) {
200 return outerClass.getSourceRemapName() + "." + name;
201 }
202 return getSimpleName();
203 }
204
205 @Override
206 public int compareTo(ClassEntry entry) {
207 String fullName = getFullName();
208 String otherFullName = entry.getFullName();
209 if (fullName.length() != otherFullName.length()) {
210 return fullName.length() - otherFullName.length();
211 }
212 return fullName.compareTo(otherFullName);
213 }
214}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/DefEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/DefEntry.java
deleted file mode 100644
index 82536c7..0000000
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/DefEntry.java
+++ /dev/null
@@ -1,7 +0,0 @@
1package cuchaz.enigma.translation.representation.entry;
2
3import cuchaz.enigma.translation.representation.AccessFlags;
4
5public interface DefEntry<P extends Entry<?>> extends Entry<P> {
6 AccessFlags getAccess();
7}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java
deleted file mode 100644
index 72b0391..0000000
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java
+++ /dev/null
@@ -1,107 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation.representation.entry;
13
14import cuchaz.enigma.throwables.IllegalNameException;
15import cuchaz.enigma.translation.Translatable;
16import cuchaz.enigma.translation.mapping.NameValidator;
17
18import javax.annotation.Nullable;
19import java.util.ArrayList;
20import java.util.List;
21
22public interface Entry<P extends Entry<?>> extends Translatable {
23 String getName();
24
25 String getJavadocs();
26
27 default String getSourceRemapName() {
28 return getName();
29 }
30
31 @Nullable
32 P getParent();
33
34 Class<P> getParentType();
35
36 Entry<P> withName(String name);
37
38 Entry<P> withParent(P parent);
39
40 boolean canConflictWith(Entry<?> entry);
41
42 @Nullable
43 default ClassEntry getContainingClass() {
44 P parent = getParent();
45 if (parent == null) {
46 return null;
47 }
48 if (parent instanceof ClassEntry) {
49 return (ClassEntry) parent;
50 }
51 return parent.getContainingClass();
52 }
53
54 default List<Entry<?>> getAncestry() {
55 P parent = getParent();
56 List<Entry<?>> entries = new ArrayList<>();
57 if (parent != null) {
58 entries.addAll(parent.getAncestry());
59 }
60 entries.add(this);
61 return entries;
62 }
63
64 @Nullable
65 @SuppressWarnings("unchecked")
66 default <E extends Entry<?>> E findAncestor(Class<E> type) {
67 List<Entry<?>> ancestry = getAncestry();
68 for (int i = ancestry.size() - 1; i >= 0; i--) {
69 Entry<?> ancestor = ancestry.get(i);
70 if (type.isAssignableFrom(ancestor.getClass())) {
71 return (E) ancestor;
72 }
73 }
74 return null;
75 }
76
77 @SuppressWarnings("unchecked")
78 default <E extends Entry<?>> Entry<P> replaceAncestor(E target, E replacement) {
79 if (replacement.equals(target)) {
80 return this;
81 }
82
83 if (equals(target)) {
84 return (Entry<P>) replacement;
85 }
86
87 P parent = getParent();
88 if (parent == null) {
89 return this;
90 }
91
92 return withParent((P) parent.replaceAncestor(target, replacement));
93 }
94
95 default void validateName(String name) throws IllegalNameException {
96 NameValidator.validateIdentifier(name);
97 }
98
99 @SuppressWarnings("unchecked")
100 @Nullable
101 default <C extends Entry<?>> Entry<C> castParent(Class<C> parentType) {
102 if (parentType.equals(getParentType())) {
103 return (Entry<C>) this;
104 }
105 return null;
106 }
107}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java
deleted file mode 100644
index f9282b2..0000000
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java
+++ /dev/null
@@ -1,71 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation.representation.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.translation.Translator;
16import cuchaz.enigma.translation.mapping.EntryMapping;
17import cuchaz.enigma.translation.representation.AccessFlags;
18import cuchaz.enigma.translation.representation.Signature;
19import cuchaz.enigma.translation.representation.TypeDescriptor;
20
21import javax.annotation.Nullable;
22
23public class FieldDefEntry extends FieldEntry implements DefEntry<ClassEntry> {
24 private final AccessFlags access;
25 private final Signature signature;
26
27 public FieldDefEntry(ClassEntry owner, String name, TypeDescriptor desc, Signature signature, AccessFlags access) {
28 this(owner, name, desc, signature, access, null);
29 }
30
31 public FieldDefEntry(ClassEntry owner, String name, TypeDescriptor desc, Signature signature, AccessFlags access, String javadocs) {
32 super(owner, name, desc, javadocs);
33 Preconditions.checkNotNull(access, "Field access cannot be null");
34 Preconditions.checkNotNull(signature, "Field signature cannot be null");
35 this.access = access;
36 this.signature = signature;
37 }
38
39 public static FieldDefEntry parse(ClassEntry owner, int access, String name, String desc, String signature) {
40 return new FieldDefEntry(owner, name, new TypeDescriptor(desc), Signature.createTypedSignature(signature), new AccessFlags(access), null);
41 }
42
43 @Override
44 public AccessFlags getAccess() {
45 return access;
46 }
47
48 public Signature getSignature() {
49 return signature;
50 }
51
52 @Override
53 public FieldDefEntry translate(Translator translator, @Nullable EntryMapping mapping) {
54 TypeDescriptor translatedDesc = translator.translate(desc);
55 Signature translatedSignature = translator.translate(signature);
56 String translatedName = mapping != null ? mapping.getTargetName() : name;
57 AccessFlags translatedAccess = mapping != null ? mapping.getAccessModifier().transform(access) : access;
58 String docs = mapping != null ? mapping.getJavadoc() : null;
59 return new FieldDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess, docs);
60 }
61
62 @Override
63 public FieldDefEntry withName(String name) {
64 return new FieldDefEntry(parent, name, desc, signature, access, javadocs);
65 }
66
67 @Override
68 public FieldDefEntry withParent(ClassEntry owner) {
69 return new FieldDefEntry(owner, this.name, this.desc, signature, access, javadocs);
70 }
71}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java
deleted file mode 100644
index bef0edf..0000000
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java
+++ /dev/null
@@ -1,96 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation.representation.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.translation.Translator;
16import cuchaz.enigma.translation.mapping.EntryMapping;
17import cuchaz.enigma.translation.representation.TypeDescriptor;
18import cuchaz.enigma.utils.Utils;
19
20import javax.annotation.Nullable;
21
22public class FieldEntry extends ParentedEntry<ClassEntry> implements Comparable<FieldEntry> {
23 protected final TypeDescriptor desc;
24
25 public FieldEntry(ClassEntry parent, String name, TypeDescriptor desc) {
26 this(parent, name, desc, null);
27 }
28
29 public FieldEntry(ClassEntry parent, String name, TypeDescriptor desc, String javadocs) {
30 super(parent, name, javadocs);
31
32 Preconditions.checkNotNull(parent, "Owner cannot be null");
33 Preconditions.checkNotNull(desc, "Field descriptor cannot be null");
34
35 this.desc = desc;
36 }
37
38 public static FieldEntry parse(String owner, String name, String desc) {
39 return new FieldEntry(new ClassEntry(owner), name, new TypeDescriptor(desc), null);
40 }
41
42 @Override
43 public Class<ClassEntry> getParentType() {
44 return ClassEntry.class;
45 }
46
47 public TypeDescriptor getDesc() {
48 return this.desc;
49 }
50
51 @Override
52 public FieldEntry withName(String name) {
53 return new FieldEntry(parent, name, desc, null);
54 }
55
56 @Override
57 public FieldEntry withParent(ClassEntry parent) {
58 return new FieldEntry(parent, this.name, this.desc, null);
59 }
60
61 @Override
62 protected FieldEntry translate(Translator translator, @Nullable EntryMapping mapping) {
63 String translatedName = mapping != null ? mapping.getTargetName() : name;
64 String docs = mapping != null ? mapping.getJavadoc() : null;
65 return new FieldEntry(parent, translatedName, translator.translate(desc), docs);
66 }
67
68 @Override
69 public int hashCode() {
70 return Utils.combineHashesOrdered(this.parent, this.name, this.desc);
71 }
72
73 @Override
74 public boolean equals(Object other) {
75 return other instanceof FieldEntry && equals((FieldEntry) other);
76 }
77
78 public boolean equals(FieldEntry other) {
79 return this.parent.equals(other.parent) && name.equals(other.name) && desc.equals(other.desc);
80 }
81
82 @Override
83 public boolean canConflictWith(Entry<?> entry) {
84 return entry instanceof FieldEntry && ((FieldEntry) entry).parent.equals(parent);
85 }
86
87 @Override
88 public String toString() {
89 return this.parent.getFullName() + "." + this.name + ":" + this.desc;
90 }
91
92 @Override
93 public int compareTo(FieldEntry entry) {
94 return (name + desc.toString()).compareTo(entry.name + entry.desc.toString());
95 }
96}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java
deleted file mode 100644
index aad4236..0000000
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java
+++ /dev/null
@@ -1,51 +0,0 @@
1package cuchaz.enigma.translation.representation.entry;
2
3import com.google.common.base.Preconditions;
4import cuchaz.enigma.translation.Translator;
5import cuchaz.enigma.translation.mapping.EntryMapping;
6import cuchaz.enigma.translation.representation.TypeDescriptor;
7
8import javax.annotation.Nullable;
9
10/**
11 * TypeDescriptor...
12 * Created by Thog
13 * 19/10/2016
14 */
15public class LocalVariableDefEntry extends LocalVariableEntry {
16 protected final TypeDescriptor desc;
17
18 public LocalVariableDefEntry(MethodEntry ownerEntry, int index, String name, boolean parameter, TypeDescriptor desc, String javadoc) {
19 super(ownerEntry, index, name, parameter, javadoc);
20 Preconditions.checkNotNull(desc, "Variable desc cannot be null");
21
22 this.desc = desc;
23 }
24
25 public TypeDescriptor getDesc() {
26 return desc;
27 }
28
29 @Override
30 public LocalVariableDefEntry translate(Translator translator, @Nullable EntryMapping mapping) {
31 TypeDescriptor translatedDesc = translator.translate(desc);
32 String translatedName = mapping != null ? mapping.getTargetName() : name;
33 String javadoc = mapping != null ? mapping.getJavadoc() : javadocs;
34 return new LocalVariableDefEntry(parent, index, translatedName, parameter, translatedDesc, javadoc);
35 }
36
37 @Override
38 public LocalVariableDefEntry withName(String name) {
39 return new LocalVariableDefEntry(parent, index, name, parameter, desc, javadocs);
40 }
41
42 @Override
43 public LocalVariableDefEntry withParent(MethodEntry entry) {
44 return new LocalVariableDefEntry(entry, index, name, parameter, desc, javadocs);
45 }
46
47 @Override
48 public String toString() {
49 return this.parent + "(" + this.index + ":" + this.name + ":" + this.desc + ")";
50 }
51}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java
deleted file mode 100644
index 3ccb1fa..0000000
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java
+++ /dev/null
@@ -1,93 +0,0 @@
1package cuchaz.enigma.translation.representation.entry;
2
3import com.google.common.base.Preconditions;
4import cuchaz.enigma.translation.Translator;
5import cuchaz.enigma.translation.mapping.EntryMapping;
6import cuchaz.enigma.utils.Utils;
7
8import javax.annotation.Nullable;
9
10/**
11 * TypeDescriptor...
12 * Created by Thog
13 * 19/10/2016
14 */
15public class LocalVariableEntry extends ParentedEntry<MethodEntry> implements Comparable<LocalVariableEntry> {
16
17 protected final int index;
18 protected final boolean parameter;
19
20 public LocalVariableEntry(MethodEntry parent, int index, String name, boolean parameter, String javadoc) {
21 super(parent, name, javadoc);
22
23 Preconditions.checkNotNull(parent, "Variable owner cannot be null");
24 Preconditions.checkArgument(index >= 0, "Index must be positive");
25
26 this.index = index;
27 this.parameter = parameter;
28 }
29
30 @Override
31 public Class<MethodEntry> getParentType() {
32 return MethodEntry.class;
33 }
34
35 public boolean isArgument() {
36 return this.parameter;
37 }
38
39 public int getIndex() {
40 return index;
41 }
42
43 @Override
44 public String getName() {
45 return this.name;
46 }
47
48 @Override
49 public LocalVariableEntry translate(Translator translator, @Nullable EntryMapping mapping) {
50 String translatedName = mapping != null ? mapping.getTargetName() : name;
51 String javadoc = mapping != null ? mapping.getJavadoc() : null;
52 return new LocalVariableEntry(parent, index, translatedName, parameter, javadoc);
53 }
54
55 @Override
56 public LocalVariableEntry withName(String name) {
57 return new LocalVariableEntry(parent, index, name, parameter, javadocs);
58 }
59
60 @Override
61 public LocalVariableEntry withParent(MethodEntry parent) {
62 return new LocalVariableEntry(parent, index, name, parameter, javadocs);
63 }
64
65 @Override
66 public int hashCode() {
67 return Utils.combineHashesOrdered(this.parent, this.index);
68 }
69
70 @Override
71 public boolean equals(Object other) {
72 return other instanceof LocalVariableEntry && equals((LocalVariableEntry) other);
73 }
74
75 public boolean equals(LocalVariableEntry other) {
76 return this.parent.equals(other.parent) && this.index == other.index;
77 }
78
79 @Override
80 public boolean canConflictWith(Entry<?> entry) {
81 return entry instanceof LocalVariableEntry && ((LocalVariableEntry) entry).parent.equals(parent);
82 }
83
84 @Override
85 public String toString() {
86 return this.parent + "(" + this.index + ":" + this.name + ")";
87 }
88
89 @Override
90 public int compareTo(LocalVariableEntry entry) {
91 return Integer.compare(index, entry.index);
92 }
93}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java
deleted file mode 100644
index 4e75a5c..0000000
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java
+++ /dev/null
@@ -1,71 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation.representation.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.translation.Translator;
16import cuchaz.enigma.translation.mapping.EntryMapping;
17import cuchaz.enigma.translation.representation.AccessFlags;
18import cuchaz.enigma.translation.representation.MethodDescriptor;
19import cuchaz.enigma.translation.representation.Signature;
20
21import javax.annotation.Nullable;
22
23public class MethodDefEntry extends MethodEntry implements DefEntry<ClassEntry> {
24 private final AccessFlags access;
25 private final Signature signature;
26
27 public MethodDefEntry(ClassEntry owner, String name, MethodDescriptor descriptor, Signature signature, AccessFlags access) {
28 this(owner, name, descriptor, signature, access, null);
29 }
30
31 public MethodDefEntry(ClassEntry owner, String name, MethodDescriptor descriptor, Signature signature, AccessFlags access, String docs) {
32 super(owner, name, descriptor, docs);
33 Preconditions.checkNotNull(access, "Method access cannot be null");
34 Preconditions.checkNotNull(signature, "Method signature cannot be null");
35 this.access = access;
36 this.signature = signature;
37 }
38
39 public static MethodDefEntry parse(ClassEntry owner, int access, String name, String desc, String signature) {
40 return new MethodDefEntry(owner, name, new MethodDescriptor(desc), Signature.createSignature(signature), new AccessFlags(access), null);
41 }
42
43 @Override
44 public AccessFlags getAccess() {
45 return access;
46 }
47
48 public Signature getSignature() {
49 return signature;
50 }
51
52 @Override
53 public MethodDefEntry translate(Translator translator, @Nullable EntryMapping mapping) {
54 MethodDescriptor translatedDesc = translator.translate(descriptor);
55 Signature translatedSignature = translator.translate(signature);
56 String translatedName = mapping != null ? mapping.getTargetName() : name;
57 AccessFlags translatedAccess = mapping != null ? mapping.getAccessModifier().transform(access) : access;
58 String docs = mapping != null ? mapping.getJavadoc() : null;
59 return new MethodDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess, docs);
60 }
61
62 @Override
63 public MethodDefEntry withName(String name) {
64 return new MethodDefEntry(parent, name, descriptor, signature, access, javadocs);
65 }
66
67 @Override
68 public MethodDefEntry withParent(ClassEntry parent) {
69 return new MethodDefEntry(new ClassEntry(parent.getFullName()), name, descriptor, signature, access, javadocs);
70 }
71}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java
deleted file mode 100644
index e1ffad3..0000000
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java
+++ /dev/null
@@ -1,105 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation.representation.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.translation.Translator;
16import cuchaz.enigma.translation.mapping.EntryMapping;
17import cuchaz.enigma.translation.representation.MethodDescriptor;
18import cuchaz.enigma.utils.Utils;
19
20import javax.annotation.Nullable;
21
22public class MethodEntry extends ParentedEntry<ClassEntry> implements Comparable<MethodEntry> {
23
24 protected final MethodDescriptor descriptor;
25
26 public MethodEntry(ClassEntry parent, String name, MethodDescriptor descriptor) {
27 this(parent, name, descriptor, null);
28 }
29
30 public MethodEntry(ClassEntry parent, String name, MethodDescriptor descriptor, String javadocs) {
31 super(parent, name, javadocs);
32
33 Preconditions.checkNotNull(parent, "Parent cannot be null");
34 Preconditions.checkNotNull(descriptor, "Method descriptor cannot be null");
35
36 this.descriptor = descriptor;
37 }
38
39 public static MethodEntry parse(String owner, String name, String desc) {
40 return new MethodEntry(new ClassEntry(owner), name, new MethodDescriptor(desc), null);
41 }
42
43 @Override
44 public Class<ClassEntry> getParentType() {
45 return ClassEntry.class;
46 }
47
48 public MethodDescriptor getDesc() {
49 return this.descriptor;
50 }
51
52 public boolean isConstructor() {
53 return name.equals("<init>") || name.equals("<clinit>");
54 }
55
56 @Override
57 public MethodEntry translate(Translator translator, @Nullable EntryMapping mapping) {
58 String translatedName = mapping != null ? mapping.getTargetName() : name;
59 String docs = mapping != null ? mapping.getJavadoc() : null;
60 return new MethodEntry(parent, translatedName, translator.translate(descriptor), docs);
61 }
62
63 @Override
64 public MethodEntry withName(String name) {
65 return new MethodEntry(parent, name, descriptor, javadocs);
66 }
67
68 @Override
69 public MethodEntry withParent(ClassEntry parent) {
70 return new MethodEntry(new ClassEntry(parent.getFullName()), name, descriptor, javadocs);
71 }
72
73 @Override
74 public int hashCode() {
75 return Utils.combineHashesOrdered(this.parent, this.name, this.descriptor);
76 }
77
78 @Override
79 public boolean equals(Object other) {
80 return other instanceof MethodEntry && equals((MethodEntry) other);
81 }
82
83 public boolean equals(MethodEntry other) {
84 return this.parent.equals(other.getParent()) && this.name.equals(other.getName()) && this.descriptor.equals(other.getDesc());
85 }
86
87 @Override
88 public boolean canConflictWith(Entry<?> entry) {
89 if (entry instanceof MethodEntry) {
90 MethodEntry methodEntry = (MethodEntry) entry;
91 return methodEntry.parent.equals(parent) && methodEntry.descriptor.canConflictWith(descriptor);
92 }
93 return false;
94 }
95
96 @Override
97 public String toString() {
98 return this.parent.getFullName() + "." + this.name + this.descriptor;
99 }
100
101 @Override
102 public int compareTo(MethodEntry entry) {
103 return (name + descriptor.toString()).compareTo(entry.name + entry.descriptor.toString());
104 }
105}
diff --git a/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java b/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java
deleted file mode 100644
index cbc5faf..0000000
--- a/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java
+++ /dev/null
@@ -1,82 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.translation.representation.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.translation.Translatable;
16import cuchaz.enigma.translation.Translator;
17import cuchaz.enigma.translation.mapping.EntryMap;
18import cuchaz.enigma.translation.mapping.EntryMapping;
19import cuchaz.enigma.translation.mapping.EntryResolver;
20import cuchaz.enigma.translation.mapping.ResolutionStrategy;
21
22import javax.annotation.Nullable;
23
24public abstract class ParentedEntry<P extends Entry<?>> implements Entry<P> {
25 protected final P parent;
26 protected final String name;
27 protected final @Nullable String javadocs;
28
29 protected ParentedEntry(P parent, String name, String javadocs) {
30 this.parent = parent;
31 this.name = name;
32 this.javadocs = javadocs;
33
34 Preconditions.checkNotNull(name, "Name cannot be null");
35 }
36
37 @Override
38 public abstract ParentedEntry<P> withParent(P parent);
39
40 @Override
41 public abstract ParentedEntry<P> withName(String name);
42
43 protected abstract ParentedEntry<P> translate(Translator translator, @Nullable EntryMapping mapping);
44
45 @Override
46 public String getName() {
47 return name;
48 }
49
50 @Override
51 @Nullable
52 public P getParent() {
53 return parent;
54 }
55
56 @Nullable
57 @Override
58 public String getJavadocs() {
59 return javadocs;
60 }
61
62 @Override
63 public ParentedEntry<P> translate(Translator translator, EntryResolver resolver, EntryMap<EntryMapping> mappings) {
64 P parent = getParent();
65 EntryMapping mapping = resolveMapping(resolver, mappings);
66 if (parent == null) {
67 return translate(translator, mapping);
68 }
69 P translatedParent = translator.translate(parent);
70 return withParent(translatedParent).translate(translator, mapping);
71 }
72
73 private EntryMapping resolveMapping(EntryResolver resolver, EntryMap<EntryMapping> mappings) {
74 for (ParentedEntry<P> entry : resolver.resolveEntry(this, ResolutionStrategy.RESOLVE_ROOT)) {
75 EntryMapping mapping = mappings.get(entry);
76 if (mapping != null) {
77 return mapping;
78 }
79 }
80 return null;
81 }
82}