diff --git a/src/main/java/net/minecraftforge/srg2source/apply/RangeApplier.java b/src/main/java/net/minecraftforge/srg2source/apply/RangeApplier.java index 029a370..964f099 100644 --- a/src/main/java/net/minecraftforge/srg2source/apply/RangeApplier.java +++ b/src/main/java/net/minecraftforge/srg2source/apply/RangeApplier.java @@ -30,6 +30,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -46,6 +47,7 @@ import net.minecraftforge.srg2source.api.InputSupplier; import net.minecraftforge.srg2source.api.OutputSupplier; +import net.minecraftforge.srg2source.range.IRange; import net.minecraftforge.srg2source.range.RangeMap; import net.minecraftforge.srg2source.range.entries.ClassLiteral; import net.minecraftforge.srg2source.range.entries.ClassReference; @@ -56,6 +58,7 @@ import net.minecraftforge.srg2source.range.entries.MethodReference; import net.minecraftforge.srg2source.range.entries.ParameterReference; import net.minecraftforge.srg2source.range.entries.RangeEntry; +import net.minecraftforge.srg2source.range.entries.StructuralEntry; import net.minecraftforge.srg2source.util.Util; import net.minecraftforge.srg2source.util.io.ConfLogger; import net.minecraftforge.srgutils.IMappingFile; @@ -75,6 +78,7 @@ public class RangeApplier extends ConfLogger { private Map guessLambdas = null; private boolean guessLocals = false; private boolean sortImports = false; + private String newline = "\n"; public void readSrg(Path srg) { try (InputStream in = Files.newInputStream(srg)) { @@ -211,37 +215,67 @@ public void run() throws IOException { output.close(); } - private List processJavaSourceFile(String fileName, String data, RangeMap rangeList, ClassMeta meta) throws IOException { - StringBuilder outData = new StringBuilder(); - outData.append(data); + public interface StructuralEntryProcessor { - Set importsToAdd = new TreeSet<>(); - int shift = 0; + /** + * Takes given {@code entry} and processes it + * + * @param entry structural entry to process + */ + void process(StructuralEntry entry); + } + + public interface RangeEntryProcessor { + + /** + * Takes given {@code entry} with {@code parent} and processes it + * + * @param entry range entry to process + * @param parent entry structural parent + */ + void process(RangeEntry entry, StructuralEntry parent); // TODO: Store parent reference in entry, to not must pass parent as second argument + } + + private List processJavaSourceFile(String fileName, String data, RangeMap rangeMap, ClassMeta meta) throws IOException { + StringBuilder outData = new StringBuilder(data); + + // Detect and store file endline type + this.newline = Util.detectLineEnd(data); // Existing package/class name (with package, internal) derived from filename String oldTopLevelClassFullName = Util.getTopLevelClassForFilename(fileName); - int idx = oldTopLevelClassFullName.lastIndexOf('/'); - String oldTopLevelClassPackage = idx == -1 ? null : oldTopLevelClassFullName.substring(0, idx); - String oldTopLevelClassName = idx == -1 ? oldTopLevelClassFullName : oldTopLevelClassFullName.substring(idx + 1); + int index = oldTopLevelClassFullName.lastIndexOf('/'); + String oldTopLevelClassPackage = index == -1 ? null : oldTopLevelClassFullName.substring(0, index); + String oldTopLevelClassName = index == -1 ? oldTopLevelClassFullName : oldTopLevelClassFullName.substring(index + 1); // New package/class name through mapping String newTopLevelClassFullName = mapClass(oldTopLevelClassFullName); - idx = newTopLevelClassFullName.lastIndexOf('/'); - String newTopLevelClassPackage = idx == -1 ? null : newTopLevelClassFullName.substring(0, idx); - String newTopLevelClassName = idx == -1 ? newTopLevelClassFullName : newTopLevelClassFullName.substring(idx + 1); + index = newTopLevelClassFullName.lastIndexOf('/'); + String newTopLevelClassPackage = index == -1 ? null : newTopLevelClassFullName.substring(0, index); + String newTopLevelClassName = index == -1 ? newTopLevelClassFullName : newTopLevelClassFullName.substring(index + 1); + + // Use this anonymous class to store shift value. Its effectively final, so can be pass to consumer lambda + var shift = new Object() { + private int value = 0; + public int get() { return value; } + public void add(int value) { this.value += value; } + }; + + Set importsToAdd = new TreeSet<>(); - //String newTopLevelQualifiedName = ((newTopLevelClassPackage == null ? "" : newTopLevelClassPackage + '/') + newTopLevelClassName).replace('\\', '/'); + StructuralEntryProcessor structProc = (structure) -> { + // Nothing, we don't process structures yet... + }; - // TODO: Track what code object we're in so we have more context? - for (RangeEntry info : rangeList.getEntries()) { + RangeEntryProcessor entryProc = (info, parent) -> { int start = info.getStart(); int end = start + info.getLength(); String expectedOldText = info.getText(); - String oldName = outData.substring(start + shift, end + shift); + String oldName = outData.substring(start + shift.get(), end + shift.get()); if (!oldName.equals(expectedOldText)) throw new RuntimeException("Rename sanity check failed: expected '" + expectedOldText + - "' at [" + start + "," + end + "] (shifted " + shift + " [" + (start + shift) + "," + (end + shift) + "]) " + + "' at [" + start + "," + end + "] (shifted " + shift.get() + " [" + (start + shift.get()) + "," + (end + shift.get()) + "]) " + "in " + fileName + ", but found '" + oldName + "'\n" + "Regenerate symbol map on latest sources or start with fresh source and try again"); @@ -255,7 +289,7 @@ private List processJavaSourceFile(String fileName, String data, RangeMa //TODO: I am unsure how we should handle mappings that change the inner class level of a class. // Right now, the outer class is it's own ClassReference entry. So we have no way to figure out if we need to qualify/import it... String fullname = fixLocalClassName(mapClass(ref.getClassName())); - idx = fullname.lastIndexOf('/'); + int idx = fullname.lastIndexOf('/'); String packagename = idx == -1 ? null : fullname.substring(0, idx); String simplename = fullname.substring(idx + 1); idx = simplename.lastIndexOf('$'); @@ -323,35 +357,57 @@ private List processJavaSourceFile(String fileName, String data, RangeMa throw new IllegalArgumentException("Unknown RangeEntry type: " + info); } - if (oldName.equals(newName)) - continue; //No rename? Skip the rest. + if (!oldName.equals(newName)) { + log("Rename " + info + " Shift[" + shift + "] " + oldName + " -> " + newName); - log("Rename " + info + " Shift[" + shift + "] " + oldName + " -> " + newName); + // Rename algorithm: + // 1. textually replace text at specified range with new text + outData.replace(start + shift.get(), end + shift.get(), newName); + // 2. shift future ranges by difference in text length + shift.add(newName.length() - oldName.length()); + } + }; - // Rename algorithm: - // 1. textually replace text at specified range with new text - // 2. shift future ranges by difference in text length - //data = data.substring(0, info.start + shift) + newName + data.substring(end + shift); - outData.replace(start + shift, end + shift, newName); - shift += (newName.length() - oldName.length()); - } + // Now recursively step all structures with entries in range map + this.stepAllStartingFrom(rangeMap.getRoot(), null, structProc, entryProc); // Lastly, update imports - this == separate from symbol range manipulation above String outString = updateImports(outData, importsToAdd); // rename? - fileName = fileName.replace('\\', '/'); + String outFileName = fileName.replace('\\', '/'); String newFileName = newTopLevelClassFullName + ".java"; - if (newFileName.charAt(0) != '/' && fileName.charAt(0) == '/') + if (newFileName.charAt(0) != '/' && outFileName.charAt(0) == '/') newFileName = '/' + newFileName; - if (!fileName.equals(newFileName)) { - log("Rename file " + fileName + " -> " + newFileName); - fileName = newFileName; + if (!outFileName.equals(newFileName)) { + log("Rename file " + outFileName + " -> " + newFileName); + outFileName = newFileName; } - return Arrays.asList(fileName, outString); + return Arrays.asList(outFileName, outString); + } + + public void stepAllStartingFrom(IRange entry, StructuralEntry parent, StructuralEntryProcessor saction, RangeEntryProcessor eaction) { + if (entry instanceof StructuralEntry) { + StructuralEntry structure = (StructuralEntry) entry; + saction.process(structure); + + List elements = new ArrayList<>(); + elements.addAll(structure.getEntries(false)); + elements.addAll(structure.getStructures(false)); + + // Sort elements to process by 'start' value for proper shifting + for (IRange element : elements.stream() + .sorted(Comparator.comparing(IRange::getStart)) + .collect(Collectors.toList())) { + stepAllStartingFrom(element, structure, saction, eaction); + } + } else + if (entry instanceof RangeEntry) { + eaction.process((RangeEntry) entry, parent); + } } private static String fixLocalClassName(String fullname) { @@ -406,7 +462,6 @@ private String updateImports(StringBuilder data, Set newImports) { boolean addedNewImports = false; boolean sawImports = false; int packageLine = -1; - String newline = data.indexOf("\r\n") != -1 ? "\r\n" : "\n"; while (nextIndex > -1) { String line = data.substring(lastIndex, nextIndex); diff --git a/src/main/java/net/minecraftforge/srg2source/range/RangeMap.java b/src/main/java/net/minecraftforge/srg2source/range/RangeMap.java index 9a0ba27..2c44ab6 100644 --- a/src/main/java/net/minecraftforge/srg2source/range/RangeMap.java +++ b/src/main/java/net/minecraftforge/srg2source/range/RangeMap.java @@ -27,8 +27,8 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Stack; @@ -41,7 +41,9 @@ import net.minecraftforge.srg2source.util.Util; public class RangeMap { - private final int SPEC = 1; + private static final int SPEC = 1; + private static final char TAB_CHAR = ' '; + private static final int TAB_SIZE = 2; //TODO: Support output types: // Directory: every range file is split into it's own file. Would allow for easier navigation/debug @@ -88,24 +90,37 @@ public static Map readAll(InputStream stream) throws IOExcepti private final String filename; private final String hash; - private final List entries; - private final List structures; + private final StructuralEntry root; private final List meta; private RangeMap(int spec, String filename, String hash, List lines, int start, int end) { this.filename = filename; this.hash = hash; - final List entries = new ArrayList<>(); - final List structures = new ArrayList<>(); + this.root = StructuralEntry.createRoot(); final List meta = new ArrayList<>(); - this.entries = Collections.unmodifiableList(entries); - this.structures = Collections.unmodifiableList(structures); this.meta = Collections.unmodifiableList(meta); + Stack stack = new Stack<>(); + stack.push(this.root); + + int lastDepth = 0; for (int x = start; x < end; x++) { + int depth = 0; + for (Character ch : lines.get(x).toCharArray()) { + if (ch.equals(RangeMap.TAB_CHAR)) depth++; else break; + } + depth /= RangeMap.TAB_SIZE; // depth is correlated with stack size, not indent + String line = stripComment(lines.get(x)).trim(); if (line.isEmpty()) continue; + + // Depth changed, remove last structure from stack + while (depth < lastDepth) { + stack.pop(); + lastDepth--; + } + int idx = line.indexOf(' '); if (idx == -1) throw new IllegalArgumentException("Invalid RangeMap line #" + x + ": " + lines.get(x)); @@ -114,21 +129,31 @@ private RangeMap(int spec, String filename, String hash, List lines, int String type = line.substring(0, idx); if ("meta".equals(type)) meta.add(MetaEntry.read(spec, line.substring(idx + 1))); - else if (type.endsWith("def")) //Structure - structures.add(StructuralEntry.read(spec, type.substring(0, type.length() - 3), line.substring(idx + 1))); - else //entry - entries.add(RangeEntry.read(spec, type, line.substring(idx + 1))); + else { + if (type.endsWith("def")) { // structure + StructuralEntry parent = stack.peek(); + StructuralEntry structure = StructuralEntry.read(spec, type.substring(0, type.length() - 3), parent, line.substring(idx + 1)); + // Store structure in parent structure + parent.addStructure(structure); + // and push new actual processed structure on stack + stack.push(structure); + } else { // entry + RangeEntry entry = RangeEntry.read(spec, type, line.substring(idx + 1)); + // Store entry in parent structure + stack.peek().addEntry(entry); + } + lastDepth = depth; + } } catch (Exception e) { throw new IllegalArgumentException("Invalid RangeMap line #" + x + ": " + lines.get(x), e); } } } - RangeMap(String filename, String hash, List entries, List structures, List meta) { + RangeMap(String filename, String hash, StructuralEntry root, List meta) { this.filename = filename; this.hash = hash; - this.entries = Collections.unmodifiableList(entries); - this.structures = Collections.unmodifiableList(structures); + this.root = root; this.meta = Collections.unmodifiableList(meta); } @@ -140,12 +165,8 @@ public String getHash() { return this.hash; } - public List getEntries() { - return this.entries; - } - - public List getStructures() { - return this.structures; + public StructuralEntry getRoot() { + return this.root; } public List getMeta() { @@ -171,58 +192,48 @@ public void write(PrintWriter out, boolean pretty) { } } - Stack stack = new Stack<>(); - Iterator segments = structures.iterator(); + // Get ROOT elements as we want only root children + List rootElements = new ArrayList<>(); + rootElements.addAll(this.root.getEntries(false)); + rootElements.addAll(this.root.getStructures(false)); - StructuralEntry last = null; - StructuralEntry next = segments.hasNext() ? segments.next() : null; + // Start printing root without tab + for (IRange element : rootElements.stream() + .sorted(Comparator.comparing(IRange::getStart)) + .collect(Collectors.toList())) { + printElement(writer, element, 0); + } - for (RangeEntry entry : entries) { - if (pretty) { - while (last != null) { - if (entry.getStart() < end(last)) - break; - writer.tabs--; - writer.accept("# End " + last.getType().name()); - last = stack.empty() ? null : stack.pop(); - } - } + writer.tabs = 0; + writer.accept("end"); + } - if (next != null) { - if (entry.getStart() > next.getStart()) { - next.write(writer); - if (pretty) { - writer.accept("# Start " + next.getType().name() + ' ' + next.getName() + (next.getDescriptor() == null ? "" : next.getDescriptor())); - writer.tabs++; - if (last != null) - stack.push(last); - last = next; - next = segments.hasNext() ? segments.next() : null; - } - } - } + public static void printElement(Writer writer, IRange entry, int tabs) { + if (entry instanceof StructuralEntry){ + StructuralEntry structure = (StructuralEntry) entry; - entry.write(writer); - } + List elements = new ArrayList<>(); + elements.addAll(structure.getEntries(false)); + elements.addAll(structure.getStructures(false)); - //Grab all the trailing things that don't have entries inside them? - //Should never be the case because we should have a entry for the name of the object at least, but hey why not. - if (next != null) { - next.write(writer); - while (segments.hasNext()) - segments.next().write(writer); - } + writer.tabs = tabs; + structure.write(writer); // print structure def + writer.accept("# Start " + structure.getType().name() + ' ' + structure.getName() + (structure.getDescriptor() == null ? "" : structure.getDescriptor())); - if (pretty) { - while (last != null) { - writer.tabs--; - writer.accept("# End " + last.getType().name()); - last = stack.empty() ? null : stack.pop(); + for (IRange element : elements.stream() + .sorted(Comparator.comparing(IRange::getStart)) + .collect(Collectors.toList())) { + printElement(writer, element, tabs + 1); } - } - writer.tabs = 0; - writer.accept("end"); + writer.tabs = tabs; + writer.accept("# End " + structure.getType().name()); + } else + if (entry instanceof RangeEntry) { + RangeEntry rentry = (RangeEntry) entry; + writer.tabs = tabs; + rentry.write(writer); + } } private static class Writer implements Consumer { @@ -235,7 +246,7 @@ private Writer(PrintWriter out) { @Override public void accept(String line) { for (int x = 0; x < tabs; x++) - out.write(" "); + out.write(String.valueOf(RangeMap.TAB_CHAR).repeat(RangeMap.TAB_SIZE)); out.print(line + '\n'); //Don't use println, as we want consistent line endings. } } diff --git a/src/main/java/net/minecraftforge/srg2source/range/RangeMapBuilder.java b/src/main/java/net/minecraftforge/srg2source/range/RangeMapBuilder.java index 6e7f3aa..0df2339 100644 --- a/src/main/java/net/minecraftforge/srg2source/range/RangeMapBuilder.java +++ b/src/main/java/net/minecraftforge/srg2source/range/RangeMapBuilder.java @@ -20,8 +20,8 @@ package net.minecraftforge.srg2source.range; import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import java.util.Stack; import net.minecraftforge.srg2source.range.entries.ClassLiteral; import net.minecraftforge.srg2source.range.entries.ClassReference; @@ -39,10 +39,11 @@ import net.minecraftforge.srg2source.util.io.ConfLogger; public class RangeMapBuilder extends ConfLogger { - private final List entries = new ArrayList<>(); - private final List structures = new ArrayList<>(); + private final StructuralEntry root = StructuralEntry.createRoot(); private final List meta = new ArrayList<>(); + private final Stack stack = new Stack<>(); + private final ConfLogger logger; private final String filename; private final String hash; @@ -51,6 +52,7 @@ public RangeMapBuilder(ConfLogger logger, String filename, String hash) { this.logger = logger; this.filename = filename; this.hash = hash; + this.stack.push(root); } public String getFilename() { @@ -64,14 +66,11 @@ public boolean loadCache(RangeMap cache) { } public RangeMap build() { - // These should be sorted already, as we should encounter them in source order. - // But lets sort them anyways - Collections.sort(entries, (a, b) -> a.getStart() - b.getStart()); - Collections.sort(structures, (a, b) -> a.getStart() - b.getStart()); - checkOverlaps(entries); - return new RangeMap(filename, hash, entries, structures, meta); + //checkOverlaps(entries); // TODO Make new check for structural hierarchy + return new RangeMap(filename, hash, root, meta); } + //TODO: Make this check used again? private void checkOverlaps(List lst) { if (lst.isEmpty()) return; @@ -87,39 +86,71 @@ private void checkOverlaps(List lst) { } } + private StructuralEntry getParent(IRange range) { + return getParent(range.getStart(), range.getLength()); + } + + private StructuralEntry getParent(int start, int length) { + StructuralEntry last = stack.peek(); + if (last.getType() != StructuralEntry.Type.ROOT) { + int newStart = start; + int newEnd = start + length; + do { + int lastStart = last.getStart(); + int lastEnd = last.getStart() + last.getLength(); + if (newEnd > lastEnd && last.getType() != StructuralEntry.Type.RECORD) { + stack.pop(); // New structure is out of last range, remove last from stack + last = stack.peek(); + } else + break; + } while (stack.size() > 1); + } else { + // Check stack size if meet root structure + if (stack.size() != 1) + throw new IllegalStateException("Stack must have one element when meet ROOT structure! Stack size: " + stack.size()); + } + + return stack.peek(); + } // Structure Elements - private void addStructure(StructuralEntry entry) { - structures.add(entry); + private void addStructure(StructuralEntry structure) { + StructuralEntry parent = getParent(structure); + // Store structure in parent structure + parent.addStructure(structure); + // and push new actual processed structure on stack + stack.push(structure); } public void addAnnotationDeclaration(int start, int length, String name) { - addStructure(StructuralEntry.createAnnotation(start, length, name)); + addStructure(StructuralEntry.createAnnotation(getParent(start, length), start, length, name)); } public void addClassDeclaration(int start, int length, String name) { - addStructure(StructuralEntry.createClass(start, length, name)); + addStructure(StructuralEntry.createClass(getParent(start, length), start, length, name)); } public void addEnumDeclaration(int start, int length, String name) { - addStructure(StructuralEntry.createEnum(start, length, name)); + addStructure(StructuralEntry.createEnum(getParent(start, length), start, length, name)); } public void addRecordDeclaration(int start, int length, String name) { - addStructure(StructuralEntry.createRecord(start, length, name)); + addStructure(StructuralEntry.createRecord(getParent(start, length), start, length, name)); } public void addMethodDeclaration(int start, int length, String name, String desc) { - addStructure(StructuralEntry.createMethod(start, length, name, desc)); + addStructure(StructuralEntry.createMethod(getParent(start, length), start, length, name, desc)); } public void addInterfaceDeclaration(int start, int length, String name) { - addStructure(StructuralEntry.createInterface(start, length, name)); + addStructure(StructuralEntry.createInterface(getParent(start, length), start, length, name)); } // Code Elements private void addCode(RangeEntry entry) { - entries.add(entry); + StructuralEntry parent = getParent(entry); + // Store entry in parent structure + parent.addEntry(entry); } public void addPackageReference(int start, int length, String name) { diff --git a/src/main/java/net/minecraftforge/srg2source/range/entries/StructuralEntry.java b/src/main/java/net/minecraftforge/srg2source/range/entries/StructuralEntry.java index ce02767..3f5b9bf 100644 --- a/src/main/java/net/minecraftforge/srg2source/range/entries/StructuralEntry.java +++ b/src/main/java/net/minecraftforge/srg2source/range/entries/StructuralEntry.java @@ -19,103 +19,166 @@ package net.minecraftforge.srg2source.range.entries; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Locale; -import java.util.function.BiFunction; import java.util.function.Consumer; import net.minecraftforge.srg2source.range.IRange; public class StructuralEntry implements IRange { public enum Type { + ROOT, CLASS, - METHOD((me, data) -> { + METHOD((me, parent, data) -> { String[] pts = data.split(" "); if (pts.length != 4) throw new IllegalArgumentException("Missing required parts. Parts Length: " + pts.length); - return new StructuralEntry(me, Integer.parseInt(pts[0]), Integer.parseInt(pts[1]), pts[2], pts[2]); + return new StructuralEntry(me, parent, Integer.parseInt(pts[0]), Integer.parseInt(pts[1]), pts[2], pts[3]); }), ENUM, - ANNOTATION, + ANNOTATION((me, parent, data) -> { + String[] pts = data.split(" "); + if (pts.length != 3) + throw new IllegalArgumentException("Missing required parts. Parts Length: " + pts.length); + return new StructuralEntry(me, parent, Integer.parseInt(pts[0]), Integer.parseInt(pts[1]), pts[2], null); + }), INTERFACE, RECORD; - private BiFunction read; - private Type(BiFunction read) { + private TriFunction read; + private Type(TriFunction read) { this.read = read; } private Type() { - this((me, data) -> { + this((me, parent, data) -> { String[] pts = data.split(" "); if (pts.length != 3) throw new IllegalArgumentException("Missing required parts. Parts Length: " + pts.length); - return new StructuralEntry(me, Integer.parseInt(pts[0]), Integer.parseInt(pts[1]), pts[2], null); + return new StructuralEntry(me, parent, Integer.parseInt(pts[0]), Integer.parseInt(pts[1]), pts[2], null); }); } - private StructuralEntry read(String data) { - return this.read.apply(this, data); + private StructuralEntry read(StructuralEntry parent, String data) { + return this.read.apply(this, parent, data); } } - public static StructuralEntry createAnnotation(int start, int length, String name) { - return new StructuralEntry(Type.ANNOTATION, start, length, name, null); + public static StructuralEntry createRoot() { + return new StructuralEntry(Type.ROOT, null,-1, -1, null, null); } - public static StructuralEntry createClass(int start, int length, String name) { - return new StructuralEntry(Type.CLASS, start, length, name, null); + public static StructuralEntry createAnnotation(StructuralEntry parent, int start, int length, String name) { + return new StructuralEntry(Type.ANNOTATION, parent, start, length, name, null); } - public static StructuralEntry createEnum(int start, int length, String name) { - return new StructuralEntry(Type.ENUM, start, length, name, null); + public static StructuralEntry createClass(StructuralEntry parent, int start, int length, String name) { + return new StructuralEntry(Type.CLASS, parent, start, length, name, null); } - public static StructuralEntry createRecord(int start, int length, String name) { - return new StructuralEntry(Type.RECORD, start, length, name, null); + public static StructuralEntry createEnum(StructuralEntry parent, int start, int length, String name) { + return new StructuralEntry(Type.ENUM, parent, start, length, name, null); } - public static StructuralEntry createInterface(int start, int length, String name) { - return new StructuralEntry(Type.INTERFACE, start, length, name, null); + public static StructuralEntry createRecord(StructuralEntry parent,int start, int length, String name) { + return new StructuralEntry(Type.RECORD, parent, start, length, name, null); } - public static StructuralEntry createMethod(int start, int length, String name, String desc) { - return new StructuralEntry(Type.METHOD, start, length, name, desc); + public static StructuralEntry createInterface(StructuralEntry parent,int start, int length, String name) { + return new StructuralEntry(Type.INTERFACE, parent, start, length, name, null); } - public static StructuralEntry read(int spec, String type, String data) { + public static StructuralEntry createMethod(StructuralEntry parent,int start, int length, String name, String desc) { + return new StructuralEntry(Type.METHOD, parent, start, length, name, desc); + } + + public static StructuralEntry read(int spec, String type, StructuralEntry parent, String data) { Type t = null; try { t = Type.valueOf(type.toUpperCase(Locale.ENGLISH)); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Unknown Structure Type: " + type); } - return t.read(data); + return t.read(parent, data); } private final Type type; + private final StructuralEntry parent; private final int start; private final int length; private final String name; private final String desc; - private StructuralEntry(Type type, int start, int length, String name, String desc) { + private final List structures; + private final List entries; + + private StructuralEntry(Type type, StructuralEntry parent, int start, int length, String name, String desc) { this.type = type; + this.parent = parent; this.start = start; this.length = length; this.name = name; this.desc = desc; + this.structures = new ArrayList<>(); + this.entries = new ArrayList<>(); + } + + public void addEntry(RangeEntry entry) { + this.entries.add(entry); + } + + public boolean hasEntries() { + return !this.entries.isEmpty(); + } + + public List getEntries(boolean children) { + final List entries = new ArrayList<>(); + entries.addAll(this.entries); + + // Scan structures for children entries + if (children) { + for (StructuralEntry structure : this.structures) + entries.addAll(structure.getEntries(children)); + } + + return Collections.unmodifiableList(entries); + } + + public void addStructure(StructuralEntry structure) { + this.structures.add(structure); + } + + public boolean hasStructures() { + return !this.structures.isEmpty(); + } + + public List getStructures(boolean children) { + final List structures = new ArrayList<>(); + structures.addAll(this.structures); + + // Scan structures for childrens + if (children) { + for (StructuralEntry structure : this.structures) + structures.addAll(structure.getStructures(children)); + } + + return Collections.unmodifiableList(structures); } @Override public String toString() { return "StructuralEntry[type: " + type.name() + ", Start: " + start + ", Len: " + length + - ", Name: \"" + name + "\", Desc: \"" + desc + "\"]"; + ", Name: \"" + name + "\", Desc: \"" + desc + "\", Parent: \"" + parent.getName() + "\", Entries: \"" + entries.size() + "\", Structures: \"" + structures.size() + "\"]"; } public Type getType() { return this.type; } + public StructuralEntry getParent() { return this.parent; } + @Override public int getStart() { return this.start; @@ -141,4 +204,18 @@ public void write(Consumer out) { line += ' ' + desc; out.accept(line); } + + @FunctionalInterface + public interface TriFunction { + + /** + * Applies this function to the given arguments. + * + * @param t the first function argument + * @param u the second function argument + * @param s the third function argument + * @return the function result + */ + R apply(T t, U u, S s); + } } \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/srg2source/util/Util.java b/src/main/java/net/minecraftforge/srg2source/util/Util.java index 653f135..437496b 100644 --- a/src/main/java/net/minecraftforge/srg2source/util/Util.java +++ b/src/main/java/net/minecraftforge/srg2source/util/Util.java @@ -132,4 +132,8 @@ public static List unquote(String data, int count) { ret.add(data); return ret; } + + public static String detectLineEnd(String data) { + return data.indexOf("\r\n" ) != -1 ? "\r\n" : "\n"; + } } diff --git a/src/test/resources/GenericClasses/original.range b/src/test/resources/GenericClasses/original.range index 088e45e..a1eeba9 100644 --- a/src/test/resources/GenericClasses/original.range +++ b/src/test/resources/GenericClasses/original.range @@ -30,9 +30,9 @@ classdef 68 488 GenericClasses class 363 6 String false java/lang/String classdef 381 110 GenericClasses$1 # Start CLASS GenericClasses$1 - class 402 6 String false java/lang/String methoddef 395 86 bar (Ljava/lang/String;)Ljava/lang/String; # Start METHOD bar(Ljava/lang/String;)Ljava/lang/String; + class 402 6 String false java/lang/String method 409 3 bar GenericClasses$IFoo bar (Ljava/lang/Object;)Ljava/lang/Object; class 413 6 String false java/lang/String parameter 420 3 arg GenericClasses$1 bar (Ljava/lang/String;)Ljava/lang/String; 0 diff --git a/src/test/resources/NestedGenerics/original.range b/src/test/resources/NestedGenerics/original.range index de103de..c9ff80b 100644 --- a/src/test/resources/NestedGenerics/original.range +++ b/src/test/resources/NestedGenerics/original.range @@ -16,9 +16,9 @@ classdef 97 189 Test local_variable 209 3 map Test main ([Ljava/lang/String;)V 1 Ljava/util/Map; method 213 15 computeIfAbsent java/util/Map computeIfAbsent (Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object; class 233 4 Test false Test - parameter 241 1 k Test lambda$0 (LTest;)Ljava/util/Set; 0 methoddef 241 20 lambda$0 (LTest;)Ljava/util/Set; # Start METHOD lambda$0(LTest;)Ljava/util/Set; + parameter 241 1 k Test lambda$0 (LTest;)Ljava/util/Set; 0 class 250 7 HashSet false java/util/HashSet # End METHOD method 263 3 add java/util/Collection add (Ljava/lang/Object;)Z