summaryrefslogtreecommitdiff
path: root/enigma/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'enigma/src/main/java')
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java2
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java12
-rw-r--r--enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java7
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryChange.java97
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java67
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java53
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryUtil.java42
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java13
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java2
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/RawEntryMapping.java12
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java56
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java96
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java9
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java46
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java2
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java6
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java11
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java8
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java28
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java14
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java10
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java10
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java10
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java12
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java10
-rw-r--r--enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java5
-rw-r--r--enigma/src/main/java/cuchaz/enigma/utils/TristateChange.java78
-rw-r--r--enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java10
-rw-r--r--enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java14
29 files changed, 486 insertions, 256 deletions
diff --git a/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java b/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java
index 9ac611f..5f371a5 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/DecompiledClassSource.java
@@ -79,7 +79,7 @@ public class DecompiledClassSource {
79 return null; 79 return null;
80 } 80 }
81 81
82 private Optional<String> proposeName(EnigmaProject project, Entry<?> entry) { 82 public static Optional<String> proposeName(EnigmaProject project, Entry<?> entry) {
83 EnigmaServices services = project.getEnigma().getServices(); 83 EnigmaServices services = project.getEnigma().getServices();
84 84
85 return services.get(NameProposalService.TYPE).stream().flatMap(nameProposalService -> { 85 return services.get(NameProposalService.TYPE).stream().flatMap(nameProposalService -> {
diff --git a/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java b/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java
index b85851f..4e8940a 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/cfr/EnigmaDumper.java
@@ -148,7 +148,7 @@ public class EnigmaDumper extends StringStreamDumper {
148 continue; 148 continue;
149 } 149 }
150 150
151 String javaDoc = mapping.getJavadoc(); 151 String javaDoc = mapping.javadoc();
152 if (javaDoc != null) { 152 if (javaDoc != null) {
153 recordComponentDocs.add(String.format("@param %s %s", field.getFieldName(), javaDoc)); 153 recordComponentDocs.add(String.format("@param %s %s", field.getFieldName(), javaDoc));
154 } 154 }
@@ -159,7 +159,7 @@ public class EnigmaDumper extends StringStreamDumper {
159 159
160 String javadoc = null; 160 String javadoc = null;
161 if (mapping != null) { 161 if (mapping != null) {
162 javadoc = mapping.getJavadoc(); 162 javadoc = mapping.javadoc();
163 } 163 }
164 164
165 if (javadoc != null || !recordComponentDocs.isEmpty()) { 165 if (javadoc != null || !recordComponentDocs.isEmpty()) {
@@ -191,7 +191,7 @@ public class EnigmaDumper extends StringStreamDumper {
191 MethodEntry methodEntry = getMethodEntry(method); 191 MethodEntry methodEntry = getMethodEntry(method);
192 EntryMapping mapping = mapper.getDeobfMapping(methodEntry); 192 EntryMapping mapping = mapper.getDeobfMapping(methodEntry);
193 if (mapping != null) { 193 if (mapping != null) {
194 String javadoc = mapping.getJavadoc(); 194 String javadoc = mapping.javadoc();
195 if (javadoc != null) { 195 if (javadoc != null) {
196 lines.addAll(Arrays.asList(javadoc.split("\\R"))); 196 lines.addAll(Arrays.asList(javadoc.split("\\R")));
197 } 197 }
@@ -204,9 +204,9 @@ public class EnigmaDumper extends StringStreamDumper {
204 if (each instanceof LocalVariableEntry) { 204 if (each instanceof LocalVariableEntry) {
205 EntryMapping paramMapping = mapper.getDeobfMapping(each); 205 EntryMapping paramMapping = mapper.getDeobfMapping(each);
206 if (paramMapping != null) { 206 if (paramMapping != null) {
207 String javadoc = paramMapping.getJavadoc(); 207 String javadoc = paramMapping.javadoc();
208 if (javadoc != null) { 208 if (javadoc != null) {
209 lines.addAll(Arrays.asList(("@param " + paramMapping.getTargetName() + " " + javadoc).split("\\R"))); 209 lines.addAll(Arrays.asList(("@param " + paramMapping.targetName() + " " + javadoc).split("\\R")));
210 } 210 }
211 } 211 }
212 } 212 }
@@ -230,7 +230,7 @@ public class EnigmaDumper extends StringStreamDumper {
230 if (mapper != null && !recordComponent) { 230 if (mapper != null && !recordComponent) {
231 EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getDescriptor())); 231 EntryMapping mapping = mapper.getDeobfMapping(getFieldEntry(owner, field.getFieldName(), field.getDescriptor()));
232 if (mapping != null) { 232 if (mapping != null) {
233 String javadoc = mapping.getJavadoc(); 233 String javadoc = mapping.javadoc();
234 if (javadoc != null) { 234 if (javadoc != null) {
235 print("/**").newln(); 235 print("/**").newln();
236 for (String line : javadoc.split("\\R")) { 236 for (String line : javadoc.split("\\R")) {
diff --git a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java
index 70fc8c6..1e5beb1 100644
--- a/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java
+++ b/enigma/src/main/java/cuchaz/enigma/source/procyon/transformers/AddJavadocsAstTransform.java
@@ -47,16 +47,17 @@ public final class AddJavadocsAstTransform implements IAstTransform {
47 47
48 private <T extends AstNode> Comment[] getComments(T node, Function<T, Entry<?>> retriever) { 48 private <T extends AstNode> Comment[] getComments(T node, Function<T, Entry<?>> retriever) {
49 final EntryMapping mapping = remapper.getDeobfMapping(retriever.apply(node)); 49 final EntryMapping mapping = remapper.getDeobfMapping(retriever.apply(node));
50 final String docs = mapping == null ? null : Strings.emptyToNull(mapping.getJavadoc()); 50 final String docs = Strings.emptyToNull(mapping.javadoc());
51 return docs == null ? null : Stream.of(docs.split("\\R")).map(st -> new Comment(st, 51 return docs == null ? null : Stream.of(docs.split("\\R")).map(st -> new Comment(st,
52 CommentType.Documentation)).toArray(Comment[]::new); 52 CommentType.Documentation)).toArray(Comment[]::new);
53 } 53 }
54 54
55 private Comment[] getParameterComments(ParameterDeclaration node, Function<ParameterDeclaration, Entry<?>> retriever) { 55 private Comment[] getParameterComments(ParameterDeclaration node, Function<ParameterDeclaration, Entry<?>> retriever) {
56 final EntryMapping mapping = remapper.getDeobfMapping(retriever.apply(node)); 56 Entry<?> entry = retriever.apply(node);
57 final EntryMapping mapping = remapper.getDeobfMapping(entry);
57 final Comment[] ret = getComments(node, retriever); 58 final Comment[] ret = getComments(node, retriever);
58 if (ret != null) { 59 if (ret != null) {
59 final String paramPrefix = "@param " + mapping.getTargetName() + " "; 60 final String paramPrefix = "@param " + (mapping.targetName() != null ? mapping.targetName() : entry.getName()) + " ";
60 final String indent = Strings.repeat(" ", paramPrefix.length()); 61 final String indent = Strings.repeat(" ", paramPrefix.length());
61 ret[0].setContent(paramPrefix + ret[0].getContent()); 62 ret[0].setContent(paramPrefix + ret[0].getContent());
62 for (int i = 1; i < ret.length; i++) { 63 for (int i = 1; i < ret.length; i++) {
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryChange.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryChange.java
new file mode 100644
index 0000000..b5ec855
--- /dev/null
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryChange.java
@@ -0,0 +1,97 @@
1package cuchaz.enigma.translation.mapping;
2
3import java.util.Objects;
4import java.util.Optional;
5
6import javax.annotation.Nullable;
7
8import cuchaz.enigma.EnigmaProject;
9import cuchaz.enigma.source.DecompiledClassSource;
10import cuchaz.enigma.translation.representation.entry.Entry;
11import cuchaz.enigma.utils.TristateChange;
12
13public final class EntryChange<E extends Entry<?>> {
14
15 private final E target;
16 private final TristateChange<String> deobfName;
17 private final TristateChange<String> javadoc;
18 private final TristateChange<AccessModifier> access;
19
20 private EntryChange(E target, TristateChange<String> deobfName, TristateChange<String> javadoc, TristateChange<AccessModifier> access) {
21 this.target = target;
22 this.deobfName = deobfName;
23 this.javadoc = javadoc;
24 this.access = access;
25 }
26
27 public static <E extends Entry<?>> EntryChange<E> modify(E target) {
28 return new EntryChange<>(target, TristateChange.unchanged(), TristateChange.unchanged(), TristateChange.unchanged());
29 }
30
31 public EntryChange<E> withDeobfName(String name) {
32 return new EntryChange<>(this.target, TristateChange.set(name), this.javadoc, this.access);
33 }
34
35 public EntryChange<E> withDefaultDeobfName(@Nullable EnigmaProject project) {
36 Optional<String> proposed = project != null ? DecompiledClassSource.proposeName(project, this.target) : Optional.empty();
37 return this.withDeobfName(proposed.orElse(this.target.getName()));
38 }
39
40 public EntryChange<E> clearDeobfName() {
41 return new EntryChange<>(this.target, TristateChange.reset(), this.javadoc, this.access);
42 }
43
44 public EntryChange<E> withJavadoc(String javadoc) {
45 return new EntryChange<>(this.target, this.deobfName, TristateChange.set(javadoc), this.access);
46 }
47
48 public EntryChange<E> clearJavadoc() {
49 return new EntryChange<>(this.target, this.deobfName, TristateChange.reset(), this.access);
50 }
51
52 public EntryChange<E> withAccess(AccessModifier access) {
53 return new EntryChange<>(this.target, this.deobfName, this.javadoc, TristateChange.set(access));
54 }
55
56 public EntryChange<E> clearAccess() {
57 return new EntryChange<>(this.target, this.deobfName, this.javadoc, TristateChange.reset());
58 }
59
60 public TristateChange<String> getDeobfName() {
61 return this.deobfName;
62 }
63
64 public TristateChange<String> getJavadoc() {
65 return this.javadoc;
66 }
67
68 public TristateChange<AccessModifier> getAccess() {
69 return this.access;
70 }
71
72 public E getTarget() {
73 return this.target;
74 }
75
76 @Override
77 public boolean equals(Object o) {
78 if (this == o) return true;
79 if (!(o instanceof EntryChange)) return false;
80 EntryChange<?> that = (EntryChange<?>) o;
81 return Objects.equals(this.target, that.target) &&
82 Objects.equals(this.deobfName, that.deobfName) &&
83 Objects.equals(this.javadoc, that.javadoc) &&
84 Objects.equals(this.access, that.access);
85 }
86
87 @Override
88 public int hashCode() {
89 return Objects.hash(this.target, this.deobfName, this.javadoc, this.access);
90 }
91
92 @Override
93 public String toString() {
94 return String.format("EntryChange { target: %s, deobfName: %s, javadoc: %s, access: %s }", this.target, this.deobfName, this.javadoc, this.access);
95 }
96
97}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java
index c607817..e916bf3 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryMapping.java
@@ -1,49 +1,37 @@
1package cuchaz.enigma.translation.mapping; 1package cuchaz.enigma.translation.mapping;
2 2
3import java.util.Arrays;
4
3import javax.annotation.Nonnull; 5import javax.annotation.Nonnull;
4import javax.annotation.Nullable; 6import javax.annotation.Nullable;
5 7
6public class EntryMapping { 8public record EntryMapping(
7 private final String targetName; 9 @Nullable String targetName,
8 private final AccessModifier accessModifier; 10 @Nonnull AccessModifier accessModifier,
9 private final @Nullable String javadoc; 11 @Nullable String javadoc
12) {
13 public static final EntryMapping DEFAULT = new EntryMapping(null, AccessModifier.UNCHANGED, null);
14
15 public EntryMapping {
16 if (accessModifier == null) {
17 accessModifier = AccessModifier.UNCHANGED;
18 System.err.println("EntryMapping initialized with 'null' accessModifier, assuming UNCHANGED. Please fix.");
19 Arrays.stream(new Exception().getStackTrace()).skip(1).map("\tat %s"::formatted).forEach(System.err::println);
20 }
21 }
10 22
11 public EntryMapping(@Nonnull String targetName) { 23 public EntryMapping(@Nullable String targetName) {
12 this(targetName, AccessModifier.UNCHANGED); 24 this(targetName, AccessModifier.UNCHANGED);
13 } 25 }
14 26
15 public EntryMapping(@Nonnull String targetName, @Nullable String javadoc) { 27 public EntryMapping(@Nullable String targetName, @Nullable String javadoc) {
16 this(targetName, AccessModifier.UNCHANGED, javadoc); 28 this(targetName, AccessModifier.UNCHANGED, javadoc);
17 } 29 }
18 30
19 public EntryMapping(@Nonnull String targetName, AccessModifier accessModifier) { 31 public EntryMapping(@Nullable String targetName, AccessModifier accessModifier) {
20 this(targetName, accessModifier, null); 32 this(targetName, accessModifier, null);
21 } 33 }
22 34
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) { 35 public EntryMapping withName(String newName) {
48 return new EntryMapping(newName, accessModifier, javadoc); 36 return new EntryMapping(newName, accessModifier, javadoc);
49 } 37 }
@@ -55,21 +43,4 @@ public class EntryMapping {
55 public EntryMapping withDocs(String newDocs) { 43 public EntryMapping withDocs(String newDocs) {
56 return new EntryMapping(targetName, accessModifier, newDocs); 44 return new EntryMapping(targetName, accessModifier, newDocs);
57 } 45 }
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} 46}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java
index 8b5490e..0977b74 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryRemapper.java
@@ -1,10 +1,11 @@
1package cuchaz.enigma.translation.mapping; 1package cuchaz.enigma.translation.mapping;
2 2
3import java.util.Collection; 3import java.util.Collection;
4import java.util.Objects;
4import java.util.List; 5import java.util.List;
5import java.util.stream.Stream; 6import java.util.stream.Stream;
6 7
7import javax.annotation.Nullable; 8import javax.annotation.Nonnull;
8 9
9import cuchaz.enigma.analysis.index.JarIndex; 10import cuchaz.enigma.analysis.index.JarIndex;
10import cuchaz.enigma.translation.MappingTranslator; 11import cuchaz.enigma.translation.MappingTranslator;
@@ -50,15 +51,15 @@ public class EntryRemapper {
50 return new EntryRemapper(index, new HashEntryTree<>()); 51 return new EntryRemapper(index, new HashEntryTree<>());
51 } 52 }
52 53
53 public <E extends Entry<?>> void mapFromObf(ValidationContext vc, E obfuscatedEntry, @Nullable EntryMapping deobfMapping) { 54 public void validatePutMapping(ValidationContext vc, Entry<?> obfuscatedEntry, @Nonnull EntryMapping deobfMapping) {
54 mapFromObf(vc, obfuscatedEntry, deobfMapping, true); 55 doPutMapping(vc, obfuscatedEntry, deobfMapping, true);
55 } 56 }
56 57
57 public <E extends Entry<?>> void mapFromObf(ValidationContext vc, E obfuscatedEntry, @Nullable EntryMapping deobfMapping, boolean renaming) { 58 public void putMapping(ValidationContext vc, Entry<?> obfuscatedEntry, @Nonnull EntryMapping deobfMapping) {
58 mapFromObf(vc, obfuscatedEntry, deobfMapping, renaming, false); 59 doPutMapping(vc, obfuscatedEntry, deobfMapping, false);
59 } 60 }
60 61
61 public <E extends Entry<?>> void mapFromObf(ValidationContext vc, E obfuscatedEntry, @Nullable EntryMapping deobfMapping, boolean renaming, boolean validateOnly) { 62 private void doPutMapping(ValidationContext vc, Entry<?> obfuscatedEntry, @Nonnull EntryMapping deobfMapping, boolean validateOnly) {
62 if (obfuscatedEntry instanceof FieldEntry) { 63 if (obfuscatedEntry instanceof FieldEntry) {
63 FieldEntry fieldEntry = (FieldEntry) obfuscatedEntry; 64 FieldEntry fieldEntry = (FieldEntry) obfuscatedEntry;
64 ClassEntry classEntry = fieldEntry.getParent(); 65 ClassEntry classEntry = fieldEntry.getParent();
@@ -66,25 +67,27 @@ public class EntryRemapper {
66 mapRecordComponentGetter(vc, classEntry, fieldEntry, deobfMapping); 67 mapRecordComponentGetter(vc, classEntry, fieldEntry, deobfMapping);
67 } 68 }
68 69
69 Collection<E> resolvedEntries = obfResolver.resolveEntry(obfuscatedEntry, renaming ? ResolutionStrategy.RESOLVE_ROOT : ResolutionStrategy.RESOLVE_CLOSEST); 70 boolean renaming = !Objects.equals(getDeobfMapping(obfuscatedEntry).targetName(), deobfMapping.targetName());
70 71
71 if (renaming && deobfMapping != null) { 72 Collection<Entry<?>> resolvedEntries = obfResolver.resolveEntry(obfuscatedEntry, renaming ? ResolutionStrategy.RESOLVE_ROOT : ResolutionStrategy.RESOLVE_CLOSEST);
72 for (E resolvedEntry : resolvedEntries) { 73
73 validator.validateRename(vc, resolvedEntry, deobfMapping.getTargetName()); 74 if (renaming && deobfMapping.targetName() != null) {
75 for (Entry<?> resolvedEntry : resolvedEntries) {
76 validator.validateRename(vc, resolvedEntry, deobfMapping.targetName());
74 } 77 }
75 } 78 }
76 79
77 if (validateOnly || !vc.canProceed()) return; 80 if (validateOnly || !vc.canProceed()) return;
78 81
79 for (E resolvedEntry : resolvedEntries) { 82 for (Entry<?> resolvedEntry : resolvedEntries) {
80 obfToDeobf.insert(resolvedEntry, deobfMapping); 83 if (deobfMapping.equals(EntryMapping.DEFAULT)) {
84 obfToDeobf.insert(resolvedEntry, null);
85 } else {
86 obfToDeobf.insert(resolvedEntry, deobfMapping);
87 }
81 } 88 }
82 } 89 }
83 90
84 public void removeByObf(ValidationContext vc, Entry<?> obfuscatedEntry) {
85 mapFromObf(vc, obfuscatedEntry, null);
86 }
87
88 // A little bit of a hack to also map the getter method for record fields/components. 91 // A little bit of a hack to also map the getter method for record fields/components.
89 private void mapRecordComponentGetter(ValidationContext vc, ClassEntry classEntry, FieldEntry fieldEntry, EntryMapping fieldMapping) { 92 private void mapRecordComponentGetter(ValidationContext vc, ClassEntry classEntry, FieldEntry fieldEntry, EntryMapping fieldMapping) {
90 if (!jarIndex.getEntryIndex().getClassAccess(classEntry).isRecord() || jarIndex.getEntryIndex().getFieldAccess(fieldEntry).isStatic()) { 93 if (!jarIndex.getEntryIndex().getClassAccess(classEntry).isRecord() || jarIndex.getEntryIndex().getFieldAccess(fieldEntry).isStatic()) {
@@ -107,20 +110,17 @@ public class EntryRemapper {
107 } 110 }
108 111
109 if (methodEntry == null && fieldMapping != null) { 112 if (methodEntry == null && fieldMapping != null) {
110 vc.raise(Message.UNKNOWN_RECORD_GETTER, fieldMapping.getTargetName()); 113 vc.raise(Message.UNKNOWN_RECORD_GETTER, fieldMapping.targetName());
111 return; 114 return;
112 } 115 }
113 116
114 mapFromObf(vc, methodEntry, fieldMapping != null ? new EntryMapping(fieldMapping.getTargetName()) : null); 117 putMapping(vc, methodEntry, fieldMapping != null ? new EntryMapping(fieldMapping.targetName()) : null);
115 } 118 }
116 119
117 @Nullable 120 @Nonnull
118 public EntryMapping getDeobfMapping(Entry<?> entry) { 121 public EntryMapping getDeobfMapping(Entry<?> entry) {
119 return obfToDeobf.get(entry); 122 EntryMapping entryMapping = obfToDeobf.get(entry);
120 } 123 return entryMapping == null ? EntryMapping.DEFAULT : entryMapping;
121
122 public boolean hasDeobfMapping(Entry<?> obfEntry) {
123 return obfToDeobf.contains(obfEntry);
124 } 124 }
125 125
126 public <T extends Translatable> TranslateResult<T> extendedDeobfuscate(T translatable) { 126 public <T extends Translatable> TranslateResult<T> extendedDeobfuscate(T translatable) {
@@ -158,4 +158,9 @@ public class EntryRemapper {
158 public EntryResolver getObfResolver() { 158 public EntryResolver getObfResolver() {
159 return obfResolver; 159 return obfResolver;
160 } 160 }
161
162 public MappingValidator getValidator() {
163 return validator;
164 }
165
161} 166}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryUtil.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryUtil.java
new file mode 100644
index 0000000..582076c
--- /dev/null
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/EntryUtil.java
@@ -0,0 +1,42 @@
1package cuchaz.enigma.translation.mapping;
2
3import javax.annotation.Nonnull;
4
5import cuchaz.enigma.translation.representation.entry.Entry;
6import cuchaz.enigma.utils.validation.ValidationContext;
7
8public class EntryUtil {
9
10 public static EntryMapping applyChange(ValidationContext vc, EntryRemapper remapper, EntryChange<?> change) {
11 Entry<?> target = change.getTarget();
12 EntryMapping prev = remapper.getDeobfMapping(target);
13 EntryMapping mapping = EntryUtil.applyChange(prev, change);
14
15 remapper.putMapping(vc, target, mapping);
16
17 return mapping;
18 }
19
20 public static EntryMapping applyChange(@Nonnull EntryMapping self, EntryChange<?> change) {
21 if (change.getDeobfName().isSet()) {
22 self = self.withName(change.getDeobfName().getNewValue());
23 } else if (change.getDeobfName().isReset()) {
24 self = self.withName(null);
25 }
26
27 if (change.getJavadoc().isSet()) {
28 self = self.withDocs(change.getJavadoc().getNewValue());
29 } else if (change.getJavadoc().isReset()) {
30 self = self.withDocs(null);
31 }
32
33 if (change.getAccess().isSet()) {
34 self = self.withModifier(change.getAccess().getNewValue());
35 } else if (change.getAccess().isReset()) {
36 self = self.withModifier(AccessModifier.UNCHANGED);
37 }
38
39 return self;
40 }
41
42}
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java
index 1615912..065e5c3 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingValidator.java
@@ -25,18 +25,22 @@ public class MappingValidator {
25 this.index = index; 25 this.index = index;
26 } 26 }
27 27
28 public void validateRename(ValidationContext vc, Entry<?> entry, String name) { 28 public boolean validateRename(ValidationContext vc, Entry<?> entry, String name) {
29 Collection<Entry<?>> equivalentEntries = index.getEntryResolver().resolveEquivalentEntries(entry); 29 Collection<Entry<?>> equivalentEntries = index.getEntryResolver().resolveEquivalentEntries(entry);
30 boolean error = false;
30 for (Entry<?> equivalentEntry : equivalentEntries) { 31 for (Entry<?> equivalentEntry : equivalentEntries) {
31 equivalentEntry.validateName(vc, name); 32 equivalentEntry.validateName(vc, name);
32 validateUnique(vc, equivalentEntry, name); 33 error |= validateUnique(vc, equivalentEntry, name);
33 } 34 }
35 return error;
34 } 36 }
35 37
36 private void validateUnique(ValidationContext vc, Entry<?> entry, String name) { 38 private boolean validateUnique(ValidationContext vc, Entry<?> entry, String name) {
37 ClassEntry containingClass = entry.getContainingClass(); 39 ClassEntry containingClass = entry.getContainingClass();
38 Collection<ClassEntry> relatedClasses = getRelatedClasses(containingClass); 40 Collection<ClassEntry> relatedClasses = getRelatedClasses(containingClass);
39 41
42 boolean error = false;
43
40 for (ClassEntry relatedClass : relatedClasses) { 44 for (ClassEntry relatedClass : relatedClasses) {
41 Entry<?> relatedEntry = entry.replaceAncestor(containingClass, relatedClass); 45 Entry<?> relatedEntry = entry.replaceAncestor(containingClass, relatedClass);
42 Entry<?> translatedEntry = deobfuscator.translate(relatedEntry); 46 Entry<?> translatedEntry = deobfuscator.translate(relatedEntry);
@@ -52,8 +56,11 @@ public class MappingValidator {
52 } else { 56 } else {
53 vc.raise(Message.NONUNIQUE_NAME, name); 57 vc.raise(Message.NONUNIQUE_NAME, name);
54 } 58 }
59 error = true;
55 } 60 }
56 } 61 }
62
63 return error;
57 } 64 }
58 65
59 private Collection<ClassEntry> getRelatedClasses(ClassEntry classEntry) { 66 private Collection<ClassEntry> getRelatedClasses(ClassEntry classEntry) {
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java
index 141a07c..392b1a6 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/MappingsChecker.java
@@ -75,7 +75,7 @@ public class MappingsChecker {
75 private final Map<Entry<?>, String> droppedMappings = new HashMap<>(); 75 private final Map<Entry<?>, String> droppedMappings = new HashMap<>();
76 76
77 public void drop(Entry<?> entry, EntryMapping mapping) { 77 public void drop(Entry<?> entry, EntryMapping mapping) {
78 droppedMappings.put(entry, mapping.getTargetName()); 78 droppedMappings.put(entry, mapping.targetName() != null ? mapping.targetName() : entry.getName());
79 } 79 }
80 80
81 void apply(EntryTree<EntryMapping> mappings) { 81 void apply(EntryTree<EntryMapping> mappings) {
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/RawEntryMapping.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/RawEntryMapping.java
index 79587a0..6465008 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/RawEntryMapping.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/RawEntryMapping.java
@@ -1,23 +1,23 @@
1package cuchaz.enigma.translation.mapping.serde; 1package cuchaz.enigma.translation.mapping.serde;
2 2
3import cuchaz.enigma.translation.mapping.AccessModifier;
4import cuchaz.enigma.translation.mapping.EntryMapping;
5
6import java.util.ArrayList; 3import java.util.ArrayList;
7import java.util.List; 4import java.util.List;
8 5
6import cuchaz.enigma.translation.mapping.AccessModifier;
7import cuchaz.enigma.translation.mapping.EntryMapping;
8
9public final class RawEntryMapping { 9public final class RawEntryMapping {
10 private final String targetName; 10 private final String targetName;
11 private final AccessModifier access; 11 private final AccessModifier access;
12 private List<String> javadocs = new ArrayList<>(); 12 private final List<String> javadocs = new ArrayList<>();
13 13
14 public RawEntryMapping(String targetName) { 14 public RawEntryMapping(String targetName) {
15 this(targetName, null); 15 this(targetName, AccessModifier.UNCHANGED);
16 } 16 }
17 17
18 public RawEntryMapping(String targetName, AccessModifier access) { 18 public RawEntryMapping(String targetName, AccessModifier access) {
19 this.access = access; 19 this.access = access;
20 this.targetName = targetName; 20 this.targetName = targetName != null && !targetName.equals("-") ? targetName : null;
21 } 21 }
22 22
23 public void addJavadocLine(String line) { 23 public void addJavadocLine(String line) {
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java
index 27545c0..ccc25df 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsReader.java
@@ -1,15 +1,21 @@
1package cuchaz.enigma.translation.mapping.serde.enigma; 1package cuchaz.enigma.translation.mapping.serde.enigma;
2 2
3import java.io.IOException;
4import java.nio.file.FileSystem;
5import java.nio.file.FileSystems;
6import java.nio.file.Files;
7import java.nio.file.Path;
8import java.util.*;
9
10import javax.annotation.Nullable;
11
3import com.google.common.base.Charsets; 12import com.google.common.base.Charsets;
13
4import cuchaz.enigma.ProgressListener; 14import cuchaz.enigma.ProgressListener;
5import cuchaz.enigma.translation.mapping.serde.MappingParseException;
6import cuchaz.enigma.translation.mapping.AccessModifier; 15import cuchaz.enigma.translation.mapping.AccessModifier;
7import cuchaz.enigma.translation.mapping.EntryMapping; 16import cuchaz.enigma.translation.mapping.EntryMapping;
8import cuchaz.enigma.translation.mapping.MappingPair; 17import cuchaz.enigma.translation.mapping.MappingPair;
9import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters; 18import cuchaz.enigma.translation.mapping.serde.*;
10import cuchaz.enigma.translation.mapping.serde.MappingHelper;
11import cuchaz.enigma.translation.mapping.serde.MappingsReader;
12import cuchaz.enigma.translation.mapping.serde.RawEntryMapping;
13import cuchaz.enigma.translation.mapping.tree.EntryTree; 19import cuchaz.enigma.translation.mapping.tree.EntryTree;
14import cuchaz.enigma.translation.mapping.tree.HashEntryTree; 20import cuchaz.enigma.translation.mapping.tree.HashEntryTree;
15import cuchaz.enigma.translation.representation.MethodDescriptor; 21import cuchaz.enigma.translation.representation.MethodDescriptor;
@@ -17,18 +23,6 @@ import cuchaz.enigma.translation.representation.TypeDescriptor;
17import cuchaz.enigma.translation.representation.entry.*; 23import cuchaz.enigma.translation.representation.entry.*;
18import cuchaz.enigma.utils.I18n; 24import cuchaz.enigma.utils.I18n;
19 25
20import javax.annotation.Nullable;
21import java.io.IOException;
22import java.nio.file.FileSystem;
23import java.nio.file.FileSystems;
24import java.nio.file.Files;
25import java.nio.file.Path;
26import java.util.ArrayDeque;
27import java.util.Arrays;
28import java.util.Deque;
29import java.util.List;
30import java.util.Locale;
31
32public enum EnigmaMappingsReader implements MappingsReader { 26public enum EnigmaMappingsReader implements MappingsReader {
33 FILE { 27 FILE {
34 @Override 28 @Override
@@ -200,12 +194,12 @@ public enum EnigmaMappingsReader implements MappingsReader {
200 throw new RuntimeException("Unknown token '" + keyToken + "'"); 194 throw new RuntimeException("Unknown token '" + keyToken + "'");
201 } 195 }
202 } 196 }
203 197
204 private static void readJavadoc(MappingPair<?, RawEntryMapping> parent, String[] tokens) { 198 private static void readJavadoc(MappingPair<?, RawEntryMapping> parent, String[] tokens) {
205 if (parent == null) 199 if (parent == null)
206 throw new IllegalStateException("Javadoc has no parent!"); 200 throw new IllegalStateException("Javadoc has no parent!");
207 // Empty string to concat 201 // Empty string to concat
208 String jdLine = tokens.length > 1 ? String.join(" ", Arrays.copyOfRange(tokens,1,tokens.length)) : ""; 202 String jdLine = tokens.length > 1 ? String.join(" ", Arrays.copyOfRange(tokens, 1, tokens.length)) : "";
209 if (parent.getMapping() == null) { 203 if (parent.getMapping() == null) {
210 parent.setMapping(new RawEntryMapping(parent.getEntry().getName(), AccessModifier.UNCHANGED)); 204 parent.setMapping(new RawEntryMapping(parent.getEntry().getName(), AccessModifier.UNCHANGED));
211 } 205 }
@@ -237,11 +231,7 @@ public enum EnigmaMappingsReader implements MappingsReader {
237 modifier = parseModifier(tokens[3]); 231 modifier = parseModifier(tokens[3]);
238 } 232 }
239 233
240 if (mapping != null) { 234 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping, modifier));
241 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping, modifier));
242 } else {
243 return new MappingPair<>(obfuscatedEntry);
244 }
245 } 235 }
246 236
247 private static MappingPair<FieldEntry, RawEntryMapping> parseField(@Nullable Entry<?> parent, String[] tokens) { 237 private static MappingPair<FieldEntry, RawEntryMapping> parseField(@Nullable Entry<?> parent, String[] tokens) {
@@ -252,7 +242,7 @@ public enum EnigmaMappingsReader implements MappingsReader {
252 ClassEntry ownerEntry = (ClassEntry) parent; 242 ClassEntry ownerEntry = (ClassEntry) parent;
253 243
254 String obfuscatedName = tokens[1]; 244 String obfuscatedName = tokens[1];
255 String mapping = obfuscatedName; 245 String mapping = null;
256 AccessModifier modifier = AccessModifier.UNCHANGED; 246 AccessModifier modifier = AccessModifier.UNCHANGED;
257 TypeDescriptor descriptor; 247 TypeDescriptor descriptor;
258 248
@@ -269,19 +259,15 @@ public enum EnigmaMappingsReader implements MappingsReader {
269 descriptor = new TypeDescriptor(tokens[3]); 259 descriptor = new TypeDescriptor(tokens[3]);
270 } 260 }
271 } else if (tokens.length == 5) { 261 } else if (tokens.length == 5) {
272 descriptor = new TypeDescriptor(tokens[3]);
273 mapping = tokens[2]; 262 mapping = tokens[2];
274 modifier = parseModifier(tokens[4]); 263 modifier = parseModifier(tokens[3]);
264 descriptor = new TypeDescriptor(tokens[4]);
275 } else { 265 } else {
276 throw new RuntimeException("Invalid field declaration"); 266 throw new RuntimeException("Invalid field declaration");
277 } 267 }
278 268
279 FieldEntry obfuscatedEntry = new FieldEntry(ownerEntry, obfuscatedName, descriptor); 269 FieldEntry obfuscatedEntry = new FieldEntry(ownerEntry, obfuscatedName, descriptor);
280 if (mapping != null) { 270 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping, modifier));
281 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping, modifier));
282 } else {
283 return new MappingPair<>(obfuscatedEntry);
284 }
285 } 271 }
286 272
287 private static MappingPair<MethodEntry, RawEntryMapping> parseMethod(@Nullable Entry<?> parent, String[] tokens) { 273 private static MappingPair<MethodEntry, RawEntryMapping> parseMethod(@Nullable Entry<?> parent, String[] tokens) {
@@ -317,11 +303,7 @@ public enum EnigmaMappingsReader implements MappingsReader {
317 } 303 }
318 304
319 MethodEntry obfuscatedEntry = new MethodEntry(ownerEntry, obfuscatedName, descriptor); 305 MethodEntry obfuscatedEntry = new MethodEntry(ownerEntry, obfuscatedName, descriptor);
320 if (mapping != null) { 306 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping, modifier));
321 return new MappingPair<>(obfuscatedEntry, new RawEntryMapping(mapping, modifier));
322 } else {
323 return new MappingPair<>(obfuscatedEntry);
324 }
325 } 307 }
326 308
327 private static MappingPair<LocalVariableEntry, RawEntryMapping> parseArgument(@Nullable Entry<?> parent, String[] tokens) { 309 private static MappingPair<LocalVariableEntry, RawEntryMapping> parseArgument(@Nullable Entry<?> parent, String[] tokens) {
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java
index d9f1470..5d7a789 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/enigma/EnigmaMappingsWriter.java
@@ -15,12 +15,7 @@ import java.io.IOException;
15import java.io.PrintWriter; 15import java.io.PrintWriter;
16import java.net.URI; 16import java.net.URI;
17import java.net.URISyntaxException; 17import java.net.URISyntaxException;
18import java.nio.file.DirectoryStream; 18import java.nio.file.*;
19import java.nio.file.FileSystem;
20import java.nio.file.FileSystems;
21import java.nio.file.Files;
22import java.nio.file.Path;
23import java.nio.file.Paths;
24import java.util.ArrayList; 19import java.util.ArrayList;
25import java.util.Collection; 20import java.util.Collection;
26import java.util.Collections; 21import java.util.Collections;
@@ -28,25 +23,19 @@ import java.util.Objects;
28import java.util.concurrent.atomic.AtomicInteger; 23import java.util.concurrent.atomic.AtomicInteger;
29import java.util.stream.Stream; 24import java.util.stream.Stream;
30 25
26import javax.annotation.Nonnull;
27
31import cuchaz.enigma.ProgressListener; 28import cuchaz.enigma.ProgressListener;
32import cuchaz.enigma.translation.MappingTranslator; 29import cuchaz.enigma.translation.MappingTranslator;
33import cuchaz.enigma.translation.Translator; 30import cuchaz.enigma.translation.Translator;
34import cuchaz.enigma.translation.mapping.AccessModifier; 31import cuchaz.enigma.translation.mapping.AccessModifier;
35import cuchaz.enigma.translation.mapping.EntryMapping; 32import cuchaz.enigma.translation.mapping.EntryMapping;
36import cuchaz.enigma.translation.mapping.MappingDelta; 33import cuchaz.enigma.translation.mapping.MappingDelta;
37import cuchaz.enigma.translation.mapping.serde.MappingFileNameFormat;
38import cuchaz.enigma.translation.mapping.serde.MappingSaveParameters;
39import cuchaz.enigma.translation.mapping.VoidEntryResolver; 34import cuchaz.enigma.translation.mapping.VoidEntryResolver;
40import cuchaz.enigma.translation.mapping.serde.LfPrintWriter; 35import cuchaz.enigma.translation.mapping.serde.*;
41import cuchaz.enigma.translation.mapping.serde.MappingHelper;
42import cuchaz.enigma.translation.mapping.serde.MappingsWriter;
43import cuchaz.enigma.translation.mapping.tree.EntryTree; 36import cuchaz.enigma.translation.mapping.tree.EntryTree;
44import cuchaz.enigma.translation.mapping.tree.EntryTreeNode; 37import cuchaz.enigma.translation.mapping.tree.EntryTreeNode;
45import cuchaz.enigma.translation.representation.entry.ClassEntry; 38import cuchaz.enigma.translation.representation.entry.*;
46import cuchaz.enigma.translation.representation.entry.Entry;
47import cuchaz.enigma.translation.representation.entry.FieldEntry;
48import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
49import cuchaz.enigma.translation.representation.entry.MethodEntry;
50import cuchaz.enigma.utils.I18n; 39import cuchaz.enigma.utils.I18n;
51 40
52public enum EnigmaMappingsWriter implements MappingsWriter { 41public enum EnigmaMappingsWriter implements MappingsWriter {
@@ -184,19 +173,22 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
184 173
185 EntryMapping classEntryMapping = mappings.get(classEntry); 174 EntryMapping classEntryMapping = mappings.get(classEntry);
186 175
176 if (classEntryMapping == null) {
177 classEntryMapping = EntryMapping.DEFAULT;
178 }
179
187 writer.println(writeClass(classEntry, classEntryMapping).trim()); 180 writer.println(writeClass(classEntry, classEntryMapping).trim());
188 if (classEntryMapping != null && classEntryMapping.getJavadoc() != null) { 181 if (classEntryMapping.javadoc() != null) {
189 writeDocs(writer, classEntryMapping, 0); 182 writeDocs(writer, classEntryMapping, 0);
190 } 183 }
191 184
192 for (Entry<?> child : children) { 185 for (Entry<?> child : children) {
193 writeEntry(writer, mappings, child, 1); 186 writeEntry(writer, mappings, child, 1);
194 } 187 }
195
196 } 188 }
197 189
198 private void writeDocs(PrintWriter writer, EntryMapping mapping, int depth) { 190 private void writeDocs(PrintWriter writer, EntryMapping mapping, int depth) {
199 String jd = mapping.getJavadoc(); 191 String jd = mapping.javadoc();
200 if (jd != null) { 192 if (jd != null) {
201 for (String line : jd.split("\\R")) { 193 for (String line : jd.split("\\R")) {
202 writer.println(indent(EnigmaFormat.COMMENT + " " + MappingHelper.escape(line), depth + 1)); 194 writer.println(indent(EnigmaFormat.COMMENT + " " + MappingHelper.escape(line), depth + 1));
@@ -212,20 +204,26 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
212 204
213 EntryMapping mapping = node.getValue(); 205 EntryMapping mapping = node.getValue();
214 206
215 if (entry instanceof ClassEntry) { 207 if (mapping == null) {
216 String line = writeClass((ClassEntry) entry, mapping); 208 mapping = EntryMapping.DEFAULT;
217 writer.println(indent(line, depth)); 209 }
218 } else if (entry instanceof MethodEntry) { 210
219 String line = writeMethod((MethodEntry) entry, mapping); 211 String line = null;
220 writer.println(indent(line, depth)); 212 if (entry instanceof ClassEntry classEntry) {
221 } else if (entry instanceof FieldEntry) { 213 line = writeClass(classEntry, mapping);
222 String line = writeField((FieldEntry) entry, mapping); 214 } else if (entry instanceof MethodEntry methodEntry) {
223 writer.println(indent(line, depth)); 215 line = writeMethod(methodEntry, mapping);
224 } else if (entry instanceof LocalVariableEntry && mapping != null) { 216 } else if (entry instanceof FieldEntry fieldEntry) {
225 String line = writeArgument((LocalVariableEntry) entry, mapping); 217 line = writeField(fieldEntry, mapping);
218 } else if (entry instanceof LocalVariableEntry varEntry && mapping.targetName() != null) {
219 line = writeArgument(varEntry, mapping);
220 }
221
222 if (line != null) {
226 writer.println(indent(line, depth)); 223 writer.println(indent(line, depth));
227 } 224 }
228 if (mapping != null && mapping.getJavadoc() != null) { 225
226 if (mapping.javadoc() != null) {
229 writeDocs(writer, mapping, depth); 227 writeDocs(writer, mapping, depth);
230 } 228 }
231 229
@@ -254,55 +252,53 @@ public enum EnigmaMappingsWriter implements MappingsWriter {
254 .forEach(result::add); 252 .forEach(result::add);
255 253
256 children.stream().filter(e -> e instanceof ClassEntry) 254 children.stream().filter(e -> e instanceof ClassEntry)
257 .map(e -> (ClassEntry) e) 255 .map(e -> (ClassEntry) e)
258 .sorted() 256 .sorted()
259 .forEach(result::add); 257 .forEach(result::add);
260 258
261 return result; 259 return result;
262 } 260 }
263 261
264 protected String writeClass(ClassEntry entry, EntryMapping mapping) { 262 protected String writeClass(ClassEntry entry, @Nonnull EntryMapping mapping) {
265 StringBuilder builder = new StringBuilder(EnigmaFormat.CLASS +" "); 263 StringBuilder builder = new StringBuilder(EnigmaFormat.CLASS + " ");
266 builder.append(entry.getName()).append(' '); 264 builder.append(entry.getName()).append(' ');
267 writeMapping(builder, mapping); 265 writeMapping(builder, mapping);
268 266
269 return builder.toString(); 267 return builder.toString();
270 } 268 }
271 269
272 protected String writeMethod(MethodEntry entry, EntryMapping mapping) { 270 protected String writeMethod(MethodEntry entry, @Nonnull EntryMapping mapping) {
273 StringBuilder builder = new StringBuilder(EnigmaFormat.METHOD + " "); 271 StringBuilder builder = new StringBuilder(EnigmaFormat.METHOD + " ");
274 builder.append(entry.getName()).append(' '); 272 builder.append(entry.getName()).append(' ');
275 if (mapping != null && !mapping.getTargetName().equals(entry.getName())) { 273 writeMapping(builder, mapping);
276 writeMapping(builder, mapping);
277 }
278 274
279 builder.append(entry.getDesc().toString()); 275 builder.append(entry.getDesc().toString());
280 276
281 return builder.toString(); 277 return builder.toString();
282 } 278 }
283 279
284 protected String writeField(FieldEntry entry, EntryMapping mapping) { 280 protected String writeField(FieldEntry entry, @Nonnull EntryMapping mapping) {
285 StringBuilder builder = new StringBuilder(EnigmaFormat.FIELD + " "); 281 StringBuilder builder = new StringBuilder(EnigmaFormat.FIELD + " ");
286 builder.append(entry.getName()).append(' '); 282 builder.append(entry.getName()).append(' ');
287 if (mapping != null && !mapping.getTargetName().equals(entry.getName())) { 283 writeMapping(builder, mapping);
288 writeMapping(builder, mapping);
289 }
290 284
291 builder.append(entry.getDesc().toString()); 285 builder.append(entry.getDesc().toString());
292 286
293 return builder.toString(); 287 return builder.toString();
294 } 288 }
295 289
296 protected String writeArgument(LocalVariableEntry entry, EntryMapping mapping) { 290 protected String writeArgument(LocalVariableEntry entry, @Nonnull EntryMapping mapping) {
297 return EnigmaFormat.PARAMETER + " " + entry.getIndex() + ' ' + mapping.getTargetName(); 291 return EnigmaFormat.PARAMETER + " " + entry.getIndex() + ' ' + mapping.targetName();
298 } 292 }
299 293
300 private void writeMapping(StringBuilder builder, EntryMapping mapping) { 294 private void writeMapping(StringBuilder builder, EntryMapping mapping) {
301 if (mapping != null) { 295 if (mapping.targetName() != null) {
302 builder.append(mapping.getTargetName()).append(' '); 296 builder.append(mapping.targetName()).append(' ');
303 if (mapping.getAccessModifier() != AccessModifier.UNCHANGED) { 297 if (mapping.accessModifier() != AccessModifier.UNCHANGED) {
304 builder.append(mapping.getAccessModifier().getFormattedName()).append(' '); 298 builder.append(mapping.accessModifier().getFormattedName()).append(' ');
305 } 299 }
300 } else if (mapping.accessModifier() != AccessModifier.UNCHANGED) {
301 builder.append("- ").append(mapping.accessModifier().getFormattedName()).append(' ');
306 } 302 }
307 } 303 }
308 304
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java
index 4f78e6f..1f785e1 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tiny/TinyMappingsWriter.java
@@ -73,13 +73,16 @@ public class TinyMappingsWriter implements MappingsWriter {
73 Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE); 73 Translator translator = new MappingTranslator(mappings, VoidEntryResolver.INSTANCE);
74 74
75 EntryMapping mapping = mappings.get(entry); 75 EntryMapping mapping = mappings.get(entry);
76 if (mapping != null && !entry.getName().equals(mapping.getTargetName())) { 76
77 // Do not write mappings without deobfuscated name since tiny v1 doesn't
78 // support comments anyway
79 if (mapping != null && mapping.targetName() != null) {
77 if (entry instanceof ClassEntry) { 80 if (entry instanceof ClassEntry) {
78 writeClass(writer, (ClassEntry) entry, translator); 81 writeClass(writer, (ClassEntry) entry, translator);
79 } else if (entry instanceof FieldEntry) { 82 } else if (entry instanceof FieldEntry) {
80 writeLine(writer, serializeEntry(entry, mapping.getTargetName())); 83 writeLine(writer, serializeEntry(entry, mapping.targetName()));
81 } else if (entry instanceof MethodEntry) { 84 } else if (entry instanceof MethodEntry) {
82 writeLine(writer, serializeEntry(entry, mapping.getTargetName())); 85 writeLine(writer, serializeEntry(entry, mapping.targetName()));
83 } 86 }
84 } 87 }
85 88
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java
index 5160eda..c400568 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/serde/tinyv2/TinyV2Writer.java
@@ -62,8 +62,8 @@ public final class TinyV2Writer implements MappingsWriter {
62 Deque<String> parts = new LinkedList<>(); 62 Deque<String> parts = new LinkedList<>();
63 do { 63 do {
64 EntryMapping mapping = tree.get(classEntry); 64 EntryMapping mapping = tree.get(classEntry);
65 if (mapping != null) { 65 if (mapping != null && mapping.targetName() != null) {
66 parts.addFirst(mapping.getTargetName()); 66 parts.addFirst(mapping.targetName());
67 } else { 67 } else {
68 parts.addFirst(classEntry.getName()); 68 parts.addFirst(classEntry.getName());
69 } 69 }
@@ -81,7 +81,7 @@ public final class TinyV2Writer implements MappingsWriter {
81 writeComment(writer, node.getValue(), 1); 81 writeComment(writer, node.getValue(), 1);
82 82
83 for (EntryTreeNode<EntryMapping> child : node.getChildNodes()) { 83 for (EntryTreeNode<EntryMapping> child : node.getChildNodes()) {
84 Entry entry = child.getEntry(); 84 Entry<?> entry = child.getEntry();
85 if (entry instanceof FieldEntry) { 85 if (entry instanceof FieldEntry) {
86 writeField(writer, child); 86 writeField(writer, child);
87 } else if (entry instanceof MethodEntry) { 87 } else if (entry instanceof MethodEntry) {
@@ -98,16 +98,21 @@ public final class TinyV2Writer implements MappingsWriter {
98 writer.print(node.getEntry().getName()); 98 writer.print(node.getEntry().getName());
99 writer.print("\t"); 99 writer.print("\t");
100 EntryMapping mapping = node.getValue(); 100 EntryMapping mapping = node.getValue();
101
101 if (mapping == null) { 102 if (mapping == null) {
102 writer.println(node.getEntry().getName()); // todo fix v2 name inference 103 mapping = EntryMapping.DEFAULT;
103 } else { 104 }
104 writer.println(mapping.getTargetName());
105 105
106 writeComment(writer, mapping, 2); 106 if (mapping.targetName() != null) {
107 writer.println(mapping.targetName());
108 } else {
109 writer.println(node.getEntry().getName()); // todo fix v2 name inference
107 } 110 }
108 111
112 writeComment(writer, mapping, 2);
113
109 for (EntryTreeNode<EntryMapping> child : node.getChildNodes()) { 114 for (EntryTreeNode<EntryMapping> child : node.getChildNodes()) {
110 Entry entry = child.getEntry(); 115 Entry<?> entry = child.getEntry();
111 if (entry instanceof LocalVariableEntry) { 116 if (entry instanceof LocalVariableEntry) {
112 writeParameter(writer, child); 117 writeParameter(writer, child);
113 } 118 }
@@ -116,7 +121,7 @@ public final class TinyV2Writer implements MappingsWriter {
116 } 121 }
117 122
118 private void writeField(PrintWriter writer, EntryTreeNode<EntryMapping> node) { 123 private void writeField(PrintWriter writer, EntryTreeNode<EntryMapping> node) {
119 if (node.getValue() == null) 124 if (node.getValue() == null || node.getValue().equals(EntryMapping.DEFAULT))
120 return; // Shortcut 125 return; // Shortcut
121 126
122 writer.print(indent(1)); 127 writer.print(indent(1));
@@ -126,17 +131,22 @@ public final class TinyV2Writer implements MappingsWriter {
126 writer.print(node.getEntry().getName()); 131 writer.print(node.getEntry().getName());
127 writer.print("\t"); 132 writer.print("\t");
128 EntryMapping mapping = node.getValue(); 133 EntryMapping mapping = node.getValue();
134
129 if (mapping == null) { 135 if (mapping == null) {
130 writer.println(node.getEntry().getName()); // todo fix v2 name inference 136 mapping = EntryMapping.DEFAULT;
131 } else { 137 }
132 writer.println(mapping.getTargetName());
133 138
134 writeComment(writer, mapping, 2); 139 if (mapping.targetName() != null) {
140 writer.println(mapping.targetName());
141 } else {
142 writer.println(node.getEntry().getName()); // todo fix v2 name inference
135 } 143 }
144
145 writeComment(writer, mapping, 2);
136 } 146 }
137 147
138 private void writeParameter(PrintWriter writer, EntryTreeNode<EntryMapping> node) { 148 private void writeParameter(PrintWriter writer, EntryTreeNode<EntryMapping> node) {
139 if (node.getValue() == null) 149 if (node.getValue() == null || node.getValue().equals(EntryMapping.DEFAULT))
140 return; // Shortcut 150 return; // Shortcut
141 151
142 writer.print(indent(2)); 152 writer.print(indent(2));
@@ -146,20 +156,20 @@ public final class TinyV2Writer implements MappingsWriter {
146 writer.print(node.getEntry().getName()); 156 writer.print(node.getEntry().getName());
147 writer.print("\t"); 157 writer.print("\t");
148 EntryMapping mapping = node.getValue(); 158 EntryMapping mapping = node.getValue();
149 if (mapping == null) { 159 if (mapping == null || mapping.targetName() == null) {
150 writer.println(); // todo ??? 160 writer.println(); // todo ???
151 } else { 161 } else {
152 writer.println(mapping.getTargetName()); 162 writer.println(mapping.targetName());
153 163
154 writeComment(writer, mapping, 3); 164 writeComment(writer, mapping, 3);
155 } 165 }
156 } 166 }
157 167
158 private void writeComment(PrintWriter writer, EntryMapping mapping, int indent) { 168 private void writeComment(PrintWriter writer, EntryMapping mapping, int indent) {
159 if (mapping != null && mapping.getJavadoc() != null) { 169 if (mapping != null && mapping.javadoc() != null) {
160 writer.print(indent(indent)); 170 writer.print(indent(indent));
161 writer.print("c\t"); 171 writer.print("c\t");
162 writer.print(MappingHelper.escape(mapping.getJavadoc())); 172 writer.print(MappingHelper.escape(mapping.javadoc()));
163 writer.println(); 173 writer.println();
164 } 174 }
165 } 175 }
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java
index 570941c..0992d34 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/mapping/tree/HashEntryTree.java
@@ -6,6 +6,7 @@ import cuchaz.enigma.translation.mapping.EntryMapping;
6import cuchaz.enigma.translation.mapping.EntryResolver; 6import cuchaz.enigma.translation.mapping.EntryResolver;
7import cuchaz.enigma.translation.representation.entry.Entry; 7import cuchaz.enigma.translation.representation.entry.Entry;
8 8
9import javax.annotation.Nonnull;
9import javax.annotation.Nullable; 10import javax.annotation.Nullable;
10import java.util.*; 11import java.util.*;
11import java.util.function.Function; 12import java.util.function.Function;
@@ -152,6 +153,7 @@ public class HashEntryTree<T> implements EntryTree<T> {
152 } 153 }
153 154
154 @Override 155 @Override
156 @Nonnull
155 public Iterator<EntryTreeNode<T>> iterator() { 157 public Iterator<EntryTreeNode<T>> iterator() {
156 Collection<EntryTreeNode<T>> nodes = new ArrayList<>(); 158 Collection<EntryTreeNode<T>> nodes = new ArrayList<>();
157 for (EntryTreeNode<T> node : root.values()) { 159 for (EntryTreeNode<T> node : root.values()) {
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java
index a6aed73..13c7cd4 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/Lambda.java
@@ -35,9 +35,9 @@ public class Lambda implements Translatable {
35 EntryMapping samMethodMapping = resolveMapping(resolver, mappings, samMethod); 35 EntryMapping samMethodMapping = resolveMapping(resolver, mappings, samMethod);
36 36
37 return TranslateResult.of( 37 return TranslateResult.of(
38 samMethodMapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, 38 samMethodMapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
39 new Lambda( 39 new Lambda(
40 samMethodMapping != null ? samMethodMapping.getTargetName() : invokedName, 40 samMethodMapping.targetName() != null ? samMethodMapping.targetName() : invokedName,
41 invokedType.extendedTranslate(translator, resolver, mappings).getValue(), 41 invokedType.extendedTranslate(translator, resolver, mappings).getValue(),
42 samMethodType.extendedTranslate(translator, resolver, mappings).getValue(), 42 samMethodType.extendedTranslate(translator, resolver, mappings).getValue(),
43 implMethod.extendedTranslate(translator, resolver, mappings).getValue(), 43 implMethod.extendedTranslate(translator, resolver, mappings).getValue(),
@@ -53,7 +53,7 @@ public class Lambda implements Translatable {
53 return mapping; 53 return mapping;
54 } 54 }
55 } 55 }
56 return null; 56 return EntryMapping.DEFAULT;
57 } 57 }
58 58
59 public ClassEntry getInterface() { 59 public ClassEntry getInterface() {
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java
index b0fd286..237c93d 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassDefEntry.java
@@ -13,6 +13,7 @@ package cuchaz.enigma.translation.representation.entry;
13 13
14import java.util.Arrays; 14import java.util.Arrays;
15 15
16import javax.annotation.Nonnull;
16import javax.annotation.Nullable; 17import javax.annotation.Nullable;
17 18
18import com.google.common.base.Preconditions; 19import com.google.common.base.Preconditions;
@@ -75,15 +76,15 @@ public class ClassDefEntry extends ClassEntry implements DefEntry<ClassEntry> {
75 } 76 }
76 77
77 @Override 78 @Override
78 public TranslateResult<ClassDefEntry> extendedTranslate(Translator translator, @Nullable EntryMapping mapping) { 79 public TranslateResult<ClassDefEntry> extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) {
79 Signature translatedSignature = translator.translate(signature); 80 Signature translatedSignature = translator.translate(signature);
80 String translatedName = mapping != null ? mapping.getTargetName() : name; 81 String translatedName = mapping.targetName() != null ? mapping.targetName() : name;
81 AccessFlags translatedAccess = mapping != null ? mapping.getAccessModifier().transform(access) : access; 82 AccessFlags translatedAccess = mapping.accessModifier().transform(access);
82 ClassEntry translatedSuper = translator.translate(superClass); 83 ClassEntry translatedSuper = translator.translate(superClass);
83 ClassEntry[] translatedInterfaces = Arrays.stream(interfaces).map(translator::translate).toArray(ClassEntry[]::new); 84 ClassEntry[] translatedInterfaces = Arrays.stream(interfaces).map(translator::translate).toArray(ClassEntry[]::new);
84 String docs = mapping != null ? mapping.getJavadoc() : null; 85 String docs = mapping.javadoc();
85 return TranslateResult.of( 86 return TranslateResult.of(
86 mapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, 87 mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
87 new ClassDefEntry(parent, translatedName, translatedSignature, translatedAccess, translatedSuper, translatedInterfaces, docs) 88 new ClassDefEntry(parent, translatedName, translatedSignature, translatedAccess, translatedSuper, translatedInterfaces, docs)
88 ); 89 );
89 } 90 }
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java
index fe56611..ec5294e 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ClassEntry.java
@@ -82,16 +82,16 @@ public class ClassEntry extends ParentedEntry<ClassEntry> implements Comparable<
82 } 82 }
83 83
84 @Override 84 @Override
85 public TranslateResult<? extends ClassEntry> extendedTranslate(Translator translator, @Nullable EntryMapping mapping) { 85 public TranslateResult<? extends ClassEntry> extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) {
86 if (name.charAt(0) == '[') { 86 if (name.charAt(0) == '[') {
87 TranslateResult<TypeDescriptor> translatedName = translator.extendedTranslate(new TypeDescriptor(name)); 87 TranslateResult<TypeDescriptor> translatedName = translator.extendedTranslate(new TypeDescriptor(name));
88 return translatedName.map(desc -> new ClassEntry(parent, desc.toString())); 88 return translatedName.map(desc -> new ClassEntry(parent, desc.toString()));
89 } 89 }
90 90
91 String translatedName = mapping != null ? mapping.getTargetName() : name; 91 String translatedName = mapping.targetName() != null ? mapping.targetName() : name;
92 String docs = mapping != null ? mapping.getJavadoc() : null; 92 String docs = mapping.javadoc();
93 return TranslateResult.of( 93 return TranslateResult.of(
94 mapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, 94 mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
95 new ClassEntry(parent, translatedName, docs) 95 new ClassEntry(parent, translatedName, docs)
96 ); 96 );
97 } 97 }
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java
index 8a81c54..956f32c 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/Entry.java
@@ -13,6 +13,7 @@ package cuchaz.enigma.translation.representation.entry;
13 13
14import java.util.ArrayList; 14import java.util.ArrayList;
15import java.util.List; 15import java.util.List;
16import java.util.Objects;
16 17
17import javax.annotation.Nullable; 18import javax.annotation.Nullable;
18 19
@@ -107,16 +108,29 @@ public interface Entry<P extends Entry<?>> extends Translatable {
107 108
108 boolean canConflictWith(Entry<?> entry); 109 boolean canConflictWith(Entry<?> entry);
109 110
110 @Nullable
111 default ClassEntry getContainingClass() { 111 default ClassEntry getContainingClass() {
112 P parent = getParent(); 112 ClassEntry last = null;
113 if (parent == null) { 113 Entry<?> current = this;
114 return null; 114 while (current != null) {
115 if (current instanceof ClassEntry) {
116 last = (ClassEntry) current;
117 break;
118 }
119 current = current.getParent();
115 } 120 }
116 if (parent instanceof ClassEntry) { 121 return Objects.requireNonNull(last, () -> String.format("%s has no containing class?", this));
117 return (ClassEntry) parent; 122 }
123
124 default ClassEntry getTopLevelClass() {
125 ClassEntry last = null;
126 Entry<?> current = this;
127 while (current != null) {
128 if (current instanceof ClassEntry) {
129 last = (ClassEntry) current;
130 }
131 current = current.getParent();
118 } 132 }
119 return parent.getContainingClass(); 133 return Objects.requireNonNull(last, () -> String.format("%s has no top level class?", this));
120 } 134 }
121 135
122 default List<Entry<?>> getAncestry() { 136 default List<Entry<?>> getAncestry() {
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java
index b9da6cc..0efb6a9 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldDefEntry.java
@@ -11,8 +11,6 @@
11 11
12package cuchaz.enigma.translation.representation.entry; 12package cuchaz.enigma.translation.representation.entry;
13 13
14import javax.annotation.Nullable;
15
16import com.google.common.base.Preconditions; 14import com.google.common.base.Preconditions;
17 15
18import cuchaz.enigma.source.RenamableTokenType; 16import cuchaz.enigma.source.RenamableTokenType;
@@ -23,6 +21,8 @@ import cuchaz.enigma.translation.representation.AccessFlags;
23import cuchaz.enigma.translation.representation.Signature; 21import cuchaz.enigma.translation.representation.Signature;
24import cuchaz.enigma.translation.representation.TypeDescriptor; 22import cuchaz.enigma.translation.representation.TypeDescriptor;
25 23
24import javax.annotation.Nonnull;
25
26public class FieldDefEntry extends FieldEntry implements DefEntry<ClassEntry> { 26public class FieldDefEntry extends FieldEntry implements DefEntry<ClassEntry> {
27 private final AccessFlags access; 27 private final AccessFlags access;
28 private final Signature signature; 28 private final Signature signature;
@@ -53,14 +53,14 @@ public class FieldDefEntry extends FieldEntry implements DefEntry<ClassEntry> {
53 } 53 }
54 54
55 @Override 55 @Override
56 protected TranslateResult<FieldEntry> extendedTranslate(Translator translator, @Nullable EntryMapping mapping) { 56 protected TranslateResult<FieldEntry> extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) {
57 TypeDescriptor translatedDesc = translator.translate(desc); 57 TypeDescriptor translatedDesc = translator.translate(desc);
58 Signature translatedSignature = translator.translate(signature); 58 Signature translatedSignature = translator.translate(signature);
59 String translatedName = mapping != null ? mapping.getTargetName() : name; 59 String translatedName = mapping.targetName() != null ? mapping.targetName() : name;
60 AccessFlags translatedAccess = mapping != null ? mapping.getAccessModifier().transform(access) : access; 60 AccessFlags translatedAccess = mapping.accessModifier().transform(access);
61 String docs = mapping != null ? mapping.getJavadoc() : null; 61 String docs = mapping.javadoc();
62 return TranslateResult.of( 62 return TranslateResult.of(
63 mapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, 63 mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
64 new FieldDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess, docs) 64 new FieldDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess, docs)
65 ); 65 );
66 } 66 }
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java
index 5ddd33d..db94011 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/FieldEntry.java
@@ -13,7 +13,7 @@ package cuchaz.enigma.translation.representation.entry;
13 13
14import java.util.Objects; 14import java.util.Objects;
15 15
16import javax.annotation.Nullable; 16import javax.annotation.Nonnull;
17 17
18import com.google.common.base.Preconditions; 18import com.google.common.base.Preconditions;
19 19
@@ -63,11 +63,11 @@ public class FieldEntry extends ParentedEntry<ClassEntry> implements Comparable<
63 } 63 }
64 64
65 @Override 65 @Override
66 protected TranslateResult<FieldEntry> extendedTranslate(Translator translator, @Nullable EntryMapping mapping) { 66 protected TranslateResult<FieldEntry> extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) {
67 String translatedName = mapping != null ? mapping.getTargetName() : name; 67 String translatedName = mapping.targetName() != null ? mapping.targetName() : name;
68 String docs = mapping != null ? mapping.getJavadoc() : null; 68 String docs = mapping.javadoc();
69 return TranslateResult.of( 69 return TranslateResult.of(
70 mapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, 70 mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
71 new FieldEntry(parent, translatedName, translator.translate(desc), docs) 71 new FieldEntry(parent, translatedName, translator.translate(desc), docs)
72 ); 72 );
73 } 73 }
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java
index 2712c65..c151de4 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableDefEntry.java
@@ -1,6 +1,6 @@
1package cuchaz.enigma.translation.representation.entry; 1package cuchaz.enigma.translation.representation.entry;
2 2
3import javax.annotation.Nullable; 3import javax.annotation.Nonnull;
4 4
5import com.google.common.base.Preconditions; 5import com.google.common.base.Preconditions;
6 6
@@ -30,12 +30,12 @@ public class LocalVariableDefEntry extends LocalVariableEntry {
30 } 30 }
31 31
32 @Override 32 @Override
33 protected TranslateResult<LocalVariableEntry> extendedTranslate(Translator translator, @Nullable EntryMapping mapping) { 33 protected TranslateResult<LocalVariableEntry> extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) {
34 TypeDescriptor translatedDesc = translator.translate(desc); 34 TypeDescriptor translatedDesc = translator.translate(desc);
35 String translatedName = mapping != null ? mapping.getTargetName() : name; 35 String translatedName = mapping.targetName() != null ? mapping.targetName() : name;
36 String javadoc = mapping != null ? mapping.getJavadoc() : javadocs; 36 String javadoc = mapping.javadoc();
37 return TranslateResult.of( 37 return TranslateResult.of(
38 mapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, 38 mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
39 new LocalVariableDefEntry(parent, index, translatedName, parameter, translatedDesc, javadoc) 39 new LocalVariableDefEntry(parent, index, translatedName, parameter, translatedDesc, javadoc)
40 ); 40 );
41 } 41 }
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java
index 154f11f..1cf1a83 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/LocalVariableEntry.java
@@ -2,7 +2,7 @@ package cuchaz.enigma.translation.representation.entry;
2 2
3import java.util.Objects; 3import java.util.Objects;
4 4
5import javax.annotation.Nullable; 5import javax.annotation.Nonnull;
6 6
7import com.google.common.base.Preconditions; 7import com.google.common.base.Preconditions;
8 8
@@ -50,11 +50,11 @@ public class LocalVariableEntry extends ParentedEntry<MethodEntry> implements Co
50 } 50 }
51 51
52 @Override 52 @Override
53 protected TranslateResult<LocalVariableEntry> extendedTranslate(Translator translator, @Nullable EntryMapping mapping) { 53 protected TranslateResult<LocalVariableEntry> extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) {
54 String translatedName = mapping != null ? mapping.getTargetName() : name; 54 String translatedName = mapping.targetName() != null ? mapping.targetName() : name;
55 String javadoc = mapping != null ? mapping.getJavadoc() : null; 55 String javadoc = mapping.javadoc();
56 return TranslateResult.of( 56 return TranslateResult.of(
57 mapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, 57 mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
58 new LocalVariableEntry(parent, index, translatedName, parameter, javadoc) 58 new LocalVariableEntry(parent, index, translatedName, parameter, javadoc)
59 ); 59 );
60 } 60 }
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java
index cc326d6..30ef706 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodDefEntry.java
@@ -11,7 +11,7 @@
11 11
12package cuchaz.enigma.translation.representation.entry; 12package cuchaz.enigma.translation.representation.entry;
13 13
14import javax.annotation.Nullable; 14import javax.annotation.Nonnull;
15 15
16import com.google.common.base.Preconditions; 16import com.google.common.base.Preconditions;
17 17
@@ -53,14 +53,14 @@ public class MethodDefEntry extends MethodEntry implements DefEntry<ClassEntry>
53 } 53 }
54 54
55 @Override 55 @Override
56 protected TranslateResult<MethodDefEntry> extendedTranslate(Translator translator, @Nullable EntryMapping mapping) { 56 protected TranslateResult<MethodDefEntry> extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) {
57 MethodDescriptor translatedDesc = translator.translate(descriptor); 57 MethodDescriptor translatedDesc = translator.translate(descriptor);
58 Signature translatedSignature = translator.translate(signature); 58 Signature translatedSignature = translator.translate(signature);
59 String translatedName = mapping != null ? mapping.getTargetName() : name; 59 String translatedName = mapping.targetName() != null ? mapping.targetName() : name;
60 AccessFlags translatedAccess = mapping != null ? mapping.getAccessModifier().transform(access) : access; 60 AccessFlags translatedAccess = mapping.accessModifier().transform(access);
61 String docs = mapping != null ? mapping.getJavadoc() : null; 61 String docs = mapping.javadoc();
62 return TranslateResult.of( 62 return TranslateResult.of(
63 mapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, 63 mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
64 new MethodDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess, docs) 64 new MethodDefEntry(parent, translatedName, translatedDesc, translatedSignature, translatedAccess, docs)
65 ); 65 );
66 } 66 }
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java
index 864a580..ab9c2d1 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/MethodEntry.java
@@ -13,7 +13,7 @@ package cuchaz.enigma.translation.representation.entry;
13 13
14import java.util.Objects; 14import java.util.Objects;
15 15
16import javax.annotation.Nullable; 16import javax.annotation.Nonnull;
17 17
18import com.google.common.base.Preconditions; 18import com.google.common.base.Preconditions;
19 19
@@ -58,11 +58,11 @@ public class MethodEntry extends ParentedEntry<ClassEntry> implements Comparable
58 } 58 }
59 59
60 @Override 60 @Override
61 protected TranslateResult<? extends MethodEntry> extendedTranslate(Translator translator, @Nullable EntryMapping mapping) { 61 protected TranslateResult<? extends MethodEntry> extendedTranslate(Translator translator, @Nonnull EntryMapping mapping) {
62 String translatedName = mapping != null ? mapping.getTargetName() : name; 62 String translatedName = mapping.targetName() != null ? mapping.targetName() : name;
63 String docs = mapping != null ? mapping.getJavadoc() : null; 63 String docs = mapping.javadoc();
64 return TranslateResult.of( 64 return TranslateResult.of(
65 mapping == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED, 65 mapping.targetName() == null ? RenamableTokenType.OBFUSCATED : RenamableTokenType.DEOBFUSCATED,
66 new MethodEntry(parent, translatedName, translator.translate(descriptor), docs) 66 new MethodEntry(parent, translatedName, translator.translate(descriptor), docs)
67 ); 67 );
68 } 68 }
diff --git a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java
index 5634891..267bc11 100644
--- a/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java
+++ b/enigma/src/main/java/cuchaz/enigma/translation/representation/entry/ParentedEntry.java
@@ -11,6 +11,7 @@
11 11
12package cuchaz.enigma.translation.representation.entry; 12package cuchaz.enigma.translation.representation.entry;
13 13
14import javax.annotation.Nonnull;
14import javax.annotation.Nullable; 15import javax.annotation.Nullable;
15 16
16import com.google.common.base.Preconditions; 17import com.google.common.base.Preconditions;
@@ -41,7 +42,7 @@ public abstract class ParentedEntry<P extends Entry<?>> implements Entry<P> {
41 @Override 42 @Override
42 public abstract ParentedEntry<P> withName(String name); 43 public abstract ParentedEntry<P> withName(String name);
43 44
44 protected abstract TranslateResult<? extends ParentedEntry<P>> extendedTranslate(Translator translator, @Nullable EntryMapping mapping); 45 protected abstract TranslateResult<? extends ParentedEntry<P>> extendedTranslate(Translator translator, @Nonnull EntryMapping mapping);
45 46
46 @Override 47 @Override
47 public String getName() { 48 public String getName() {
@@ -93,6 +94,6 @@ public abstract class ParentedEntry<P extends Entry<?>> implements Entry<P> {
93 return mapping; 94 return mapping;
94 } 95 }
95 } 96 }
96 return null; 97 return EntryMapping.DEFAULT;
97 } 98 }
98} 99}
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/TristateChange.java b/enigma/src/main/java/cuchaz/enigma/utils/TristateChange.java
new file mode 100644
index 0000000..864154c
--- /dev/null
+++ b/enigma/src/main/java/cuchaz/enigma/utils/TristateChange.java
@@ -0,0 +1,78 @@
1package cuchaz.enigma.utils;
2
3import java.util.Objects;
4
5public final class TristateChange<T> {
6
7 private static final TristateChange<?> UNCHANGED = new TristateChange<>(Type.UNCHANGED, null);
8 private static final TristateChange<?> RESET = new TristateChange<>(Type.RESET, null);
9
10 private final Type type;
11 private final T val;
12
13 @SuppressWarnings("unchecked")
14 public static <T> TristateChange<T> unchanged() {
15 return (TristateChange<T>) TristateChange.UNCHANGED;
16 }
17
18 @SuppressWarnings("unchecked")
19 public static <T> TristateChange<T> reset() {
20 return (TristateChange<T>) TristateChange.RESET;
21 }
22
23 public static <T> TristateChange<T> set(T value) {
24 return new TristateChange<>(Type.SET, value);
25 }
26
27 private TristateChange(Type type, T val) {
28 this.type = type;
29 this.val = val;
30 }
31
32 public Type getType() {
33 return this.type;
34 }
35
36 public boolean isUnchanged() {
37 return this.type == Type.UNCHANGED;
38 }
39
40 public boolean isReset() {
41 return this.type == Type.RESET;
42 }
43
44 public boolean isSet() {
45 return this.type == Type.SET;
46 }
47
48 public T getNewValue() {
49 if (this.type != Type.SET) throw new IllegalStateException(String.format("No concrete value in %s", this));
50 return this.val;
51 }
52
53 @Override
54 public boolean equals(Object o) {
55 if (this == o) return true;
56 if (o == null || getClass() != o.getClass()) return false;
57 TristateChange<?> that = (TristateChange<?>) o;
58 return type == that.type &&
59 Objects.equals(val, that.val);
60 }
61
62 @Override
63 public int hashCode() {
64 return Objects.hash(type, val);
65 }
66
67 @Override
68 public String toString() {
69 return String.format("TristateChange { type: %s, val: %s }", type, val);
70 }
71
72 public enum Type {
73 UNCHANGED,
74 RESET,
75 SET,
76 }
77
78}
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java
index 81b6c43..5067d7e 100644
--- a/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java
+++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/PrintValidatable.java
@@ -1,5 +1,6 @@
1package cuchaz.enigma.utils.validation; 1package cuchaz.enigma.utils.validation;
2 2
3import java.io.PrintStream;
3import java.util.Arrays; 4import java.util.Arrays;
4 5
5public class PrintValidatable implements Validatable { 6public class PrintValidatable implements Validatable {
@@ -8,6 +9,10 @@ public class PrintValidatable implements Validatable {
8 9
9 @Override 10 @Override
10 public void addMessage(ParameterizedMessage message) { 11 public void addMessage(ParameterizedMessage message) {
12 formatMessage(System.out, message);
13 }
14
15 public static void formatMessage(PrintStream w, ParameterizedMessage message) {
11 String text = message.getText(); 16 String text = message.getText();
12 String longText = message.getLongText(); 17 String longText = message.getLongText();
13 String type = switch (message.message.type) { 18 String type = switch (message.message.type) {
@@ -15,9 +20,10 @@ public class PrintValidatable implements Validatable {
15 case WARNING -> "warning"; 20 case WARNING -> "warning";
16 case ERROR -> "error"; 21 case ERROR -> "error";
17 }; 22 };
18 System.out.printf("%s: %s\n", type, text); 23 w.printf("%s: %s\n", type, text);
24
19 if (!longText.isEmpty()) { 25 if (!longText.isEmpty()) {
20 Arrays.stream(longText.split("\n")).forEach(s -> System.out.printf(" %s\n", s)); 26 Arrays.stream(longText.split("\n")).forEach(s -> w.printf(" %s\n", s));
21 } 27 }
22 } 28 }
23 29
diff --git a/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java b/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java
index ee2b595..0ecb9fb 100644
--- a/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java
+++ b/enigma/src/main/java/cuchaz/enigma/utils/validation/ValidationContext.java
@@ -62,6 +62,20 @@ public class ValidationContext {
62 return messages.stream().noneMatch(m -> m.message.type == Type.ERROR); 62 return messages.stream().noneMatch(m -> m.message.type == Type.ERROR);
63 } 63 }
64 64
65 /**
66 * If this validation context has at least one error, throw an exception.
67 *
68 * @throws IllegalStateException if errors are present
69 */
70 public void throwOnError() {
71 if (!this.canProceed()) {
72 for (ParameterizedMessage message : this.messages) {
73 PrintValidatable.formatMessage(System.err, message);
74 }
75 throw new IllegalStateException("Errors encountered; cannot continue! Check error log for details.");
76 }
77 }
78
65 public List<ParameterizedMessage> getMessages() { 79 public List<ParameterizedMessage> getMessages() {
66 return Collections.unmodifiableList(messages); 80 return Collections.unmodifiableList(messages);
67 } 81 }