1

I want to convert

x.foo(a, b);

into

x.foo(a).bar(b);

I can easily match for acme.X foo(acme.A, acme.B), but how do I build a JavaTemplate that can do that replacement for me?

When I run

@Override
protected TreeVisitor<?, ExecutionContext> getVisitor() {
    return new JavaIsoVisitor<>() {

        private final JavaTemplate template = JavaTemplate.builder(this::getCursor,
                "foo(#{any(java.lang.String)}).bar(#{any(java.lang.String)})")
                .build();

        @Override
        public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext executionContext) {
            J.MethodInvocation m = super.visitMethodInvocation(method, executionContext);
            if (....matches(method)) {
                List<Expression> arguments = m.getArguments();
                m = m.withTemplate(template, m.getCoordinates().replace(), arguments.get(0), arguments.get(1));
            }
            return m;
        }
    };
}

I get

foo(a).bar(b);

instead of

x.foo(a).bar(b);
Geoffrey De Smet
  • 26,223
  • 11
  • 73
  • 120
  • 1
    On mobile, so quick hints rather than a full answer.. you'll want to use a JavaVisitor (not a JavaIsoVisitor), with a MethodMatcher to find the old method instances. And then override visitMethod to return the response from a JavaTemplate, with the template string containing the new method invocations. Hope that helps! – Tim Nov 06 '22 at 12:34
  • What's the motivation for using a JavaVisitor instead of a JavaIsoVisitor? – Geoffrey De Smet Nov 07 '22 at 19:59
  • I tried using a JavaVisitor, but I couldn't find a way to call getCoordinates() etc. – Geoffrey De Smet Nov 07 '22 at 20:00
  • 1
    I thought it works work with overriding visitMethod, but then your replacement Java template would be an expression. Now that you're overriding visitExpression then return types of the input and output match up again. That's the distinction between IsoVisitor or non-IsoVisitor: of you're changing the tree element type use the latter. – Tim Nov 08 '22 at 13:14

1 Answers1

1

This worked for me (credit to Patrick Way on slack):

private static final MethodMatcher MATCHER =
        new MethodMatcher("org.optaplanner.core.api.score.stream.ConstraintStream " +
                "penalize(java.lang.String, org.optaplanner.core.api.score.Score)");

@Override
protected TreeVisitor<?, ExecutionContext> getVisitor() {
    return new JavaIsoVisitor<>() {

        private final JavaTemplate template = JavaTemplate.builder(() -> getCursor().getParentOrThrow(),
                "#{any(org.optaplanner.core.api.score.stream.ConstraintStream)}" +
                        ".penalize(#{any(org.optaplanner.core.api.score.Score)})" +
                        ".asConstraint(#{any(java.lang.String)})"
        ).build();

        @Override
        public Expression visitExpression(Expression expression, ExecutionContext executionContext) {
            Expression e = super.visitExpression(expression, executionContext);
            if (MATCHER.matches(e)){
                J.MethodInvocation mi = (J.MethodInvocation) e;
                e = e.withTemplate(template,
                        e.getCoordinates().replace(), mi.getSelect(),
                        mi.getArguments().get(1), mi.getArguments().get(0));
            }
            return e;
        }
    };
}
Geoffrey De Smet
  • 26,223
  • 11
  • 73
  • 120