package xapi.javac.dev.search; import com.sun.source.tree.AnnotationTree; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.tree.LabeledStatementTree; import com.sun.source.tree.MethodInvocationTree; import com.sun.source.tree.MethodTree; import com.sun.source.tree.ModifiersTree; import com.sun.source.tree.Tree; import com.sun.source.tree.Tree.Kind; import com.sun.source.tree.VariableTree; import com.sun.source.util.TreePathScanner; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.util.Name; import xapi.inject.X_Inject; import xapi.javac.dev.api.JavacService; import xapi.javac.dev.model.InjectionBinding; import xapi.source.read.JavaModel.IsNamedType; import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; /** * Searches for calls to GWT.create within a compilation unit * * @author "James X. Nelson (james@wetheinter.net)" * */ public class InjectionTargetSearchVisitor extends TreePathScanner<List<InjectionBinding>, List<InjectionBinding>>{ private final JavacService service; private final List<InjectionBinding> targets; private final CompilationUnitTree cup; private Predicate<VariableTree> injectNulls; public InjectionTargetSearchVisitor(JavacService service, CompilationUnitTree cup) { this.service = service; injectNulls = v->false; this.cup = cup; targets = new ArrayList<>(); } @Override public List<InjectionBinding> visitVariable( VariableTree node, List<InjectionBinding> injectionBindings ) { if (hasInjectAnnotation(node.getModifiers()) ) { // We've found an injection target! Lets store this as a potential binding targets.add(service.createInjectionBinding(node)); } else if (isInitializedToNull(node) && injectNulls.test(node)) { targets.add(service.createInjectionBinding(node)); } return super.visitVariable(node, injectionBindings); } private boolean isInitializedToNull(VariableTree node) { return node.getInitializer() != null && node.getInitializer().getKind() == Kind.NULL_LITERAL; } private boolean isInjectionAnnotation(AnnotationTree anno) { final Tree type = anno.getAnnotationType(); final Name name = TreeInfo.name((JCTree) type); return name.contentEquals("Inject"); // Any annotation named inject is suitable } @Override public List<InjectionBinding> visitLabeledStatement( LabeledStatementTree node, List<InjectionBinding> injectionBindings ) { if (node.getLabel().contentEquals("inject")) { // using an inject label, like so: inject : { // will cause all variables initialized to null to become injected. final Predicate<VariableTree> oldPredicate = injectNulls; try { injectNulls = v->true; return super.visitLabeledStatement(node, injectionBindings); } finally { injectNulls = oldPredicate; } } } return super.visitLabeledStatement(node, injectionBindings); } @Override public List<InjectionBinding> visitMethod( MethodTree node, List<InjectionBinding> injectionBindings ) { if (hasInjectAnnotation(node.getModifiers())) { injectionBindings.add(service.createInjectionBinding(node)); } return super.visitMethod(node, injectionBindings); } private boolean hasInjectAnnotation(ModifiersTree modifiers) { return modifiers.getAnnotations() .stream() .anyMatch(this::isInjectionAnnotation); } @Override public List<InjectionBinding> visitMethodInvocation(MethodInvocationTree node, List<InjectionBinding> list) { // Is this a method invocation to a magic method? // For now, lets just handle X_Inject. if (isXInjectMethodCall(node)) { } return super.visitMethodInvocation(node, list); } private boolean isXInjectMethodCall(MethodInvocationTree node) { final IsNamedType exprName = service.getName(cup, node); if (exprName.typeName().equals(X_Inject.class.getCanonicalName())) { // for now, we are just going to handle X_Inject.instance, X_Inject.singleton and X_Inject.initialize switch (exprName.getName()) { case "inject": case "singleton": case "intialize": return true; } } return false; } }