From a27d20a10f1ff6f457ac3c6e9a1f3130b2a0bbd2 Mon Sep 17 00:00:00 2001 From: NebelNidas <48808497+NebelNidas@users.noreply.github.com> Date: Mon, 26 Aug 2024 01:34:23 +0200 Subject: [PATCH] Add IntelliJ IDEA Migration Map reader and writer (#93) --- CHANGELOG.md | 1 + .../net/fabricmc/mappingio/MappingReader.java | 8 +- .../net/fabricmc/mappingio/MappingWriter.java | 2 + .../mappingio/format/MappingFormat.java | 13 ++ .../intellij/MigrationMapFileReader.java | 180 ++++++++++++++++++ .../intellij/MigrationMapFileWriter.java | 143 ++++++++++++++ .../mappingio/format/srg/TsrgFileReader.java | 7 + .../format/tiny/Tiny1FileReader.java | 5 + .../format/tiny/Tiny2FileReader.java | 4 + .../net/fabricmc/mappingio/TestHelper.java | 2 + .../mappingio/read/DetectionTest.java | 6 + .../mappingio/read/EmptyContentReadTest.java | 61 +++++- .../mappingio/read/ValidContentReadTest.java | 7 + .../mappingio/visiting/VisitEndTest.java | 6 + .../fabricmc/mappingio/write/WriteTest.java | 5 + .../resources/detection/migration-map.xml | 3 + .../read/valid-with-holes/migration-map.xml | 23 +++ .../resources/read/valid/migration-map.xml | 7 + 18 files changed, 477 insertions(+), 6 deletions(-) create mode 100644 src/main/java/net/fabricmc/mappingio/format/intellij/MigrationMapFileReader.java create mode 100644 src/main/java/net/fabricmc/mappingio/format/intellij/MigrationMapFileWriter.java create mode 100644 src/test/resources/detection/migration-map.xml create mode 100644 src/test/resources/read/valid-with-holes/migration-map.xml create mode 100644 src/test/resources/read/valid/migration-map.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index b762a2f6..e27a4b0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +- Added IntelliJ IDEA migration map reader and writer - Added `MappingFormat#features()` to allow for more fine-grained programmatic querying of format capabilities - Overhauled the internal `ColumnFileReader` to behave more consistently - Made handling of the `NEEDS_MULTIPLE_PASSES` flag more consistent, reducing memory usage in a few cases diff --git a/src/main/java/net/fabricmc/mappingio/MappingReader.java b/src/main/java/net/fabricmc/mappingio/MappingReader.java index 4ed107ab..3201bf61 100644 --- a/src/main/java/net/fabricmc/mappingio/MappingReader.java +++ b/src/main/java/net/fabricmc/mappingio/MappingReader.java @@ -31,6 +31,7 @@ import net.fabricmc.mappingio.format.MappingFormat; import net.fabricmc.mappingio.format.enigma.EnigmaDirReader; import net.fabricmc.mappingio.format.enigma.EnigmaFileReader; +import net.fabricmc.mappingio.format.intellij.MigrationMapFileReader; import net.fabricmc.mappingio.format.jobf.JobfFileReader; import net.fabricmc.mappingio.format.proguard.ProGuardFileReader; import net.fabricmc.mappingio.format.simple.RecafSimpleFileReader; @@ -105,7 +106,9 @@ private static MappingFormat detectFormat(Reader reader, @Nullable String fileEx String headerStr = String.valueOf(buffer, 0, pos); - if ((headerStr.startsWith("p ") + if (headerStr.contains("")) { + return MappingFormat.INTELLIJ_MIGRATION_MAP_FILE; + } else if ((headerStr.startsWith("p ") || headerStr.startsWith("c ") || headerStr.startsWith("f ") || headerStr.startsWith("m ")) @@ -296,6 +299,9 @@ public static void read(Reader reader, MappingFormat format, MappingVisitor visi case PROGUARD_FILE: ProGuardFileReader.read(reader, visitor); break; + case INTELLIJ_MIGRATION_MAP_FILE: + MigrationMapFileReader.read(reader, visitor); + break; case RECAF_SIMPLE_FILE: RecafSimpleFileReader.read(reader, visitor); break; diff --git a/src/main/java/net/fabricmc/mappingio/MappingWriter.java b/src/main/java/net/fabricmc/mappingio/MappingWriter.java index f7a11cf9..2063c1ad 100644 --- a/src/main/java/net/fabricmc/mappingio/MappingWriter.java +++ b/src/main/java/net/fabricmc/mappingio/MappingWriter.java @@ -27,6 +27,7 @@ import net.fabricmc.mappingio.format.MappingFormat; import net.fabricmc.mappingio.format.enigma.EnigmaDirWriter; import net.fabricmc.mappingio.format.enigma.EnigmaFileWriter; +import net.fabricmc.mappingio.format.intellij.MigrationMapFileWriter; import net.fabricmc.mappingio.format.jobf.JobfFileWriter; import net.fabricmc.mappingio.format.proguard.ProGuardFileWriter; import net.fabricmc.mappingio.format.simple.RecafSimpleFileWriter; @@ -65,6 +66,7 @@ static MappingWriter create(Writer writer, MappingFormat format) throws IOExcept case TSRG_FILE: return new TsrgFileWriter(writer, false); case TSRG_2_FILE: return new TsrgFileWriter(writer, true); case PROGUARD_FILE: return new ProGuardFileWriter(writer); + case INTELLIJ_MIGRATION_MAP_FILE: return new MigrationMapFileWriter(writer); case RECAF_SIMPLE_FILE: return new RecafSimpleFileWriter(writer); case JOBF_FILE: return new JobfFileWriter(writer); default: return null; diff --git a/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java b/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java index 8d1f972f..02e76411 100644 --- a/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java +++ b/src/main/java/net/fabricmc/mappingio/format/MappingFormat.java @@ -223,6 +223,19 @@ public enum MappingFormat { .withSrcDescs(FeaturePresence.REQUIRED)) .withFileComments(true)), + /** + * The IntelliJ IDEA migration map format, as implemented here. + */ + INTELLIJ_MIGRATION_MAP_FILE("IntelliJ migration map file", "xml", true, FeatureSetBuilder.create() + .withFileMetadata(MetadataSupport.FIXED) // migration map name and description + .withPackages(p -> p + .withSrcNames(FeaturePresence.REQUIRED) + .withDstNames(FeaturePresence.REQUIRED)) + .withClasses(c -> c + .withSrcNames(FeaturePresence.REQUIRED) + .withDstNames(FeaturePresence.REQUIRED)) + .withFileComments(true)), + /** * Recaf's {@code Simple} mapping format, as specified here. */ diff --git a/src/main/java/net/fabricmc/mappingio/format/intellij/MigrationMapFileReader.java b/src/main/java/net/fabricmc/mappingio/format/intellij/MigrationMapFileReader.java new file mode 100644 index 00000000..4968e945 --- /dev/null +++ b/src/main/java/net/fabricmc/mappingio/format/intellij/MigrationMapFileReader.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2024 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.intellij; + +import java.io.BufferedReader; +import java.io.CharArrayReader; +import java.io.IOException; +import java.io.Reader; +import java.util.Arrays; +import java.util.Collections; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +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.MappingFormat; + +/** + * {@linkplain MappingFormat#INTELLIJ_MIGRATION_MAP_FILE IntelliJ migration map} reader. + * + *

Crashes if a second visit pass is requested without + * {@link MappingFlag#NEEDS_MULTIPLE_PASSES} having been passed beforehand. + */ +public final class MigrationMapFileReader { + private MigrationMapFileReader() { + } + + public static void read(Reader reader, MappingVisitor visitor) throws IOException { + read(reader, MappingUtil.NS_SOURCE_FALLBACK, MappingUtil.NS_TARGET_FALLBACK, visitor); + } + + public static void read(Reader reader, String sourceNs, String targetNs, MappingVisitor visitor) throws IOException { + BufferedReader br = reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader); + + read(br, sourceNs, targetNs, visitor); + } + + private static void read(BufferedReader reader, String sourceNs, String targetNs, MappingVisitor visitor) throws IOException { + try { + read0(reader, sourceNs, targetNs, visitor); + } catch (XMLStreamException e) { + throw new IOException(e); + } + } + + private static void read0(BufferedReader reader, String sourceNs, String targetNs, MappingVisitor visitor) throws IOException, XMLStreamException { + CharArrayReader parentReader = null; + + if (visitor.getFlags().contains(MappingFlag.NEEDS_MULTIPLE_PASSES)) { + char[] buffer = new char[100_000]; + int pos = 0; + int len; + + while ((len = reader.read(buffer, pos, buffer.length - pos)) >= 0) { + pos += len; + + if (pos == buffer.length) buffer = Arrays.copyOf(buffer, buffer.length * 2); + } + + parentReader = new CharArrayReader(buffer, 0, pos); + reader = new CustomBufferedReader(parentReader); + } + + XMLInputFactory factory = XMLInputFactory.newInstance(); + + for (;;) { + XMLStreamReader xmlReader = factory.createXMLStreamReader(reader); + boolean visitHeader; + + if (visitHeader = visitor.visitHeader()) { + visitor.visitNamespaces(sourceNs, Collections.singletonList(targetNs)); + } + + if (visitor.visitContent()) { + int depth = 0; + + while (xmlReader.hasNext()) { + int event = xmlReader.next(); + + switch (event) { + case XMLStreamConstants.START_ELEMENT: + String name = xmlReader.getLocalName(); + + if (depth != (name.equals("migrationMap") ? 0 : 1)) { + throw new IOException("unexpected depth at line "+xmlReader.getLocation().getLineNumber()); + } + + depth++; + + switch (name) { + case "name": + case "description": + if (visitHeader) { + // TODO: visit as metadata once https://github.com/FabricMC/mapping-io/pull/29 is merged + } + + break; + case "entry": + String type = xmlReader.getAttributeValue(null, "type"); + + if (type == null || type.isEmpty()) throw new IOException("missing/empty type attribute at line "+xmlReader.getLocation().getLineNumber()); + if (type.equals("package")) continue; // TODO: support packages + if (!type.equals("class")) throw new IOException("unexpected entry type "+type+" at line "+xmlReader.getLocation().getLineNumber()); + + String srcName = xmlReader.getAttributeValue(null, "oldName"); + String dstName = xmlReader.getAttributeValue(null, "newName"); + // String recursive = xmlReader.getAttributeValue(null, "recursive"); // only used for packages + + if (srcName == null || srcName.isEmpty()) throw new IOException("missing/empty oldName attribute at line "+xmlReader.getLocation().getLineNumber()); + if (dstName == null || dstName.isEmpty()) throw new IOException("missing/empty newName attribute at line "+xmlReader.getLocation().getLineNumber()); + + srcName = srcName.replace('.', '/'); + dstName = dstName.replace('.', '/'); + + if (visitor.visitClass(srcName)) { + visitor.visitDstName(MappedElementKind.CLASS, 0, dstName); + visitor.visitElementContent(MappedElementKind.CLASS); + } + + break; + } + + break; + case XMLStreamConstants.END_ELEMENT: + depth--; + break; + } + } + } + + if (visitor.visitEnd()) { + if (parentReader != null) { + ((CustomBufferedReader) reader).forceClose(); + } + + break; + } + + if (parentReader == null) { + throw new IllegalStateException("repeated visitation requested without NEEDS_MULTIPLE_PASSES"); + } else { + parentReader.reset(); + reader = new CustomBufferedReader(parentReader); + } + } + } + + private static class CustomBufferedReader extends BufferedReader { + private CustomBufferedReader(Reader in) { + super(in); + } + + public void forceClose() throws IOException { + super.close(); + } + + @Override + public void close() throws IOException { + } + } +} diff --git a/src/main/java/net/fabricmc/mappingio/format/intellij/MigrationMapFileWriter.java b/src/main/java/net/fabricmc/mappingio/format/intellij/MigrationMapFileWriter.java new file mode 100644 index 00000000..98d91c9d --- /dev/null +++ b/src/main/java/net/fabricmc/mappingio/format/intellij/MigrationMapFileWriter.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2024 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.intellij; + +import java.io.IOException; +import java.io.Writer; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; + +import javax.xml.stream.FactoryConfigurationError; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.mappingio.MappedElementKind; +import net.fabricmc.mappingio.MappingFlag; +import net.fabricmc.mappingio.MappingWriter; +import net.fabricmc.mappingio.format.MappingFormat; + +/** + * {@linkplain MappingFormat#INTELLIJ_MIGRATION_MAP_FILE IntelliJ migration map} writer. + */ +public final class MigrationMapFileWriter implements MappingWriter { + public MigrationMapFileWriter(Writer writer) { + this.writer = writer; + } + + @Override + public void close() throws IOException { + try { + if (xmlWriter != null) { + xmlWriter.writeEndDocument(); + xmlWriter.close(); + } + } catch (XMLStreamException e) { + throw new IOException(e); + } finally { + writer.close(); + } + } + + @Override + public Set getFlags() { + return flags; + } + + @Override + public void visitNamespaces(String srcNamespace, List dstNamespaces) throws IOException { + } + + @Override + public void visitMetadata(String key, @Nullable String value) throws IOException { + // TODO: Support once https://github.com/FabricMC/mapping-io/pull/29 is merged + } + + @Override + public boolean visitClass(String srcName) throws IOException { + this.srcName = srcName; + this.dstName = null; + + return true; + } + + @Override + public boolean visitField(String srcName, @Nullable String srcDesc) throws IOException { + return false; + } + + @Override + public boolean visitMethod(String srcName, @Nullable String srcDesc) throws IOException { + return false; + } + + @Override + public boolean visitMethodArg(int argPosition, int lvIndex, @Nullable String srcName) throws IOException { + return false; + } + + @Override + public boolean visitMethodVar(int lvtRowIndex, int lvIndex, int startOpIdx, int endOpIdx, @Nullable String srcName) throws IOException { + return false; + } + + @Override + public void visitDstName(MappedElementKind targetKind, int namespace, String name) { + if (namespace != 0) return; + + dstName = name; + } + + @Override + public boolean visitElementContent(MappedElementKind targetKind) throws IOException { + if (dstName == null) return false; + + try { + if (xmlWriter == null) { + xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(writer); + + xmlWriter.writeStartDocument("UTF-8", "1.0"); + xmlWriter.writeStartElement("migrationMap"); + } + + xmlWriter.writeStartElement("entry"); + xmlWriter.writeAttribute("oldName", srcName.replace('/', '.')); + xmlWriter.writeAttribute("newName", dstName.replace('/', '.')); + xmlWriter.writeAttribute("type", "class"); + xmlWriter.writeEndElement(); + + return false; + } catch (XMLStreamException | FactoryConfigurationError e) { + throw new IOException(e); + } + } + + @Override + public void visitComment(MappedElementKind targetKind, String comment) throws IOException { + // not supported, skip + } + + private static final Set flags = EnumSet.of(MappingFlag.NEEDS_ELEMENT_UNIQUENESS); + + private final Writer writer; + private XMLStreamWriter xmlWriter; + private String srcName; + private String dstName; +} diff --git a/src/main/java/net/fabricmc/mappingio/format/srg/TsrgFileReader.java b/src/main/java/net/fabricmc/mappingio/format/srg/TsrgFileReader.java index 242ed78e..e5f75eb1 100644 --- a/src/main/java/net/fabricmc/mappingio/format/srg/TsrgFileReader.java +++ b/src/main/java/net/fabricmc/mappingio/format/srg/TsrgFileReader.java @@ -77,16 +77,23 @@ private static void read(ColumnFileReader reader, String sourceNs, String target if (format == MappingFormat.TSRG_2_FILE) { srcNamespace = reader.nextCol(); + if (srcNamespace == null || srcNamespace.isEmpty()) throw new IOException("no source namespace in TSRG v2 header"); + dstNamespaces = new ArrayList<>(); String dstNamespace; while ((dstNamespace = reader.nextCol()) != null) { + if (dstNamespace.isEmpty()) throw new IOException("empty destination namespace in TSRG v2 header"); dstNamespaces.add(dstNamespace); } + if (dstNamespaces.isEmpty()) throw new IOException("no destination namespaces in TSRG v2 header"); reader.nextLine(0); } else { + if (sourceNs == null || sourceNs.isEmpty()) throw new IllegalArgumentException("provided source namespace must not be null or empty"); srcNamespace = sourceNs; + + if (targetNs == null || targetNs.isEmpty()) throw new IllegalArgumentException("provided target namespace must not be null or empty"); dstNamespaces = Collections.singletonList(targetNs); } 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 a199e1b1..e2cf183f 100644 --- a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileReader.java +++ b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny1FileReader.java @@ -69,14 +69,19 @@ private static void read(ColumnFileReader reader, MappingVisitor visitor) throws } String srcNamespace = reader.nextCol(); + if (srcNamespace == null || srcNamespace.isEmpty()) throw new IOException("no source namespace in Tiny v1 header"); + List dstNamespaces = new ArrayList<>(); String dstNamespace; while ((dstNamespace = reader.nextCol()) != null) { + if (dstNamespace.isEmpty()) throw new IOException("empty destination namespace in Tiny v1 header"); dstNamespaces.add(dstNamespace); } int dstNsCount = dstNamespaces.size(); + if (dstNsCount == 0) throw new IOException("no destination namespaces in Tiny v1 header"); + Set flags = visitor.getFlags(); MappingVisitor parentVisitor = null; boolean readerMarked = false; 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 f805d063..d6643a32 100644 --- a/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileReader.java +++ b/src/main/java/net/fabricmc/mappingio/format/tiny/Tiny2FileReader.java @@ -70,14 +70,18 @@ private static void read(ColumnFileReader reader, MappingVisitor visitor) throws } String srcNamespace = reader.nextCol(); + if (srcNamespace == null || srcNamespace.isEmpty()) throw new IOException("no source namespace in Tiny v2 header"); + List dstNamespaces = new ArrayList<>(); String dstNamespace; while ((dstNamespace = reader.nextCol()) != null) { + if (dstNamespace.isEmpty()) throw new IOException("empty destination namespace in Tiny v2 header"); dstNamespaces.add(dstNamespace); } int dstNsCount = dstNamespaces.size(); + if (dstNsCount == 0) throw new IOException("no destination namespaces in Tiny v2 header"); boolean readerMarked = false; if (visitor.getFlags().contains(MappingFlag.NEEDS_MULTIPLE_PASSES)) { diff --git a/src/test/java/net/fabricmc/mappingio/TestHelper.java b/src/test/java/net/fabricmc/mappingio/TestHelper.java index e6363e6a..4df90857 100644 --- a/src/test/java/net/fabricmc/mappingio/TestHelper.java +++ b/src/test/java/net/fabricmc/mappingio/TestHelper.java @@ -63,6 +63,8 @@ public static String getFileName(MappingFormat format) { return "tsrgV2.tsrg"; case PROGUARD_FILE: return "proguard.txt"; + case INTELLIJ_MIGRATION_MAP_FILE: + return "migration-map.xml"; case RECAF_SIMPLE_FILE: return "recaf-simple.txt"; case JOBF_FILE: diff --git a/src/test/java/net/fabricmc/mappingio/read/DetectionTest.java b/src/test/java/net/fabricmc/mappingio/read/DetectionTest.java index 94890650..477bfebe 100644 --- a/src/test/java/net/fabricmc/mappingio/read/DetectionTest.java +++ b/src/test/java/net/fabricmc/mappingio/read/DetectionTest.java @@ -102,6 +102,12 @@ public void proguardFile() throws Exception { check(format); } + @Test + public void migrationMapFile() throws Exception { + MappingFormat format = MappingFormat.INTELLIJ_MIGRATION_MAP_FILE; + check(format); + } + @Test public void recafSimpleFile() throws Exception { MappingFormat format = MappingFormat.RECAF_SIMPLE_FILE; diff --git a/src/test/java/net/fabricmc/mappingio/read/EmptyContentReadTest.java b/src/test/java/net/fabricmc/mappingio/read/EmptyContentReadTest.java index e480d298..a1a9f777 100644 --- a/src/test/java/net/fabricmc/mappingio/read/EmptyContentReadTest.java +++ b/src/test/java/net/fabricmc/mappingio/read/EmptyContentReadTest.java @@ -27,6 +27,7 @@ import net.fabricmc.mappingio.MappingVisitor; import net.fabricmc.mappingio.VisitOrderVerifyingVisitor; import net.fabricmc.mappingio.format.enigma.EnigmaFileReader; +import net.fabricmc.mappingio.format.intellij.MigrationMapFileReader; import net.fabricmc.mappingio.format.jobf.JobfFileReader; import net.fabricmc.mappingio.format.proguard.ProGuardFileReader; import net.fabricmc.mappingio.format.simple.RecafSimpleFileReader; @@ -52,14 +53,36 @@ public void emptyEnigmaFile() throws Exception { @Test public void emptyTinyFile() throws Exception { - assertThrows(IOException.class, () -> Tiny1FileReader.read(new StringReader(""), target)); - Tiny1FileReader.read(new StringReader("v1\t"), target); + String header0 = ""; + String header1 = "v1"; + String header2 = header1 + "\t"; + String header3 = header2 + "srcNs"; + String header4 = header3 + "\t"; + String header5 = header4 + "dstNs"; + + assertThrows(IOException.class, () -> Tiny1FileReader.read(new StringReader(header0), target)); + assertThrows(IOException.class, () -> Tiny1FileReader.read(new StringReader(header1), target)); + assertThrows(IOException.class, () -> Tiny1FileReader.read(new StringReader(header2), target)); + assertThrows(IOException.class, () -> Tiny1FileReader.read(new StringReader(header3), target)); + assertThrows(IOException.class, () -> Tiny1FileReader.read(new StringReader(header4), target)); + Tiny1FileReader.read(new StringReader(header5), target); } @Test public void emptyTinyV2File() throws Exception { - assertThrows(IOException.class, () -> Tiny2FileReader.read(new StringReader(""), target)); - Tiny2FileReader.read(new StringReader("tiny\t2\t0"), target); + String header0 = ""; + String header1 = "tiny\t2\t0"; + String header2 = header1 + "\t"; + String header3 = header2 + "srcNs"; + String header4 = header3 + "\t"; + String header5 = header4 + "dstNs"; + + assertThrows(IOException.class, () -> Tiny2FileReader.read(new StringReader(header0), target)); + assertThrows(IOException.class, () -> Tiny2FileReader.read(new StringReader(header1), target)); + assertThrows(IOException.class, () -> Tiny2FileReader.read(new StringReader(header2), target)); + assertThrows(IOException.class, () -> Tiny2FileReader.read(new StringReader(header3), target)); + assertThrows(IOException.class, () -> Tiny2FileReader.read(new StringReader(header4), target)); + Tiny2FileReader.read(new StringReader(header5), target); } @Test @@ -79,7 +102,35 @@ public void emptyJamFile() throws Exception { @Test public void emptyTsrgFile() throws Exception { - TsrgFileReader.read(new StringReader(""), target); + String header0 = ""; + String header1 = "tsrg2"; + String header2 = header1 + " "; + String header3 = header2 + "srcNs"; + String header4 = header3 + " "; + String header5 = header4 + "dstNs"; + + TsrgFileReader.read(new StringReader(header0), target); // interpreted as TSRG v1 + assertThrows(IOException.class, () -> TsrgFileReader.read(new StringReader(header1), target)); + assertThrows(IOException.class, () -> TsrgFileReader.read(new StringReader(header2), target)); + assertThrows(IOException.class, () -> TsrgFileReader.read(new StringReader(header3), target)); + assertThrows(IOException.class, () -> TsrgFileReader.read(new StringReader(header4), target)); + instantiateTree(); + TsrgFileReader.read(new StringReader(header5), target); + } + + @Test + public void emptyMigrationMapFile() throws Exception { + assertThrows(IOException.class, () -> MigrationMapFileReader.read(new StringReader(""), target)); + + instantiateTree(); + assertThrows(IOException.class, () -> MigrationMapFileReader.read(new StringReader(""), target)); + + instantiateTree(); + MigrationMapFileReader.read( + new StringReader("\n" + + "\n" + + ""), + target); } @Test diff --git a/src/test/java/net/fabricmc/mappingio/read/ValidContentReadTest.java b/src/test/java/net/fabricmc/mappingio/read/ValidContentReadTest.java index 7f0ca2e0..530dbaee 100644 --- a/src/test/java/net/fabricmc/mappingio/read/ValidContentReadTest.java +++ b/src/test/java/net/fabricmc/mappingio/read/ValidContentReadTest.java @@ -117,6 +117,13 @@ public void proguardFile() throws Exception { checkHoles(format); } + @Test + public void migrationMapFile() throws Exception { + MappingFormat format = MappingFormat.INTELLIJ_MIGRATION_MAP_FILE; + checkDefault(format); + checkHoles(format); + } + @Test public void recafSimpleFile() throws Exception { MappingFormat format = MappingFormat.RECAF_SIMPLE_FILE; diff --git a/src/test/java/net/fabricmc/mappingio/visiting/VisitEndTest.java b/src/test/java/net/fabricmc/mappingio/visiting/VisitEndTest.java index 1478e93e..d301f236 100644 --- a/src/test/java/net/fabricmc/mappingio/visiting/VisitEndTest.java +++ b/src/test/java/net/fabricmc/mappingio/visiting/VisitEndTest.java @@ -107,6 +107,12 @@ public void proguardFile() throws Exception { check(format); } + @Test + public void migrationMapFile() throws Exception { + MappingFormat format = MappingFormat.INTELLIJ_MIGRATION_MAP_FILE; + check(format); + } + @Test public void recafSimpleFile() throws Exception { MappingFormat format = MappingFormat.RECAF_SIMPLE_FILE; diff --git a/src/test/java/net/fabricmc/mappingio/write/WriteTest.java b/src/test/java/net/fabricmc/mappingio/write/WriteTest.java index 9f2621f3..3a3cb5ed 100644 --- a/src/test/java/net/fabricmc/mappingio/write/WriteTest.java +++ b/src/test/java/net/fabricmc/mappingio/write/WriteTest.java @@ -98,6 +98,11 @@ public void proguardFile() throws Exception { check(MappingFormat.PROGUARD_FILE); } + @Test + public void migrationMapFile() throws Exception { + check(MappingFormat.INTELLIJ_MIGRATION_MAP_FILE); + } + @Test public void recafSimpleFile() throws Exception { check(MappingFormat.RECAF_SIMPLE_FILE); diff --git a/src/test/resources/detection/migration-map.xml b/src/test/resources/detection/migration-map.xml new file mode 100644 index 00000000..82555dab --- /dev/null +++ b/src/test/resources/detection/migration-map.xml @@ -0,0 +1,3 @@ + + + diff --git a/src/test/resources/read/valid-with-holes/migration-map.xml b/src/test/resources/read/valid-with-holes/migration-map.xml new file mode 100644 index 00000000..29d91399 --- /dev/null +++ b/src/test/resources/read/valid-with-holes/migration-map.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/read/valid/migration-map.xml b/src/test/resources/read/valid/migration-map.xml new file mode 100644 index 00000000..161a8c9d --- /dev/null +++ b/src/test/resources/read/valid/migration-map.xml @@ -0,0 +1,7 @@ + + + + + + +