aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/ftbsc/lll/processor/containers/ClassContainer.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/ftbsc/lll/processor/containers/ClassContainer.java')
-rw-r--r--src/main/java/ftbsc/lll/processor/containers/ClassContainer.java130
1 files changed, 130 insertions, 0 deletions
diff --git a/src/main/java/ftbsc/lll/processor/containers/ClassContainer.java b/src/main/java/ftbsc/lll/processor/containers/ClassContainer.java
new file mode 100644
index 0000000..297349a
--- /dev/null
+++ b/src/main/java/ftbsc/lll/processor/containers/ClassContainer.java
@@ -0,0 +1,130 @@
+package ftbsc.lll.processor.containers;
+
+import ftbsc.lll.exceptions.TargetNotFoundException;
+import ftbsc.lll.mapper.tools.data.ClassData;
+import ftbsc.lll.processor.annotations.Find;
+import ftbsc.lll.processor.annotations.Patch;
+import ftbsc.lll.processor.ProcessorOptions;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+import java.lang.annotation.Annotation;
+import java.util.function.Function;
+
+import static ftbsc.lll.processor.utils.ASTUtils.*;
+
+/**
+ * Container for information about a class.
+ * Used internally for efficiency reasons.
+ * @since 0.5.0
+ */
+public class ClassContainer {
+ /**
+ * The {@link ClassData} for the class represented by this container.
+ */
+ public final ClassData data;
+
+ /**
+ * The {@link Element} corresponding to the class.
+ * May only be null intentionally i.e. when the associated element is
+ * an anonymous class or a child of an anonymous class.
+ */
+ public final Element elem;
+
+ /**
+ * Private constructor, called from {@link #from(Annotation, Function, String, ProcessorOptions)}.
+ * @param fqn the fully-qualified name of the target class
+ * @param innerNames an array of Strings containing the path to the inner class, may be null
+ * @param options the {@link ProcessorOptions} to be used
+ */
+ private ClassContainer(String fqn, String[] innerNames, ProcessorOptions options) {
+ //find and validate
+ Element elem = options.env.getElementUtils().getTypeElement(fqn);
+
+ if(elem == null)
+ throw new TargetNotFoundException("class", fqn);
+
+ StringBuilder fqnBuilder = new StringBuilder(
+ internalNameFromType(elem.asType(), options.env).replace('/', '.')
+ );
+
+ if(innerNames != null) {
+ for(String inner : innerNames) {
+ if(inner == null) continue;
+ fqnBuilder.append("$").append(inner);
+ try {
+ int anonClassCounter = Integer.parseInt(inner);
+ //anonymous classes cannot be validated!
+ if(options.anonymousClassWarning)
+ options.env.getMessager().printMessage(
+ Diagnostic.Kind.WARNING,
+ String.format(
+ "Anonymous classes cannot be verified by the processor. The existence of %s$%s is not guaranteed!",
+ fqnBuilder, anonClassCounter
+ )
+ );
+ elem = null;
+ break;
+ } catch(NumberFormatException exc) {
+ elem = elem
+ .getEnclosedElements()
+ .stream()
+ .filter(e -> e instanceof TypeElement)
+ .filter(e -> e.getSimpleName().contentEquals(inner))
+ .findFirst()
+ .orElse(null);
+ }
+ if(elem == null)
+ throw new TargetNotFoundException("class", inner);
+ }
+ }
+ this.data = getClassData(fqnBuilder.toString(), options.mapper);
+ this.elem = elem;
+ }
+
+ /**
+ * Safely extracts a {@link Class} from an annotation and gets its fully qualified name.
+ * @param ann the annotation containing the class
+ * @param classFunction the annotation function returning the class
+ * @param innerName a string containing the inner class name or nothing
+ * @param options the {@link ProcessorOptions} to be used
+ * @param <T> the type of the annotation carrying the information
+ * @return the fully qualified name of the given class
+ * @since 0.5.0
+ */
+ public static <T extends Annotation> ClassContainer from(T ann, Function<T, Class<?>> classFunction, String innerName, ProcessorOptions options) {
+ String fqn;
+ String[] inner;
+ fqn = getTypeFromAnnotation(ann, classFunction, options.env).toString();
+ inner = innerName.equals("") ? null : innerName.split("//$");
+ return new ClassContainer(fqn, inner, options);
+ }
+
+ /**
+ * Safely extracts a {@link Class} from an annotation and gets its fully qualified name.
+ * @param cl the {@link TypeElement} representing the class
+ * @param options the {@link ProcessorOptions} to be used
+ * @return the fully qualified name of the given class
+ * @since 0.6.0
+ */
+ public static ClassContainer from(TypeElement cl, ProcessorOptions options) {
+ return new ClassContainer(cl.getQualifiedName().toString(), null, options);
+ }
+
+ /**
+ * Finds and builds a {@link ClassContainer} based on information contained
+ * within {@link Patch} or a {@link Find} annotations, else returns a fallback.
+ * @param fallback the {@link ClassContainer} it falls back on
+ * @param p the {@link Patch} annotation to get info from
+ * @param f the {@link Find} annotation to get info from
+ * @param options the {@link ProcessorOptions} to be used
+ * @return the built {@link ClassContainer} or the fallback if not enough information was present
+ * @since 0.5.0
+ */
+ public static ClassContainer findOrFallback(ClassContainer fallback, Patch p, Find f, ProcessorOptions options) {
+ if(f == null) return ClassContainer.from(p, Patch::value, p.innerName(), options);
+ ClassContainer cl = ClassContainer.from(f, Find::value, f.innerName(), options);
+ return cl.data.name.equals("java/lang/Object") ? fallback : cl;
+ }
+}