diff options
| author | 2020-06-03 13:39:42 -0400 | |
|---|---|---|
| committer | 2020-06-03 18:39:42 +0100 | |
| commit | 0f47403d0220757fed189b76e2071e25b1025cb8 (patch) | |
| tree | 879bf72c4476f0a5e0d82da99d7ff2b2276bcaca /src/main/java/cuchaz/enigma/translation | |
| parent | Fix search dialog hanging for a short time sometimes (#250) (diff) | |
| download | enigma-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')
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 @@ | |||
| 1 | package cuchaz.enigma.translation; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.mapping.NameValidator; | ||
| 4 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 5 | |||
| 6 | import java.util.Collection; | ||
| 7 | import java.util.Locale; | ||
| 8 | |||
| 9 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 4 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 6 | |||
| 7 | public 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 | |||
| 12 | package cuchaz.enigma.translation; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | |||
| 16 | import java.io.IOException; | ||
| 17 | import java.io.StringReader; | ||
| 18 | import java.util.List; | ||
| 19 | |||
| 20 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 4 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 6 | |||
| 7 | public 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 | |||
| 12 | package cuchaz.enigma.translation; | ||
| 13 | |||
| 14 | public 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 | |||
| 12 | package cuchaz.enigma.translation; | ||
| 13 | |||
| 14 | import com.google.common.collect.HashMultimap; | ||
| 15 | import com.google.common.collect.Multimap; | ||
| 16 | |||
| 17 | import java.util.Collection; | ||
| 18 | import java.util.HashMap; | ||
| 19 | import java.util.Map; | ||
| 20 | import java.util.Set; | ||
| 21 | import java.util.stream.Collectors; | ||
| 22 | |||
| 23 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation; | ||
| 2 | |||
| 3 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 4 | |||
| 5 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 4 | |||
| 5 | import javax.annotation.Nullable; | ||
| 6 | import java.util.stream.Stream; | ||
| 7 | |||
| 8 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import javax.annotation.Nonnull; | ||
| 4 | import javax.annotation.Nullable; | ||
| 5 | |||
| 6 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 4 | import cuchaz.enigma.translation.MappingTranslator; | ||
| 5 | import cuchaz.enigma.translation.Translatable; | ||
| 6 | import cuchaz.enigma.translation.Translator; | ||
| 7 | import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; | ||
| 8 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 9 | import cuchaz.enigma.translation.mapping.tree.HashEntryTree; | ||
| 10 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 11 | |||
| 12 | import javax.annotation.Nullable; | ||
| 13 | import java.util.Collection; | ||
| 14 | import java.util.function.UnaryOperator; | ||
| 15 | import java.util.stream.Stream; | ||
| 16 | |||
| 17 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import com.google.common.collect.Streams; | ||
| 4 | import cuchaz.enigma.analysis.EntryReference; | ||
| 5 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 6 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 7 | |||
| 8 | import java.util.Collection; | ||
| 9 | import java.util.Set; | ||
| 10 | import java.util.stream.Collectors; | ||
| 11 | |||
| 12 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import com.google.common.collect.Sets; | ||
| 4 | import cuchaz.enigma.analysis.IndexTreeBuilder; | ||
| 5 | import cuchaz.enigma.analysis.MethodImplementationsTreeNode; | ||
| 6 | import cuchaz.enigma.analysis.MethodInheritanceTreeNode; | ||
| 7 | import cuchaz.enigma.analysis.index.BridgeMethodIndex; | ||
| 8 | import cuchaz.enigma.analysis.index.EntryIndex; | ||
| 9 | import cuchaz.enigma.analysis.index.InheritanceIndex; | ||
| 10 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 11 | import cuchaz.enigma.translation.VoidTranslator; | ||
| 12 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 14 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 15 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 16 | |||
| 17 | import javax.annotation.Nullable; | ||
| 18 | import java.util.*; | ||
| 19 | import java.util.stream.Collectors; | ||
| 20 | |||
| 21 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.Translatable; | ||
| 4 | import cuchaz.enigma.translation.Translator; | ||
| 5 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 6 | import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; | ||
| 7 | import cuchaz.enigma.translation.mapping.tree.HashEntryTree; | ||
| 8 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 9 | |||
| 10 | import java.util.stream.Stream; | ||
| 11 | |||
| 12 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import com.google.gson.annotations.SerializedName; | ||
| 4 | |||
| 5 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 4 | |||
| 5 | import javax.annotation.Nullable; | ||
| 6 | |||
| 7 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import com.google.gson.annotations.SerializedName; | ||
| 4 | |||
| 5 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import cuchaz.enigma.analysis.index.InheritanceIndex; | ||
| 4 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 5 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 6 | import cuchaz.enigma.translation.Translator; | ||
| 7 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 8 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 9 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 10 | |||
| 11 | import java.util.Collection; | ||
| 12 | import java.util.HashSet; | ||
| 13 | import java.util.stream.Collectors; | ||
| 14 | |||
| 15 | public 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 | |||
| 12 | package cuchaz.enigma.translation.mapping; | ||
| 13 | |||
| 14 | import cuchaz.enigma.ProgressListener; | ||
| 15 | import cuchaz.enigma.analysis.index.JarIndex; | ||
| 16 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 17 | import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; | ||
| 18 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 19 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 21 | import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; | ||
| 22 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 23 | |||
| 24 | import java.util.Collection; | ||
| 25 | import java.util.HashMap; | ||
| 26 | import java.util.Map; | ||
| 27 | import java.util.stream.Collectors; | ||
| 28 | |||
| 29 | public 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 | |||
| 12 | package cuchaz.enigma.translation.mapping; | ||
| 13 | |||
| 14 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 15 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 16 | |||
| 17 | import java.util.Arrays; | ||
| 18 | import java.util.List; | ||
| 19 | import java.util.regex.Pattern; | ||
| 20 | |||
| 21 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 4 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 5 | |||
| 6 | import java.util.Collection; | ||
| 7 | import java.util.Collections; | ||
| 8 | import java.util.Set; | ||
| 9 | |||
| 10 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | import com.google.common.base.Charsets; | ||
| 4 | import cuchaz.enigma.ProgressListener; | ||
| 5 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 6 | import cuchaz.enigma.translation.mapping.AccessModifier; | ||
| 7 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 8 | import cuchaz.enigma.translation.mapping.MappingPair; | ||
| 9 | import cuchaz.enigma.translation.mapping.MappingSaveParameters; | ||
| 10 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 11 | import cuchaz.enigma.translation.mapping.tree.HashEntryTree; | ||
| 12 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 13 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 14 | import cuchaz.enigma.translation.representation.entry.*; | ||
| 15 | import cuchaz.enigma.utils.I18n; | ||
| 16 | |||
| 17 | import javax.annotation.Nullable; | ||
| 18 | import java.io.IOException; | ||
| 19 | import java.nio.file.FileSystem; | ||
| 20 | import java.nio.file.FileSystems; | ||
| 21 | import java.nio.file.Files; | ||
| 22 | import java.nio.file.Path; | ||
| 23 | import java.util.ArrayDeque; | ||
| 24 | import java.util.Arrays; | ||
| 25 | import java.util.Deque; | ||
| 26 | import java.util.List; | ||
| 27 | import java.util.Locale; | ||
| 28 | import java.util.stream.Collectors; | ||
| 29 | |||
| 30 | public 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 | |||
| 12 | package cuchaz.enigma.translation.mapping.serde; | ||
| 13 | |||
| 14 | import java.io.IOException; | ||
| 15 | import java.io.PrintWriter; | ||
| 16 | import java.net.URI; | ||
| 17 | import java.net.URISyntaxException; | ||
| 18 | import java.nio.file.DirectoryStream; | ||
| 19 | import java.nio.file.FileSystem; | ||
| 20 | import java.nio.file.FileSystems; | ||
| 21 | import java.nio.file.Files; | ||
| 22 | import java.nio.file.Path; | ||
| 23 | import java.nio.file.Paths; | ||
| 24 | import java.util.ArrayList; | ||
| 25 | import java.util.Collection; | ||
| 26 | import java.util.Collections; | ||
| 27 | import java.util.Objects; | ||
| 28 | import java.util.concurrent.atomic.AtomicInteger; | ||
| 29 | import java.util.stream.Collectors; | ||
| 30 | import java.util.stream.Stream; | ||
| 31 | |||
| 32 | import cuchaz.enigma.ProgressListener; | ||
| 33 | import cuchaz.enigma.translation.MappingTranslator; | ||
| 34 | import cuchaz.enigma.translation.Translator; | ||
| 35 | import cuchaz.enigma.translation.mapping.AccessModifier; | ||
| 36 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 37 | import cuchaz.enigma.translation.mapping.MappingDelta; | ||
| 38 | import cuchaz.enigma.translation.mapping.MappingFileNameFormat; | ||
| 39 | import cuchaz.enigma.translation.mapping.MappingSaveParameters; | ||
| 40 | import cuchaz.enigma.translation.mapping.VoidEntryResolver; | ||
| 41 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 42 | import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; | ||
| 43 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 44 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 45 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 46 | import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; | ||
| 47 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 48 | import cuchaz.enigma.utils.I18n; | ||
| 49 | import cuchaz.enigma.utils.LFPrintWriter; | ||
| 50 | |||
| 51 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | import cuchaz.enigma.ProgressListener; | ||
| 4 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 6 | import cuchaz.enigma.translation.mapping.MappingDelta; | ||
| 7 | import cuchaz.enigma.translation.mapping.MappingSaveParameters; | ||
| 8 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 9 | |||
| 10 | import javax.annotation.Nullable; | ||
| 11 | import java.io.IOException; | ||
| 12 | import java.nio.file.Path; | ||
| 13 | |||
| 14 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | import cuchaz.enigma.ProgressListener; | ||
| 4 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 6 | import cuchaz.enigma.translation.mapping.MappingSaveParameters; | ||
| 7 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 8 | |||
| 9 | import java.io.IOException; | ||
| 10 | import java.nio.file.Path; | ||
| 11 | |||
| 12 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | import cuchaz.enigma.ProgressListener; | ||
| 4 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 5 | import cuchaz.enigma.translation.mapping.MappingDelta; | ||
| 6 | import cuchaz.enigma.translation.mapping.MappingSaveParameters; | ||
| 7 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 8 | |||
| 9 | import java.nio.file.Path; | ||
| 10 | |||
| 11 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | import cuchaz.enigma.ProgressListener; | ||
| 4 | import cuchaz.enigma.command.MappingCommandsUtil; | ||
| 5 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 7 | import cuchaz.enigma.translation.mapping.MappingSaveParameters; | ||
| 8 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 9 | import cuchaz.enigma.translation.mapping.tree.HashEntryTree; | ||
| 10 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 11 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 12 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 14 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 15 | |||
| 16 | import java.io.IOException; | ||
| 17 | import java.nio.charset.StandardCharsets; | ||
| 18 | import java.nio.file.Files; | ||
| 19 | import java.nio.file.Path; | ||
| 20 | import java.util.regex.Matcher; | ||
| 21 | import java.util.regex.Pattern; | ||
| 22 | |||
| 23 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.mapping.AccessModifier; | ||
| 4 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 5 | |||
| 6 | import java.util.ArrayList; | ||
| 7 | import java.util.List; | ||
| 8 | |||
| 9 | final 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | import com.google.common.collect.Lists; | ||
| 4 | import cuchaz.enigma.ProgressListener; | ||
| 5 | import cuchaz.enigma.translation.MappingTranslator; | ||
| 6 | import cuchaz.enigma.translation.Translator; | ||
| 7 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 8 | import cuchaz.enigma.translation.mapping.MappingDelta; | ||
| 9 | import cuchaz.enigma.translation.mapping.MappingSaveParameters; | ||
| 10 | import cuchaz.enigma.translation.mapping.VoidEntryResolver; | ||
| 11 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 12 | import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 14 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 15 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 16 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 17 | import cuchaz.enigma.utils.I18n; | ||
| 18 | import cuchaz.enigma.utils.LFPrintWriter; | ||
| 19 | |||
| 20 | import java.io.IOException; | ||
| 21 | import java.io.PrintWriter; | ||
| 22 | import java.nio.file.Files; | ||
| 23 | import java.nio.file.Path; | ||
| 24 | import java.util.ArrayList; | ||
| 25 | import java.util.Collection; | ||
| 26 | import java.util.Comparator; | ||
| 27 | import java.util.List; | ||
| 28 | import java.util.stream.Collectors; | ||
| 29 | |||
| 30 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | import com.google.common.base.Charsets; | ||
| 4 | import cuchaz.enigma.ProgressListener; | ||
| 5 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 7 | import cuchaz.enigma.translation.mapping.MappingPair; | ||
| 8 | import cuchaz.enigma.translation.mapping.MappingSaveParameters; | ||
| 9 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 10 | import cuchaz.enigma.translation.mapping.tree.HashEntryTree; | ||
| 11 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 12 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 14 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 15 | import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; | ||
| 16 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 17 | import cuchaz.enigma.utils.I18n; | ||
| 18 | |||
| 19 | import java.io.IOException; | ||
| 20 | import java.nio.file.Files; | ||
| 21 | import java.nio.file.Path; | ||
| 22 | import java.util.List; | ||
| 23 | |||
| 24 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | import com.google.common.base.Joiner; | ||
| 4 | import com.google.common.collect.Lists; | ||
| 5 | import cuchaz.enigma.ProgressListener; | ||
| 6 | import cuchaz.enigma.translation.MappingTranslator; | ||
| 7 | import cuchaz.enigma.translation.Translator; | ||
| 8 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 9 | import cuchaz.enigma.translation.mapping.MappingDelta; | ||
| 10 | import cuchaz.enigma.translation.mapping.MappingSaveParameters; | ||
| 11 | import cuchaz.enigma.translation.mapping.VoidEntryResolver; | ||
| 12 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 13 | import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; | ||
| 14 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 15 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 16 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 17 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 18 | |||
| 19 | import java.io.BufferedWriter; | ||
| 20 | import java.io.IOException; | ||
| 21 | import java.io.Writer; | ||
| 22 | import java.nio.charset.StandardCharsets; | ||
| 23 | import java.nio.file.Files; | ||
| 24 | import java.nio.file.Path; | ||
| 25 | import java.util.Comparator; | ||
| 26 | import java.util.HashSet; | ||
| 27 | import java.util.Set; | ||
| 28 | |||
| 29 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | import cuchaz.enigma.ProgressListener; | ||
| 4 | import cuchaz.enigma.throwables.MappingParseException; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 6 | import cuchaz.enigma.translation.mapping.MappingPair; | ||
| 7 | import cuchaz.enigma.translation.mapping.MappingSaveParameters; | ||
| 8 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 9 | import cuchaz.enigma.translation.mapping.tree.HashEntryTree; | ||
| 10 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 11 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 12 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 14 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 15 | import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; | ||
| 16 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 17 | import cuchaz.enigma.utils.I18n; | ||
| 18 | |||
| 19 | import java.io.IOException; | ||
| 20 | import java.nio.charset.StandardCharsets; | ||
| 21 | import java.nio.file.Files; | ||
| 22 | import java.nio.file.Path; | ||
| 23 | import java.util.BitSet; | ||
| 24 | import java.util.List; | ||
| 25 | |||
| 26 | final 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.serde; | ||
| 2 | |||
| 3 | import com.google.common.base.Strings; | ||
| 4 | import cuchaz.enigma.ProgressListener; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 7 | import cuchaz.enigma.translation.mapping.MappingDelta; | ||
| 8 | import cuchaz.enigma.translation.mapping.MappingSaveParameters; | ||
| 9 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | ||
| 10 | import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; | ||
| 11 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 12 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 13 | import cuchaz.enigma.translation.representation.entry.FieldEntry; | ||
| 14 | import cuchaz.enigma.translation.representation.entry.LocalVariableEntry; | ||
| 15 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 16 | import cuchaz.enigma.utils.LFPrintWriter; | ||
| 17 | |||
| 18 | import java.io.IOException; | ||
| 19 | import java.io.PrintWriter; | ||
| 20 | import java.nio.file.Files; | ||
| 21 | import java.nio.file.Path; | ||
| 22 | import java.util.Deque; | ||
| 23 | import java.util.LinkedList; | ||
| 24 | import java.util.List; | ||
| 25 | import java.util.stream.Collectors; | ||
| 26 | import java.util.stream.StreamSupport; | ||
| 27 | |||
| 28 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.tree; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.Translator; | ||
| 4 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 7 | import cuchaz.enigma.translation.mapping.MappingDelta; | ||
| 8 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 9 | |||
| 10 | import javax.annotation.Nullable; | ||
| 11 | import java.util.Collection; | ||
| 12 | import java.util.Iterator; | ||
| 13 | import java.util.stream.Stream; | ||
| 14 | |||
| 15 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.tree; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.Translatable; | ||
| 4 | import cuchaz.enigma.translation.Translator; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 7 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 8 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 9 | |||
| 10 | import javax.annotation.Nullable; | ||
| 11 | import java.util.Collection; | ||
| 12 | import java.util.stream.Stream; | ||
| 13 | |||
| 14 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.tree; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 4 | |||
| 5 | import javax.annotation.Nullable; | ||
| 6 | import java.util.ArrayList; | ||
| 7 | import java.util.Collection; | ||
| 8 | import java.util.stream.Collectors; | ||
| 9 | |||
| 10 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.tree; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.Translator; | ||
| 4 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 7 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 8 | |||
| 9 | import javax.annotation.Nullable; | ||
| 10 | import java.util.*; | ||
| 11 | import java.util.function.Function; | ||
| 12 | import java.util.stream.Stream; | ||
| 13 | import java.util.stream.StreamSupport; | ||
| 14 | |||
| 15 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.mapping.tree; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.entry.Entry; | ||
| 4 | |||
| 5 | import javax.annotation.Nonnull; | ||
| 6 | import javax.annotation.Nullable; | ||
| 7 | import java.util.Collection; | ||
| 8 | import java.util.HashMap; | ||
| 9 | import java.util.Iterator; | ||
| 10 | import java.util.Map; | ||
| 11 | |||
| 12 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.representation; | ||
| 2 | |||
| 3 | import cuchaz.enigma.analysis.Access; | ||
| 4 | import org.objectweb.asm.Opcodes; | ||
| 5 | |||
| 6 | import java.lang.reflect.Modifier; | ||
| 7 | |||
| 8 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.representation; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.Translatable; | ||
| 4 | import cuchaz.enigma.translation.Translator; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 7 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 8 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | ||
| 9 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 10 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | ||
| 11 | import cuchaz.enigma.translation.representation.entry.ParentedEntry; | ||
| 12 | |||
| 13 | import java.util.Objects; | ||
| 14 | |||
| 15 | public 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 | |||
| 12 | package cuchaz.enigma.translation.representation; | ||
| 13 | |||
| 14 | import com.google.common.collect.Lists; | ||
| 15 | import cuchaz.enigma.translation.Translatable; | ||
| 16 | import cuchaz.enigma.translation.Translator; | ||
| 17 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 18 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 19 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 20 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 21 | import cuchaz.enigma.utils.Utils; | ||
| 22 | |||
| 23 | import java.util.ArrayList; | ||
| 24 | import java.util.List; | ||
| 25 | import java.util.function.Function; | ||
| 26 | |||
| 27 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.representation; | ||
| 2 | |||
| 3 | import cuchaz.enigma.bytecode.translators.TranslationSignatureVisitor; | ||
| 4 | import cuchaz.enigma.translation.Translatable; | ||
| 5 | import cuchaz.enigma.translation.Translator; | ||
| 6 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 7 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 8 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 9 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 10 | import org.objectweb.asm.signature.SignatureReader; | ||
| 11 | import org.objectweb.asm.signature.SignatureVisitor; | ||
| 12 | import org.objectweb.asm.signature.SignatureWriter; | ||
| 13 | |||
| 14 | import java.util.function.Function; | ||
| 15 | import java.util.regex.Pattern; | ||
| 16 | |||
| 17 | public 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 | |||
| 12 | package cuchaz.enigma.translation.representation; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import com.google.common.collect.Maps; | ||
| 16 | import cuchaz.enigma.translation.Translatable; | ||
| 17 | import cuchaz.enigma.translation.Translator; | ||
| 18 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 19 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 20 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 21 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | ||
| 22 | |||
| 23 | import java.util.Map; | ||
| 24 | import java.util.function.Function; | ||
| 25 | |||
| 26 | public 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 | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.translation.Translator; | ||
| 16 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 17 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 18 | import cuchaz.enigma.translation.representation.Signature; | ||
| 19 | |||
| 20 | import javax.annotation.Nullable; | ||
| 21 | import java.util.Arrays; | ||
| 22 | |||
| 23 | public 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 | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | ||
| 13 | |||
| 14 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 15 | import cuchaz.enigma.translation.Translator; | ||
| 16 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 17 | import cuchaz.enigma.translation.mapping.NameValidator; | ||
| 18 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 19 | |||
| 20 | import javax.annotation.Nonnull; | ||
| 21 | import javax.annotation.Nullable; | ||
| 22 | import java.util.List; | ||
| 23 | import java.util.Objects; | ||
| 24 | |||
| 25 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.representation.entry; | ||
| 2 | |||
| 3 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 4 | |||
| 5 | public 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 | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | ||
| 13 | |||
| 14 | import cuchaz.enigma.throwables.IllegalNameException; | ||
| 15 | import cuchaz.enigma.translation.Translatable; | ||
| 16 | import cuchaz.enigma.translation.mapping.NameValidator; | ||
| 17 | |||
| 18 | import javax.annotation.Nullable; | ||
| 19 | import java.util.ArrayList; | ||
| 20 | import java.util.List; | ||
| 21 | |||
| 22 | public 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 | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.translation.Translator; | ||
| 16 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 17 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 18 | import cuchaz.enigma.translation.representation.Signature; | ||
| 19 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 20 | |||
| 21 | import javax.annotation.Nullable; | ||
| 22 | |||
| 23 | public 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 | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.translation.Translator; | ||
| 16 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 17 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 18 | import cuchaz.enigma.utils.Utils; | ||
| 19 | |||
| 20 | import javax.annotation.Nullable; | ||
| 21 | |||
| 22 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.representation.entry; | ||
| 2 | |||
| 3 | import com.google.common.base.Preconditions; | ||
| 4 | import cuchaz.enigma.translation.Translator; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 6 | import cuchaz.enigma.translation.representation.TypeDescriptor; | ||
| 7 | |||
| 8 | import javax.annotation.Nullable; | ||
| 9 | |||
| 10 | /** | ||
| 11 | * TypeDescriptor... | ||
| 12 | * Created by Thog | ||
| 13 | * 19/10/2016 | ||
| 14 | */ | ||
| 15 | public 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 @@ | |||
| 1 | package cuchaz.enigma.translation.representation.entry; | ||
| 2 | |||
| 3 | import com.google.common.base.Preconditions; | ||
| 4 | import cuchaz.enigma.translation.Translator; | ||
| 5 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 6 | import cuchaz.enigma.utils.Utils; | ||
| 7 | |||
| 8 | import javax.annotation.Nullable; | ||
| 9 | |||
| 10 | /** | ||
| 11 | * TypeDescriptor... | ||
| 12 | * Created by Thog | ||
| 13 | * 19/10/2016 | ||
| 14 | */ | ||
| 15 | public 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 | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.translation.Translator; | ||
| 16 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 17 | import cuchaz.enigma.translation.representation.AccessFlags; | ||
| 18 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 19 | import cuchaz.enigma.translation.representation.Signature; | ||
| 20 | |||
| 21 | import javax.annotation.Nullable; | ||
| 22 | |||
| 23 | public 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 | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.translation.Translator; | ||
| 16 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 17 | import cuchaz.enigma.translation.representation.MethodDescriptor; | ||
| 18 | import cuchaz.enigma.utils.Utils; | ||
| 19 | |||
| 20 | import javax.annotation.Nullable; | ||
| 21 | |||
| 22 | public 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 | |||
| 12 | package cuchaz.enigma.translation.representation.entry; | ||
| 13 | |||
| 14 | import com.google.common.base.Preconditions; | ||
| 15 | import cuchaz.enigma.translation.Translatable; | ||
| 16 | import cuchaz.enigma.translation.Translator; | ||
| 17 | import cuchaz.enigma.translation.mapping.EntryMap; | ||
| 18 | import cuchaz.enigma.translation.mapping.EntryMapping; | ||
| 19 | import cuchaz.enigma.translation.mapping.EntryResolver; | ||
| 20 | import cuchaz.enigma.translation.mapping.ResolutionStrategy; | ||
| 21 | |||
| 22 | import javax.annotation.Nullable; | ||
| 23 | |||
| 24 | public 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 | } | ||