diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 753fcd682a..4cf6811725 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -133,6 +133,7 @@ methods on {% jdoc apex::lang.apex.ast.ApexParserVisitor %} and its implementati * pmd-core * Many methods on the {% jdoc core::lang.ast.Node %} interface and {% jdoc core::lang.ast.AbstractNode %} base class. See their javadoc for details. + * {% jdoc !!core::lang.ast.Node#isFindBoundary() %} is deprecated for XPath queries. * pmd-java * {% jdoc java::lang.java.AbstractJavaParser %} * {% jdoc java::lang.java.AbstractJavaHandler %} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Node.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Node.java index c5a401fa47..e1571e8a5f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Node.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Node.java @@ -12,6 +12,7 @@ import org.w3c.dom.Document; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.ast.xpath.Attribute; +import net.sourceforge.pmd.lang.ast.xpath.internal.DeprecatedAttribute; import net.sourceforge.pmd.lang.dfa.DataFlowNode; /** @@ -202,7 +203,11 @@ public interface Node { * look past such boundaries by default, which is usually the expected thing * to do. For example, in Java, lambdas and nested classes are considered * find boundaries. + * + *

Note: This attribute is deprecated for XPath queries. It is not useful + * for XPath queries and will be removed with PMD 7.0.0. */ + @DeprecatedAttribute boolean isFindBoundary(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java index 85cca22010..cee0734a9c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java @@ -16,6 +16,7 @@ import java.util.logging.Logger; import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.xpath.internal.DeprecatedAttribute; /** * Represents an XPath attribute of a specific node. @@ -31,7 +32,7 @@ public class Attribute { private static final Logger LOG = Logger.getLogger(Attribute.class.getName()); - private static final ConcurrentMap DETECTED_DEPRECATED_ATTRIBUTES = new ConcurrentHashMap<>(); + static final ConcurrentMap DETECTED_DEPRECATED_ATTRIBUTES = new ConcurrentHashMap<>(); private static final Object[] EMPTY_OBJ_ARRAY = new Object[0]; @@ -72,12 +73,17 @@ public class Attribute { return method == null ? String.class : method.getReturnType(); } + private boolean isAttributeDeprecated() { + return method != null && (method.isAnnotationPresent(Deprecated.class) + || method.isAnnotationPresent(DeprecatedAttribute.class)); + } + public Object getValue() { if (value != null) { return value.get(0); } - if (method.isAnnotationPresent(Deprecated.class) && LOG.isLoggable(Level.WARNING) + if (LOG.isLoggable(Level.WARNING) && isAttributeDeprecated() && DETECTED_DEPRECATED_ATTRIBUTES.putIfAbsent(getLoggableAttributeName(), Boolean.TRUE) == null) { // this message needs to be kept in sync with PMDCoverageTest / BinaryDistributionIT LOG.warning("Use of deprecated attribute '" + getLoggableAttributeName() + "' in XPath query"); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/DeprecatedAttribute.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/DeprecatedAttribute.java new file mode 100644 index 0000000000..97346d92a7 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/internal/DeprecatedAttribute.java @@ -0,0 +1,25 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.ast.xpath.internal; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +/** + * Node attribute getter methods might be annotated with {@code DeprecatedAttribute} + * to mark the attribute as deprecated for XPath. Unlike {@link Deprecated}, this + * annotation does not deprecate the method for java usage. + * + * @since 6.21.0 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface DeprecatedAttribute { +} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNodeWithDeprecatedAttribute.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNodeWithDeprecatedAttribute.java index 7f6f67d8f7..7deca9984b 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNodeWithDeprecatedAttribute.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNodeWithDeprecatedAttribute.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang.ast; +import net.sourceforge.pmd.lang.ast.xpath.internal.DeprecatedAttribute; + /** * @author Clément Fournier * @since 6.3.0 @@ -20,4 +22,11 @@ public class DummyNodeWithDeprecatedAttribute extends DummyNode { public int getSize() { return 2; } + + // this is a attribute that is deprecated for xpath, because it will be removed. + // it should still be available via Java. + @DeprecatedAttribute + public String getName() { + return "foo"; + } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIteratorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIteratorTest.java index 45eefbb33f..1d9e274921 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIteratorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIteratorTest.java @@ -16,10 +16,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.hamcrest.Matchers; import org.hamcrest.collection.IsMapContaining; import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; +import net.sourceforge.pmd.junit.JavaUtilLoggingRule; import net.sourceforge.pmd.lang.ast.DummyNode; import net.sourceforge.pmd.lang.ast.DummyNodeWithDeprecatedAttribute; import net.sourceforge.pmd.lang.ast.Node; @@ -30,10 +33,30 @@ import net.sourceforge.pmd.lang.ast.Node; */ public class AttributeAxisIteratorTest { + @Rule + public JavaUtilLoggingRule loggingRule = new JavaUtilLoggingRule(Attribute.class.getName()); + + /** + * Verifies that attributes are returned, even if they are deprecated. + * Deprecated attributes are still accessible, but a warning is logged, when + * the value is used. + */ @Test public void testAttributeDeprecation() { + // make sure, we log + Attribute.DETECTED_DEPRECATED_ATTRIBUTES.clear(); + Node dummy = new DummyNodeWithDeprecatedAttribute(2); - assertThat(toMap(new AttributeAxisIterator(dummy)), IsMapContaining.hasKey("Size")); + Map attributes = toMap(new AttributeAxisIterator(dummy)); + assertThat(attributes, IsMapContaining.hasKey("Size")); + assertThat(attributes, IsMapContaining.hasKey("Name")); + + assertThat(attributes.get("Size").getStringValue(), Matchers.is("2")); + assertThat(attributes.get("Name").getStringValue(), Matchers.is("foo")); + + String log = loggingRule.getLog(); + assertThat(log, Matchers.containsString("Use of deprecated attribute 'dummyNode/@Size' in XPath query")); + assertThat(log, Matchers.containsString("Use of deprecated attribute 'dummyNode/@Name' in XPath query")); } /**