From 7754224f9d0c0c9dda5b39d8baa321a75b7829d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 26 Mar 2018 20:29:10 +0200 Subject: [PATCH 1/7] Fix deprecated XPath attribute not found Deprecated attributes are not eliminated from the attribute iterator anymore, which made deprecating attributes API breaking. Instead, deprecated attribute usage are now detected and logged as warnings --- .../pmd/lang/ast/xpath/Attribute.java | 23 +++++++- .../lang/ast/xpath/AttributeAxisIterator.java | 7 ++- .../pmd/lang/ast/AbstractNodeTest.java | 58 ++++++++++++++++++- .../ast/DummyNodeWithDeprecatedAttribute.java | 23 ++++++++ .../ast/xpath/AttributeAxisIteratorTest.java | 27 +++++++-- 5 files changed, 126 insertions(+), 12 deletions(-) create mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNodeWithDeprecatedAttribute.java 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 9e2de28f7a..d54c2ccd42 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 @@ -6,6 +6,10 @@ package net.sourceforge.pmd.lang.ast.xpath; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import net.sourceforge.pmd.lang.ast.Node; @@ -14,6 +18,11 @@ import net.sourceforge.pmd.lang.ast.Node; */ public class Attribute { + + private static final Logger LOG = Logger.getLogger(Attribute.class.getName()); + private static final Set DETECTED_DEPRECATED_ATTRIBUTES = new HashSet<>(); + + private static final Object[] EMPTY_OBJ_ARRAY = new Object[0]; private Node parent; private String name; @@ -38,13 +47,17 @@ public class Attribute { if (value != null) { return value; } + + if (method.isAnnotationPresent(Deprecated.class) && LOG.isLoggable(Level.WARNING) && !DETECTED_DEPRECATED_ATTRIBUTES.contains(getLoggableAttributeName())) { + DETECTED_DEPRECATED_ATTRIBUTES.add(getLoggableAttributeName()); + LOG.warning("Use of deprecated attribute '" + getLoggableAttributeName() + "' in xpath query"); + } + // this lazy loading reduces calls to Method.invoke() by about 90% try { return method.invoke(parent, EMPTY_OBJ_ARRAY); - } catch (IllegalAccessException iae) { + } catch (IllegalAccessException | InvocationTargetException iae) { iae.printStackTrace(); - } catch (InvocationTargetException ite) { - ite.printStackTrace(); } return null; } @@ -65,6 +78,10 @@ public class Attribute { return stringValue; } + private String getLoggableAttributeName() { + return parent.getXPathNodeName() + "/@" + name; + } + public String getName() { return name; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIterator.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIterator.java index 27a4800b7d..2096309099 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIterator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIterator.java @@ -101,6 +101,9 @@ public class AttributeAxisIterator implements Iterator { return new Attribute(node, m.name, m.method); } + + + private static final Set> CONSIDERED_RETURN_TYPES = new HashSet<>(Arrays.>asList(Integer.TYPE, Boolean.TYPE, Double.TYPE, String.class, Long.TYPE, Character.TYPE, Float.TYPE)); @@ -109,10 +112,8 @@ public class AttributeAxisIterator implements Iterator { protected boolean isAttributeAccessor(Method method) { String methodName = method.getName(); - boolean deprecated = method.getAnnotation(Deprecated.class) != null; - return !deprecated - && CONSIDERED_RETURN_TYPES.contains(method.getReturnType()) + return CONSIDERED_RETURN_TYPES.contains(method.getReturnType()) && method.getParameterTypes().length == 0 && !methodName.startsWith("jjt") && !FILTERED_OUT_NAMES.contains(methodName); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/AbstractNodeTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/AbstractNodeTest.java index c0deef7585..18619ecd27 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/AbstractNodeTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/AbstractNodeTest.java @@ -6,15 +6,26 @@ package net.sourceforge.pmd.lang.ast; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.io.StringWriter; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +import org.jaxen.JaxenException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import net.sourceforge.pmd.lang.ast.xpath.Attribute; + import junitparams.JUnitParamsRunner; import junitparams.Parameters; + /** * Unit test for {@link AbstractNode}. */ @@ -65,9 +76,10 @@ public class AbstractNodeTest { return new DummyNode(nextId()); } - private static void addChild(final Node parent, final Node child) { + private static Node addChild(final Node parent, final Node child) { parent.jjtAddChild(child, parent.jjtGetNumChildren()); // Append child at the end child.jjtSetParent(parent); + return parent; } @Before @@ -216,4 +228,48 @@ public class AbstractNodeTest { // Check that this node still does not have any children assertEquals(0, grandChild.jjtGetNumChildren()); } + + + @Test + public void testDeprecatedAttributeXPathQuery() throws JaxenException { + final StringWriter writer = new StringWriter(); + + class MyRootNode extends DummyNode implements RootNode { + + private MyRootNode(int id) { + super(id); + } + } + + // intercept log + Logger.getLogger(Attribute.class.getName()).setLevel(Level.WARNING); + Logger.getLogger(Attribute.class.getName()).addHandler(new Handler() { + @Override + public void publish(LogRecord record) { + writer.write(record.getMessage()); + writer.write('\n'); + } + + + @Override + public void flush() { + writer.flush(); + } + + + @Override + public void close() throws SecurityException { + // empty + } + }); + addChild(new MyRootNode(nextId()), new DummyNodeWithDeprecatedAttribute(2)).findChildNodesWithXPath("//dummyNode[@Size=1]"); + + writer.flush(); + + assertTrue(writer.toString().contains("deprecated")); + assertTrue(writer.toString().contains("attribute")); + assertTrue(writer.toString().contains("dummyNode/@Size")); + } + + } 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 new file mode 100644 index 0000000000..68e278ad4a --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNodeWithDeprecatedAttribute.java @@ -0,0 +1,23 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.ast; + +/** + * @author Clément Fournier + * @since 6.2.0 + */ +public class DummyNodeWithDeprecatedAttribute extends DummyNode { + + + public DummyNodeWithDeprecatedAttribute(int id) { + super(id); + } + + + @Deprecated + public int getSize() { + return 2; + } +} 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 89bdc7772d..e04d981fad 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 @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang.ast.xpath; +import static junit.framework.TestCase.assertTrue; + import java.util.HashMap; import java.util.Map; @@ -11,12 +13,21 @@ import org.junit.Assert; import org.junit.Test; import net.sourceforge.pmd.lang.ast.DummyNode; +import net.sourceforge.pmd.lang.ast.DummyNodeWithDeprecatedAttribute; +import net.sourceforge.pmd.lang.ast.Node; + /** * Unit test for {@link AttributeAxisIterator} */ public class AttributeAxisIteratorTest { + @Test + public void testAttributeDeprecation() { + Node dummy = new DummyNodeWithDeprecatedAttribute(2); + assertTrue(toMap(new AttributeAxisIterator(dummy)).containsKey("Size")); + } + /** * Test hasNext and next. */ @@ -27,11 +38,7 @@ public class AttributeAxisIteratorTest { dummyNode.testingOnlySetBeginColumn(1); AttributeAxisIterator it = new AttributeAxisIterator(dummyNode); - Map atts = new HashMap<>(); - while (it.hasNext()) { - Attribute attribute = it.next(); - atts.put(attribute.getName(), attribute); - } + Map atts = toMap(it); Assert.assertEquals(7, atts.size()); Assert.assertTrue(atts.containsKey("BeginColumn")); Assert.assertTrue(atts.containsKey("BeginLine")); @@ -41,4 +48,14 @@ public class AttributeAxisIteratorTest { Assert.assertTrue(atts.containsKey("EndColumn")); Assert.assertTrue(atts.containsKey("EndLine")); } + + + private Map toMap(AttributeAxisIterator it) { + Map atts = new HashMap<>(); + while (it.hasNext()) { + Attribute attribute = it.next(); + atts.put(attribute.getName(), attribute); + } + return atts; + } } From 74bb7598c37f0c9a3dc9527d308354f59e0f5d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 26 Mar 2018 20:36:15 +0200 Subject: [PATCH 2/7] Ensure java rules don't use deprecated attributes This could be extended to other languages once we tackle #360 --- .../test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java index addf48577f..a4b82e3736 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/coverage/PMDCoverageTest.java @@ -58,6 +58,7 @@ public class PMDCoverageTest { assertFalse("There was at least one exception mentioned in the output", output.getLog().contains("Exception applying rule")); assertFalse("Wrong configuration? Ruleset not found", output.getLog().contains("Ruleset not found")); + assertFalse("Some XPath rules use deprecated attributes", output.getLog().contains("Use of deprecated attribute")); String report = FileUtils.readFileToString(f); assertEquals("No processing errors expected", 0, StringUtils.countMatches(report, "Error while processing")); From 958a26db382c37e4533ad152dfb6316dc2b0cf3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 26 Mar 2018 20:53:29 +0200 Subject: [PATCH 3/7] CStyle --- .../java/net/sourceforge/pmd/lang/ast/AbstractNodeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/AbstractNodeTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/AbstractNodeTest.java index 18619ecd27..9824322c5c 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/AbstractNodeTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/AbstractNodeTest.java @@ -236,7 +236,7 @@ public class AbstractNodeTest { class MyRootNode extends DummyNode implements RootNode { - private MyRootNode(int id) { + private MyRootNode(int id) { super(id); } } From 0138b1a69bfb4051f3fe6adb5731ec450bf35fc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Apr 2018 03:34:36 +0200 Subject: [PATCH 4/7] Use ConcurrentMap instead of Set --- .../net/sourceforge/pmd/lang/ast/xpath/Attribute.java | 10 +++++----- .../pmd/lang/ast/DummyNodeWithDeprecatedAttribute.java | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) 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 d54c2ccd42..7a759c8c48 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 @@ -6,8 +6,8 @@ package net.sourceforge.pmd.lang.ast.xpath; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.HashSet; -import java.util.Set; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -20,7 +20,7 @@ public class Attribute { private static final Logger LOG = Logger.getLogger(Attribute.class.getName()); - private static final Set DETECTED_DEPRECATED_ATTRIBUTES = new HashSet<>(); + private static final Map DETECTED_DEPRECATED_ATTRIBUTES = new ConcurrentHashMap<>(); private static final Object[] EMPTY_OBJ_ARRAY = new Object[0]; @@ -48,8 +48,8 @@ public class Attribute { return value; } - if (method.isAnnotationPresent(Deprecated.class) && LOG.isLoggable(Level.WARNING) && !DETECTED_DEPRECATED_ATTRIBUTES.contains(getLoggableAttributeName())) { - DETECTED_DEPRECATED_ATTRIBUTES.add(getLoggableAttributeName()); + if (method.isAnnotationPresent(Deprecated.class) && LOG.isLoggable(Level.WARNING) && !DETECTED_DEPRECATED_ATTRIBUTES.containsKey(getLoggableAttributeName())) { + DETECTED_DEPRECATED_ATTRIBUTES.put(getLoggableAttributeName(), Boolean.TRUE); LOG.warning("Use of deprecated attribute '" + getLoggableAttributeName() + "' in xpath query"); } 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 68e278ad4a..7f6f67d8f7 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 @@ -6,7 +6,7 @@ package net.sourceforge.pmd.lang.ast; /** * @author Clément Fournier - * @since 6.2.0 + * @since 6.3.0 */ public class DummyNodeWithDeprecatedAttribute extends DummyNode { @@ -15,7 +15,7 @@ public class DummyNodeWithDeprecatedAttribute extends DummyNode { super(id); } - + // this is the deprecated attribute @Deprecated public int getSize() { return 2; From 5a65701d0c6e1cc9589573b6611b9c7ef40c7c5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Apr 2018 03:51:35 +0200 Subject: [PATCH 5/7] Add hamcrest matchers to pom for pmd-core --- pmd-core/pom.xml | 5 +++++ .../ast/xpath/AttributeAxisIteratorTest.java | 21 +++++++++++-------- pom.xml | 6 ++++++ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index f762c76d97..55b84f03c8 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -121,6 +121,11 @@ junit test + + org.hamcrest + hamcrest-library + test + pl.pragmatists JUnitParams 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 e04d981fad..e8326ca32d 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 @@ -4,11 +4,14 @@ package net.sourceforge.pmd.lang.ast.xpath; -import static junit.framework.TestCase.assertTrue; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertTrue; import java.util.HashMap; import java.util.Map; +import org.hamcrest.collection.IsMapContaining; import org.junit.Assert; import org.junit.Test; @@ -25,7 +28,7 @@ public class AttributeAxisIteratorTest { @Test public void testAttributeDeprecation() { Node dummy = new DummyNodeWithDeprecatedAttribute(2); - assertTrue(toMap(new AttributeAxisIterator(dummy)).containsKey("Size")); + assertThat(toMap(new AttributeAxisIterator(dummy)), IsMapContaining.hasKey("Size")); } /** @@ -40,13 +43,13 @@ public class AttributeAxisIteratorTest { AttributeAxisIterator it = new AttributeAxisIterator(dummyNode); Map atts = toMap(it); Assert.assertEquals(7, atts.size()); - Assert.assertTrue(atts.containsKey("BeginColumn")); - Assert.assertTrue(atts.containsKey("BeginLine")); - Assert.assertTrue(atts.containsKey("FindBoundary")); - Assert.assertTrue(atts.containsKey("Image")); - Assert.assertTrue(atts.containsKey("SingleLine")); - Assert.assertTrue(atts.containsKey("EndColumn")); - Assert.assertTrue(atts.containsKey("EndLine")); + assertTrue(atts.containsKey("BeginColumn")); + assertTrue(atts.containsKey("BeginLine")); + assertTrue(atts.containsKey("FindBoundary")); + assertTrue(atts.containsKey("Image")); + assertTrue(atts.containsKey("SingleLine")); + assertTrue(atts.containsKey("EndColumn")); + assertTrue(atts.containsKey("EndLine")); } diff --git a/pom.xml b/pom.xml index 456a9a74e8..48fc8fd04d 100644 --- a/pom.xml +++ b/pom.xml @@ -800,6 +800,12 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code junit 4.12 + + org.hamcrest + hamcrest-library + 1.3 + test + pl.pragmatists JUnitParams From 83ce60078266a04f25dd799965b773ce69dd8e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Apr 2018 04:00:38 +0200 Subject: [PATCH 6/7] Use JunitLoggingRule to intercept log --- .../pmd/lang/ast/AbstractNodeTest.java | 42 ++++--------------- 1 file changed, 9 insertions(+), 33 deletions(-) diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/AbstractNodeTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/AbstractNodeTest.java index 9824322c5c..f17a8c2a3e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/AbstractNodeTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/AbstractNodeTest.java @@ -9,17 +9,13 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import java.io.StringWriter; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import java.util.logging.Logger; - import org.jaxen.JaxenException; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import net.sourceforge.pmd.junit.JavaUtilLoggingRule; import net.sourceforge.pmd.lang.ast.xpath.Attribute; import junitparams.JUnitParamsRunner; @@ -34,6 +30,9 @@ public class AbstractNodeTest { private static final int NUM_CHILDREN = 3; private static final int NUM_GRAND_CHILDREN = 3; + @Rule + public JavaUtilLoggingRule loggingRule = new JavaUtilLoggingRule(Attribute.class.getName()); + // Note that in order to successfully run JUnitParams, we need to explicitly use `Integer` instead of `int` private Integer[] childrenIndexes() { @@ -232,8 +231,6 @@ public class AbstractNodeTest { @Test public void testDeprecatedAttributeXPathQuery() throws JaxenException { - final StringWriter writer = new StringWriter(); - class MyRootNode extends DummyNode implements RootNode { private MyRootNode(int id) { @@ -241,34 +238,13 @@ public class AbstractNodeTest { } } - // intercept log - Logger.getLogger(Attribute.class.getName()).setLevel(Level.WARNING); - Logger.getLogger(Attribute.class.getName()).addHandler(new Handler() { - @Override - public void publish(LogRecord record) { - writer.write(record.getMessage()); - writer.write('\n'); - } - - - @Override - public void flush() { - writer.flush(); - } - - - @Override - public void close() throws SecurityException { - // empty - } - }); addChild(new MyRootNode(nextId()), new DummyNodeWithDeprecatedAttribute(2)).findChildNodesWithXPath("//dummyNode[@Size=1]"); - writer.flush(); + String log = loggingRule.getLog(); - assertTrue(writer.toString().contains("deprecated")); - assertTrue(writer.toString().contains("attribute")); - assertTrue(writer.toString().contains("dummyNode/@Size")); + assertTrue(log.contains("deprecated")); + assertTrue(log.contains("attribute")); + assertTrue(log.contains("dummyNode/@Size")); } From 9d4354333da6c0d81670ee0cab525d35b203a9fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Sun, 15 Apr 2018 02:05:45 -0300 Subject: [PATCH 7/7] Use atomic operations on concurrentmap - Take the chance to actually use the lazy loading --- .../net/sourceforge/pmd/lang/ast/xpath/Attribute.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 7a759c8c48..0e7a9b721f 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 @@ -6,8 +6,8 @@ package net.sourceforge.pmd.lang.ast.xpath; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -20,7 +20,7 @@ public class Attribute { private static final Logger LOG = Logger.getLogger(Attribute.class.getName()); - private static final Map DETECTED_DEPRECATED_ATTRIBUTES = new ConcurrentHashMap<>(); + private static final ConcurrentMap DETECTED_DEPRECATED_ATTRIBUTES = new ConcurrentHashMap<>(); private static final Object[] EMPTY_OBJ_ARRAY = new Object[0]; @@ -48,14 +48,14 @@ public class Attribute { return value; } - if (method.isAnnotationPresent(Deprecated.class) && LOG.isLoggable(Level.WARNING) && !DETECTED_DEPRECATED_ATTRIBUTES.containsKey(getLoggableAttributeName())) { - DETECTED_DEPRECATED_ATTRIBUTES.put(getLoggableAttributeName(), Boolean.TRUE); + if (method.isAnnotationPresent(Deprecated.class) && LOG.isLoggable(Level.WARNING) + && DETECTED_DEPRECATED_ATTRIBUTES.putIfAbsent(getLoggableAttributeName(), Boolean.TRUE) == null) { LOG.warning("Use of deprecated attribute '" + getLoggableAttributeName() + "' in xpath query"); } // this lazy loading reduces calls to Method.invoke() by about 90% try { - return method.invoke(parent, EMPTY_OBJ_ARRAY); + return value = method.invoke(parent, EMPTY_OBJ_ARRAY); } catch (IllegalAccessException | InvocationTargetException iae) { iae.printStackTrace(); }