I know that I was a little late, but today I had to solve the same problem, and I found this question without a real solution or workaround.
However, I managed to "surrogate" the recursion using the following structure:
@Inherited @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface Expression { Node value(); SubExpression[] subExpressions() default {}; } @Retention(RetentionPolicy.RUNTIME) @interface SubExpression { String id(); String operator(); Node[] nodes(); } @Retention(RetentionPolicy.RUNTIME) @interface Node { String subExpression() default ""; String name() default ""; String value() default ""; } @Expression( value = @Node(subExpression = "1"), subExpressions = { @SubExpression(id = "1", operator = "AND", nodes = { @Node(name = "responsible", value = "foo"), @Node(subExpression = "2") }), @SubExpression(id = "2", operator = "OR", nodes = { @Node(name = "status", value = "closed"), @Node(name = "visibility", value = "public") }), }) public class TestAnnotationRecursion { public static void main(String[] args) { Expression expression = TestAnnotationRecursion.class.getAnnotation(Expression.class); Map<String, SubExpression> subExpressionMap = Arrays.stream(expression.subExpressions()) .collect(Collectors.toMap(x -> x.id(), x -> x)); String result = parseNode(expression.value(), subExpressionMap); System.out.println(result); } public static String parseNode(Node node, Map<String, SubExpression> subExpressionMap) { String subExpressionId = node.subExpression(); if(subExpressionId.isEmpty()) { return node.name() + " = '" + node.value() + "'"; } SubExpression subExpression = subExpressionMap.get(subExpressionId); return Arrays.stream(subExpression.nodes()) .map(n -> parseNode(n, subExpressionMap)) .collect(Collectors.joining(" " + subExpression.operator() + " ", "(", ")")); } }
And it is evaluated as follows:
(responsible = 'foo' AND (status = 'closed' OR visibility = 'public'))
Although its readability is doubtful, I think this is the best compromise we can achieve when explicit recursion is not allowed.
source share