mirror of
https://github.com/zaaarf/lillero-processor.git
synced 2024-11-22 17:24:51 +01:00
chore: moved methods around, should now be a lot more organised
This commit is contained in:
parent
dca13711d5
commit
2999aef5a5
3 changed files with 276 additions and 247 deletions
|
@ -4,8 +4,6 @@ import com.squareup.javapoet.*;
|
||||||
import ftbsc.lll.IInjector;
|
import ftbsc.lll.IInjector;
|
||||||
import ftbsc.lll.exceptions.AmbiguousDefinitionException;
|
import ftbsc.lll.exceptions.AmbiguousDefinitionException;
|
||||||
import ftbsc.lll.exceptions.InvalidResourceException;
|
import ftbsc.lll.exceptions.InvalidResourceException;
|
||||||
import ftbsc.lll.exceptions.MappingNotFoundException;
|
|
||||||
import ftbsc.lll.exceptions.TargetNotFoundException;
|
|
||||||
import ftbsc.lll.processor.annotations.*;
|
import ftbsc.lll.processor.annotations.*;
|
||||||
import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper;
|
import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper;
|
||||||
import ftbsc.lll.proxies.FieldProxy;
|
import ftbsc.lll.proxies.FieldProxy;
|
||||||
|
@ -21,16 +19,15 @@ import javax.tools.FileObject;
|
||||||
import javax.tools.JavaFileObject;
|
import javax.tools.JavaFileObject;
|
||||||
import javax.tools.StandardLocation;
|
import javax.tools.StandardLocation;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static ftbsc.lll.processor.tools.ASTUtils.*;
|
import static ftbsc.lll.processor.tools.ASTUtils.*;
|
||||||
import static ftbsc.lll.processor.tools.ASTUtils.getClassFullyQualifiedName;
|
import static ftbsc.lll.processor.tools.ASTUtils.getClassFullyQualifiedName;
|
||||||
|
import static ftbsc.lll.processor.tools.JavaPoetUtils.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The actual annotation processor behind the magic.
|
* The actual annotation processor behind the magic.
|
||||||
|
@ -59,6 +56,7 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
* @param processingEnv environment to access facilities the tool framework
|
* @param processingEnv environment to access facilities the tool framework
|
||||||
* provides to the processor
|
* provides to the processor
|
||||||
* @throws IllegalStateException if this method is called more than once.
|
* @throws IllegalStateException if this method is called more than once.
|
||||||
|
* @since 0.3.0
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public synchronized void init(ProcessingEnvironment processingEnv) {
|
public synchronized void init(ProcessingEnvironment processingEnv) {
|
||||||
|
@ -147,153 +145,6 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the class name and maps it to the correct format.
|
|
||||||
* @param name the fully qualified name of the class to convert
|
|
||||||
* @param mapper the {@link ObfuscationMapper} to use, may be null
|
|
||||||
* @return the fully qualified class name
|
|
||||||
* @since 0.3.0
|
|
||||||
*/
|
|
||||||
private static String findClassName(String name, ObfuscationMapper mapper) {
|
|
||||||
try {
|
|
||||||
return mapper == null ? name : mapper.obfuscateClass(name).replace('/', '.');
|
|
||||||
} catch(MappingNotFoundException e) {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the class name and maps it to the correct format.
|
|
||||||
*
|
|
||||||
* @param patchAnn the {@link Patch} annotation containing target class info
|
|
||||||
* @param finderAnn an annotation containing metadata to fall back on, may be null
|
|
||||||
* @param parentFun the function to get the parent from the finderAnn
|
|
||||||
* @return the fully qualified class name
|
|
||||||
* @since 0.3.0
|
|
||||||
*/
|
|
||||||
private static <T extends Annotation> String findClassName(Patch patchAnn, T finderAnn, Function<T, Class<?>> parentFun) {
|
|
||||||
String fullyQualifiedName =
|
|
||||||
finderAnn == null || parentFun.apply(finderAnn) == Object.class
|
|
||||||
? getClassFullyQualifiedName(patchAnn, Patch::value)
|
|
||||||
: getClassFullyQualifiedName(finderAnn, parentFun);
|
|
||||||
return findClassName(fullyQualifiedName, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the member name and maps it to the correct format.
|
|
||||||
* @param parentFQN the already mapped FQN of the parent class
|
|
||||||
* @param memberName the name of the member
|
|
||||||
* @param mapper the {@link ObfuscationMapper} to use, may be null
|
|
||||||
* @return the internal class name
|
|
||||||
* @since 0.3.0
|
|
||||||
*/
|
|
||||||
private static String findMemberName(String parentFQN, String memberName, String methodDescriptor, ObfuscationMapper mapper) {
|
|
||||||
try {
|
|
||||||
return mapper == null ? memberName : mapper.obfuscateMember(parentFQN, memberName, methodDescriptor);
|
|
||||||
} catch(MappingNotFoundException e) {
|
|
||||||
return memberName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds a method given name, container and descriptor.
|
|
||||||
* @param parentFQN the fully qualified name of the parent class of the method
|
|
||||||
* @param name the name to search for
|
|
||||||
* @param descr the descriptor to search for
|
|
||||||
* @param strict whether the search should be strict (see {@link Target#strict()} for more info)
|
|
||||||
* @return the desired method, if it exists
|
|
||||||
* @throws AmbiguousDefinitionException if it finds more than one candidate
|
|
||||||
* @throws TargetNotFoundException if it finds no valid candidate
|
|
||||||
* @since 0.3.0
|
|
||||||
*/
|
|
||||||
private ExecutableElement findMethod(String parentFQN, String name, String descr, boolean strict) {
|
|
||||||
TypeElement parent = processingEnv.getElementUtils().getTypeElement(parentFQN);
|
|
||||||
if(parent == null)
|
|
||||||
throw new AmbiguousDefinitionException(String.format("Could not find parent class %s!", parentFQN));
|
|
||||||
|
|
||||||
//try to find by name
|
|
||||||
List<ExecutableElement> candidates = parent.getEnclosedElements()
|
|
||||||
.stream()
|
|
||||||
.filter(e -> e instanceof ExecutableElement)
|
|
||||||
.map(e -> (ExecutableElement) e)
|
|
||||||
.filter(e -> e.getSimpleName().contentEquals(name))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
if(candidates.size() == 0)
|
|
||||||
throw new TargetNotFoundException(String.format("%s %s", name, descr));
|
|
||||||
if(candidates.size() == 1 && !strict)
|
|
||||||
return candidates.get(0);
|
|
||||||
if(descr == null) {
|
|
||||||
throw new AmbiguousDefinitionException(
|
|
||||||
String.format("Found %d methods named %s in class %s!", candidates.size(), name, parentFQN)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
candidates = candidates.stream()
|
|
||||||
.filter(strict
|
|
||||||
? c -> descr.equals(descriptorFromExecutableElement(c))
|
|
||||||
: c -> descr.split("\\)")[0].equalsIgnoreCase(descriptorFromExecutableElement(c).split("\\)")[0])
|
|
||||||
).collect(Collectors.toList());
|
|
||||||
if(candidates.size() == 0)
|
|
||||||
throw new TargetNotFoundException(String.format("%s %s", name, descr));
|
|
||||||
if(candidates.size() > 1)
|
|
||||||
throw new AmbiguousDefinitionException(
|
|
||||||
String.format("Found %d methods named %s in class %s!", candidates.size(), name, parentFQN)
|
|
||||||
);
|
|
||||||
return candidates.get(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the real class member (field or method) corresponding to a stub annotated with
|
|
||||||
* {@link Target} or {@link FindMethod} or {@link FindField}.
|
|
||||||
* @param stub the {@link ExecutableElement} for the stub
|
|
||||||
* @return the {@link Element} corresponding to the method or field
|
|
||||||
* @throws AmbiguousDefinitionException if it finds more than one candidate
|
|
||||||
* @throws TargetNotFoundException if it finds no valid candidate
|
|
||||||
* @since 0.3.0
|
|
||||||
*/
|
|
||||||
private Element findMemberFromStub(ExecutableElement stub) {
|
|
||||||
//the parent always has a @Patch annotation
|
|
||||||
Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class);
|
|
||||||
//there should ever only be one of these
|
|
||||||
Target targetAnn = stub.getAnnotation(Target.class); //if this is null strict mode is always disabled
|
|
||||||
FindMethod findMethodAnn = stub.getAnnotation(FindMethod.class); //this may be null, it means no fallback info
|
|
||||||
FindField findFieldAnn = stub.getAnnotation(FindField.class);
|
|
||||||
String parentFQN, memberName;
|
|
||||||
if(findFieldAnn == null) { //methods
|
|
||||||
parentFQN = findClassName(patchAnn, findMethodAnn, FindMethod::parent);
|
|
||||||
String methodDescriptor =
|
|
||||||
findMethodAnn != null
|
|
||||||
? methodDescriptorFromParams(findMethodAnn, FindMethod::params, processingEnv.getElementUtils())
|
|
||||||
: descriptorFromExecutableElement(stub);
|
|
||||||
memberName =
|
|
||||||
findMethodAnn != null && !findMethodAnn.name().equals("")
|
|
||||||
? findMethodAnn.name()
|
|
||||||
: stub.getSimpleName().toString();
|
|
||||||
return findMethod(
|
|
||||||
parentFQN,
|
|
||||||
memberName,
|
|
||||||
methodDescriptor,
|
|
||||||
targetAnn != null && targetAnn.strict()
|
|
||||||
);
|
|
||||||
} else { //fields
|
|
||||||
parentFQN = findClassName(patchAnn, findFieldAnn, FindField::parent);
|
|
||||||
memberName = findFieldAnn.name().equals("")
|
|
||||||
? stub.getSimpleName().toString()
|
|
||||||
: findFieldAnn.name();
|
|
||||||
TypeElement parent = processingEnv.getElementUtils().getTypeElement(parentFQN);
|
|
||||||
List<VariableElement> candidates =
|
|
||||||
parent.getEnclosedElements()
|
|
||||||
.stream()
|
|
||||||
.filter(f -> f instanceof VariableElement)
|
|
||||||
.filter(f -> f.getSimpleName().contentEquals(memberName))
|
|
||||||
.map(f -> (VariableElement) f)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
if(candidates.size() == 0)
|
|
||||||
throw new TargetNotFoundException(stub.getSimpleName().toString());
|
|
||||||
else return candidates.get(0); //there can only ever be one
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the Injector(s) contained in the given class.
|
* Generates the Injector(s) contained in the given class.
|
||||||
* Basically implements the {@link IInjector} interface for you.
|
* Basically implements the {@link IInjector} interface for you.
|
||||||
|
@ -448,21 +299,6 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a {@link MethodSpec} for a public method whose body simply returns a {@link String}.
|
|
||||||
* @param name the name of the method
|
|
||||||
* @param returnString the {@link String} to return
|
|
||||||
* @return the built {@link MethodSpec}
|
|
||||||
*/
|
|
||||||
private static MethodSpec buildStringReturnMethod(String name, String returnString) {
|
|
||||||
return MethodSpec.methodBuilder(name)
|
|
||||||
.addModifiers(Modifier.PUBLIC)
|
|
||||||
.addAnnotation(Override.class)
|
|
||||||
.returns(String.class)
|
|
||||||
.addStatement("return $S", returnString)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds any method annotated with {@link FindMethod} or {@link FindField} within the given
|
* Finds any method annotated with {@link FindMethod} or {@link FindField} within the given
|
||||||
* class, and builds the {@link MethodSpec} necessary for building it.
|
* class, and builds the {@link MethodSpec} necessary for building it.
|
||||||
|
@ -477,7 +313,7 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
.filter(m -> !m.getModifiers().contains(Modifier.STATIC)) //skip static stuff as we can't override it
|
.filter(m -> !m.getModifiers().contains(Modifier.STATIC)) //skip static stuff as we can't override it
|
||||||
.filter(m -> !m.getModifiers().contains(Modifier.FINAL)) //in case someone is trying to be funny
|
.filter(m -> !m.getModifiers().contains(Modifier.FINAL)) //in case someone is trying to be funny
|
||||||
.forEach(m -> {
|
.forEach(m -> {
|
||||||
ExecutableElement targetMethod = (ExecutableElement) findMemberFromStub(m);
|
ExecutableElement targetMethod = (ExecutableElement) findMemberFromStub(m, processingEnv);
|
||||||
MethodSpec.Builder b = MethodSpec.overriding(m);
|
MethodSpec.Builder b = MethodSpec.overriding(m);
|
||||||
|
|
||||||
String targetParentFQN = findClassName(((TypeElement) targetMethod.getEnclosingElement()).getQualifiedName().toString(), mapper);
|
String targetParentFQN = findClassName(((TypeElement) targetMethod.getEnclosingElement()).getQualifiedName().toString(), mapper);
|
||||||
|
@ -506,7 +342,7 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
.filter(m -> !m.getModifiers().contains(Modifier.STATIC))
|
.filter(m -> !m.getModifiers().contains(Modifier.STATIC))
|
||||||
.filter(m -> !m.getModifiers().contains(Modifier.FINAL))
|
.filter(m -> !m.getModifiers().contains(Modifier.FINAL))
|
||||||
.forEach(m -> {
|
.forEach(m -> {
|
||||||
VariableElement targetField = (VariableElement) findMemberFromStub(m);
|
VariableElement targetField = (VariableElement) findMemberFromStub(m, processingEnv);
|
||||||
MethodSpec.Builder b = MethodSpec.overriding(m);
|
MethodSpec.Builder b = MethodSpec.overriding(m);
|
||||||
|
|
||||||
String targetParentFQN = findClassName(((TypeElement) targetField.getEnclosingElement()).getQualifiedName().toString(), mapper);
|
String targetParentFQN = findClassName(((TypeElement) targetField.getEnclosingElement()).getQualifiedName().toString(), mapper);
|
||||||
|
@ -551,7 +387,7 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
* Container for information about a class that is to be generated.
|
* Container for information about a class that is to be generated.
|
||||||
* Only used internally.
|
* Only used internally.
|
||||||
*/
|
*/
|
||||||
public class InjectorInfo {
|
private class InjectorInfo {
|
||||||
/**
|
/**
|
||||||
* The {@link ExecutableElement} corresponding to the injector method.
|
* The {@link ExecutableElement} corresponding to the injector method.
|
||||||
*/
|
*/
|
||||||
|
@ -575,7 +411,7 @@ public class LilleroProcessor extends AbstractProcessor {
|
||||||
public InjectorInfo(ExecutableElement injector, ExecutableElement targetStub) {
|
public InjectorInfo(ExecutableElement injector, ExecutableElement targetStub) {
|
||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
this.targetStub = targetStub;
|
this.targetStub = targetStub;
|
||||||
this.target = (ExecutableElement) findMemberFromStub(targetStub);
|
this.target = (ExecutableElement) findMemberFromStub(targetStub, processingEnv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,12 +1,17 @@
|
||||||
package ftbsc.lll.processor.tools;
|
package ftbsc.lll.processor.tools;
|
||||||
|
|
||||||
import com.squareup.javapoet.*;
|
import com.squareup.javapoet.*;
|
||||||
import ftbsc.lll.tools.DescriptorBuilder;
|
import ftbsc.lll.exceptions.AmbiguousDefinitionException;
|
||||||
|
import ftbsc.lll.exceptions.MappingNotFoundException;
|
||||||
|
import ftbsc.lll.exceptions.TargetNotFoundException;
|
||||||
|
import ftbsc.lll.processor.annotations.FindField;
|
||||||
|
import ftbsc.lll.processor.annotations.FindMethod;
|
||||||
|
import ftbsc.lll.processor.annotations.Patch;
|
||||||
|
import ftbsc.lll.processor.annotations.Target;
|
||||||
|
import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper;
|
||||||
|
|
||||||
import javax.annotation.processing.ProcessingEnvironment;
|
import javax.annotation.processing.ProcessingEnvironment;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.*;
|
||||||
import javax.lang.model.element.Modifier;
|
|
||||||
import javax.lang.model.element.TypeElement;
|
|
||||||
import javax.lang.model.type.MirroredTypeException;
|
import javax.lang.model.type.MirroredTypeException;
|
||||||
import javax.lang.model.type.MirroredTypesException;
|
import javax.lang.model.type.MirroredTypesException;
|
||||||
import javax.lang.model.type.TypeMirror;
|
import javax.lang.model.type.TypeMirror;
|
||||||
|
@ -18,8 +23,11 @@ import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static ftbsc.lll.processor.tools.JavaPoetUtils.descriptorFromExecutableElement;
|
||||||
|
import static ftbsc.lll.processor.tools.JavaPoetUtils.methodDescriptorFromParams;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collection of static utils that didn't really fit into the main class.
|
* Collection of AST-related static utils that didn't really fit into the main class.
|
||||||
*/
|
*/
|
||||||
public class ASTUtils {
|
public class ASTUtils {
|
||||||
/**
|
/**
|
||||||
|
@ -38,67 +46,6 @@ public class ASTUtils {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a type descriptor from the given {@link TypeName}.
|
|
||||||
* @param type the {@link TypeName} representing the desired type
|
|
||||||
* @return a {@link String} containing the relevant descriptor
|
|
||||||
*/
|
|
||||||
public static String descriptorFromType(TypeName type) {
|
|
||||||
StringBuilder desc = new StringBuilder();
|
|
||||||
//add array brackets
|
|
||||||
while(type instanceof ArrayTypeName) {
|
|
||||||
desc.append("[");
|
|
||||||
type = ((ArrayTypeName) type).componentType;
|
|
||||||
}
|
|
||||||
if(type instanceof ClassName || type instanceof ParameterizedTypeName) {
|
|
||||||
ClassName var = type instanceof ParameterizedTypeName ? ((ParameterizedTypeName) type).rawType : (ClassName) type;
|
|
||||||
desc.append(DescriptorBuilder.nameToDescriptor(var.canonicalName(), 0));
|
|
||||||
} else {
|
|
||||||
if(TypeName.BOOLEAN.equals(type))
|
|
||||||
desc.append("Z");
|
|
||||||
else if(TypeName.CHAR.equals(type))
|
|
||||||
desc.append("C");
|
|
||||||
else if(TypeName.BYTE.equals(type))
|
|
||||||
desc.append("B");
|
|
||||||
else if(TypeName.SHORT.equals(type))
|
|
||||||
desc.append("S");
|
|
||||||
else if(TypeName.INT.equals(type))
|
|
||||||
desc.append("I");
|
|
||||||
else if(TypeName.FLOAT.equals(type))
|
|
||||||
desc.append("F");
|
|
||||||
else if(TypeName.LONG.equals(type))
|
|
||||||
desc.append("J");
|
|
||||||
else if(TypeName.DOUBLE.equals(type))
|
|
||||||
desc.append("D");
|
|
||||||
else if(TypeName.VOID.equals(type))
|
|
||||||
desc.append("V");
|
|
||||||
}
|
|
||||||
return desc.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a type descriptor from the given {@link TypeMirror}.
|
|
||||||
* @param t the {@link TypeMirror} representing the desired type
|
|
||||||
* @return a {@link String} containing the relevant descriptor
|
|
||||||
*/
|
|
||||||
public static String descriptorFromType(TypeMirror t) {
|
|
||||||
return descriptorFromType(TypeName.get(t));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a method descriptor from the given {@link ExecutableElement}.
|
|
||||||
* @param m the {@link ExecutableElement} for the method
|
|
||||||
* @return a {@link String} containing the relevant descriptor
|
|
||||||
*/
|
|
||||||
public static String descriptorFromExecutableElement(ExecutableElement m) {
|
|
||||||
StringBuilder methodSignature = new StringBuilder();
|
|
||||||
methodSignature.append("(");
|
|
||||||
m.getParameters().forEach(p -> methodSignature.append(descriptorFromType(p.asType())));
|
|
||||||
methodSignature.append(")");
|
|
||||||
methodSignature.append(descriptorFromType(m.getReturnType()));
|
|
||||||
return methodSignature.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps a {@link javax.lang.model.element.Modifier} to its reflective
|
* Maps a {@link javax.lang.model.element.Modifier} to its reflective
|
||||||
* {@link java.lang.reflect.Modifier} equivalent.
|
* {@link java.lang.reflect.Modifier} equivalent.
|
||||||
|
@ -172,18 +119,154 @@ public class ASTUtils {
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a (partial, not including the return type) method descriptor from its parameters
|
* Finds the class name and maps it to the correct format.
|
||||||
* @param ann the annotation containing the class
|
* @param name the fully qualified name of the class to convert
|
||||||
* @param fun the annotation function returning the class
|
* @param mapper the {@link ObfuscationMapper} to use, may be null
|
||||||
* @return the method descriptor
|
* @return the fully qualified class name
|
||||||
|
* @since 0.3.0
|
||||||
*/
|
*/
|
||||||
public static <T extends Annotation> String methodDescriptorFromParams(T ann, Function<T, Class<?>[]> fun, Elements elementUtils) {
|
public static String findClassName(String name, ObfuscationMapper mapper) {
|
||||||
List<TypeMirror> mirrors = classArrayFromAnnotation(ann, fun, elementUtils);
|
try {
|
||||||
StringBuilder sb = new StringBuilder("(");
|
return mapper == null ? name : mapper.obfuscateClass(name).replace('/', '.');
|
||||||
for(TypeMirror t : mirrors)
|
} catch(MappingNotFoundException e) {
|
||||||
sb.append(descriptorFromType(t));
|
return name;
|
||||||
sb.append(")");
|
}
|
||||||
return sb.toString();
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the class name and maps it to the correct format.
|
||||||
|
*
|
||||||
|
* @param patchAnn the {@link Patch} annotation containing target class info
|
||||||
|
* @param finderAnn an annotation containing metadata to fall back on, may be null
|
||||||
|
* @param parentFun the function to get the parent from the finderAnn
|
||||||
|
* @return the fully qualified class name
|
||||||
|
* @since 0.3.0
|
||||||
|
*/
|
||||||
|
private static <T extends Annotation> String findClassName(Patch patchAnn, T finderAnn, Function<T, Class<?>> parentFun) {
|
||||||
|
String fullyQualifiedName =
|
||||||
|
finderAnn == null || parentFun.apply(finderAnn) == Object.class
|
||||||
|
? getClassFullyQualifiedName(patchAnn, Patch::value)
|
||||||
|
: getClassFullyQualifiedName(finderAnn, parentFun);
|
||||||
|
return findClassName(fullyQualifiedName, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the member name and maps it to the correct format.
|
||||||
|
* @param parentFQN the already mapped FQN of the parent class
|
||||||
|
* @param memberName the name of the member
|
||||||
|
* @param mapper the {@link ObfuscationMapper} to use, may be null
|
||||||
|
* @return the internal class name
|
||||||
|
* @since 0.3.0
|
||||||
|
*/
|
||||||
|
public static String findMemberName(String parentFQN, String memberName, String methodDescriptor, ObfuscationMapper mapper) {
|
||||||
|
try {
|
||||||
|
return mapper == null ? memberName : mapper.obfuscateMember(parentFQN, memberName, methodDescriptor);
|
||||||
|
} catch(MappingNotFoundException e) {
|
||||||
|
return memberName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a method given name, container and descriptor.
|
||||||
|
* @param parentFQN the fully qualified name of the parent class of the method
|
||||||
|
* @param name the name to search for
|
||||||
|
* @param descr the descriptor to search for
|
||||||
|
* @param strict whether the search should be strict (see {@link Target#strict()} for more info)
|
||||||
|
* @param env the {@link ProcessingEnvironment} to perform the operation in
|
||||||
|
* @return the desired method, if it exists
|
||||||
|
* @throws AmbiguousDefinitionException if it finds more than one candidate
|
||||||
|
* @throws TargetNotFoundException if it finds no valid candidate
|
||||||
|
* @since 0.3.0
|
||||||
|
*/
|
||||||
|
private static ExecutableElement findMethod(String parentFQN, String name, String descr, boolean strict, ProcessingEnvironment env) {
|
||||||
|
TypeElement parent = env.getElementUtils().getTypeElement(parentFQN);
|
||||||
|
if(parent == null)
|
||||||
|
throw new AmbiguousDefinitionException(String.format("Could not find parent class %s!", parentFQN));
|
||||||
|
|
||||||
|
//try to find by name
|
||||||
|
List<ExecutableElement> candidates = parent.getEnclosedElements()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> e instanceof ExecutableElement)
|
||||||
|
.map(e -> (ExecutableElement) e)
|
||||||
|
.filter(e -> e.getSimpleName().contentEquals(name))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if(candidates.size() == 0)
|
||||||
|
throw new TargetNotFoundException(String.format("%s %s", name, descr));
|
||||||
|
if(candidates.size() == 1 && !strict)
|
||||||
|
return candidates.get(0);
|
||||||
|
if(descr == null) {
|
||||||
|
throw new AmbiguousDefinitionException(
|
||||||
|
String.format("Found %d methods named %s in class %s!", candidates.size(), name, parentFQN)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
candidates = candidates.stream()
|
||||||
|
.filter(strict
|
||||||
|
? c -> descr.equals(descriptorFromExecutableElement(c))
|
||||||
|
: c -> descr.split("\\)")[0].equalsIgnoreCase(descriptorFromExecutableElement(c).split("\\)")[0])
|
||||||
|
).collect(Collectors.toList());
|
||||||
|
if(candidates.size() == 0)
|
||||||
|
throw new TargetNotFoundException(String.format("%s %s", name, descr));
|
||||||
|
if(candidates.size() > 1)
|
||||||
|
throw new AmbiguousDefinitionException(
|
||||||
|
String.format("Found %d methods named %s in class %s!", candidates.size(), name, parentFQN)
|
||||||
|
);
|
||||||
|
return candidates.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the real class member (field or method) corresponding to a stub annotated with
|
||||||
|
* {@link Target} or {@link FindMethod} or {@link FindField}.
|
||||||
|
* @param stub the {@link ExecutableElement} for the stub
|
||||||
|
* @param env the {@link ProcessingEnvironment} to perform the operation in
|
||||||
|
* @return the {@link Element} corresponding to the method or field
|
||||||
|
* @throws AmbiguousDefinitionException if it finds more than one candidate
|
||||||
|
* @throws TargetNotFoundException if it finds no valid candidate
|
||||||
|
* @since 0.3.0
|
||||||
|
*/
|
||||||
|
public static Element findMemberFromStub(ExecutableElement stub, ProcessingEnvironment env) {
|
||||||
|
//the parent always has a @Patch annotation
|
||||||
|
Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class);
|
||||||
|
//there should ever only be one of these
|
||||||
|
Target targetAnn = stub.getAnnotation(Target.class); //if this is null strict mode is always disabled
|
||||||
|
FindMethod findMethodAnn = stub.getAnnotation(FindMethod.class); //this may be null, it means no fallback info
|
||||||
|
FindField findFieldAnn = stub.getAnnotation(FindField.class);
|
||||||
|
String parentFQN, memberName;
|
||||||
|
if(findFieldAnn == null) { //methods
|
||||||
|
parentFQN = findClassName(patchAnn, findMethodAnn, FindMethod::parent);
|
||||||
|
String methodDescriptor =
|
||||||
|
findMethodAnn != null
|
||||||
|
? methodDescriptorFromParams(findMethodAnn, FindMethod::params, env.getElementUtils())
|
||||||
|
: descriptorFromExecutableElement(stub);
|
||||||
|
memberName =
|
||||||
|
findMethodAnn != null && !findMethodAnn.name().equals("")
|
||||||
|
? findMethodAnn.name()
|
||||||
|
: stub.getSimpleName().toString();
|
||||||
|
return findMethod(
|
||||||
|
parentFQN,
|
||||||
|
memberName,
|
||||||
|
methodDescriptor,
|
||||||
|
targetAnn != null && targetAnn.strict(),
|
||||||
|
env
|
||||||
|
);
|
||||||
|
} else { //fields
|
||||||
|
parentFQN = findClassName(patchAnn, findFieldAnn, FindField::parent);
|
||||||
|
memberName = findFieldAnn.name().equals("")
|
||||||
|
? stub.getSimpleName().toString()
|
||||||
|
: findFieldAnn.name();
|
||||||
|
TypeElement parent = env.getElementUtils().getTypeElement(parentFQN);
|
||||||
|
List<VariableElement> candidates =
|
||||||
|
parent.getEnclosedElements()
|
||||||
|
.stream()
|
||||||
|
.filter(f -> f instanceof VariableElement)
|
||||||
|
.filter(f -> f.getSimpleName().contentEquals(memberName))
|
||||||
|
.map(f -> (VariableElement) f)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if(candidates.size() == 0)
|
||||||
|
throw new TargetNotFoundException(stub.getSimpleName().toString());
|
||||||
|
else return candidates.get(0); //there can only ever be one
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
110
src/main/java/ftbsc/lll/processor/tools/JavaPoetUtils.java
Normal file
110
src/main/java/ftbsc/lll/processor/tools/JavaPoetUtils.java
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
package ftbsc.lll.processor.tools;
|
||||||
|
|
||||||
|
import com.squareup.javapoet.*;
|
||||||
|
import ftbsc.lll.tools.DescriptorBuilder;
|
||||||
|
|
||||||
|
import javax.lang.model.element.ExecutableElement;
|
||||||
|
import javax.lang.model.element.Modifier;
|
||||||
|
import javax.lang.model.type.TypeMirror;
|
||||||
|
import javax.lang.model.util.Elements;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import static ftbsc.lll.processor.tools.ASTUtils.classArrayFromAnnotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of static utils that rely on JavaPoet to function.
|
||||||
|
*/
|
||||||
|
public class JavaPoetUtils {
|
||||||
|
/**
|
||||||
|
* Builds a {@link MethodSpec} for a public method whose body simply returns a {@link String}.
|
||||||
|
* @param name the name of the method
|
||||||
|
* @param returnString the {@link String} to return
|
||||||
|
* @return the built {@link MethodSpec}
|
||||||
|
*/
|
||||||
|
public static MethodSpec buildStringReturnMethod(String name, String returnString) {
|
||||||
|
return MethodSpec.methodBuilder(name)
|
||||||
|
.addModifiers(Modifier.PUBLIC)
|
||||||
|
.addAnnotation(Override.class)
|
||||||
|
.returns(String.class)
|
||||||
|
.addStatement("return $S", returnString)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a type descriptor from the given {@link TypeName}.
|
||||||
|
* @param type the {@link TypeName} representing the desired type
|
||||||
|
* @return a {@link String} containing the relevant descriptor
|
||||||
|
*/
|
||||||
|
public static String descriptorFromType(TypeName type) {
|
||||||
|
StringBuilder desc = new StringBuilder();
|
||||||
|
//add array brackets
|
||||||
|
while(type instanceof ArrayTypeName) {
|
||||||
|
desc.append("[");
|
||||||
|
type = ((ArrayTypeName) type).componentType;
|
||||||
|
}
|
||||||
|
if(type instanceof ClassName || type instanceof ParameterizedTypeName) {
|
||||||
|
ClassName var = type instanceof ParameterizedTypeName ? ((ParameterizedTypeName) type).rawType : (ClassName) type;
|
||||||
|
desc.append(DescriptorBuilder.nameToDescriptor(var.canonicalName(), 0));
|
||||||
|
} else {
|
||||||
|
if(TypeName.BOOLEAN.equals(type))
|
||||||
|
desc.append("Z");
|
||||||
|
else if(TypeName.CHAR.equals(type))
|
||||||
|
desc.append("C");
|
||||||
|
else if(TypeName.BYTE.equals(type))
|
||||||
|
desc.append("B");
|
||||||
|
else if(TypeName.SHORT.equals(type))
|
||||||
|
desc.append("S");
|
||||||
|
else if(TypeName.INT.equals(type))
|
||||||
|
desc.append("I");
|
||||||
|
else if(TypeName.FLOAT.equals(type))
|
||||||
|
desc.append("F");
|
||||||
|
else if(TypeName.LONG.equals(type))
|
||||||
|
desc.append("J");
|
||||||
|
else if(TypeName.DOUBLE.equals(type))
|
||||||
|
desc.append("D");
|
||||||
|
else if(TypeName.VOID.equals(type))
|
||||||
|
desc.append("V");
|
||||||
|
}
|
||||||
|
return desc.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a type descriptor from the given {@link TypeMirror}.
|
||||||
|
* @param t the {@link TypeMirror} representing the desired type
|
||||||
|
* @return a {@link String} containing the relevant descriptor
|
||||||
|
*/
|
||||||
|
public static String descriptorFromType(TypeMirror t) {
|
||||||
|
return descriptorFromType(TypeName.get(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a method descriptor from the given {@link ExecutableElement}.
|
||||||
|
* @param m the {@link ExecutableElement} for the method
|
||||||
|
* @return a {@link String} containing the relevant descriptor
|
||||||
|
*/
|
||||||
|
public static String descriptorFromExecutableElement(ExecutableElement m) {
|
||||||
|
StringBuilder methodSignature = new StringBuilder();
|
||||||
|
methodSignature.append("(");
|
||||||
|
m.getParameters().forEach(p -> methodSignature.append(descriptorFromType(p.asType())));
|
||||||
|
methodSignature.append(")");
|
||||||
|
methodSignature.append(descriptorFromType(m.getReturnType()));
|
||||||
|
return methodSignature.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a (partial, not including the return type) method descriptor from its parameters
|
||||||
|
* @param ann the annotation containing the class
|
||||||
|
* @param fun the annotation function returning the class
|
||||||
|
* @return the method descriptor
|
||||||
|
*/
|
||||||
|
public static <T extends Annotation> String methodDescriptorFromParams(T ann, Function<T, Class<?>[]> fun, Elements elementUtils) {
|
||||||
|
List<TypeMirror> mirrors = classArrayFromAnnotation(ann, fun, elementUtils);
|
||||||
|
StringBuilder sb = new StringBuilder("(");
|
||||||
|
for(TypeMirror t : mirrors)
|
||||||
|
sb.append(descriptorFromType(t));
|
||||||
|
sb.append(")");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue