我正在尝试在注释中创建一些信息树结构。经过一些尝试和帮助(见Type hierarchy in java annotations)后,我转向了以下模型。
@interface Node {
LogicalExpression logicalExpression() default LogicalExpression.AND;
Attribute[] attributes() default {};
Node[] nodes() default {};
}
这个节点应该允许我定义一级条件树。 logicalExpression中的值定义了子节点(属性和另一个节点)之间的关系。问题是注释不允许递归依赖:
Cycle detected: the annotation type Node cannot contain attributes of the annotation type itself
即使我将一些NodeList注释放入Node并且NodeList包含节点列表,也会再次识别循环依赖。
@interface NodeList {
Node[] nodes();
}
@interface Node {
LogicalExpression logicalExpression() default LogicalExpression.AND;
Attribute[] attributes() default {};
NodeList nodes() default EmptyList;
}
循环注释定义是否有任何解决方案?
答案 0 :(得分:2)
这是因为bug。
annotations' inheritance, polymorphism, 'cycle detected' limitations are...线程讨论它。
您可以创建如下所示的内容
@interface NodeInfo {
LogicalExpression logicalExpression() default LogicalExpression.AND;
Attribute[] attributes() default {};
}
@interface Node {
NodeInfo[] nodes() default {};
}
答案 1 :(得分:2)
由于上面提到的Java限制,您无法定义无限递归定义。但是你可以支持一些固定深度的结构,感觉就像递归一样(直到你达到深度限制)
以下是深度为3的布尔表达式语言示例:
public @interface Expression {
public Term value () default @Term;
public And and () default @And;
public Or or () default @Or;
}
定义"和"每个级别的操作:
public @interface And {
public boolean not () default false;
public Term[] value () default {};
public Or1[] or1 () default {};
public Or2[] or2 () default {};
public And1[] and1 () default {};
public And2[] and2 () default {};
}
public @interface And1 {
public boolean not () default false;
public Term[] value () default {};
public Or2[] or2 () default {};
public And2[] and2 () default {};
}
public @interface And2 {
public boolean not () default false;
public Term[] value () default {};
}
定义"或"每个级别的操作:
public @interface Or {
public boolean not () default false;
public Term[] value() default {};
public Or1[] or1 () default {};
public Or2[] or2 () default {};
public And1[] and1 () default {};
public And2[] and2 () default {};
}
public @interface Or1 {
public boolean not () default false;
public Term[] value () default {};
public Or2[] or2 () default {};
public And2[] and2 () default {};
}
public @interface Or2 {
public boolean not () default false;
public Term[] value () default {};
}
现在我们可以像这样使用它:
@Expression(@Term("a"))
class A{}
// a or b
@Expression(or=@Or({@Term("a"), @Term("b")}))
class B{}
// a or (not(b and c))
@Expression(or=@Or(
value=@Term("a"),
and1=@And1(not=true, value={
@Term("b"),
@Term("b")
})
))
class B{}
正如您所看到的,每次添加嵌套表达式时都会强调操作符注释的索引。
答案 2 :(得分:0)
我知道我有点晚了,但今天我必须解决同样的问题而且我没有真正的解决方案或解决方法就找到了这个问题。
然而,我设法代理"使用以下结构进行递归:
@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() + " ", "(", ")"));
}
}
这评估为:
(responsible = 'foo' AND (status = 'closed' OR visibility = 'public'))
虽然它的可读性值得怀疑,但我认为这是我们在不允许显式递归时可以达到的最佳折衷方案。