diff --git a/src/main/java/net/fabricmc/mappingio/FlatMappingVisitor.java b/src/main/java/net/fabricmc/mappingio/FlatMappingVisitor.java
index 056020ed..da650649 100644
--- a/src/main/java/net/fabricmc/mappingio/FlatMappingVisitor.java
+++ b/src/main/java/net/fabricmc/mappingio/FlatMappingVisitor.java
@@ -57,16 +57,23 @@ default boolean visitContent() throws IOException {
}
boolean visitClass(String srcName, String[] dstNames) throws IOException;
+ void visitClassMetadata(String srcName, String[] dstNames, String propertyKey, String[] propertyValues) throws IOException;
void visitClassComment(String srcName, String[] dstNames, String comment) throws IOException;
boolean visitField(String srcClsName, String srcName, String srcDesc,
String[] dstClsNames, String[] dstNames, String[] dstDescs) throws IOException;
+ void visitFieldMetadata(String srcClsName, String srcName, String srcDesc,
+ String[] dstClsNames, String[] dstNames, String[] dstDescs,
+ String propertyKey, String[] propertyValues) throws IOException;
void visitFieldComment(String srcClsName, String srcName, String srcDesc,
String[] dstClsNames, String[] dstNames, String[] dstDescs,
String comment) throws IOException;
boolean visitMethod(String srcClsName, String srcName, String srcDesc,
String[] dstClsNames, String[] dstNames, String[] dstDescs) throws IOException;
+ void visitMethodMetadata(String srcClsName, String srcName, String srcDesc,
+ String[] dstClsNames, String[] dstNames, String[] dstDescs,
+ String propertyKey, String[] propertyValues) throws IOException;
void visitMethodComment(String srcClsName, String srcName, String srcDesc,
String[] dstClsNames, String[] dstNames, String[] dstDescs,
String comment) throws IOException;
@@ -74,6 +81,10 @@ void visitMethodComment(String srcClsName, String srcName, String srcDesc,
boolean visitMethodArg(String srcClsName, String srcMethodName, String srcMethodDesc,
int argPosition, int lvIndex, String srcArgName,
String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstArgNames) throws IOException;
+ void visitMethodArgMetadata(String srcClsName, String srcMethodName, String srcMethodDesc,
+ int argPosition, int lvIndex, String srcArgName,
+ String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstArgNames,
+ String propertyKey, String[] propertyValues) throws IOException;
void visitMethodArgComment(String srcClsName, String srcMethodName, String srcMethodDesc,
int argPosition, int lvIndex, String srcArgName,
String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstArgNames,
@@ -82,6 +93,10 @@ void visitMethodArgComment(String srcClsName, String srcMethodName, String srcMe
boolean visitMethodVar(String srcClsName, String srcMethodName, String srcMethodDesc,
int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName,
String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstVarNames) throws IOException;
+ void visitMethodVarMetadata(String srcClsName, String srcMethodName, String srcMethodDesc,
+ int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName,
+ String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstVarNames,
+ String propertyKey, String[] propertyValues) throws IOException;
void visitMethodVarComment(String srcClsName, String srcMethodName, String srcMethodDesc,
int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName,
String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstVarNames,
@@ -135,37 +150,53 @@ default boolean visitMethodVar(String srcClsName, String srcMethodName, String s
// convenience / potentially higher efficiency visit methods for only one dst name
+ // Class
default boolean visitClass(String srcName, String dstName) throws IOException {
return visitClass(srcName, toArray(dstName));
}
-
+ default void visitClassMetadata(String srcName, String propertyKey, String[] propertyValues) throws IOException {
+ visitClassMetadata(srcName, (String) null, propertyKey, propertyValues);
+ }
+ default void visitClassMetadata(String srcName, String dstName, String propertyKey, String[] propertyValues) throws IOException {
+ visitClassMetadata(srcName, toArray(dstName), propertyKey, propertyValues);
+ }
default void visitClassComment(String srcName, String comment) throws IOException {
visitClassComment(srcName, (String) null, comment);
}
-
default void visitClassComment(String srcName, String dstName, String comment) throws IOException {
visitClassComment(srcName, toArray(dstName), comment);
}
+ // Field
default boolean visitField(String srcClsName, String srcName, String srcDesc,
String dstName) throws IOException {
return visitField(srcClsName, srcName, srcDesc,
null, dstName, null);
}
-
default boolean visitField(String srcClsName, String srcName, String srcDesc,
String dstClsName, String dstName, String dstDesc) throws IOException {
return visitField(srcClsName, srcName, srcDesc,
toArray(dstClsName), toArray(dstName), toArray(dstDesc));
}
-
+ default void visitFieldMetadata(String srcClsName, String srcName, String srcDesc,
+ String propertyKey, String[] propertyValues) throws IOException {
+ visitFieldMetadata(srcClsName, srcName, srcDesc,
+ (String) null, null, null,
+ propertyKey, propertyValues);
+ }
+ default void visitFieldMetadata(String srcClsName, String srcName, String srcDesc,
+ String dstClsName, String dstName, String dstDesc,
+ String propertyKey, String[] propertyValues) throws IOException {
+ visitFieldMetadata(srcClsName, srcName, srcDesc,
+ toArray(dstClsName), toArray(dstName), toArray(dstDesc),
+ propertyKey, propertyValues);
+ }
default void visitFieldComment(String srcClsName, String srcName, String srcDesc,
String comment) throws IOException {
visitFieldComment(srcClsName, srcName, srcDesc,
(String) null, null, null,
comment);
}
-
default void visitFieldComment(String srcClsName, String srcName, String srcDesc,
String dstClsName, String dstName, String dstDesc,
String comment) throws IOException {
@@ -174,25 +205,36 @@ default void visitFieldComment(String srcClsName, String srcName, String srcDesc
comment);
}
+ // Method
default boolean visitMethod(String srcClsName, String srcName, String srcDesc,
String dstName) throws IOException {
return visitMethod(srcClsName, srcName, srcDesc,
null, dstName, null);
}
-
default boolean visitMethod(String srcClsName, String srcName, String srcDesc,
String dstClsName, String dstName, String dstDesc) throws IOException {
return visitMethod(srcClsName, srcName, srcDesc,
toArray(dstClsName), toArray(dstName), toArray(dstDesc));
}
-
+ default void visitMethodMetadata(String srcClsName, String srcName, String srcDesc,
+ String propertyKey, String[] propertyValues) throws IOException {
+ visitMethodMetadata(srcClsName, srcName, srcDesc,
+ (String) null, null, null,
+ propertyKey, propertyValues);
+ }
+ default void visitMethodMetadata(String srcClsName, String srcName, String srcDesc,
+ String dstClsName, String dstName, String dstDesc,
+ String propertyKey, String[] propertyValues) throws IOException {
+ visitMethodMetadata(srcClsName, srcName, srcDesc,
+ toArray(dstClsName), toArray(dstName), toArray(dstDesc),
+ propertyKey, propertyValues);
+ }
default void visitMethodComment(String srcClsName, String srcName, String srcDesc,
String comment) throws IOException {
visitMethodComment(srcClsName, srcName, srcDesc,
(String) null, null, null,
comment);
}
-
default void visitMethodComment(String srcClsName, String srcName, String srcDesc,
String dstClsName, String dstName, String dstDesc,
String comment) throws IOException {
@@ -201,6 +243,7 @@ default void visitMethodComment(String srcClsName, String srcName, String srcDes
comment);
}
+ // Method Arg
default boolean visitMethodArg(String srcClsName, String srcMethodName, String srcMethodDesc,
int argPosition, int lvIndex, String srcArgName,
String dstArgName) throws IOException {
@@ -208,7 +251,6 @@ default boolean visitMethodArg(String srcClsName, String srcMethodName, String s
argPosition, lvIndex, srcArgName,
null, null, null, dstArgName);
}
-
default boolean visitMethodArg(String srcClsName, String srcMethodName, String srcMethodDesc,
int argPosition, int lvIndex, String srcArgName,
String dstClsName, String dstMethodName, String dstMethodDesc, String dstArgName) throws IOException {
@@ -216,7 +258,22 @@ default boolean visitMethodArg(String srcClsName, String srcMethodName, String s
argPosition, lvIndex, srcArgName,
toArray(dstClsName), toArray(dstMethodName), toArray(dstMethodDesc), toArray(dstArgName));
}
-
+ default void visitMethodArgMetadata(String srcClsName, String srcMethodName, String srcMethodDesc,
+ int argPosition, int lvIndex, String srcArgName,
+ String propertyKey, String[] propertyValues) throws IOException {
+ visitMethodArgMetadata(srcClsName, srcMethodName, srcMethodDesc,
+ argPosition, lvIndex, srcArgName,
+ (String) null, null, null, null,
+ propertyKey, propertyValues);
+ }
+ default void visitMethodArgMetadata(String srcClsName, String srcMethodName, String srcMethodDesc,
+ int argPosition, int lvIndex, String srcArgName,
+ String dstClsName, String dstMethodName, String dstMethodDesc, String dstArgName,
+ String propertyKey, String[] propertyValues) throws IOException {
+ visitMethodArgMetadata(srcClsName, srcMethodName, srcMethodDesc, argPosition, lvIndex, srcArgName,
+ toArray(dstClsName), toArray(dstMethodName), toArray(dstMethodDesc), toArray(dstArgName),
+ propertyKey, propertyValues);
+ }
default void visitMethodArgComment(String srcClsName, String srcMethodName, String srcMethodDesc,
int argPosition, int lvIndex, String srcArgName,
String comment) throws IOException {
@@ -225,7 +282,6 @@ default void visitMethodArgComment(String srcClsName, String srcMethodName, Stri
(String) null, null, null, null,
comment);
}
-
default void visitMethodArgComment(String srcClsName, String srcMethodName, String srcMethodDesc,
int argPosition, int lvIndex, String srcArgName,
String dstClsName, String dstMethodName, String dstMethodDesc, String dstArgName,
@@ -235,6 +291,7 @@ default void visitMethodArgComment(String srcClsName, String srcMethodName, Stri
comment);
}
+ // Method Var
default boolean visitMethodVar(String srcClsName, String srcMethodName, String srcMethodDesc,
int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName,
String dstVarName) throws IOException {
@@ -242,7 +299,6 @@ default boolean visitMethodVar(String srcClsName, String srcMethodName, String s
lvtRowIndex, lvIndex, startOpIdx, endOpIdx, srcVarName,
null, null, null, dstVarName);
}
-
default boolean visitMethodVar(String srcClsName, String srcMethodName, String srcMethodDesc,
int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName,
String dstClsName, String dstMethodName, String dstMethodDesc, String dstVarName) throws IOException {
@@ -250,7 +306,23 @@ default boolean visitMethodVar(String srcClsName, String srcMethodName, String s
lvtRowIndex, lvIndex, startOpIdx, endOpIdx, srcVarName,
toArray(dstClsName), toArray(dstMethodName), toArray(dstMethodDesc), toArray(dstVarName));
}
-
+ default void visitMethodVarMetadata(String srcClsName, String srcMethodName, String srcMethodDesc,
+ int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName,
+ String propertyKey, String[] propertyValues) throws IOException {
+ visitMethodVarMetadata(srcClsName, srcMethodName, srcMethodDesc,
+ lvtRowIndex, lvIndex, startOpIdx, endOpIdx, srcVarName,
+ (String) null, null, null, null,
+ propertyKey, propertyValues);
+ }
+ default void visitMethodVarMetadata(String srcClsName, String srcMethodName, String srcMethodDesc,
+ int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName,
+ String dstClsName, String dstMethodName, String dstMethodDesc, String dstVarName,
+ String propertyKey, String[] propertyValues) throws IOException {
+ visitMethodVarMetadata(srcClsName, srcMethodName, srcMethodDesc,
+ lvtRowIndex, lvIndex, startOpIdx, endOpIdx, srcVarName,
+ toArray(dstClsName), toArray(dstMethodName), toArray(dstMethodDesc), toArray(dstVarName),
+ propertyKey, propertyValues);
+ }
default void visitMethodVarComment(String srcClsName, String srcMethodName, String srcMethodDesc,
int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName,
String comment) throws IOException {
@@ -259,7 +331,6 @@ default void visitMethodVarComment(String srcClsName, String srcMethodName, Stri
(String) null, null, null, null,
comment);
}
-
default void visitMethodVarComment(String srcClsName, String srcMethodName, String srcMethodDesc,
int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName,
String dstClsName, String dstMethodName, String dstMethodDesc, String dstVarName,
diff --git a/src/main/java/net/fabricmc/mappingio/MappingVisitor.java b/src/main/java/net/fabricmc/mappingio/MappingVisitor.java
index 5c2455ce..f14752ee 100644
--- a/src/main/java/net/fabricmc/mappingio/MappingVisitor.java
+++ b/src/main/java/net/fabricmc/mappingio/MappingVisitor.java
@@ -27,11 +27,11 @@
*
- overall: header -> content -> End -> overall
*
- header: Header -> Namespaces [-> Metadata]*
*
- content: Content [-> class|Metadata]*
- *
- class: Class [-> DstName]* -> ElementContent [-> field|method|Comment]*
- *
- field: Field [-> DstName|DstDesc]* -> ElementContent [-> Comment]
- *
- method: Method [-> DstName|DstDesc]* -> ElementContent [-> arg|var|Comment]*
- *
- arg: Arg [-> DstName]* -> ElementContent [-> Comment]
- *
- var: Var [-> DstName]* -> ElementContent [-> Comment]
+ *
- class: Class [-> DstName]* -> ElementContent [-> ElementMetadata|field|method|Comment]*
+ *
- field: Field [-> DstName|DstDesc]* -> ElementContent [-> ElementMetadata|Comment]*
+ *
- method: Method [-> DstName|DstDesc]* -> ElementContent [-> ElementMetadata|arg|var|Comment]*
+ *
- arg: Arg [-> DstName]* -> ElementContent [-> ElementMetadata|Comment]*
+ *
- var: Var [-> DstName]* -> ElementContent [-> ElementMetadata|Comment]*
*
*
* The elements with a skip-return (Header/Content/Class/Field/Method/Arg/Var/ElementContent) abort processing the
@@ -118,6 +118,11 @@ default boolean visitElementContent(MappedElementKind targetKind) throws IOExcep
return true;
}
+ /**
+ * Metadata for the specified element (last content-visited or any parent).
+ */
+ default void visitElementMetadata(MappedElementKind targetKind, String key, int namespace, String value) throws IOException { }
+
/**
* Comment for the specified element (last content-visited or any parent).
*
diff --git a/src/main/java/net/fabricmc/mappingio/adapter/FlatAsRegularMappingVisitor.java b/src/main/java/net/fabricmc/mappingio/adapter/FlatAsRegularMappingVisitor.java
index 34da310f..b16ac554 100644
--- a/src/main/java/net/fabricmc/mappingio/adapter/FlatAsRegularMappingVisitor.java
+++ b/src/main/java/net/fabricmc/mappingio/adapter/FlatAsRegularMappingVisitor.java
@@ -17,8 +17,11 @@
package net.fabricmc.mappingio.adapter;
import java.io.IOException;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import net.fabricmc.mappingio.FlatMappingVisitor;
@@ -75,7 +78,8 @@ public boolean visitContent() throws IOException {
}
@Override
- public boolean visitClass(String srcName) {
+ public boolean visitClass(String srcName) throws IOException {
+ relayPendingElementMetadata();
this.srcClsName = srcName;
Arrays.fill(dstNames, null);
@@ -85,7 +89,8 @@ public boolean visitClass(String srcName) {
}
@Override
- public boolean visitField(String srcName, String srcDesc) {
+ public boolean visitField(String srcName, String srcDesc) throws IOException {
+ relayPendingElementMetadata();
this.srcMemberName = srcName;
this.srcMemberDesc = srcDesc;
@@ -97,7 +102,8 @@ public boolean visitField(String srcName, String srcDesc) {
}
@Override
- public boolean visitMethod(String srcName, String srcDesc) {
+ public boolean visitMethod(String srcName, String srcDesc) throws IOException {
+ relayPendingElementMetadata();
this.srcMemberName = srcName;
this.srcMemberDesc = srcDesc;
@@ -109,7 +115,8 @@ public boolean visitMethod(String srcName, String srcDesc) {
}
@Override
- public boolean visitMethodArg(int argPosition, int lvIndex, String srcName) {
+ public boolean visitMethodArg(int argPosition, int lvIndex, String srcName) throws IOException {
+ relayPendingElementMetadata();
this.srcMemberSubName = srcName;
this.argIdx = argPosition;
this.lvIndex = lvIndex;
@@ -120,7 +127,8 @@ public boolean visitMethodArg(int argPosition, int lvIndex, String srcName) {
}
@Override
- public boolean visitMethodVar(int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcName) {
+ public boolean visitMethodVar(int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcName) throws IOException {
+ relayPendingElementMetadata();
this.srcMemberSubName = srcName;
this.argIdx = lvtRowIndex;
this.lvIndex = lvIndex;
@@ -134,6 +142,7 @@ public boolean visitMethodVar(int lvtRowIndex, int lvIndex, int startOpIdx, int
@Override
public boolean visitEnd() throws IOException {
+ relayPendingElementMetadata();
return next.visitEnd();
}
@@ -149,6 +158,7 @@ public void visitDstDesc(MappedElementKind targetKind, int namespace, String des
@Override
public boolean visitElementContent(MappedElementKind targetKind) throws IOException {
+ currentElementKind = targetKind;
boolean relay;
switch (targetKind) {
@@ -181,8 +191,36 @@ public boolean visitElementContent(MappedElementKind targetKind) throws IOExcept
return relay;
}
+ @Override
+ public void visitElementMetadata(MappedElementKind targetKind, String key, int namespace, String value) throws IOException {
+ if (!key.equals(elementMetadata.getKey())) {
+ relayPendingElementMetadata();
+ elementMetadata = new SimpleEntry<>(key, null);
+ }
+
+ List valueStack = elementMetadata.getValue();
+
+ if (valueStack == null) {
+ valueStack = new ArrayList<>(4);
+ valueStack.add(new String[dstNames.length + 1]);
+ elementMetadata.setValue(valueStack);
+ }
+
+ String[] values = valueStack.get(valueStack.size() - 1);
+
+ // Merge value into existing array, unless already set, then append to stack and add there.
+ if (values[namespace] != null) {
+ values = new String[dstNames.length + 1];
+ valueStack.add(values);
+ }
+
+ values[namespace] = value == null ? nullSubstitute : value;
+ }
+
@Override
public void visitComment(MappedElementKind targetKind, String comment) throws IOException {
+ relayPendingElementMetadata();
+
switch (targetKind) {
case CLASS:
next.visitClassComment(srcClsName, dstClassNames, comment);
@@ -206,8 +244,57 @@ public void visitComment(MappedElementKind targetKind, String comment) throws IO
}
}
+ private void relayPendingElementMetadata() throws IOException {
+ if (elementMetadata.getValue() == null) return;
+ String key = elementMetadata.getKey();
+ String[] lastValues = null;
+
+ for (String[] values : elementMetadata.getValue()) {
+ for (int i = 0; i < values.length; i++) {
+ if (values[i] == nullSubstitute) {
+ values[i] = null;
+ } else if (values[i] == null && lastValues != null) {
+ // Fill in holes
+ values[i] = lastValues[i];
+ }
+ }
+
+ switch (currentElementKind) {
+ case CLASS:
+ next.visitClassMetadata(srcClsName, dstClassNames, key, values);
+ break;
+ case FIELD:
+ next.visitFieldMetadata(srcClsName, srcMemberName, srcMemberDesc,
+ dstClassNames, dstMemberNames, dstMemberDescs, key, values);
+ break;
+ case METHOD:
+ next.visitMethodMetadata(srcClsName, srcMemberName, srcMemberDesc,
+ dstClassNames, dstMemberNames, dstMemberDescs, key, values);
+ break;
+ case METHOD_ARG:
+ next.visitMethodArgMetadata(srcClsName, srcMemberName, srcMemberDesc, argIdx, lvIndex, srcMemberSubName,
+ dstClassNames, dstMemberNames, dstMemberDescs, dstNames, key, values);
+ break;
+ case METHOD_VAR:
+ next.visitMethodVarMetadata(srcClsName, srcMemberName, srcMemberDesc, argIdx, lvIndex, startOpIdx, endOpIdx, srcMemberSubName,
+ dstClassNames, dstMemberNames, dstMemberDescs, dstNames, key, values);
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+
+ lastValues = values;
+ }
+
+ elementMetadata.setValue(null);
+ currentElementKind = null;
+ }
+
+ private static final String nullSubstitute = new String();
private final FlatMappingVisitor next;
+ private Map.Entry> elementMetadata = new SimpleEntry<>(null, null);
+ private MappedElementKind currentElementKind;
private String srcClsName;
private String srcMemberName;
private String srcMemberDesc;
diff --git a/src/main/java/net/fabricmc/mappingio/adapter/ForwardingMappingVisitor.java b/src/main/java/net/fabricmc/mappingio/adapter/ForwardingMappingVisitor.java
index 71d9574f..b06b617c 100644
--- a/src/main/java/net/fabricmc/mappingio/adapter/ForwardingMappingVisitor.java
+++ b/src/main/java/net/fabricmc/mappingio/adapter/ForwardingMappingVisitor.java
@@ -107,6 +107,11 @@ public boolean visitElementContent(MappedElementKind targetKind) throws IOExcept
return next.visitElementContent(targetKind);
}
+ @Override
+ public void visitElementMetadata(MappedElementKind targetKind, String key, int namespace, String value) throws IOException {
+ next.visitElementMetadata(targetKind, key, namespace, value);
+ }
+
@Override
public void visitComment(MappedElementKind targetKind, String comment) throws IOException {
next.visitComment(targetKind, comment);
diff --git a/src/main/java/net/fabricmc/mappingio/adapter/MappingDstNsReorder.java b/src/main/java/net/fabricmc/mappingio/adapter/MappingDstNsReorder.java
index b0232d25..1086828a 100644
--- a/src/main/java/net/fabricmc/mappingio/adapter/MappingDstNsReorder.java
+++ b/src/main/java/net/fabricmc/mappingio/adapter/MappingDstNsReorder.java
@@ -66,6 +66,15 @@ public void visitDstDesc(MappedElementKind targetKind, int namespace, String des
}
}
+ @Override
+ public void visitElementMetadata(MappedElementKind targetKind, String key, int namespace, String value) throws IOException {
+ if (namespace >= 0) namespace = nsMap[namespace];
+
+ if (namespace >= 0) {
+ super.visitElementMetadata(targetKind, key, namespace, value);
+ }
+ }
+
private final List newDstNs;
private int[] nsMap;
}
diff --git a/src/main/java/net/fabricmc/mappingio/adapter/MappingSourceNsSwitch.java b/src/main/java/net/fabricmc/mappingio/adapter/MappingSourceNsSwitch.java
index de52a900..91da8920 100644
--- a/src/main/java/net/fabricmc/mappingio/adapter/MappingSourceNsSwitch.java
+++ b/src/main/java/net/fabricmc/mappingio/adapter/MappingSourceNsSwitch.java
@@ -300,6 +300,17 @@ public boolean visitElementContent(MappedElementKind targetKind) throws IOExcept
return relay;
}
+ @Override
+ public void visitElementMetadata(MappedElementKind targetKind, String key, int namespace, String value) throws IOException {
+ if (namespace == newSourceNs) {
+ namespace = -1;
+ } else if (namespace == -1) {
+ namespace = newSourceNs;
+ }
+
+ next.visitElementMetadata(targetKind, key, namespace, value);
+ }
+
private final String newSourceNsName;
private final boolean dropMissingNewSrcName;
diff --git a/src/main/java/net/fabricmc/mappingio/adapter/RegularAsFlatMappingVisitor.java b/src/main/java/net/fabricmc/mappingio/adapter/RegularAsFlatMappingVisitor.java
index 2c6408e8..7af2850b 100644
--- a/src/main/java/net/fabricmc/mappingio/adapter/RegularAsFlatMappingVisitor.java
+++ b/src/main/java/net/fabricmc/mappingio/adapter/RegularAsFlatMappingVisitor.java
@@ -89,6 +89,18 @@ private boolean visitClass(String srcName, String[] dstNames, String dstName) th
return relayLastClass;
}
+ @Override
+ public void visitClassMetadata(String srcName, String[] dstNames, String propertyKey, String[] propertyValues) throws IOException {
+ if (!visitClass(srcName, dstNames, null)) return;
+ visitElementMetadata(MappedElementKind.CLASS, propertyKey, propertyValues);
+ }
+
+ @Override
+ public void visitClassMetadata(String srcName, String dstName, String propertyKey, String[] propertyValues) throws IOException {
+ if (!visitClass(srcName, null, dstName)) return;
+ visitElementMetadata(MappedElementKind.CLASS, propertyKey, propertyValues);
+ }
+
@Override
public void visitClassComment(String srcName, String[] dstNames, String comment) throws IOException {
if (!visitClass(srcName, dstNames, null)) return;
@@ -128,6 +140,22 @@ private boolean visitField(String srcClsName, String srcName, String srcDesc,
return relayLastMember;
}
+ @Override
+ public void visitFieldMetadata(String srcClsName, String srcName, String srcDesc,
+ String[] dstClsNames, String[] dstNames, String[] dstDescs,
+ String propertyKey, String[] propertyValues) throws IOException {
+ if (!visitField(srcClsName, srcName, srcDesc, dstClsNames, dstNames, dstDescs, null, null, null)) return;
+ visitElementMetadata(MappedElementKind.FIELD, propertyKey, propertyValues);
+ }
+
+ @Override
+ public void visitFieldMetadata(String srcClsName, String srcName, String srcDesc,
+ String dstClsName, String dstName, String dstDesc,
+ String propertyKey, String[] propertyValues) throws IOException {
+ if (!visitField(srcClsName, srcName, srcDesc, null, null, null, dstClsName, dstName, dstDesc)) return;
+ visitElementMetadata(MappedElementKind.FIELD, propertyKey, propertyValues);
+ }
+
@Override
public void visitFieldComment(String srcClsName, String srcName, String srcDesc,
String[] dstClsNames, String[] dstNames, String[] dstDescs,
@@ -171,6 +199,22 @@ private boolean visitMethod(String srcClsName, String srcName, String srcDesc,
return relayLastMember;
}
+ @Override
+ public void visitMethodMetadata(String srcClsName, String srcName, String srcDesc,
+ String[] dstClsNames, String[] dstNames, String[] dstDescs,
+ String propertyKey, String[] propertyValues) throws IOException {
+ if (!visitMethod(srcClsName, srcName, srcDesc, dstClsNames, dstNames, dstDescs, null, null, null)) return;
+ visitElementMetadata(MappedElementKind.METHOD, propertyKey, propertyValues);
+ }
+
+ @Override
+ public void visitMethodMetadata(String srcClsName, String srcName, String srcDesc,
+ String dstClsName, String dstName, String dstDesc,
+ String propertyKey, String[] propertyValues) throws IOException {
+ if (!visitMethod(srcClsName, srcName, srcDesc, null, null, null, dstClsName, dstName, dstDesc)) return;
+ visitElementMetadata(MappedElementKind.METHOD, propertyKey, propertyValues);
+ }
+
@Override
public void visitMethodComment(String srcClsName, String srcName, String srcDesc,
String[] dstClsNames, String[] dstNames, String[] dstDescs,
@@ -219,6 +263,30 @@ private boolean visitMethodArg(String srcClsName, String srcMethodName, String s
return relayLastMethodSub;
}
+ @Override
+ public void visitMethodArgMetadata(String srcClsName, String srcMethodName, String srcMethodDesc, int argPosition, int lvIndex, String srcArgName,
+ String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstArgNames,
+ String propertyKey, String[] propertyValues) throws IOException {
+ if (!visitMethodArg(srcClsName, srcMethodName, srcMethodDesc, argPosition, lvIndex, srcArgName,
+ dstClsNames, dstMethodNames, dstMethodDescs, dstArgNames, null, null, null, null)) {
+ return;
+ }
+
+ visitElementMetadata(MappedElementKind.METHOD_ARG, propertyKey, propertyValues);
+ }
+
+ @Override
+ public void visitMethodArgMetadata(String srcClsName, String srcMethodName, String srcMethodDesc, int argPosition,
+ int lvIndex, String srcArgName, String dstClsName, String dstMethodName, String dstMethodDesc, String dstArgName,
+ String propertyKey, String[] propertyValues) throws IOException {
+ if (!visitMethodArg(srcClsName, srcMethodName, srcMethodDesc, argPosition, lvIndex, srcArgName,
+ null, null, null, null, dstClsName, dstMethodName, dstMethodDesc, dstArgName)) {
+ return;
+ }
+
+ visitElementMetadata(MappedElementKind.METHOD_ARG, propertyKey, propertyValues);
+ }
+
@Override
public void visitMethodArgComment(String srcClsName, String srcMethodName, String srcMethodDesc, int argPosition, int lvIndex, String srcArgName,
String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstArgNames,
@@ -233,8 +301,8 @@ public void visitMethodArgComment(String srcClsName, String srcMethodName, Strin
@Override
public void visitMethodArgComment(String srcClsName, String srcMethodName, String srcMethodDesc, int argPosition,
- int lvIndex, String srcArgName, String dstClsName, String dstMethodName, String dstMethodDesc,
- String dstArgName, String comment) throws IOException {
+ int lvIndex, String srcArgName, String dstClsName, String dstMethodName, String dstMethodDesc, String dstArgName,
+ String comment) throws IOException {
if (!visitMethodArg(srcClsName, srcMethodName, srcMethodDesc, argPosition, lvIndex, srcArgName,
null, null, null, null, dstClsName, dstMethodName, dstMethodDesc, dstArgName)) {
return;
@@ -274,6 +342,32 @@ private boolean visitMethodVar(String srcClsName, String srcMethodName, String s
return relayLastMethodSub;
}
+ @Override
+ public void visitMethodVarMetadata(String srcClsName, String srcMethodName, String srcMethodDesc,
+ int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName,
+ String[] dstClsNames, String[] dstMethodNames, String[] dstMethodDescs, String[] dstVarNames,
+ String propertyKey, String[] propertyValues) throws IOException {
+ if (!visitMethodVar(srcClsName, srcMethodName, srcMethodDesc, lvtRowIndex, lvIndex, startOpIdx, endOpIdx, srcVarName,
+ dstClsNames, dstMethodNames, dstMethodDescs, dstVarNames, null, null, null, null)) {
+ return;
+ }
+
+ visitElementMetadata(MappedElementKind.METHOD_VAR, propertyKey, propertyValues);
+ }
+
+ @Override
+ public void visitMethodVarMetadata(String srcClsName, String srcMethodName, String srcMethodDesc,
+ int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName,
+ String dstClsName, String dstMethodName, String dstMethodDesc, String dstVarName,
+ String propertyKey, String[] propertyValues) throws IOException {
+ if (!visitMethodVar(srcClsName, srcMethodName, srcMethodDesc, lvtRowIndex, lvIndex, startOpIdx, endOpIdx, srcVarName,
+ null, null, null, null, dstClsName, dstMethodName, dstMethodDesc, dstVarName)) {
+ return;
+ }
+
+ visitElementMetadata(MappedElementKind.METHOD_VAR, propertyKey, propertyValues);
+ }
+
@Override
public void visitMethodVarComment(String srcClsName, String srcMethodName, String srcMethodDesc,
int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcVarName,
@@ -344,6 +438,15 @@ private boolean visitDstNamesDescs(MappedElementKind targetKind, String[] dstNam
return next.visitElementContent(targetKind);
}
+ private void visitElementMetadata(MappedElementKind targetKind, String key, String[] values) throws IOException {
+ if (values != null) {
+ for (int i = 0; i < values.length; i++) {
+ String value = values[i];
+ if (value != null) next.visitElementMetadata(targetKind, key, i-1, value);
+ }
+ }
+ }
+
private final MappingVisitor next;
private boolean relayDstFieldDescs;
diff --git a/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java b/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java
index ac7bfcf6..66d87e46 100644
--- a/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java
+++ b/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java
@@ -17,19 +17,20 @@
package net.fabricmc.mappingio.format;
public enum MappingFormat {
- TINY_FILE("Tiny file", "tiny", true, true, false, false, false),
- TINY_2_FILE("Tiny v2 file", "tiny", true, true, true, true, true),
- ENIGMA_FILE("Enigma file", "mappings", false, true, true, true, false),
- ENIGMA_DIR("Enigma directory", null, false, true, true, true, false),
- MCP_DIR("MCP directory", null, false, false, true, true, false),
- SRG_FILE("SRG file", "srg", false, false, false, false, false),
- TSRG_FILE("TSRG file", "tsrg", false, false, false, false, false),
- TSRG_2_FILE("TSRG2 file", "tsrg", true, false, false, true, false),
- PROGUARD_FILE("ProGuard file", "map", false, true, false, false, false);
+ TINY_FILE("Tiny file", "tiny", true, true, false, false, false, MetadataSupport.ARBITRARY, MetadataSupport.NONE),
+ TINY_2_FILE("Tiny v2 file", "tiny", true, true, true, true, true, MetadataSupport.ARBITRARY, MetadataSupport.NONE),
+ ENIGMA_FILE("Enigma file", "mappings", false, true, true, true, false, MetadataSupport.NONE, MetadataSupport.HARDCODED),
+ ENIGMA_DIR("Enigma directory", null, false, true, true, true, false, MetadataSupport.NONE, MetadataSupport.HARDCODED),
+ MCP_DIR("MCP directory", null, false, false, true, true, false, MetadataSupport.NONE, MetadataSupport.NONE),
+ SRG_FILE("SRG file", "srg", false, false, false, false, false, MetadataSupport.NONE, MetadataSupport.NONE),
+ TSRG_FILE("TSRG file", "tsrg", false, false, false, false, false, MetadataSupport.NONE, MetadataSupport.NONE),
+ TSRG_2_FILE("TSRG2 file", "tsrg", true, false, false, true, false, MetadataSupport.NONE, MetadataSupport.HARDCODED),
+ PROGUARD_FILE("ProGuard file", "map", false, true, false, false, false, MetadataSupport.NONE, MetadataSupport.HARDCODED);
MappingFormat(String name, String fileExt,
boolean hasNamespaces, boolean hasFieldDescriptors,
- boolean supportsComments, boolean supportsArgs, boolean supportsLocals) {
+ boolean supportsComments, boolean supportsArgs, boolean supportsLocals,
+ MetadataSupport fileMetadataSupport, MetadataSupport elementMetadataSupport) {
this.name = name;
this.fileExt = fileExt;
this.hasNamespaces = hasNamespaces;
@@ -37,6 +38,8 @@ public enum MappingFormat {
this.supportsComments = supportsComments;
this.supportsArgs = supportsArgs;
this.supportsLocals = supportsLocals;
+ this.fileMetadataSupport = fileMetadataSupport;
+ this.elementMetadataSupport = elementMetadataSupport;
}
public boolean hasSingleFile() {
@@ -56,4 +59,17 @@ public String getGlobPattern() {
public final boolean supportsComments;
public final boolean supportsArgs;
public final boolean supportsLocals;
+ public final MetadataSupport fileMetadataSupport;
+ public final MetadataSupport elementMetadataSupport;
+
+ public enum MetadataSupport {
+ /** No metadata at all. */
+ NONE,
+
+ /** Only some select properties. */
+ HARDCODED,
+
+ /** Arbitrary metadata may be attached. */
+ ARBITRARY
+ }
}
diff --git a/src/main/java/net/fabricmc/mappingio/format/StandardProperties.java b/src/main/java/net/fabricmc/mappingio/format/StandardProperties.java
new file mode 100644
index 00000000..dc22319e
--- /dev/null
+++ b/src/main/java/net/fabricmc/mappingio/format/StandardProperties.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2023 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.mappingio.format;
+
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.jetbrains.annotations.ApiStatus;
+
+import net.fabricmc.mappingio.MappedElementKind;
+
+public final class StandardProperties {
+ private StandardProperties() {
+ }
+
+ public static Set values() {
+ return Collections.unmodifiableSet(values);
+ }
+
+ public static StandardProperty getByName(String name) {
+ return valuesByName.get(name);
+ }
+
+ @ApiStatus.Internal
+ public static StandardProperty getById(String id) {
+ return valuesById.get(id);
+ }
+
+ public static final StandardProperty NEXT_INTERMEDIARY_CLASS;
+ public static final StandardProperty NEXT_INTERMEDIARY_FIELD;
+ public static final StandardProperty NEXT_INTERMEDIARY_METHOD;
+ public static final StandardProperty NEXT_INTERMEDIARY_COMPONENT;
+ public static final StandardProperty MISSING_LVT_INDICES;
+ public static final StandardProperty ESCAPED_NAMES;
+ public static final StandardProperty MODIFIED_ACCESS;
+ public static final StandardProperty IS_STATIC;
+ public static final StandardProperty START_LINE_NUMBER;
+ public static final StandardProperty END_LINE_NUMBER;
+ private static final Set values = new HashSet<>();
+ private static final Map valuesByName = new HashMap<>();
+ private static final Map valuesById = new HashMap<>();
+
+ static {
+ NEXT_INTERMEDIARY_CLASS = register("next-intermediary-class")
+ .addMapping(MappingFormat.TINY_FILE, "INTERMEDIARY_COUNTER class")
+ .addMapping(MappingFormat.TINY_2_FILE, "next-intermediary-class");
+ NEXT_INTERMEDIARY_FIELD = register("next-intermediary-field")
+ .addMapping(MappingFormat.TINY_FILE, "INTERMEDIARY_COUNTER field")
+ .addMapping(MappingFormat.TINY_2_FILE, "next-intermediary-field");
+ NEXT_INTERMEDIARY_METHOD = register("next-intermediary-method")
+ .addMapping(MappingFormat.TINY_FILE, "INTERMEDIARY_COUNTER method")
+ .addMapping(MappingFormat.TINY_2_FILE, "next-intermediary-method");
+ NEXT_INTERMEDIARY_COMPONENT = register("next-intermediary-component")
+ .addMapping(MappingFormat.TINY_FILE, "INTERMEDIARY_COUNTER component")
+ .addMapping(MappingFormat.TINY_2_FILE, "next-intermediary-component");
+ MISSING_LVT_INDICES = register("missing-lvt-indices")
+ .addMapping(MappingFormat.TINY_2_FILE, "missing-lvt-indices");
+ ESCAPED_NAMES = register("escaped-names")
+ .addMapping(MappingFormat.TINY_2_FILE, "escaped-names");
+ MODIFIED_ACCESS = register("modified-access")
+ .addMapping(MappingFormat.ENIGMA_FILE, MappedElementKind.CLASS, "ACC:")
+ .addMapping(MappingFormat.ENIGMA_FILE, MappedElementKind.FIELD, "ACC:")
+ .addMapping(MappingFormat.ENIGMA_FILE, MappedElementKind.METHOD, "ACC:")
+ .addMapping(MappingFormat.ENIGMA_DIR, MappedElementKind.CLASS, "ACC:")
+ .addMapping(MappingFormat.ENIGMA_DIR, MappedElementKind.FIELD, "ACC:")
+ .addMapping(MappingFormat.ENIGMA_DIR, MappedElementKind.METHOD, "ACC:");
+ IS_STATIC = register("is-static")
+ .addMapping(MappingFormat.TSRG_2_FILE, MappedElementKind.METHOD, "static");
+ START_LINE_NUMBER = register("start-line-number")
+ .addMapping(MappingFormat.PROGUARD_FILE, MappedElementKind.METHOD, null);
+ END_LINE_NUMBER = register("end-line-number")
+ .addMapping(MappingFormat.PROGUARD_FILE, MappedElementKind.METHOD, null);
+ }
+
+ private static StandardPropertyImpl register(String id) {
+ return new StandardPropertyImpl(id);
+ }
+
+ private static class StandardPropertyImpl implements StandardProperty {
+ StandardPropertyImpl(String id) {
+ this.id = id;
+ values.add(this);
+ valuesById.put(id, this);
+ }
+
+ private StandardPropertyImpl addMapping(MappingFormat format, String name) {
+ filePropNameByFormat.put(format, name);
+ valuesByName.put(name, this);
+ return this;
+ }
+
+ private StandardPropertyImpl addMapping(MappingFormat format, MappedElementKind elementKind, String name) {
+ propElementKindByFormat.put(format, elementKind);
+ elementPropNameByFormat.put(new SimpleEntry<>(format, elementKind), name);
+ valuesByName.put(name, this);
+ return this;
+ }
+
+ @Override
+ public boolean isFileProperty() {
+ return !filePropNameByFormat.isEmpty();
+ }
+
+ @Override
+ public boolean isElementProperty() {
+ return !elementPropNameByFormat.isEmpty();
+ }
+
+ @Override
+ public Set getApplicableFormats() {
+ return filePropNameByFormat.keySet();
+ }
+
+ @Override
+ public Map getApplicableElementKinds() {
+ return propElementKindByFormat;
+ }
+
+ @Override
+ public boolean isApplicableTo(MappingFormat format) {
+ return filePropNameByFormat.containsKey(format);
+ }
+
+ @Override
+ public boolean isApplicableTo(MappingFormat format, MappedElementKind elementKind) {
+ return elementPropNameByFormat.containsKey(new SimpleEntry<>(format, elementKind));
+ }
+
+ @Override
+ public String getNameFor(MappingFormat format) {
+ return filePropNameByFormat.get(format);
+ }
+
+ @Override
+ public String getNameFor(MappingFormat format, MappedElementKind elementKind) {
+ return elementPropNameByFormat.get(new SimpleEntry<>(format, elementKind));
+ }
+
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ private final String id;
+ private final Map filePropNameByFormat = new HashMap<>(4);
+ private final Map, String> elementPropNameByFormat = new HashMap<>(4);
+ private final Map propElementKindByFormat = new HashMap<>(4);
+ }
+}
diff --git a/src/main/java/net/fabricmc/mappingio/format/StandardProperty.java b/src/main/java/net/fabricmc/mappingio/format/StandardProperty.java
new file mode 100644
index 00000000..84d5ea8e
--- /dev/null
+++ b/src/main/java/net/fabricmc/mappingio/format/StandardProperty.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2023 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.mappingio.format;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.jetbrains.annotations.ApiStatus;
+
+import net.fabricmc.mappingio.MappedElementKind;
+
+public interface StandardProperty {
+ boolean isFileProperty();
+ boolean isElementProperty();
+
+ Set getApplicableFormats();
+ Map getApplicableElementKinds();
+
+ boolean isApplicableTo(MappingFormat format);
+ boolean isApplicableTo(MappingFormat format, MappedElementKind elementKind);
+
+ String getNameFor(MappingFormat format);
+ String getNameFor(MappingFormat format, MappedElementKind elementKind);
+
+ /**
+ * Used internally by MappingTrees, consistency between JVM sessions
+ * or library versions isn't guaranteed!
+ */
+ @ApiStatus.Internal
+ String getId();
+}
diff --git a/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaDirWriter.java b/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaDirWriter.java
index ea5332e1..4e09852c 100644
--- a/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaDirWriter.java
+++ b/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaDirWriter.java
@@ -29,6 +29,7 @@
import java.util.List;
import net.fabricmc.mappingio.MappedElementKind;
+import net.fabricmc.mappingio.format.MappingFormat;
public final class EnigmaDirWriter extends EnigmaWriterBase {
public EnigmaDirWriter(Path dir, boolean deleteExistingFiles) throws IOException {
@@ -60,6 +61,11 @@ public FileVisitResult postVisitDirectory(Path file, IOException exc) throws IOE
}
}
+ @Override
+ protected MappingFormat getFormat() {
+ return MappingFormat.ENIGMA_DIR;
+ }
+
@Override
public void close() throws IOException {
if (writer != null) {
@@ -128,16 +134,9 @@ public boolean visitElementContent(MappedElementKind targetKind) throws IOExcept
writer = Files.newBufferedWriter(file, StandardOpenOption.WRITE, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
}
-
- writeMismatchedOrMissingClasses();
- } else if (targetKind == MappedElementKind.FIELD || targetKind == MappedElementKind.METHOD) {
- writer.write(' ');
- writer.write(desc);
- writer.write('\n');
- } else {
- writer.write('\n');
}
+ super.visitElementContent(targetKind);
return true;
}
diff --git a/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaFileReader.java b/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaFileReader.java
index 05ffce65..f8885567 100644
--- a/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaFileReader.java
+++ b/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaFileReader.java
@@ -19,13 +19,17 @@
import java.io.IOException;
import java.io.Reader;
import java.util.Collections;
+import java.util.Locale;
import java.util.Set;
+import org.jetbrains.annotations.Nullable;
+
import net.fabricmc.mappingio.MappedElementKind;
import net.fabricmc.mappingio.MappingFlag;
import net.fabricmc.mappingio.MappingUtil;
import net.fabricmc.mappingio.MappingVisitor;
import net.fabricmc.mappingio.format.ColumnFileReader;
+import net.fabricmc.mappingio.format.StandardProperties;
import net.fabricmc.mappingio.tree.MappingTree;
import net.fabricmc.mappingio.tree.MemoryMappingTree;
@@ -61,7 +65,7 @@ public static void read(ColumnFileReader reader, String sourceNs, String targetN
final MappingVisitor finalVisitor = visitor;
do {
- if (reader.nextCol("CLASS")) { // class: CLASS []
+ if (reader.nextCol("CLASS")) { // class: CLASS [] []
readClass(reader, 0, null, null, commentSb, finalVisitor);
}
} while (reader.nextLine(0));
@@ -75,8 +79,11 @@ public static void read(ColumnFileReader reader, String sourceNs, String targetN
}
private static void readClass(ColumnFileReader reader, int indent, String outerSrcClass, String outerDstClass, StringBuilder commentSb, MappingVisitor visitor) throws IOException {
- String srcInnerName = reader.nextCol();
- if (srcInnerName == null || srcInnerName.isEmpty()) throw new IOException("missing class-name-a in line "+reader.getLineNumber());
+ String line = reader.nextCols(false);
+ String[] parts = line.split(" ");
+
+ if (parts.length == 0 || parts[0].isEmpty()) throw new IOException("missing class-name-a in line "+reader.getLineNumber());
+ String srcInnerName = parts[0];
String srcName = srcInnerName;
@@ -84,7 +91,22 @@ private static void readClass(ColumnFileReader reader, int indent, String outerS
srcName = String.format("%s$%s", outerSrcClass, srcInnerName);
}
- String dstInnerName = reader.nextCol();
+ String dstInnerName = null;
+ String accessModifier = null;
+
+ if (parts.length == 2) { // |
+ String parsedModifier = parseModifier(parts[1]);
+
+ if (parsedModifier == null) {
+ dstInnerName = parts[1];
+ } else {
+ accessModifier = parsedModifier;
+ }
+ } else {
+ dstInnerName = parts[1];
+ accessModifier = parts[2];
+ }
+
String dstName = dstInnerName;
// merge with outer name if available
@@ -96,19 +118,20 @@ private static void readClass(ColumnFileReader reader, int indent, String outerS
dstName = String.format("%s$%s", outerDstClass, dstInnerName);
}
- readClassBody(reader, indent, srcName, dstName, commentSb, visitor);
+ readClassBody(reader, indent, srcName, dstName, accessModifier, commentSb, visitor);
}
- private static void readClassBody(ColumnFileReader reader, int indent, String srcClass, String dstClass, StringBuilder commentSb, MappingVisitor visitor) throws IOException {
+ private static void readClassBody(ColumnFileReader reader, int indent, String srcClass, String dstClass,
+ String classAccess, StringBuilder commentSb, MappingVisitor visitor) throws IOException {
boolean visited = false;
int state = 0; // 0=invalid 1=visit -1=skip
while (reader.nextLine(indent + 1)) {
boolean isMethod;
- if (reader.nextCol("CLASS")) { // nested class: CLASS []
+ if (reader.nextCol("CLASS")) { // nested class: CLASS [] []
if (!visited || commentSb.length() > 0) {
- visitClass(srcClass, dstClass, state, commentSb, visitor);
+ visitClass(srcClass, dstClass, state, classAccess, commentSb, visitor);
visited = true;
}
@@ -116,46 +139,64 @@ private static void readClassBody(ColumnFileReader reader, int indent, String sr
state = 0;
} else if (reader.nextCol("COMMENT")) { // comment: COMMENT
readComment(reader, commentSb);
- } else if ((isMethod = reader.nextCol("METHOD")) || reader.nextCol("FIELD")) { // method: METHOD [] or field: FIELD []
- state = visitClass(srcClass, dstClass, state, commentSb, visitor);
+ } else if ((isMethod = reader.nextCol("METHOD")) || reader.nextCol("FIELD")) { // METHOD|FIELD [] []
+ state = visitClass(srcClass, dstClass, state, classAccess, commentSb, visitor);
visited = true;
if (state < 0) continue;
- String srcName = reader.nextCol();
- if (srcName == null || srcName.isEmpty()) throw new IOException("missing field-name-a in line "+reader.getLineNumber());
+ String line = reader.nextCols(false);
+ String[] parts = line.split(" ");
+
+ if (parts.length == 0 || parts[0].isEmpty()) throw new IOException("missing member-name-a in line "+reader.getLineNumber());
+ if (parts.length == 1 || parts[1].isEmpty()) throw new IOException("missing member-desc-a in line "+reader.getLineNumber());
+ String srcName = parts[0];
+ String dstName = null;
+ String modifier = null;
+ String srcDesc;
+
+ if (parts.length == 2) { //
+ srcDesc = parts[1];
+ } else if (parts.length == 3) { // |
+ String parsedModifier = parseModifier(parts[2]);
+
+ if (parsedModifier == null) {
+ dstName = parts[1];
+ srcDesc = parts[2];
+ } else {
+ srcDesc = parts[1];
+ modifier = parsedModifier;
+ }
+ } else { //
+ dstName = parts[1];
+ srcDesc = parts[2];
+ modifier = parts[3];
+ }
- String dstNameOrSrcDesc = reader.nextCol();
- if (dstNameOrSrcDesc == null || dstNameOrSrcDesc.isEmpty()) throw new IOException("missing field-desc-b in line "+reader.getLineNumber());
+ MappedElementKind targetKind = isMethod && visitor.visitMethod(srcName, srcDesc) ? MappedElementKind.METHOD
+ : !isMethod && visitor.visitField(srcName, srcDesc) ? MappedElementKind.FIELD : null;
- String srcDesc = reader.nextCol();
- String dstName;
+ if (targetKind != null) {
+ if (dstName != null && !dstName.isEmpty()) visitor.visitDstName(targetKind, 0, dstName);
+ if (modifier != null) visitAccessModifier(targetKind, modifier, visitor);
- if (srcDesc == null) {
- dstName = null;
- srcDesc = dstNameOrSrcDesc;
- } else {
- dstName = dstNameOrSrcDesc;
- }
-
- if (isMethod && visitor.visitMethod(srcName, srcDesc)) {
- if (dstName != null && !dstName.isEmpty()) visitor.visitDstName(MappedElementKind.METHOD, 0, dstName);
- readMethod(reader, indent, commentSb, visitor);
- } else if (!isMethod && visitor.visitField(srcName, srcDesc)) {
- if (dstName != null && !dstName.isEmpty()) visitor.visitDstName(MappedElementKind.FIELD, 0, dstName);
- readElement(reader, MappedElementKind.FIELD, indent, commentSb, visitor);
+ if (targetKind == MappedElementKind.METHOD) {
+ readMethod(reader, indent, commentSb, visitor);
+ } else {
+ readElement(reader, targetKind, indent, commentSb, visitor);
+ }
}
}
}
if (!visited || commentSb.length() > 0) {
- visitClass(srcClass, dstClass, state, commentSb, visitor);
+ visitClass(srcClass, dstClass, state, classAccess, commentSb, visitor);
}
}
/**
* Re-visit a class if necessary and visit its comment if available.
*/
- private static int visitClass(String srcClass, String dstClass, int state, StringBuilder commentSb, MappingVisitor visitor) throws IOException {
+ private static int visitClass(String srcClass, String dstClass, int state, String accessModifier, StringBuilder commentSb, MappingVisitor visitor) throws IOException {
// state: 0=invalid 1=visit -1=skip
if (state == 0) {
@@ -168,6 +209,10 @@ private static int visitClass(String srcClass, String dstClass, int state, Strin
state = visitContent ? 1 : -1;
+ if (accessModifier != null) {
+ visitAccessModifier(MappedElementKind.CLASS, accessModifier, visitor);
+ }
+
if (commentSb.length() > 0) {
if (state > 0) visitor.visitComment(MappedElementKind.CLASS, commentSb.toString());
@@ -178,6 +223,10 @@ private static int visitClass(String srcClass, String dstClass, int state, Strin
return state;
}
+ private static void visitAccessModifier(MappedElementKind targetKind, String modifier, MappingVisitor visitor) throws IOException {
+ visitor.visitElementMetadata(targetKind, StandardProperties.MODIFIED_ACCESS.getId(), 0, modifier);
+ }
+
private static void readMethod(ColumnFileReader reader, int indent, StringBuilder commentSb, MappingVisitor visitor) throws IOException {
if (!visitor.visitElementContent(MappedElementKind.METHOD)) return;
@@ -233,4 +282,13 @@ private static void submitComment(MappedElementKind kind, StringBuilder commentS
visitor.visitComment(kind, commentSb.toString());
commentSb.setLength(0);
}
+
+ @Nullable
+ private static String parseModifier(String token) {
+ if (!token.startsWith("ACC:")) {
+ return null;
+ }
+
+ return token.substring(4).toLowerCase(Locale.ROOT);
+ }
}
diff --git a/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaFileWriter.java b/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaFileWriter.java
index 4c285707..4ce1c70f 100644
--- a/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaFileWriter.java
+++ b/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaFileWriter.java
@@ -19,7 +19,7 @@
import java.io.IOException;
import java.io.Writer;
-import net.fabricmc.mappingio.MappedElementKind;
+import net.fabricmc.mappingio.format.MappingFormat;
public final class EnigmaFileWriter extends EnigmaWriterBase {
public EnigmaFileWriter(Writer writer) throws IOException {
@@ -27,17 +27,7 @@ public EnigmaFileWriter(Writer writer) throws IOException {
}
@Override
- public boolean visitElementContent(MappedElementKind targetKind) throws IOException {
- if (targetKind == MappedElementKind.CLASS) {
- writeMismatchedOrMissingClasses();
- } else if (targetKind == MappedElementKind.FIELD || targetKind == MappedElementKind.METHOD) {
- writer.write(' ');
- writer.write(desc);
- writer.write('\n');
- } else {
- writer.write('\n');
- }
-
- return true;
+ protected MappingFormat getFormat() {
+ return MappingFormat.ENIGMA_FILE;
}
}
diff --git a/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaWriterBase.java b/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaWriterBase.java
index 11277530..818d27be 100644
--- a/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaWriterBase.java
+++ b/src/main/java/net/fabricmc/mappingio/format/enigma/EnigmaWriterBase.java
@@ -19,18 +19,26 @@
import java.io.IOException;
import java.io.Writer;
import java.util.EnumSet;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Locale;
+import java.util.Map;
import java.util.Set;
import net.fabricmc.mappingio.MappedElementKind;
import net.fabricmc.mappingio.MappingFlag;
import net.fabricmc.mappingio.MappingWriter;
+import net.fabricmc.mappingio.format.MappingFormat;
+import net.fabricmc.mappingio.format.StandardProperties;
+import net.fabricmc.mappingio.format.StandardProperty;
abstract class EnigmaWriterBase implements MappingWriter {
EnigmaWriterBase(Writer writer) throws IOException {
this.writer = writer;
}
+ protected abstract MappingFormat getFormat();
+
@Override
public void close() throws IOException {
writer.close();
@@ -46,12 +54,14 @@ public void visitNamespaces(String srcNamespace, List dstNamespaces) { }
@Override
public boolean visitClass(String srcName) throws IOException {
+ if (writer != null && srcClassName != null) writePendingElementMetadata(true);
srcClassName = srcName;
return true;
}
@Override
public boolean visitField(String srcName, String srcDesc) throws IOException {
+ writePendingElementMetadata(true);
writeIndent(0);
writer.write("FIELD ");
writer.write(srcName);
@@ -63,6 +73,7 @@ public boolean visitField(String srcName, String srcDesc) throws IOException {
@Override
public boolean visitMethod(String srcName, String srcDesc) throws IOException {
+ writePendingElementMetadata(true);
writeIndent(0);
writer.write("METHOD ");
writer.write(srcName);
@@ -74,6 +85,7 @@ public boolean visitMethod(String srcName, String srcDesc) throws IOException {
@Override
public boolean visitMethodArg(int argPosition, int lvIndex, String srcName) throws IOException {
+ writePendingElementMetadata(true);
writeIndent(1);
writer.write("ARG ");
writer.write(Integer.toString(lvIndex));
@@ -82,12 +94,13 @@ public boolean visitMethodArg(int argPosition, int lvIndex, String srcName) thro
}
@Override
- public boolean visitMethodVar(int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcName) {
+ public boolean visitMethodVar(int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcName) throws IOException {
return false;
}
@Override
public boolean visitEnd() throws IOException {
+ writePendingElementMetadata(false);
close();
return true;
@@ -106,7 +119,28 @@ public void visitDstName(MappedElementKind targetKind, int namespace, String nam
}
@Override
- public abstract boolean visitElementContent(MappedElementKind targetKind) throws IOException;
+ public boolean visitElementContent(MappedElementKind targetKind) throws IOException {
+ if (targetKind == MappedElementKind.CLASS) {
+ writeMismatchedOrMissingClasses();
+ } else if (targetKind == MappedElementKind.FIELD || targetKind == MappedElementKind.METHOD) {
+ writer.write(' ');
+ writer.write(desc);
+ }
+
+ return true;
+ }
+
+ @Override
+ public void visitElementMetadata(MappedElementKind target, String key, int namespace, String value) {
+ if (namespace != 0) return;
+
+ StandardProperty property = StandardProperties.getById(key);
+ if (property == null) return;
+ if (!property.isApplicableTo(getFormat(), target)) return;
+
+ key = property.getNameFor(getFormat(), target);
+ elementMetadata.put(property, value);
+ }
protected static int getNextOuterEnd(String name, int startPos) {
int pos;
@@ -121,6 +155,7 @@ protected static int getNextOuterEnd(String name, int startPos) {
@Override
public void visitComment(MappedElementKind targetKind, String comment) throws IOException {
+ writePendingElementMetadata(true);
int start = 0;
int pos;
@@ -150,7 +185,9 @@ public void visitComment(MappedElementKind targetKind, String comment) throws IO
if (start < end) writer.write(comment, start, end - start);
}
- writer.write('\n');
+ if (pos >= 0) {
+ writer.write('\n');
+ }
start = end + 1;
} while (pos >= 0);
@@ -159,6 +196,7 @@ public void visitComment(MappedElementKind targetKind, String comment) throws IO
protected void writeMismatchedOrMissingClasses() throws IOException {
indent = 0;
int srcStart = 0;
+ boolean writeNewLines = false;
do {
int srcEnd = getNextOuterEnd(srcClassName, srcStart);
@@ -167,6 +205,11 @@ protected void writeMismatchedOrMissingClasses() throws IOException {
if (!lastWrittenClass.regionMatches(srcStart, srcClassName, srcStart, srcLen) // writtenPart.startsWith(srcPart)
|| srcEnd < lastWrittenClass.length() && lastWrittenClass.charAt(srcEnd) != '$') { // no trailing characters in writtenPart -> startsWith = equals
+ if (writeNewLines) {
+ writer.write('\n');
+ }
+
+ writeNewLines = true;
writeIndent(0);
writer.write("CLASS ");
writer.write(srcClassName, srcStart, srcLen);
@@ -192,7 +235,7 @@ protected void writeMismatchedOrMissingClasses() throws IOException {
}
}
- writer.write('\n');
+ writePendingElementMetadata(false);
}
indent++;
@@ -203,6 +246,22 @@ protected void writeMismatchedOrMissingClasses() throws IOException {
dstName = null;
}
+ protected void writePendingElementMetadata(boolean appendLineBreak) throws IOException {
+ if (!elementMetadata.isEmpty()) {
+ for (Map.Entry entry : elementMetadata.entrySet()) {
+ if (entry.getKey() != StandardProperties.MODIFIED_ACCESS) throw new IllegalStateException();
+
+ writer.write(" ACC:");
+ writer.write(entry.getValue().toUpperCase(Locale.ROOT));
+ break;
+ }
+
+ elementMetadata.clear();
+ }
+
+ if (appendLineBreak) writer.write('\n');
+ }
+
protected void writeIndent(int extra) throws IOException {
for (int i = 0; i < indent + extra; i++) {
writer.write('\t');
@@ -212,6 +271,7 @@ protected void writeIndent(int extra) throws IOException {
protected static final Set flags = EnumSet.of(MappingFlag.NEEDS_UNIQUENESS, MappingFlag.NEEDS_SRC_FIELD_DESC, MappingFlag.NEEDS_SRC_METHOD_DESC);
protected static final String toEscape = "\\\n\r\0\t";
protected static final String escaped = "\\nr0t";
+ protected static final LinkedHashMap elementMetadata = new LinkedHashMap<>();
protected Writer writer;
protected int indent;
@@ -220,7 +280,6 @@ protected void writeIndent(int extra) throws IOException {
protected String currentClass;
protected String lastWrittenClass = "";
protected String dstName;
- protected String[] dstNames;
protected String desc;
}
diff --git a/src/main/java/net/fabricmc/mappingio/format/proguard/ProGuardFileReader.java b/src/main/java/net/fabricmc/mappingio/format/proguard/ProGuardFileReader.java
index d1670b7d..9193e79a 100644
--- a/src/main/java/net/fabricmc/mappingio/format/proguard/ProGuardFileReader.java
+++ b/src/main/java/net/fabricmc/mappingio/format/proguard/ProGuardFileReader.java
@@ -27,6 +27,7 @@
import net.fabricmc.mappingio.MappingFlag;
import net.fabricmc.mappingio.MappingUtil;
import net.fabricmc.mappingio.MappingVisitor;
+import net.fabricmc.mappingio.format.StandardProperties;
public final class ProGuardFileReader {
private ProGuardFileReader() {
@@ -115,6 +116,8 @@ private static void read(BufferedReader reader, String sourceNs, String targetNs
// lineStart, lineEndIncl, rtype
String part0 = parts[0];
int pos = part0.indexOf(':');
+ String lineStart = null;
+ String lineEnd = null;
String retType;
@@ -124,6 +127,8 @@ private static void read(BufferedReader reader, String sourceNs, String targetNs
int pos2 = part0.indexOf(':', pos + 1);
assert pos2 != -1;
+ lineStart = part0.substring(0, pos);
+ lineEnd = part0.substring(pos + 1, pos2);
retType = part0.substring(pos2 + 1);
}
@@ -141,7 +146,11 @@ private static void read(BufferedReader reader, String sourceNs, String targetNs
if (visitor.visitMethod(name, desc)) {
String mappedName = parts[3];
visitor.visitDstName(MappedElementKind.METHOD, 0, mappedName);
- visitor.visitElementContent(MappedElementKind.METHOD);
+
+ if (visitor.visitElementContent(MappedElementKind.METHOD) && lineStart != null) {
+ visitor.visitElementMetadata(MappedElementKind.METHOD, StandardProperties.START_LINE_NUMBER.getId(), 0, lineStart);
+ visitor.visitElementMetadata(MappedElementKind.METHOD, StandardProperties.END_LINE_NUMBER.getId(), 0, lineEnd);
+ }
}
}
}
diff --git a/src/main/java/net/fabricmc/mappingio/format/proguard/ProGuardFileWriter.java b/src/main/java/net/fabricmc/mappingio/format/proguard/ProGuardFileWriter.java
index f41d4450..d0f6a738 100644
--- a/src/main/java/net/fabricmc/mappingio/format/proguard/ProGuardFileWriter.java
+++ b/src/main/java/net/fabricmc/mappingio/format/proguard/ProGuardFileWriter.java
@@ -18,6 +18,7 @@
import java.io.IOException;
import java.io.Writer;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -25,6 +26,8 @@
import net.fabricmc.mappingio.MappedElementKind;
import net.fabricmc.mappingio.MappingWriter;
+import net.fabricmc.mappingio.format.StandardProperties;
+import net.fabricmc.mappingio.format.StandardProperty;
/**
* A mapping writer for the ProGuard mapping format.
@@ -36,8 +39,11 @@
*/
public final class ProGuardFileWriter implements MappingWriter {
private final Writer writer;
- private int dstNamespace = -1;
private final String dstNamespaceString;
+ private int dstNamespace = -1;
+ private MappedElementKind pendingMemberType;
+ /** srcName, srcDesc, dstName, [lineStart, lineEnd]. */
+ private String[] pendingMemberData = new String[5];
/**
* Constructs a ProGuard mapping writer that uses
@@ -103,6 +109,8 @@ public void visitNamespaces(String srcNamespace, List dstNamespaces) thr
@Override
public boolean visitClass(String srcName) throws IOException {
+ writePendingMember();
+
writer.write(toJavaClassName(srcName));
writeArrow();
return true;
@@ -110,34 +118,21 @@ public boolean visitClass(String srcName) throws IOException {
@Override
public boolean visitField(String srcName, String srcDesc) throws IOException {
- writeIndent();
- writer.write(toJavaType(srcDesc));
- writer.write(' ');
- writer.write(srcName);
- writeArrow();
+ writePendingMember();
+
+ pendingMemberType = MappedElementKind.FIELD;
+ pendingMemberData[0] = srcName;
+ pendingMemberData[1] = srcDesc;
return true;
}
@Override
public boolean visitMethod(String srcName, String srcDesc) throws IOException {
- Type type = Type.getMethodType(srcDesc);
- writeIndent();
- writer.write(toJavaType(type.getReturnType().getDescriptor()));
- writer.write(' ');
- writer.write(srcName);
- writer.write('(');
- Type[] args = type.getArgumentTypes();
-
- for (int i = 0; i < args.length; i++) {
- if (i > 0) {
- writer.write(',');
- }
-
- writer.write(toJavaType(args[i].getDescriptor()));
- }
+ writePendingMember();
- writer.write(')');
- writeArrow();
+ pendingMemberType = MappedElementKind.METHOD;
+ pendingMemberData[0] = srcName;
+ pendingMemberData[1] = srcDesc;
return true;
}
@@ -162,11 +157,21 @@ public void visitDstName(MappedElementKind targetKind, int namespace, String nam
if (targetKind == MappedElementKind.CLASS) {
writer.write(toJavaClassName(name));
writer.write(':');
+ writer.write('\n');
} else {
- writer.write(name);
+ pendingMemberData[2] = name;
}
+ }
- writer.write('\n');
+ @Override
+ public void visitElementMetadata(MappedElementKind target, String key, int namespace, String value) {
+ StandardProperty property = StandardProperties.getById(key);
+
+ if (property == StandardProperties.START_LINE_NUMBER) {
+ pendingMemberData[3] = value;
+ } else if (property == StandardProperties.END_LINE_NUMBER) {
+ pendingMemberData[4] = value;
+ }
}
@Override
@@ -174,6 +179,63 @@ public void visitComment(MappedElementKind targetKind, String comment) throws IO
// ignored
}
+ @Override
+ public boolean visitEnd() throws IOException {
+ writePendingMember();
+ return true;
+ }
+
+ private void writePendingMember() throws IOException {
+ if (pendingMemberType == null) return;
+ String srcName = pendingMemberData[0];
+ String srcDesc = pendingMemberData[1];
+ String dstName = pendingMemberData[2];
+ String startLine = pendingMemberData[3];
+ String endLine = pendingMemberData[4];
+
+ writeIndent();
+
+ if (startLine != null && endLine != null) {
+ writer.write(startLine);
+ writer.write(':');
+ writer.write(endLine);
+ writer.write(':');
+ }
+
+ if (pendingMemberType == MappedElementKind.FIELD) {
+ writer.write(toJavaType(srcDesc));
+ writer.write(' ');
+ writer.write(srcName);
+ writeArrow();
+ } else {
+ Type type = Type.getMethodType(srcDesc);
+ writer.write(toJavaType(type.getReturnType().getDescriptor()));
+ writer.write(' ');
+ writer.write(srcName);
+ writer.write('(');
+ Type[] args = type.getArgumentTypes();
+
+ for (int i = 0; i < args.length; i++) {
+ if (i > 0) {
+ writer.write(',');
+ }
+
+ writer.write(toJavaType(args[i].getDescriptor()));
+ }
+
+ writer.write(')');
+ writeArrow();
+ }
+
+ if (dstName != null) {
+ writer.write(dstName);
+ writer.write('\n');
+ }
+
+ Arrays.fill(pendingMemberData, null);
+ pendingMemberType = null;
+ }
+
private void writeArrow() throws IOException {
writer.write(" -> ");
}
diff --git a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileReader.java b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileReader.java
index 56e2517c..9d807b6d 100644
--- a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileReader.java
+++ b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileReader.java
@@ -26,6 +26,9 @@
import net.fabricmc.mappingio.MappingFlag;
import net.fabricmc.mappingio.MappingVisitor;
import net.fabricmc.mappingio.format.ColumnFileReader;
+import net.fabricmc.mappingio.format.MappingFormat;
+import net.fabricmc.mappingio.format.StandardProperties;
+import net.fabricmc.mappingio.format.StandardProperty;
import net.fabricmc.mappingio.tree.MappingTree;
import net.fabricmc.mappingio.tree.MemoryMappingTree;
@@ -134,28 +137,27 @@ private static void read(ColumnFileReader reader, MappingVisitor visitor) throws
}
} else {
String line = reader.nextCol();
- final String prefix = "# INTERMEDIARY-COUNTER ";
- String[] parts;
-
- if (line.startsWith(prefix)
- && (parts = line.substring(prefix.length()).split(" ")).length == 2) {
- String property = null;
-
- switch (parts[0]) {
- case "class":
- property = nextIntermediaryClassProperty;
- break;
- case "field":
- property = nextIntermediaryFieldProperty;
- break;
- case "method":
- property = nextIntermediaryMethodProperty;
- break;
+
+ if (line.startsWith("# ") && line.length() >= 3 && line.charAt(3) != ' ') { // Metadata
+ line = line.substring(2);
+ String[] parts = line.split(" ");
+ String value = parts[parts.length - 1];
+ String key = line.substring(0, line.lastIndexOf(value));
+
+ if (key.isEmpty()) {
+ String oldValue = value;
+ value = key;
+ key = oldValue;
}
+ StandardProperty property = StandardProperties.getByName(key);
+
if (property != null) {
- visitor.visitMetadata(property, parts[1]);
+ if (!property.isApplicableTo(format)) continue; // How did it get there?
+ key = property.getId();
}
+
+ visitor.visitMetadata(key, value);
}
}
}
@@ -180,7 +182,5 @@ private static void readDstNames(ColumnFileReader reader, MappedElementKind subj
}
}
- static final String nextIntermediaryClassProperty = "next-intermediary-class";
- static final String nextIntermediaryFieldProperty = "next-intermediary-field";
- static final String nextIntermediaryMethodProperty = "next-intermediary-method";
+ private static final MappingFormat format = MappingFormat.TINY_FILE;
}
diff --git a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileWriter.java b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileWriter.java
index bcdf2406..2685c033 100644
--- a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileWriter.java
+++ b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileWriter.java
@@ -26,6 +26,9 @@
import net.fabricmc.mappingio.MappedElementKind;
import net.fabricmc.mappingio.MappingFlag;
import net.fabricmc.mappingio.MappingWriter;
+import net.fabricmc.mappingio.format.MappingFormat;
+import net.fabricmc.mappingio.format.StandardProperties;
+import net.fabricmc.mappingio.format.StandardProperty;
public final class Tiny1FileWriter implements MappingWriter {
public Tiny1FileWriter(Writer writer) {
@@ -59,30 +62,18 @@ public void visitNamespaces(String srcNamespace, List dstNamespaces) thr
@Override
public void visitMetadata(String key, String value) throws IOException {
- switch (key) {
- case Tiny1FileReader.nextIntermediaryClassProperty:
- case Tiny1FileReader.nextIntermediaryFieldProperty:
- case Tiny1FileReader.nextIntermediaryMethodProperty:
- write("# INTERMEDIARY-COUNTER ");
-
- switch (key) {
- case Tiny1FileReader.nextIntermediaryClassProperty:
- write("class");
- break;
- case Tiny1FileReader.nextIntermediaryFieldProperty:
- write("field");
- break;
- case Tiny1FileReader.nextIntermediaryMethodProperty:
- write("method");
- break;
- default:
- throw new IllegalStateException();
- }
+ StandardProperty property = StandardProperties.getById(key);
- write(" ");
- write(value);
- writeLn();
+ if (property != null) {
+ if (!property.isApplicableTo(format)) return;
+ key = property.getNameFor(format);
}
+
+ write("# ");
+ write(key);
+ write(" ");
+ write(value);
+ writeLn();
}
@Override
@@ -190,7 +181,8 @@ private void writeTab() throws IOException {
writer.write('\t');
}
- private static final Set flags = EnumSet.of(MappingFlag.NEEDS_SRC_FIELD_DESC, MappingFlag.NEEDS_SRC_METHOD_DESC);
+ private static final Set flags = EnumSet.of(MappingFlag.NEEDS_UNIQUENESS, MappingFlag.NEEDS_SRC_FIELD_DESC, MappingFlag.NEEDS_SRC_METHOD_DESC);
+ private static final MappingFormat format = MappingFormat.TINY_FILE;
private final Writer writer;
private String classSrcName;
diff --git a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileReader.java b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileReader.java
index 6381d80f..7c174ea1 100644
--- a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileReader.java
+++ b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileReader.java
@@ -25,6 +25,9 @@
import net.fabricmc.mappingio.MappingFlag;
import net.fabricmc.mappingio.MappingVisitor;
import net.fabricmc.mappingio.format.ColumnFileReader;
+import net.fabricmc.mappingio.format.MappingFormat;
+import net.fabricmc.mappingio.format.StandardProperties;
+import net.fabricmc.mappingio.format.StandardProperty;
public final class Tiny2FileReader {
private Tiny2FileReader() {
@@ -89,16 +92,22 @@ private static void read(ColumnFileReader reader, MappingVisitor visitor) throws
if (visitHeader || firstIteration) {
while (reader.nextLine(1)) {
if (!visitHeader) {
- if (!escapeNames && reader.nextCol(Tiny2Util.escapedNamesProperty)) {
+ if (!escapeNames && reader.nextCol(StandardProperties.ESCAPED_NAMES.getNameFor(format))) {
escapeNames = true;
}
} else {
String key = reader.nextCol();
if (key == null) throw new IOException("missing property key in line "+reader.getLineNumber());
String value = reader.nextEscapedCol(); // may be missing -> null
+ StandardProperty property = StandardProperties.getByName(key);
- if (key.equals(Tiny2Util.escapedNamesProperty)) {
- escapeNames = true;
+ if (property != null) {
+ if (!property.isApplicableTo(format)) continue; // How did it get there?
+ key = property.getId();
+
+ if (property == StandardProperties.ESCAPED_NAMES) {
+ escapeNames = true;
+ }
}
visitor.visitMetadata(key, value);
@@ -215,4 +224,6 @@ private static void readDstNames(ColumnFileReader reader, MappedElementKind subj
if (!name.isEmpty()) visitor.visitDstName(subjectKind, dstNs, name);
}
}
+
+ private static final MappingFormat format = MappingFormat.TINY_2_FILE;
}
diff --git a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileWriter.java b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileWriter.java
index aee1bdc0..2a37e263 100644
--- a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileWriter.java
+++ b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileWriter.java
@@ -26,6 +26,9 @@
import net.fabricmc.mappingio.MappedElementKind;
import net.fabricmc.mappingio.MappingFlag;
import net.fabricmc.mappingio.MappingWriter;
+import net.fabricmc.mappingio.format.MappingFormat;
+import net.fabricmc.mappingio.format.StandardProperties;
+import net.fabricmc.mappingio.format.StandardProperty;
public final class Tiny2FileWriter implements MappingWriter {
public Tiny2FileWriter(Writer writer, boolean escapeNames) {
@@ -60,9 +63,16 @@ public void visitNamespaces(String srcNamespace, List dstNamespaces) thr
@Override
public void visitMetadata(String key, String value) throws IOException {
- if (key.equals(Tiny2Util.escapedNamesProperty)) {
- escapeNames = true;
- wroteEscapedNamesProperty = true;
+ StandardProperty property = StandardProperties.getById(key);
+
+ if (property != null) {
+ if (!property.isApplicableTo(format)) return;
+ key = property.getNameFor(format);
+
+ if (property == StandardProperties.ESCAPED_NAMES) {
+ escapeNames = true;
+ wroteEscapedNamesProperty = true;
+ }
}
writeTab();
@@ -80,7 +90,7 @@ public void visitMetadata(String key, String value) throws IOException {
public boolean visitContent() throws IOException {
if (escapeNames && !wroteEscapedNamesProperty) {
write("\t");
- write(Tiny2Util.escapedNamesProperty);
+ write(StandardProperties.ESCAPED_NAMES.getNameFor(format));
writeLn();
}
@@ -201,6 +211,7 @@ private void writeTabs(int count) throws IOException {
}
private static final Set flags = EnumSet.of(MappingFlag.NEEDS_HEADER_METADATA, MappingFlag.NEEDS_UNIQUENESS, MappingFlag.NEEDS_SRC_FIELD_DESC, MappingFlag.NEEDS_SRC_METHOD_DESC);
+ private static final MappingFormat format = MappingFormat.TINY_2_FILE;
private final Writer writer;
private boolean escapeNames;
diff --git a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2Util.java b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2Util.java
index 68bee840..341d297d 100644
--- a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2Util.java
+++ b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2Util.java
@@ -84,6 +84,4 @@ public static String unescape(String str) {
private static final String toEscape = "\\\n\r\0\t";
private static final String escaped = "\\nr0t";
-
- static final String escapedNamesProperty = "escaped-names";
}
diff --git a/src/main/java/net/fabricmc/mappingio/format/tsrg/TsrgFileReader.java b/src/main/java/net/fabricmc/mappingio/format/tsrg/TsrgFileReader.java
index 8ef79883..9eb1def0 100644
--- a/src/main/java/net/fabricmc/mappingio/format/tsrg/TsrgFileReader.java
+++ b/src/main/java/net/fabricmc/mappingio/format/tsrg/TsrgFileReader.java
@@ -28,6 +28,8 @@
import net.fabricmc.mappingio.MappingUtil;
import net.fabricmc.mappingio.MappingVisitor;
import net.fabricmc.mappingio.format.ColumnFileReader;
+import net.fabricmc.mappingio.format.StandardProperties;
+import net.fabricmc.mappingio.tree.MappingTree;
public final class TsrgFileReader {
private TsrgFileReader() {
@@ -185,8 +187,8 @@ private static void readMethod(ColumnFileReader reader, int dstNsCount, MappingV
while (reader.nextLine(2)) {
if (reader.hasExtraIndents()) continue;
- if (reader.nextCol("static")) {
- // method is static
+ if (reader.nextCol("static")) { // method is static
+ visitor.visitElementMetadata(MappedElementKind.METHOD, StandardProperties.IS_STATIC.getId(), MappingTree.SRC_NAMESPACE_ID, "true");
} else {
int lvIndex = reader.nextIntCol();
if (lvIndex < 0) throw new IOException("missing/invalid parameter lv-index in line "+reader.getLineNumber());
diff --git a/src/main/java/net/fabricmc/mappingio/tree/MappingTree.java b/src/main/java/net/fabricmc/mappingio/tree/MappingTree.java
index b99d749b..26b98001 100644
--- a/src/main/java/net/fabricmc/mappingio/tree/MappingTree.java
+++ b/src/main/java/net/fabricmc/mappingio/tree/MappingTree.java
@@ -23,8 +23,28 @@ public interface MappingTree extends MappingTreeView {
String setSrcNamespace(String namespace);
List setDstNamespaces(List namespaces);
- void addMetadata(String key, String value);
- String removeMetadata(String key);
+ /**
+ * @return A modifiable list of all metadata entries currently present in the tree.
+ * The list's order is equal to the order in which the entries have been originally added.
+ */
+ @Override
+ List extends MetadataEntry> getMetadata();
+
+ /**
+ * @return An unmodifiable list of all metadata entries currently present
+ * in the tree whose key is equal to the passed one.
+ * The list's order is equal to the order in which the entries have been originally added.
+ */
+ @Override
+ List extends MetadataEntry> getMetadata(String key);
+
+ void addMetadata(MetadataEntry entry);
+
+ /**
+ * Removes all metadata entries whose key is equal to the passed one.
+ * @return Whether or not any entries have been removed.
+ */
+ boolean removeMetadata(String key);
@Override
Collection extends ClassMapping> getClasses();
@@ -59,11 +79,49 @@ default MethodMapping getMethod(String ownerName, String name, String desc, int
return (MethodMapping) MappingTreeView.super.getMethod(ownerName, name, desc, namespace);
}
+ interface MetadataEntry extends MetadataEntryView {
+ }
+
+ interface ElementMetadataEntry extends ElementMetadataEntryView {
+ }
+
interface ElementMapping extends ElementMappingView {
@Override
MappingTree getTree();
void setDstName(String name, int namespace);
+
+ /**
+ * @return A modifiable list of all metadata entries currently associated with the element.
+ * The list's order is equal to the order in which the entries have been originally added.
+ */
+ @Override
+ List extends ElementMetadataEntry> getMetadata();
+
+ /**
+ * @return An unmodifiable list of all metadata entries currently associated with the element
+ * whose key is equal to the passed one.
+ * The list's order is equal to the order in which the entries have been originally added.
+ */
+ @Override
+ List extends ElementMetadataEntry> getMetadata(String key);
+
+ /**
+ * @return An unmodifiable list of all metadata entries currently associated with the element
+ * whose key and namespace are equal to the passed ones.
+ * The list's order is equal to the order in which the entries have been originally added.
+ */
+ @Override
+ List extends ElementMetadataEntry> getMetadata(String key, int namespace);
+
+ void addMetadata(ElementMetadataEntry entry);
+
+ /**
+ * Removes all metadata entries whose key is equal to the passed one.
+ * @return Whether or not any entries have been removed.
+ */
+ boolean removeMetadata(String key);
+
void setComment(String comment);
}
diff --git a/src/main/java/net/fabricmc/mappingio/tree/MappingTreeView.java b/src/main/java/net/fabricmc/mappingio/tree/MappingTreeView.java
index 3de70989..fc146a69 100644
--- a/src/main/java/net/fabricmc/mappingio/tree/MappingTreeView.java
+++ b/src/main/java/net/fabricmc/mappingio/tree/MappingTreeView.java
@@ -19,7 +19,6 @@
import java.io.IOException;
import java.util.Collection;
import java.util.List;
-import java.util.Map.Entry;
import net.fabricmc.mappingio.MappingVisitor;
@@ -57,8 +56,8 @@ default String getNamespaceName(int id) {
return getDstNamespaces().get(id);
}
- Collection> getMetadata();
- String getMetadata(String key);
+ List extends MetadataEntryView> getMetadata();
+ List extends MetadataEntryView> getMetadata(String key);
Collection extends ClassMappingView> getClasses();
ClassMappingView getClass(String srcName);
@@ -182,6 +181,21 @@ default String mapDesc(CharSequence desc, int start, int end, int srcNamespace,
return ret.toString();
}
+ interface MetadataEntryView {
+ String getKey();
+ String getValue();
+ }
+
+ interface ElementMetadataEntryView {
+ String getKey();
+
+ /**
+ * @return Values by namespace, offset by +1 (value for namespace x is at index x+1).
+ */
+ String[] getValues();
+ String getValue(int namespace);
+ }
+
interface ElementMappingView {
MappingTreeView getTree();
@@ -206,6 +220,9 @@ default String getName(String namespace) {
}
}
+ List extends ElementMetadataEntryView> getMetadata();
+ List extends ElementMetadataEntryView> getMetadata(String key);
+ List extends ElementMetadataEntryView> getMetadata(String key, int namespace);
String getComment();
}
diff --git a/src/main/java/net/fabricmc/mappingio/tree/MemoryMappingTree.java b/src/main/java/net/fabricmc/mappingio/tree/MemoryMappingTree.java
index 9956cce0..d160bfa0 100644
--- a/src/main/java/net/fabricmc/mappingio/tree/MemoryMappingTree.java
+++ b/src/main/java/net/fabricmc/mappingio/tree/MemoryMappingTree.java
@@ -17,19 +17,20 @@
package net.fabricmc.mappingio.tree;
import java.io.IOException;
-import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.IdentityHashMap;
-import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
import net.fabricmc.mappingio.MappedElementKind;
import net.fabricmc.mappingio.MappingFlag;
@@ -52,8 +53,8 @@ public MemoryMappingTree(MappingTree src) {
setSrcNamespace(src.getSrcNamespace());
setDstNamespaces(src.getDstNamespaces());
- for (Map.Entry entry : src.getMetadata()) {
- addMetadata(entry.getKey(), entry.getValue());
+ for (MetadataEntry entry : src.getMetadata()) {
+ addMetadata(entry);
}
for (ClassMapping cls : src.getClasses()) {
@@ -205,37 +206,25 @@ private void updateDstNames(int[] nameMap) {
}
@Override
- public Collection> getMetadata() {
+ public List extends MetadataEntry> getMetadata() {
return metadata;
}
@Override
- public String getMetadata(String key) {
- for (Map.Entry entry : metadata) {
- if (entry.getKey().equals(key)) return entry.getValue();
- }
-
- return null;
+ public List extends MetadataEntry> getMetadata(String key) {
+ return Collections.unmodifiableList(metadata.stream()
+ .filter(entry -> entry.getKey().equals(key))
+ .collect(Collectors.toList()));
}
@Override
- public void addMetadata(String key, String value) {
- metadata.add(new AbstractMap.SimpleEntry<>(key, value));
+ public void addMetadata(MetadataEntry entry) {
+ metadata.add(entry);
}
@Override
- public String removeMetadata(String key) {
- for (Iterator> it = metadata.iterator(); it.hasNext(); ) {
- Map.Entry entry = it.next();
-
- if (entry.getKey().equals(key)) {
- it.remove();
-
- return entry.getValue();
- }
- }
-
- return null;
+ public boolean removeMetadata(String key) {
+ return metadata.removeIf(entry -> entry.getKey().equals(key));
}
@Override
@@ -301,10 +290,28 @@ public ClassMapping removeClass(String srcName) {
@Override
public void accept(MappingVisitor visitor, VisitOrder order) throws IOException {
do {
+ boolean needsUniqueness = visitor.getFlags().contains(MappingFlag.NEEDS_UNIQUENESS);
+
if (visitor.visitHeader()) {
visitor.visitNamespaces(srcNamespace, dstNamespaces);
+ List metadataToVisit = metadata;
- for (Map.Entry entry : metadata) {
+ if (needsUniqueness) {
+ metadataToVisit = new LinkedList<>();
+ Set addedKeys = new HashSet<>();
+
+ // Iterate last-to-first to construct a list of each key's latest occurrence.
+ for (int i = metadata.size() - 1; i >= 0; i--) {
+ MetadataEntry entry = metadata.get(i);
+
+ if (!addedKeys.contains(entry.getKey())) {
+ addedKeys.add(entry.getKey());
+ metadataToVisit.add(0, entry);
+ }
+ }
+ }
+
+ for (MetadataEntry entry : metadataToVisit) {
visitor.visitMetadata(entry.getKey(), entry.getValue());
}
}
@@ -391,7 +398,8 @@ public void visitNamespaces(String srcNamespace, List dstNamespaces) {
@Override
public void visitMetadata(String key, String value) {
- this.metadata.add(new AbstractMap.SimpleEntry<>(key, value));
+ MetadataEntryImpl entry = new MetadataEntryImpl(key, value);
+ addMetadata(entry);
}
@Override
@@ -670,8 +678,17 @@ public boolean visitElementContent(MappedElementKind targetKind) throws IOExcept
return targetKind != MappedElementKind.CLASS || currentClass.getSrcName() != null; // reject classes that never received a src name
}
+ @Override
+ public void visitElementMetadata(MappedElementKind targetKind, String propertyKey, int namespace, String propertyValue) {
+ getCurrentEntry(targetKind).visitMetadata(propertyKey, namespace, propertyValue);
+ }
+
@Override
public void visitComment(MappedElementKind targetKind, String comment) {
+ getCurrentEntry(targetKind).setComment(comment);
+ }
+
+ private Entry> getCurrentEntry(MappedElementKind targetKind) {
Entry> entry;
switch (targetKind) {
@@ -685,8 +702,8 @@ public void visitComment(MappedElementKind targetKind, String comment) {
entry = currentEntry;
}
- if (entry == null) throw new UnsupportedOperationException("Tried to visit comment before owning target");
- entry.setComment(comment);
+ if (entry == null) throw new UnsupportedOperationException("Tried to visit element content before owning target");
+ return entry;
}
abstract static class Entry> implements ElementMapping {
@@ -728,6 +745,18 @@ public void setDstName(String name, int namespace) {
void resizeDstNames(int newSize) {
dstNames = Arrays.copyOf(dstNames, newSize);
+
+ for (ElementMetadataEntry entry : metadata) {
+ String[] resizedValues = Arrays.copyOf(entry.getValues(), newSize);
+
+ if (entry instanceof ElementMetadataEntryImpl) {
+ ElementMetadataEntryImpl impl = (ElementMetadataEntryImpl) entry;
+ impl.values = resizedValues;
+ } else {
+ metadata.add(metadata.indexOf(entry), new ElementMetadataEntryImpl(entry.getKey(), resizedValues));
+ metadata.remove(entry);
+ }
+ }
}
void updateDstNames(int[] map) {
@@ -744,6 +773,72 @@ void updateDstNames(int[] map) {
dstNames = newDstNames;
}
+ @Override
+ public List extends ElementMetadataEntry> getMetadata() {
+ return metadata;
+ }
+
+ @Override
+ public List extends ElementMetadataEntry> getMetadata(String key) {
+ return Collections.unmodifiableList(metadata.stream()
+ .filter(entry -> entry.getKey().equals(key))
+ .collect(Collectors.toList()));
+ }
+
+ @Override
+ public List extends ElementMetadataEntry> getMetadata(String key, int namespace) {
+ return Collections.unmodifiableList(metadata.stream()
+ .filter(entry -> entry.getKey().equals(key))
+ .filter(entry -> entry.getValue(namespace) != null)
+ .collect(Collectors.toList()));
+ }
+
+ @Override
+ public void addMetadata(ElementMetadataEntry entry) {
+ ElementMetadataEntry lastEntry;
+
+ if (metadata.isEmpty() || !(lastEntry = metadata.get(metadata.size() - 1)).getKey().equals(entry.getKey())) {
+ metadata.add(entry);
+ return;
+ }
+
+ for (int i = -1; i < entry.getValues().length - 1; i++) {
+ lastEntry = mergeMetadata(entry.getKey(), i, entry.getValue(i), lastEntry);
+ }
+ }
+
+ void visitMetadata(String key, int namespace, String value) {
+ ElementMetadataEntry lastEntry;
+
+ if (metadata.isEmpty() || !(lastEntry = metadata.get(metadata.size() - 1)).getKey().equals(key)) {
+ String[] values = new String[dstNames.length + 1];
+ values[namespace + 1] = value;
+ metadata.add(new ElementMetadataEntryImpl(key, values));
+ return;
+ }
+
+ mergeMetadata(key, namespace, value, lastEntry);
+ }
+
+ private ElementMetadataEntry mergeMetadata(String key, int namespace, String value, ElementMetadataEntry lastEntry) {
+ String[] values = lastEntry.getValues();
+
+ if (lastEntry.getValue(namespace) != null) {
+ // Can't merge, create new entry
+ values = new String[dstNames.length + 1];
+ lastEntry = new ElementMetadataEntryImpl(key, values);
+ metadata.add(lastEntry);
+ }
+
+ values[namespace + 1] = value;
+ return lastEntry;
+ }
+
+ @Override
+ public boolean removeMetadata(String key) {
+ return metadata.removeIf(entry -> entry.getKey().equals(key));
+ }
+
@Override
public final String getComment() {
return comment;
@@ -775,6 +870,44 @@ protected final boolean acceptElement(MappingVisitor visitor, String[] dstDescs)
return false;
}
+ List metadataToVisit = metadata;
+
+ if (visitor.getFlags().contains(MappingFlag.NEEDS_UNIQUENESS)) {
+ metadataToVisit = new LinkedList<>();
+ Set addedKeys = new HashSet<>();
+
+ // Iterate last-to-first to construct a list of each key's latest occurrence.
+ for (int i = metadata.size() - 1; i >= 0; i--) {
+ ElementMetadataEntry entry = metadata.get(i);
+
+ if (!addedKeys.contains(entry.getKey())) {
+ addedKeys.add(entry.getKey());
+ metadataToVisit.add(0, entry);
+ }
+ }
+ }
+
+ String lastKey = null;
+ String[] lastValues = null;
+
+ for (ElementMetadataEntry entry : metadataToVisit) {
+ String[] values = entry.getValues();
+
+ for (int ns = -1; ns < values.length - 1; ns++) {
+ if (entry.getKey().equals(lastKey)) {
+ if (values[ns+1] == null && lastValues != null) {
+ // Fill in holes
+ values[ns+1] = lastValues[ns+1];
+ }
+ } else {
+ lastKey = entry.getKey();
+ }
+
+ lastValues = values;
+ visitor.visitElementMetadata(kind, entry.getKey(), ns, entry.getValue(ns));
+ }
+ }
+
if (comment != null) visitor.visitComment(kind, comment);
return true;
@@ -794,6 +927,7 @@ protected void copyFrom(T o, boolean replace) {
// TODO: copy args+vars
}
+ private final List metadata = new ArrayList<>();
protected String srcName;
protected String[] dstNames;
protected String comment;
@@ -1699,6 +1833,85 @@ public String toString() {
private final int hash;
}
+ static final class MetadataEntryImpl implements MetadataEntry {
+ MetadataEntryImpl(String key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) return true;
+
+ if (!(other instanceof MetadataEntryImpl)) {
+ return false;
+ }
+
+ MetadataEntryImpl entry = (MetadataEntryImpl) other;
+
+ return this.key.equals(entry.key) && this.value.equals(entry.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return key.hashCode() | value.hashCode();
+ }
+
+ final String key;
+ final String value;
+ }
+
+ static final class ElementMetadataEntryImpl implements ElementMetadataEntry {
+ ElementMetadataEntryImpl(String key, String[] values) {
+ this.key = key;
+ this.values = values;
+ }
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public String[] getValues() {
+ return values;
+ }
+
+ public String getValue(int namespace) {
+ return values[namespace + 1];
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) return true;
+
+ if (!(other instanceof ElementMetadataEntryImpl)) {
+ return false;
+ }
+
+ ElementMetadataEntryImpl entry = (ElementMetadataEntryImpl) other;
+ return this.key.equals(entry.key) && this.values.equals(entry.values);
+ }
+
+ @Override
+ public int hashCode() {
+ return key.hashCode() | values.hashCode();
+ }
+
+ final String key;
+ String[] values;
+ }
+
static final class GlobalMemberKey {
GlobalMemberKey(ClassEntry owner, String name, String desc, boolean isField) {
this.owner = owner;
@@ -1739,7 +1952,7 @@ public String toString() {
private boolean indexByDstNames;
private String srcNamespace;
private List dstNamespaces = Collections.emptyList();
- private final List> metadata = new ArrayList<>();
+ private final List metadata = new ArrayList<>();
private final Map classesBySrcName = new LinkedHashMap<>();
private Map[] classesByDstNames;
diff --git a/src/test/java/net/fabricmc/mappingio/MetadataTest.java b/src/test/java/net/fabricmc/mappingio/MetadataTest.java
new file mode 100644
index 00000000..aa9e7008
--- /dev/null
+++ b/src/test/java/net/fabricmc/mappingio/MetadataTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2023 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.mappingio;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import net.fabricmc.mappingio.tree.VisitableMappingTree;
+
+public class MetadataTest {
+ private static final Random random = new Random();
+ private static final List keys = new ArrayList<>();
+ private static final List values = new ArrayList<>();
+ private static VisitableMappingTree tree;
+
+ @BeforeAll
+ public static void setup() throws Exception {
+ tree = TestHelper.createTestTree();
+
+ for (int i = 0; i < 40; i++) {
+ String key = "key" + random.nextInt(3);
+ String value = "position" + i;
+
+ keys.add(key);
+ values.add(value);
+ tree.visitMetadata(key, value);
+ }
+ }
+
+ @Test
+ public void testOrder() throws Exception {
+ tree.accept(new NopMappingVisitor(true) {
+ @Override
+ public void visitMetadata(String key, String value) {
+ assertEquals(key, keys.get(visitCount));
+ assertEquals(value, values.get(visitCount));
+ visitCount++;
+ }
+
+ int visitCount;
+ });
+ }
+
+ @Test
+ public void testUniqueness() throws Exception {
+ tree.accept(new NopMappingVisitor(true) {
+ @Override
+ public Set getFlags() {
+ return EnumSet.of(MappingFlag.NEEDS_UNIQUENESS);
+ }
+
+ @Override
+ public void visitMetadata(String key, String value) {
+ assertFalse(visitedKeys.contains(key));
+ visitedKeys.add(key);
+ }
+
+ Set visitedKeys = new HashSet<>();
+ });
+ }
+}
diff --git a/src/test/java/net/fabricmc/mappingio/NopMappingVisitor.java b/src/test/java/net/fabricmc/mappingio/NopMappingVisitor.java
new file mode 100644
index 00000000..81d37607
--- /dev/null
+++ b/src/test/java/net/fabricmc/mappingio/NopMappingVisitor.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2023 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.mappingio;
+
+import java.io.IOException;
+import java.util.List;
+
+public class NopMappingVisitor implements MappingVisitor {
+ NopMappingVisitor(boolean visitSubVisitors) {
+ this.visitSubVisitors = visitSubVisitors;
+ }
+
+ @Override
+ public boolean visitHeader() throws IOException {
+ return visitSubVisitors;
+ }
+
+ @Override
+ public void visitNamespaces(String srcNamespace, List dstNamespaces) throws IOException {
+ }
+
+ @Override
+ public boolean visitContent() throws IOException {
+ return visitSubVisitors;
+ }
+
+ @Override
+ public boolean visitClass(String srcName) throws IOException {
+ return visitSubVisitors;
+ }
+
+ @Override
+ public boolean visitField(String srcName, String srcDesc) throws IOException {
+ return visitSubVisitors;
+ }
+
+ @Override
+ public boolean visitMethod(String srcName, String srcDesc) throws IOException {
+ return visitSubVisitors;
+ }
+
+ @Override
+ public boolean visitMethodArg(int argPosition, int lvIndex, String srcName) throws IOException {
+ return visitSubVisitors;
+ }
+
+ @Override
+ public boolean visitMethodVar(int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, String srcName) throws IOException {
+ return visitSubVisitors;
+ }
+
+ @Override
+ public void visitDstName(MappedElementKind targetKind, int namespace, String name) throws IOException {
+ }
+
+ @Override
+ public boolean visitElementContent(MappedElementKind targetKind) throws IOException {
+ return visitSubVisitors;
+ }
+
+ @Override
+ public void visitComment(MappedElementKind targetKind, String comment) throws IOException {
+ }
+
+ protected final boolean visitSubVisitors;
+}
diff --git a/src/test/java/net/fabricmc/mappingio/TestHelper.java b/src/test/java/net/fabricmc/mappingio/TestHelper.java
index 0cf4ea4d..f7930496 100644
--- a/src/test/java/net/fabricmc/mappingio/TestHelper.java
+++ b/src/test/java/net/fabricmc/mappingio/TestHelper.java
@@ -20,9 +20,12 @@
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Arrays;
import net.fabricmc.mappingio.format.MappingFormat;
+import net.fabricmc.mappingio.format.StandardProperties;
import net.fabricmc.mappingio.tree.MappingTree;
+import net.fabricmc.mappingio.tree.MemoryMappingTree;
public final class TestHelper {
public static Path getResource(String slashPrefixedResourcePath) {
@@ -33,6 +36,42 @@ public static Path getResource(String slashPrefixedResourcePath) {
}
}
+ public static MemoryMappingTree createTestTree() {
+ MemoryMappingTree tree = new MemoryMappingTree();
+ tree.visitNamespaces(MappingUtil.NS_SOURCE_FALLBACK, Arrays.asList(MappingUtil.NS_TARGET_FALLBACK));
+
+ tree.visitClass("class_1");
+ tree.visitDstName(MappedElementKind.CLASS, 0, "RenamedClass1");
+ tree.visitElementMetadata(MappedElementKind.CLASS, StandardProperties.MODIFIED_ACCESS.getId(), 0, "public");
+
+ tree.visitField("field_1", "I");
+ tree.visitDstName(MappedElementKind.FIELD, 0, "renamedField");
+ tree.visitElementMetadata(MappedElementKind.FIELD, StandardProperties.MODIFIED_ACCESS.getId(), 0, "protected");
+
+ tree.visitMethod("method_1", "(F)I");
+ tree.visitDstName(MappedElementKind.METHOD, 0, "renamedMethod");
+ tree.visitElementMetadata(MappedElementKind.METHOD, StandardProperties.MODIFIED_ACCESS.getId(), 0, "private");
+ tree.visitElementMetadata(MappedElementKind.METHOD, StandardProperties.START_LINE_NUMBER.getId(), 0, "20");
+ tree.visitElementMetadata(MappedElementKind.METHOD, StandardProperties.END_LINE_NUMBER.getId(), 0, "25");
+
+ tree.visitMethodArg(0, 0, "param_1");
+ tree.visitDstName(MappedElementKind.METHOD_ARG, 0, "renamedParameter");
+
+ tree.visitMethodVar(0, 0, 0, 0, "var_1");
+ tree.visitDstName(MappedElementKind.METHOD_VAR, 0, "renamedVariable");
+
+ tree.visitClass("class_1$class_2");
+ tree.visitDstName(MappedElementKind.CLASS, 0, "RenamedClass1$RenamedInnerClass");
+
+ tree.visitField("field_1", "I");
+ tree.visitDstName(MappedElementKind.FIELD, 0, "renamedField2");
+
+ tree.visitClass("class_3");
+ tree.visitDstName(MappedElementKind.CLASS, 0, "RenamedClass2");
+
+ return tree;
+ }
+
public static void writeToDir(MappingTree tree, MappingFormat format, Path dir) throws IOException {
MappingWriter writer = MappingWriter.create(dir.resolve(format.name() + "." + format.fileExt), format);
tree.accept(writer);
diff --git a/src/test/java/net/fabricmc/mappingio/WriteTest.java b/src/test/java/net/fabricmc/mappingio/WriteTest.java
index 19bacaca..100e6890 100644
--- a/src/test/java/net/fabricmc/mappingio/WriteTest.java
+++ b/src/test/java/net/fabricmc/mappingio/WriteTest.java
@@ -17,14 +17,12 @@
package net.fabricmc.mappingio;
import java.nio.file.Path;
-import java.util.Arrays;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import net.fabricmc.mappingio.format.MappingFormat;
-import net.fabricmc.mappingio.tree.MemoryMappingTree;
import net.fabricmc.mappingio.tree.VisitableMappingTree;
public class WriteTest {
@@ -34,32 +32,7 @@ public class WriteTest {
@BeforeAll
public static void setup() throws Exception {
- tree = new MemoryMappingTree();
- tree.visitNamespaces(MappingUtil.NS_SOURCE_FALLBACK, Arrays.asList(MappingUtil.NS_TARGET_FALLBACK));
-
- tree.visitClass("class_1");
- tree.visitDstName(MappedElementKind.CLASS, 0, "RenamedClass1");
-
- tree.visitField("field_1", "I");
- tree.visitDstName(MappedElementKind.FIELD, 0, "renamedField");
-
- tree.visitMethod("method_1", "(F)I");
- tree.visitDstName(MappedElementKind.METHOD, 0, "renamedMethod");
-
- tree.visitMethodArg(0, 0, "param_1");
- tree.visitDstName(MappedElementKind.METHOD_ARG, 0, "renamedParameter");
-
- tree.visitMethodVar(0, 0, 0, 0, "param_1");
- tree.visitDstName(MappedElementKind.METHOD_VAR, 0, "renamedVariable");
-
- tree.visitClass("class_1$class_2");
- tree.visitDstName(MappedElementKind.CLASS, 0, "RenamedClass1$RenamedInnerClass2");
-
- tree.visitField("field_1", "I");
- tree.visitDstName(MappedElementKind.FIELD, 0, "renamedField2");
-
- tree.visitClass("class_3");
- tree.visitDstName(MappedElementKind.CLASS, 0, "RenamedClass3");
+ tree = TestHelper.createTestTree();
}
@Test
@@ -82,6 +55,11 @@ public void tinyV2File() throws Exception {
write(MappingFormat.TINY_2_FILE);
}
+ @Test
+ public void proguardFile() throws Exception {
+ write(MappingFormat.PROGUARD_FILE);
+ }
+
private void write(MappingFormat format) throws Exception {
TestHelper.writeToDir(tree, format, dir);
}