-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add IntelliJ IDEA Migration Map reader and writer (#93)
- Loading branch information
1 parent
368598c
commit a27d20a
Showing
18 changed files
with
477 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
180 changes: 180 additions & 0 deletions
180
src/main/java/net/fabricmc/mappingio/format/intellij/MigrationMapFileReader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
* | ||
* <p>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 { | ||
} | ||
} | ||
} |
143 changes: 143 additions & 0 deletions
143
src/main/java/net/fabricmc/mappingio/format/intellij/MigrationMapFileWriter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<MappingFlag> getFlags() { | ||
return flags; | ||
} | ||
|
||
@Override | ||
public void visitNamespaces(String srcNamespace, List<String> 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<MappingFlag> flags = EnumSet.of(MappingFlag.NEEDS_ELEMENT_UNIQUENESS); | ||
|
||
private final Writer writer; | ||
private XMLStreamWriter xmlWriter; | ||
private String srcName; | ||
private String dstName; | ||
} |
Oops, something went wrong.