This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/groovy.git
commit ff4da8dbd88e419b4de74c6807c1262c4b8246ff Author: Eric Milles <[email protected]> AuthorDate: Sun Mar 1 11:59:55 2026 -0600 minor items --- .../org/codehaus/groovy/ast/AnnotationNode.java | 212 +++++++++++---------- .../ast/expr/AnnotationConstantExpression.java | 15 +- .../org/codehaus/groovy/vmplugin/v8/Java8.java | 41 ++-- .../codehaus/groovy/ast/AnnotationNodeTest.groovy | 42 ++++ .../groovy/tools/groovydoc/GroovyDocToolTest.java | 6 +- 5 files changed, 185 insertions(+), 131 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/ast/AnnotationNode.java b/src/main/java/org/codehaus/groovy/ast/AnnotationNode.java index cadb2e9a6c..4dc697a1b6 100644 --- a/src/main/java/org/codehaus/groovy/ast/AnnotationNode.java +++ b/src/main/java/org/codehaus/groovy/ast/AnnotationNode.java @@ -19,15 +19,14 @@ package org.codehaus.groovy.ast; import org.codehaus.groovy.GroovyBugError; -import org.codehaus.groovy.ast.expr.AnnotationConstantExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.ast.expr.ListExpression; import java.util.Collections; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; -import java.util.stream.Collectors; +import java.util.StringJoiner; import static java.util.Objects.requireNonNull; @@ -35,30 +34,89 @@ import static java.util.Objects.requireNonNull; * Represents an annotation which can be attached to interfaces, classes, methods, fields, parameters, and other places. */ public class AnnotationNode extends ASTNode { - public static final int CONSTRUCTOR_TARGET = 1 << 1; - public static final int METHOD_TARGET = 1 << 2; - public static final int FIELD_TARGET = 1 << 3; - public static final int PARAMETER_TARGET = 1 << 4; - public static final int LOCAL_VARIABLE_TARGET = 1 << 5; - public static final int ANNOTATION_TARGET = 1 << 6; - public static final int PACKAGE_TARGET = 1 << 7; - public static final int TYPE_PARAMETER_TARGET = 1 << 8; - public static final int TYPE_USE_TARGET = 1 << 9; + + public static final int CONSTRUCTOR_TARGET = 1 << 1; + public static final int METHOD_TARGET = 1 << 2; + public static final int FIELD_TARGET = 1 << 3; + public static final int PARAMETER_TARGET = 1 << 4; + public static final int LOCAL_VARIABLE_TARGET = 1 << 5; + public static final int ANNOTATION_TARGET = 1 << 6; + public static final int PACKAGE_TARGET = 1 << 7; + public static final int TYPE_PARAMETER_TARGET = 1 << 8; + public static final int TYPE_USE_TARGET = 1 << 9; public static final int RECORD_COMPONENT_TARGET = 1 << 10; - public static final int TYPE_TARGET = 1 + ANNOTATION_TARGET; //GROOVY-7151 + public static final int TYPE_TARGET = ANNOTATION_TARGET | 1; // GROOVY-7151 + private static final int ALL_TARGETS = TYPE_TARGET | CONSTRUCTOR_TARGET | METHOD_TARGET | FIELD_TARGET | PARAMETER_TARGET | LOCAL_VARIABLE_TARGET | ANNOTATION_TARGET | PACKAGE_TARGET | TYPE_PARAMETER_TARGET | TYPE_USE_TARGET | RECORD_COMPONENT_TARGET; private final ClassNode classNode; private Map<String, Expression> members; - private boolean runtimeRetention = false, sourceRetention = false, /* explicit */ classRetention = false; private int allowedTargets = ALL_TARGETS; + private boolean runtimeRetention = false, sourceRetention = false, /*explicit*/ classRetention = false; + + public AnnotationNode(final ClassNode type) { + classNode = requireNonNull(type); + } + + public void addMember(final String name, final Expression value) { + ensureMembers(); + Expression oldValue = members.get(name); + if (oldValue == null) { + members.put(name, value); + } else { + throw new GroovyBugError(String.format("Annotation member %s has already been added", name)); + } + } + + public void setMember(final String name, final Expression value) { + ensureMembers(); + members.put(name, value); + } + + private void ensureMembers() { + if (members == null) { + members = new LinkedHashMap<>(); + } + } + + public void setAllowedTargets(final int bitmap) { + allowedTargets = bitmap; + } + + /** + * Sets the internal flag if the current annotation has <code>RetentionPolicy.RUNTIME</code>. + * + * @param value if <tt>true</tt> then current annotation is marked as having + * <code>RetentionPolicy.RUNTIME</code>. + */ + public void setRuntimeRetention(final boolean value) { + runtimeRetention = value; + } + + /** + * Sets the internal flag if the current annotation has <code>RetentionPolicy.SOURCE</code>. + * + * @param value if <tt>true</tt> then current annotation is marked as having + * <code>RetentionPolicy.SOURCE</code>. + */ + public void setSourceRetention(final boolean value) { + sourceRetention = value; + } - public AnnotationNode(ClassNode classNode) { - this.classNode = requireNonNull(classNode); + /** + * Sets the internal flag if the current annotation has an explicit <code>RetentionPolicy.CLASS</code>. + * + * @param value if <tt>true</tt> then current annotation is marked as having + * <code>RetentionPolicy.CLASS</code>. + */ + public void setClassRetention(final boolean value) { + classRetention = value; } + //-------------------------------------------------------------------------- + public ClassNode getClassNode() { return classNode; } @@ -70,37 +128,20 @@ public class AnnotationNode extends ASTNode { return members; } - public Expression getMember(String name) { + public Expression getMember(final String name) { if (members == null) { return null; } return members.get(name); } - private void assertMembers() { - if (members == null) { - members = new LinkedHashMap<>(); - } - } - - public void addMember(String name, Expression value) { - assertMembers(); - Expression oldValue = members.get(name); - if (oldValue == null) { - members.put(name, value); - } - else { - throw new GroovyBugError(String.format("Annotation member %s has already been added", name)); - } - } - - public void setMember(String name, Expression value) { - assertMembers(); - members.put(name, value); + @Deprecated(since = "6.0.0") + public boolean isBuiltIn() { + return false; } - public boolean isBuiltIn(){ - return false; + public boolean isTargetAllowed(final int target) { + return (this.allowedTargets & target) == target; } /** @@ -112,17 +153,6 @@ public class AnnotationNode extends ASTNode { return this.runtimeRetention; } - /** - * Sets the internal flag if the current annotation has - * <code>RetentionPolicy.SOURCE</code>. - * - * @param flag if <tt>true</tt> then current annotation is marked as having - * <code>RetentionPolicy.RUNTIME</code>. - */ - public void setRuntimeRetention(boolean flag) { - this.runtimeRetention = flag; - } - /** * Flag corresponding to <code>RetentionPolicy.SOURCE</code>. * @return <tt>true</tt> if the annotation is only allowed in sources @@ -132,16 +162,6 @@ public class AnnotationNode extends ASTNode { return this.sourceRetention; } - /** - * Sets the internal flag if the current annotation has <code>RetentionPolicy.SOURCE</code>. - * - * @param flag if <tt>true</tt> then current annotation is marked as having - * <code>RetentionPolicy.SOURCE</code>. - */ - public void setSourceRetention(boolean flag) { - this.sourceRetention = flag; - } - /** * Flag corresponding to <code>RetentionPolicy.CLASS</code>. * This is the default when no <code>RetentionPolicy</code> annotations are present. @@ -154,25 +174,41 @@ public class AnnotationNode extends ASTNode { return this.classRetention; } - /** - * Sets the internal flag if the current annotation has an explicit <code>RetentionPolicy.CLASS</code>. - * - * @param flag if <tt>true</tt> then current annotation is marked as having - * <code>RetentionPolicy.CLASS</code>. - */ - public void setClassRetention(boolean flag) { - this.classRetention = flag; + @Override + public String toString() { + return super.toString() + "[" + getText() + "]"; } - public void setAllowedTargets(int bitmap) { - this.allowedTargets = bitmap; + @Override + public String getText() { + String text = "@" + classNode.getName(); + if (members != null) { + var memberText = new StringJoiner(", ", "(", ")"); + for (Map.Entry<String,Expression> entry : members.entrySet()) { + memberText.add(entry.getKey() + "=" + getText(entry.getValue())); + } + text += memberText; + } + return text; } - public boolean isTargetAllowed(int target) { - return (this.allowedTargets & target) == target; + private static String getText(final Expression e) { + String text; + if (e instanceof ConstantExpression ce && ce.getValue() instanceof String string) { + text = '"' + string + '"'; + } else if (e instanceof ListExpression list) { + var listText = new StringJoiner(", ", "[", "]"); + for (var item : list.getExpressions()) { + listText.add(getText(item)); + } + text = listText.toString(); + } else { + text = e.getText(); + } + return text; } - public static String targetToName(int target) { + public static String targetToName(final int target) { return switch (target) { case TYPE_TARGET -> "TYPE"; case CONSTRUCTOR_TARGET -> "CONSTRUCTOR"; @@ -188,34 +224,4 @@ public class AnnotationNode extends ASTNode { default -> "unknown target"; }; } - - @Override - public String toString() { - return super.toString() + "[" + getText() + "]"; - } - - @Override - public String getText() { - StringBuilder memberText = new StringBuilder(); - if (members != null) { - boolean first = true; - for (Map.Entry<String, Expression> next : members.entrySet()) { - if (first) { - first = false; - } else { - memberText.append(", "); - } - Expression value = next.getValue(); - String text; - if (value instanceof ListExpression) { - List result = ((ListExpression) value).getExpressions().stream().map(exp -> { return exp instanceof AnnotationConstantExpression ? ((ASTNode)((AnnotationConstantExpression)exp).getValue()).getText() : exp.getText(); }).collect(Collectors.toList()); - text = result.toString(); - } else { - text = value.getText(); - } - memberText.append(next.getKey()).append(": ").append(text); - } - } - return "@" + classNode.getText() + "(" + memberText + ")"; - } } diff --git a/src/main/java/org/codehaus/groovy/ast/expr/AnnotationConstantExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/AnnotationConstantExpression.java index 27ad9593ef..4febc19eb0 100644 --- a/src/main/java/org/codehaus/groovy/ast/expr/AnnotationConstantExpression.java +++ b/src/main/java/org/codehaus/groovy/ast/expr/AnnotationConstantExpression.java @@ -34,6 +34,16 @@ public class AnnotationConstantExpression extends ConstantExpression { setType(node.getClassNode()); } + @Override + public String getText() { + return ((AnnotationNode) getValue()).getText(); + } + + @Override + public String toString() { + return super.toString() + "[" + getText() + "]"; + } + @Override public void visit(final GroovyCodeVisitor visitor) { super.visit(visitor); // GROOVY-9980 @@ -44,9 +54,4 @@ public class AnnotationConstantExpression extends ConstantExpression { expr.visit(visitor); } } - - @Override - public String toString() { - return super.toString() + "[" + getValue() + "]"; - } } diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java index cab8b23ae8..67860da52a 100644 --- a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java +++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java @@ -360,27 +360,28 @@ public class Java8 implements VMPlugin { )); @Override - public void configureAnnotationNodeFromDefinition(final AnnotationNode definition, final AnnotationNode root) { - String typeName = definition.getClassNode().getName(); - if ("java.lang.annotation.Retention".equals(typeName)) { - Expression exp = definition.getMember("value"); - if (!(exp instanceof PropertyExpression pe)) return; - String name = pe.getPropertyAsString(); - RetentionPolicy policy = RetentionPolicy.valueOf(name); - setRetentionPolicy(policy, root); - } else if ("java.lang.annotation.Target".equals(typeName)) { - Expression exp = definition.getMember("value"); - if (!(exp instanceof ListExpression list)) return; - int targets = 0; - for (Expression e : list.getExpressions()) { - if (!(e instanceof PropertyExpression element)) return; - String name = element.getPropertyAsString(); - ElementType type = ElementType.valueOf(name); - Integer target = elementTypeToTarget.get(type); - if (target == null) throw new GroovyBugError("unsupported Target " + type); - targets |= target; + public void configureAnnotationNodeFromDefinition(final AnnotationNode definition, final AnnotationNode node) { + switch (definition.getClassNode().getName()) { + case "java.lang.annotation.Retention": + if (definition.getMember("value") instanceof PropertyExpression value) { + var policy = RetentionPolicy.valueOf(value.getPropertyAsString()); + setRetentionPolicy(policy, node); + } + break; + case "java.lang.annotation.Target": + if (definition.getMember("value") instanceof ListExpression list) { + int targets = 0; + for (Expression e : list.getExpressions()) { + if (e instanceof PropertyExpression item) { + String name = item.getPropertyAsString(); + ElementType type = ElementType.valueOf(name); + Integer target = elementTypeToTarget.get(type); + if (target == null) throw new GroovyBugError("unsupported Target " + type); + targets |= target; + } + } + node.setAllowedTargets(targets); } - root.setAllowedTargets(targets); } } diff --git a/src/test/groovy/org/codehaus/groovy/ast/AnnotationNodeTest.groovy b/src/test/groovy/org/codehaus/groovy/ast/AnnotationNodeTest.groovy new file mode 100644 index 0000000000..862d9b7bc6 --- /dev/null +++ b/src/test/groovy/org/codehaus/groovy/ast/AnnotationNodeTest.groovy @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.ast + +import org.codehaus.groovy.ast.expr.ConstantExpression +import org.junit.jupiter.api.Test + +import static org.junit.jupiter.api.Assertions.* + +final class AnnotationNodeTest { + + @Test + void testGetText() { + def node = new AnnotationNode(ClassHelper.OVERRIDE_TYPE) + + assertEquals('@java.lang.Override', node.getText()) + } + + @Test + void testGetText2() { + def node = new AnnotationNode(ClassHelper.make(Deprecated)) + node.addMember('since', new ConstantExpression('1.2.3')) + + assertEquals('@java.lang.Deprecated(since="1.2.3")', node.getText()) + } +} diff --git a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java index b053c1b086..782ebfa3c2 100644 --- a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java +++ b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java @@ -1237,7 +1237,7 @@ public class GroovyDocToolTest extends GroovyTestCase { final String javadoc = output.getText(MOCK_DIR + "/" + base + "/Java.html"); assertTrue("The Groovy class declaration header should have the annotation in:\n" + groovydoc, Pattern.compile(Pattern.quote( - "<pre>@groovy.transform.EqualsAndHashCode(cache: true)\n" + + "<pre>@groovy.transform.EqualsAndHashCode(cache=true)\n" + "class Groovy" )).matcher(groovydoc).find()); @@ -1275,7 +1275,7 @@ public class GroovyDocToolTest extends GroovyTestCase { assertTrue("The Groovy ctor details should have the annotation in:\n" + groovydoc, Pattern.compile( "<h4>@groovy.transform.Generated<br>" + "<strong>Groovy</strong>\\(" + - "@groovy.transform.NamedParam\\(value: ctorParam, type: java.util.List\\)<br>" + + "@groovy.transform.NamedParam\\(value=\"ctorParam\", type=java.util.List\\)<br>" + "(<a href='https://docs.oracle.com/javase/8/docs/api/java/util/Map.html' title='Map'>Map</a>|java.util.Map) namedArgs" + "\\)</h4>" ).matcher(groovydoc).find()); @@ -1296,7 +1296,7 @@ public class GroovyDocToolTest extends GroovyTestCase { assertTrue("The Groovy method details should have the annotations in:\n" + groovydoc, Pattern.compile( "<h4>@groovy.transform.Generated<br>" + "void <strong>annotatedMethod</strong>\\(" + - "@groovy.transform.NamedParam\\(required: true, value: methodParam, type: java.lang.String\\)<br>" + + "@groovy.transform.NamedParam\\(required=true, value=\"methodParam\", type=java.lang.String\\)<br>" + "(<a href='https://docs.oracle.com/javase/8/docs/api/java/util/Map.html' title='Map'>Map</a>|java.util.Map) namedArgs" + "\\)</h4>" ).matcher(groovydoc).find());
