summaryrefslogtreecommitdiff
path: root/src/main/java/ftbsc/lll
diff options
context:
space:
mode:
author zaaarf <zaaarf@proton.me>2023-08-26 18:09:36 +0200
committer zaaarf <zaaarf@proton.me>2023-08-26 18:09:52 +0200
commit7898882bc5811e5f06e70bc2bb6925e114869a56 (patch)
treeee2f2a4f75c72f4beaead402d7cbf2ad8d71bfe0 /src/main/java/ftbsc/lll
parentc3cc90d7743b18aaef0d22adb7292c63ef48603f (diff)
feat: implemented abstraction logic
Diffstat (limited to 'src/main/java/ftbsc/lll')
-rw-r--r--src/main/java/ftbsc/lll/exceptions/InvalidResourceException.java23
-rw-r--r--src/main/java/ftbsc/lll/exceptions/MalformedMappingsException.java7
-rw-r--r--src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java15
-rw-r--r--src/main/java/ftbsc/lll/mapper/AbstractMapper.java142
-rw-r--r--src/main/java/ftbsc/lll/mapper/IMapper.java76
-rw-r--r--src/main/java/ftbsc/lll/mapper/MapperProvider.java73
-rw-r--r--src/main/java/ftbsc/lll/mapper/impl/TSRGMapper.java77
-rw-r--r--src/main/java/ftbsc/lll/mapper/tools/ClassData.java100
-rw-r--r--src/main/java/ftbsc/lll/mapper/tools/data/ClassData.java117
-rw-r--r--src/main/java/ftbsc/lll/mapper/tools/data/FieldData.java34
-rw-r--r--src/main/java/ftbsc/lll/mapper/tools/data/MethodData.java35
-rw-r--r--src/main/java/ftbsc/lll/mapper/tools/data/MethodSignature.java51
12 files changed, 566 insertions, 184 deletions
diff --git a/src/main/java/ftbsc/lll/exceptions/InvalidResourceException.java b/src/main/java/ftbsc/lll/exceptions/InvalidResourceException.java
new file mode 100644
index 0000000..060d57c
--- /dev/null
+++ b/src/main/java/ftbsc/lll/exceptions/InvalidResourceException.java
@@ -0,0 +1,23 @@
+package ftbsc.lll.exceptions;
+
+/**
+ * Thrown when a resource passed as an argument is not found.
+ */
+public class InvalidResourceException extends RuntimeException {
+
+ /**
+ * Empty constructor, used when the provided resource exists but no
+ * mapper was able to read it.
+ */
+ public InvalidResourceException() {
+ super("The given resource was not claimed by any mapper!");
+ }
+
+ /**
+ * Named constructor, used when the specified resource doesn't exist.
+ * @param name the resource name
+ */
+ public InvalidResourceException(String name) {
+ super(String.format("Specified resource %s was not found!", name));
+ }
+}
diff --git a/src/main/java/ftbsc/lll/exceptions/MalformedMappingsException.java b/src/main/java/ftbsc/lll/exceptions/MalformedMappingsException.java
new file mode 100644
index 0000000..1352855
--- /dev/null
+++ b/src/main/java/ftbsc/lll/exceptions/MalformedMappingsException.java
@@ -0,0 +1,7 @@
+package ftbsc.lll.exceptions;
+
+public class MalformedMappingsException extends Exception {
+ public MalformedMappingsException(String mapping, String type) {
+ super(String.format("Unexpected token at line %s for mapper type %s!", mapping, type));
+ }
+}
diff --git a/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java b/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java
index 6c286fc..85ac08f 100644
--- a/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java
+++ b/src/main/java/ftbsc/lll/exceptions/MappingNotFoundException.java
@@ -9,19 +9,10 @@ public class MappingNotFoundException extends RuntimeException {
/**
* Constructs a new mapping not found exception for the specified mapping.
+ * @param type the type of mapping
* @param mapping the relevant mapping
*/
- public MappingNotFoundException(String mapping) {
- super(String.format("Could not find mapping for %s!", mapping));
- }
-
- /**
- * Constructs a new mapping not found exception for the specified mapping
- * with the specified reason.
- * @param mapping the relevant mapping
- * @param reason the reason message
- */
- public MappingNotFoundException(String mapping, String reason) {
- this(mapping + ": " + reason);
+ public MappingNotFoundException(String type, String mapping) {
+ super(String.format("Could not find mapping for %s %s!", type, mapping));
}
}
diff --git a/src/main/java/ftbsc/lll/mapper/AbstractMapper.java b/src/main/java/ftbsc/lll/mapper/AbstractMapper.java
new file mode 100644
index 0000000..037e7c9
--- /dev/null
+++ b/src/main/java/ftbsc/lll/mapper/AbstractMapper.java
@@ -0,0 +1,142 @@
+package ftbsc.lll.mapper;
+
+import ftbsc.lll.exceptions.MalformedMappingsException;
+import ftbsc.lll.exceptions.MappingNotFoundException;
+import ftbsc.lll.mapper.tools.data.ClassData;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A default implementation of {@link IMapper} meant to
+ * recycle as much code as possible.
+ */
+public abstract class AbstractMapper implements IMapper {
+
+ /**
+ * A {@link Map} tying each plain class name to its class data.
+ */
+ protected final Map<String, ClassData> mappings = new HashMap<>();
+
+ /**
+ * A {@link Map} tying each obfuscated name to its class data.
+ */
+ protected final Map<String, ClassData> mappingsInverted = new HashMap<>();
+
+ /**
+ * Populates the {@link IMapper} given the lines, ignoring errors depending on the
+ * given ignoreErrors flag.
+ * @param lines the lines to read
+ * @param ignoreErrors try to ignore errors and keep going
+ * @throws MalformedMappingsException if an error is encountered and ignoreErrors is false
+ */
+ @Override
+ public void populate(List<String> lines, boolean ignoreErrors) throws MalformedMappingsException {
+ this.processLines(lines, ignoreErrors);
+ this.mappings.forEach((name, data) -> {
+ ClassData reverse = data.generateReverseMappings(this);
+ this.mappingsInverted.put(data.nameMapped, reverse);
+ });
+ }
+
+ /**
+ * Reads the given lines of text and attempts to interpret them as
+ * mappings of the given type.
+ * @param lines the lines to read
+ * @param ignoreErrors try to ignore errors and keep going
+ * @throws MalformedMappingsException if an error is encountered and ignoreErrors is false
+ */
+ protected abstract void processLines(List<String> lines, boolean ignoreErrors) throws MalformedMappingsException;
+
+ /**
+ * Completely resets the mapper, clearing it of all existing mappings.
+ */
+ @Override
+ public void reset() {
+ this.mappings.clear();
+ this.mappingsInverted.clear();
+ }
+
+ /**
+ * Gets a name of a class from the given {@link Map}.
+ * @param name the name
+ * @param mappings the {@link Map} to pull data from
+ * @return the mapped name
+ * @throws MappingNotFoundException if no mapping is found
+ */
+ private static String mapClass(String name, Map<String, ClassData> mappings) {
+ ClassData data = mappings.get(name.replace('.', '/'));
+ if(data == null)
+ throw new MappingNotFoundException("class", name);
+ else return data.nameMapped;
+ }
+
+ /**
+ * Gets the obfuscated name of the class.
+ * @param name the plain internal name of the desired class
+ * @return the obfuscated name of the class
+ * @throws MappingNotFoundException if no mapping is found
+ */
+ @Override
+ public String obfuscateClass(String name) {
+ return mapClass(name, this.mappings);
+ }
+
+ /**
+ * Gets the plain name of the class.
+ * @param nameObf the obfuscated internal name of the desired class
+ * @return the plain name of the class
+ * @throws MappingNotFoundException if no mapping is found
+ */
+ @Override
+ public String deobfuscateClass(String nameObf) throws MappingNotFoundException {
+ return mapClass(nameObf, this.mappingsInverted);
+ }
+
+ /**
+ * Gets the name of a member from the given {@link Map}.
+ * @param parentName the parent class
+ * @param mappings the {@link Map} to pull data from
+ * @param memberName the field or method name
+ * @param methodDescriptor the method descriptor, may be null or partial
+ * @return the mapped member name
+ * @throws MappingNotFoundException if no mapping is found
+ */
+ private static String mapMember(String parentName, Map<String, ClassData> mappings,
+ String memberName, String methodDescriptor) {
+ ClassData data = mappings.get(parentName.replace('.', '/'));
+ if(data == null)
+ throw new MappingNotFoundException("class", parentName);
+
+ if(methodDescriptor == null)
+ return data.mapField(memberName).name;
+ else return data.mapMethod(memberName, methodDescriptor).signature.name;
+ }
+
+ /**
+ * Gets the obfuscated name of a class member (field or method).
+ * @param parentName the unobfuscated internal name of the parent class
+ * @param memberName the field or method name
+ * @param methodDescriptor the optional descriptor of the member, may be null or partial
+ * @return the obfuscated name of the given member
+ * @throws MappingNotFoundException if no mapping is found
+ */
+ @Override
+ public String obfuscateMember(String parentName, String memberName, String methodDescriptor) {
+ return mapMember(parentName, this.mappings, memberName, methodDescriptor);
+ }
+
+ /**
+ * Gets the plain name of a class member (field or method).
+ * @param parentName the obfuscated internal name of the parent class
+ * @param memberName the obfuscated field name or method signature
+ * @param methodDescriptor the obfuscated descriptor of the member (only for methods)
+ * @return the plain name of the given member
+ * @throws MappingNotFoundException if no mapping is found
+ */
+ @Override
+ public String deobfuscateMember(String parentName, String memberName, String methodDescriptor) throws MappingNotFoundException {
+ return mapMember(parentName, this.mappingsInverted, memberName, methodDescriptor);
+ }
+}
diff --git a/src/main/java/ftbsc/lll/mapper/IMapper.java b/src/main/java/ftbsc/lll/mapper/IMapper.java
index 8adf860..96b5d6f 100644
--- a/src/main/java/ftbsc/lll/mapper/IMapper.java
+++ b/src/main/java/ftbsc/lll/mapper/IMapper.java
@@ -1,56 +1,80 @@
package ftbsc.lll.mapper;
+import ftbsc.lll.exceptions.MalformedMappingsException;
import ftbsc.lll.exceptions.MappingNotFoundException;
-import java.util.HashSet;
-import java.util.ServiceLoader;
-import java.util.Set;
+import java.util.*;
/**
* A generic obfuscation mapper.
*/
public interface IMapper {
+ /**
+ * Checks whether this mapper can process the given lines.
+ * @param lines the lines to read
+ * @return whether this type of mapper can process these lines
+ */
+ boolean claim(List<String> lines);
/**
- * Reads the given lines of text and attempts to interpret them as
- * mappings of the given type.
+ * Defines a priority for this implementation: the higher the number,
+ * the higher the priority.
+ * This is used to resolve conflicts when multiple mappers attempt to
+ * {@link #claim(List) claim} a given mapping file.
+ * @return the priority
+ */
+ default int priority() {
+ return 0;
+ }
+
+ /**
+ * Populates the {@link IMapper} given the lines, ignoring errors depending on the
+ * given ignoreErrors flag.
* @param lines the lines to read
+ * @param ignoreErrors try to ignore errors and keep going
+ * @throws MalformedMappingsException if an error is encountered and ignoreErrors is false
*/
- void populate(Iterable<String> lines);
+ void populate(List<String> lines, boolean ignoreErrors) throws MalformedMappingsException;
+
+ /**
+ * Completely resets the mapper, clearing it of all existing mappings.
+ */
+ void reset();
/**
* Gets the obfuscated name of the class.
- * @param name the unobfuscated internal name of the desired class
+ * @param name the plain internal name of the desired class
* @return the obfuscated name of the class
* @throws MappingNotFoundException if no mapping is found
*/
- String obfuscateClass(String name);
+ String obfuscateClass(String name) throws MappingNotFoundException;
+
+ /**
+ * Gets the plain name of the class.
+ * @param nameObf the obfuscated internal name of the desired class
+ * @return the plain name of the class
+ * @throws MappingNotFoundException if no mapping is found
+ */
+ String deobfuscateClass(String nameObf) throws MappingNotFoundException;
/**
* Gets the obfuscated name of a class member (field or method).
- * @param parentName the unobfuscated internal name of the parent class
+ * @param parentName the plain internal name of the parent class
* @param memberName the field name or method signature
- * @param methodDescriptor the descriptor of the member
+ * @param methodDescriptor the descriptor of the member (only for methods)
* @return the obfuscated name of the given member
* @throws MappingNotFoundException if no mapping is found
*/
- String obfuscateMember(String parentName, String memberName, String methodDescriptor);
+ String obfuscateMember(String parentName, String memberName, String methodDescriptor) throws MappingNotFoundException;
/**
- * Loads all valid parsers available in the classpath (via the Java Service API),
- * attempts to parse the given lines into mappings, and returns all built mappers
- * that succeeded without throwing errors or ftbsc.lll.exceptions.
- * @param lines the lines of the mapping file
- * @return a {@link Set} of mappers that could interpret the given input
+ * Gets the plain name of a class member (field or method).
+ * @param parentName the obfuscated internal name of the parent class
+ * @param memberName the obfuscated field name or method signature
+ * @param methodDescriptor the obfuscated descriptor of the member (only for methods)
+ * @return the plain name of the given member
+ * @throws MappingNotFoundException if no mapping is found
*/
- static Set<IMapper> getMappers(Iterable<String> lines) {
- Set<IMapper> parsed = new HashSet<>();
- for(IMapper mapper: ServiceLoader.load(IMapper.class)) {
- try {
- mapper.populate(lines);
- parsed.add(mapper);
- } catch(Throwable ignored) {}
- }
- return parsed;
- }
+ String deobfuscateMember(String parentName, String memberName, String methodDescriptor) throws MappingNotFoundException;
+
}
diff --git a/src/main/java/ftbsc/lll/mapper/MapperProvider.java b/src/main/java/ftbsc/lll/mapper/MapperProvider.java
new file mode 100644
index 0000000..819e792
--- /dev/null
+++ b/src/main/java/ftbsc/lll/mapper/MapperProvider.java
@@ -0,0 +1,73 @@
+package ftbsc.lll.mapper;
+
+import ftbsc.lll.exceptions.InvalidResourceException;
+
+import java.io.*;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * The main class of the mapper library. It loads all the
+ * valid {@link IMapper}s and gets information from them.
+ */
+public class MapperProvider {
+ private static MapperProvider INSTANCE = null;
+
+ private static MapperProvider getInstance() {
+ return INSTANCE == null ? (INSTANCE = new MapperProvider()) : INSTANCE;
+ }
+
+ private Set<IMapper> loadedMappers = null;
+
+ private void loadMappers() {
+ this.loadedMappers = new HashSet<>();
+ for(IMapper mapper: ServiceLoader.load(IMapper.class))
+ this.loadedMappers.add(mapper);
+ if(this.loadedMappers.isEmpty())
+ throw new RuntimeException("Something went wrong: no mapper types were loaded successfully!");
+ }
+
+ /**
+ * Loads all valid parsers available in the classpath (via the Java Service API),
+ * attempts to load the resource at given location and to populate a mapper with
+ * its data.
+ * @param data the file as a list of strings
+ * @return a {@link IMapper} (populating it is left to the user)
+ */
+ public static IMapper getMapper(List<String> data) {
+ if(getInstance().loadedMappers == null)
+ getInstance().loadMappers();
+ return getInstance().loadedMappers.stream()
+ .filter(m -> m.claim(data))
+ .max(Comparator.comparingInt(IMapper::priority))
+ .orElseThrow(InvalidResourceException::new);
+ }
+
+ /**
+ * Gets a resource and parses it into a {@link List} of {@link String}s.
+ * @param location either a URL or a local path
+ * @return a {@link List} containing the lines of the resource
+ * @throws InvalidResourceException if provided an invalid resource
+ */
+ public static List<String> fetchFromLocalOrRemote(String location) {
+ InputStream targetStream;
+ try {
+ URI target = new URI(location);
+ targetStream = target.toURL().openStream();
+ } catch(URISyntaxException | IOException e) {
+ //may be a local file path
+ File f = new File(location);
+ try {
+ targetStream = new FileInputStream(f);
+ } catch(FileNotFoundException ex) {
+ throw new InvalidResourceException(location);
+ }
+ }
+
+ return new BufferedReader(new InputStreamReader(targetStream,
+ StandardCharsets.UTF_8)).lines().collect(Collectors.toList());
+ }
+}
diff --git a/src/main/java/ftbsc/lll/mapper/impl/TSRGMapper.java b/src/main/java/ftbsc/lll/mapper/impl/TSRGMapper.java
index 041fb2f..2bd4d28 100644
--- a/src/main/java/ftbsc/lll/mapper/impl/TSRGMapper.java
+++ b/src/main/java/ftbsc/lll/mapper/impl/TSRGMapper.java
@@ -1,75 +1,60 @@
package ftbsc.lll.mapper.impl;
import com.google.auto.service.AutoService;
-import ftbsc.lll.exceptions.MappingNotFoundException;
+import ftbsc.lll.exceptions.MalformedMappingsException;
+import ftbsc.lll.mapper.AbstractMapper;
import ftbsc.lll.mapper.IMapper;
-import ftbsc.lll.mapper.tools.ClassData;
+import ftbsc.lll.mapper.tools.data.ClassData;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
/**
- * Parses a .tsrg file into a mapper capable of converting from
- * plain names to obfuscated ones and vice versa.
+ * A {@link IMapper} capable of parsing TSRG (an intermediary
+ * format used by Forge) files.
*/
@AutoService(IMapper.class)
-public class TSRGMapper implements IMapper {
+public class TSRGMapper extends AbstractMapper {
/**
- * A Map containing the deobfuscated names as keys and information about
- * each class as values.
+ * Checks whether this mapper can process the given lines.
+ * @param lines the lines to read
+ * @return whether this type of mapper can process these lines
*/
- private final Map<String, ClassData> mappings = new HashMap<>();
+ @Override
+ public boolean claim(List<String> lines) {
+ return lines.get(0).startsWith("tsrg2 left right");
+ }
/**
* Reads the given lines of text and attempts to interpret them as
* mappings of the given type.
* @param lines the lines to read
+ * @param ignoreErrors try to ignore errors and keep going
+ * @throws MalformedMappingsException if an error is encountered and ignoreErrors is false
*/
@Override
- public void populate(Iterable<String> lines) {
+ protected void processLines(List<String> lines, boolean ignoreErrors) throws MalformedMappingsException {
+ //skip the first line ("tsrg2 left right")
+ lines = new ArrayList<>(lines);
+ lines.remove(0);
+
String currentClass = "";
for(String l : lines) {
if(l == null) continue;
- if(l.startsWith("\t"))
- mappings.get(currentClass).addMember(l);
- else {
+ if(l.startsWith("\t") || l.startsWith(" ")) {
+ String[] split = l.trim().split(" ");
+ if(split.length == 2) //field
+ this.mappings.get(currentClass).addField(split[0], split[1]);
+ else if (split.length == 3)//method
+ this.mappings.get(currentClass).addMethod(split[0], split[2], split[1]); //add child
+ } else {
String[] sp = l.split(" ");
ClassData s = new ClassData(sp[0], sp[1]);
- currentClass = s.unobf;
- mappings.put(s.unobf, s);
+ currentClass = s.name;
+ this.mappings.put(s.name, s);
}
}
}
-
- /**
- * Gets the obfuscated name of the class.
- * @param name the unobfuscated internal name of the desired class
- * @return the obfuscated name of the class
- * @throws MappingNotFoundException if no mapping is found
- */
- @Override
- public String obfuscateClass(String name) {
- ClassData data = mappings.get(name.replace('.', '/'));
- if(data == null)
- throw new MappingNotFoundException(name);
- else return data.obf;
- }
-
- /**
- * Gets the obfuscated name of a class member (field or method).
- * @param parentName the unobfuscated internal name of the parent class
- * @param memberName the field name or method signature
- * @param methodDescriptor the optional descriptor of the member, may be null or partial
- * @return the obfuscated name of the given member
- * @throws MappingNotFoundException if no mapping is found
- */
- @Override
- public String obfuscateMember(String parentName, String memberName, String methodDescriptor) {
- ClassData data = mappings.get(parentName.replace('.', '/'));
- if(data == null)
- throw new MappingNotFoundException(parentName + "::" + memberName);
- return data.get(memberName, methodDescriptor);
- }
}
diff --git a/src/main/java/ftbsc/lll/mapper/tools/ClassData.java b/src/main/java/ftbsc/lll/mapper/tools/ClassData.java
deleted file mode 100644
index a9fbcec..0000000
--- a/src/main/java/ftbsc/lll/mapper/tools/ClassData.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package ftbsc.lll.mapper.tools;
-
-import ftbsc.lll.exceptions.AmbiguousMappingException;
-import ftbsc.lll.exceptions.MappingNotFoundException;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-/**
- * Container class used to store information about classes.
- */
-public class ClassData {
- /**
- * The unobfuscated name (FQN with '/' instad of '.') of the class.
- */
- public final String unobf;
-
- /**
- * The obfuscated internal name (FQN with '/' instad of '.') of the class.
- */
- public final String obf;
-
- /**
- * A {@link Map} tying each member's name or signature to its
- * obfuscated counterpart.
- */
- public final Map<String, String> members;
-
- /**
- * The constructor. It takes in the names (obfuscated and non-obfuscated)
- * of a class.
- * @param unobf the unobfuscated name
- * @param obf the obfuscated name
- */
- public ClassData(String unobf, String obf) {
- this.unobf = unobf;
- this.obf = obf;
- this.members = new HashMap<>();
- }
-
- /**
- * Adds a member to the target class.
- * For fields only the names are required; for methods,
- * this takes in the full signature ({@code name + " " + space}).
- * @param s the String representing the declaration line
- */
- public void addMember(String s) {
- String[] split = s.trim().split(" ");
- if(split.length == 2) //field
- members.put(split[0], split[1]);
- else if (split.length == 3) //method
- members.put(split[0] + " " + split[1], split[2]);
- }
-
- /**
- * Gets an obfuscated member given the method name and a method descriptor,
- * which may be partial (i.e. not include return type) or null if the member
- * is not a method.
- * @param memberName member name
- * @param methodDescriptor the method descriptor, or null if it's not a method
- * @return the requested obfuscated name, or null if nothing was found
- * @throws AmbiguousMappingException if not enough data was given to uniquely identify a mapping
- */
- public String get(String memberName, String methodDescriptor) {
-
- //find all keys that start with the name
- List<String> candidates = members.keySet().stream().filter(
- m -> m.split(" ")[0].equals(memberName)
- ).collect(Collectors.toList());
-
- if(methodDescriptor != null) {
- String signature = String.format("%s %s", memberName, methodDescriptor);
- candidates = candidates.stream().filter(
- m -> m.equals(signature)
- ).collect(Collectors.toList());
- }
-
- switch(candidates.size()) {
- case 0:
- throw new MappingNotFoundException(String.format(
- "%s.%s%s",
- this.unobf,
- memberName,
- methodDescriptor == null ? "" : "()"
- ));
- case 1:
- return members.get(candidates.get(0));
- default:
- throw new AmbiguousMappingException(String.format(
- "Mapper could not uniquely identify member %s.%s%s, found %d!",
- this.unobf,
- memberName,
- methodDescriptor == null ? "" : "()",
- candidates.size()
- ));
- }
- }
-} \ No newline at end of file
diff --git a/src/main/java/ftbsc/lll/mapper/tools/data/ClassData.java b/src/main/java/ftbsc/lll/mapper/tools/data/ClassData.java
new file mode 100644
index 0000000..5ae4124
--- /dev/null
+++ b/src/main/java/ftbsc/lll/mapper/tools/data/ClassData.java
@@ -0,0 +1,117 @@
+package ftbsc.lll.mapper.tools.data;
+
+import ftbsc.lll.exceptions.MappingNotFoundException;
+import ftbsc.lll.mapper.IMapper;
+import ftbsc.lll.mapper.tools.MappingUtils;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * Container class used to store information about classes.
+ */
+public class ClassData {
+
+ /**
+ * The internal (like the fully-qualified name, but with '/' instead
+ * of '.') of the class.
+ */
+ public final String name;
+
+ /**
+ * The mapped internal (like the fully-qualified name, but with '/'
+ * instead of '.') of the class.
+ */
+ public final String nameMapped;
+
+ /**
+ * A {@link Map} tying each method's signature to its data class.
+ */
+ private final Map<MethodSignature, MethodData> methods;
+
+ /**
+ * A {@link Map} tying each field's name to its data class.
+ */
+ private final Map<String, FieldData> fields;
+
+ /**
+ * The constructor. It takes in the names (plain and mapped) of a class.
+ * @param name the plain name
+ * @param nameMapped the mapped name
+ */
+ public ClassData(String name, String nameMapped) {
+ this.name = name;
+ this.nameMapped = nameMapped;
+ this.methods = new HashMap<>();
+ this.fields = new HashMap<>();
+ }
+
+ /**
+ * Adds a method to the target class.
+ * @param name the method name
+ * @param nameMapped the mapped method name
+ * @param descriptor the descriptor of the method
+ */
+ public void addMethod(String name, String nameMapped, String descriptor) {
+ MethodData data = new MethodData(this, name, nameMapped, descriptor);
+ this.methods.put(data.signature, data);
+ }
+
+ /**
+ * Adds a field to the target class.
+ * @param plain the name of the field
+ * @param mapped the mapped name of the field
+ */
+ public void addField(String plain, String mapped) {
+ this.fields.put(plain, new FieldData(this, plain, mapped));
+ }
+
+ /**
+ * Generates the reverse mappings for this class.
+ * Should always be called only after the given mapper has finished
+ * processing all classes.
+ * @param mapper the mapper that generated this data
+ */
+ public ClassData generateReverseMappings(IMapper mapper) {
+ ClassData reverse = new ClassData(this.nameMapped, this.name);
+ this.methods.forEach((signature, data) -> reverse.addMethod(nameMapped, signature.name,
+ MappingUtils.obfuscateMethodDescriptor(signature.descriptor, mapper)));
+ this.fields.forEach((name, data) -> reverse.addField(data.nameMapped, name));
+ return reverse;
+ }
+
+ /**
+ * Gets the {@link MethodData} from its name and descriptor, which may be partial
+ * (i.e. not include the return type).
+ * @param methodName the method name
+ * @param methodDescriptor the method descriptor, which may be partial
+ * @return the requested {@link MethodData}
+ * @throws MappingNotFoundException if the mapping wasn't found
+ */
+ public MethodData mapMethod(String methodName, String methodDescriptor) {
+ List<MethodSignature> signatures = this.methods.keySet().stream().filter(
+ s -> s.name.equals(methodName) && s.descriptor.startsWith(methodDescriptor)
+ ).collect(Collectors.toList());
+ if(signatures.size() > 1)
+ throw new RuntimeException(); //should never happen unless something goes horribly wrong
+ else if(signatures.isEmpty())
+ throw new MappingNotFoundException("method",
+ String.format("%s::%s%s", this.name, methodName, methodDescriptor));
+ return this.methods.get(signatures.get(0));
+ }
+
+ /**
+ * Gets the {@link FieldData} its name.
+ * @param fieldName the field name
+ * @return the requested {@link FieldData}
+ * @throws MappingNotFoundException if the mapping wasn't found
+ */
+ public FieldData mapField(String fieldName) {
+ FieldData data = this.fields.get(fieldName);
+ if(data == null)
+ throw new MappingNotFoundException("field", String.format("%s.%s", this.name, fieldName));
+ else return data;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/ftbsc/lll/mapper/tools/data/FieldData.java b/src/main/java/ftbsc/lll/mapper/tools/data/FieldData.java
new file mode 100644
index 0000000..bcc3985
--- /dev/null
+++ b/src/main/java/ftbsc/lll/mapper/tools/data/FieldData.java
@@ -0,0 +1,34 @@
+
+package ftbsc.lll.mapper.tools.data;
+
+/**
+ * Container class for method data.
+ */
+public class FieldData {
+ /**
+ * The internal name of the parent class.
+ */
+ public final ClassData parentClass;
+
+ /**
+ * The name of the method.
+ */
+ public final String name;
+
+ /**
+ * The name mapped.
+ */
+ public final String nameMapped;
+
+ /**
+ * Constructs a new {@link FieldData}.
+ * @param parentClass the {@link ClassData} representation of the parent class
+ * @param name the field name
+ * @param nameMapped the mapped field name
+ */
+ public FieldData(ClassData parentClass, String name, String nameMapped) {
+ this.parentClass = parentClass;
+ this.name = name;
+ this.nameMapped = nameMapped;
+ }
+}
diff --git a/src/main/java/ftbsc/lll/mapper/tools/data/MethodData.java b/src/main/java/ftbsc/lll/mapper/tools/data/MethodData.java
new file mode 100644
index 0000000..27c418c
--- /dev/null
+++ b/src/main/java/ftbsc/lll/mapper/tools/data/MethodData.java
@@ -0,0 +1,35 @@
+package ftbsc.lll.mapper.tools.data;
+
+/**
+ * Container class for method data.
+ */
+public class MethodData {
+
+ /**
+ * The internal name of the parent class.
+ */
+ public final ClassData parentClass;
+
+ /**
+ * The signature of the method.
+ */
+ public final MethodSignature signature;
+
+ /**
+ * The mapped name of the method.
+ */
+ final String nameMapped;
+
+ /**
+ * Constructs a new {@link MethodData}.
+ * @param parentClass the {@link ClassData} representation of the parent class
+ * @param name the method name
+ * @param nameMapped the mapped method name
+ * @param descriptor the method's descriptor
+ */
+ public MethodData(ClassData parentClass, String name, String nameMapped, String descriptor) {
+ this.parentClass = parentClass;
+ this.signature = new MethodSignature(name, descriptor);
+ this.nameMapped = nameMapped;
+ }
+}
diff --git a/src/main/java/ftbsc/lll/mapper/tools/data/MethodSignature.java b/src/main/java/ftbsc/lll/mapper/tools/data/MethodSignature.java
new file mode 100644
index 0000000..08770be
--- /dev/null
+++ b/src/main/java/ftbsc/lll/mapper/tools/data/MethodSignature.java
@@ -0,0 +1,51 @@
+package ftbsc.lll.mapper.tools.data;
+
+import java.util.Objects;
+
+/**
+ * Container class for method signature data.
+ */
+public class MethodSignature {
+ /**
+ * The name of the method.
+ */
+ public final String name;
+
+ /**
+ * The descriptor of the method.
+ */
+ public final String descriptor;
+
+ /**
+ * Constructs a new {@link MethodSignature}. The parameters should be
+ * either plain or obfuscated in the same way;
+ * @param name the method name
+ * @param descriptor the method descriptor
+ */
+ public MethodSignature(String name, String descriptor) {
+ this.name = name;
+ this.descriptor = descriptor;
+ }
+
+ /**
+ * Checks if two {@link MethodSignature}s represent the same method.
+ * @param o the other signature
+ * @return whether they represent the same method
+ */
+ @Override
+ public boolean equals(Object o) {
+ if(this == o) return true;
+ if(o == null || getClass() != o.getClass()) return false;
+ MethodSignature signature = (MethodSignature) o;
+ return Objects.equals(name, signature.name) && Objects.equals(descriptor, signature.descriptor);
+ }
+
+ /**
+ * Calculates a hash based on name and descriptor.
+ * @return the hash code
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, descriptor);
+ }
+}