Affects Version: 6.0.0
Rule: DataClass
Description:
We're upgrading to PMD 6.0.0 and found a bug in new rule DataClass
Code Sample demonstrating the issue:
// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.gui.tagging.presets.items; import static org.openstreetmap.josm.tools.I18n.tr; import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import java.util.NoSuchElementException; import java.util.SortedSet; import java.util.TreeSet; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.data.osm.OsmUtils; import org.openstreetmap.josm.data.preferences.BooleanProperty; import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItem; /** * Preset item associated to an OSM key. */ public abstract class KeyedItem extends TaggingPresetItem { /** Translatation of "<different>". Use in combo boxes to display an entry matching several different values. */ protected static final String DIFFERENT = tr("<different>"); protected static final BooleanProperty PROP_FILL_DEFAULT = new BooleanProperty("taggingpreset.fill-default-for-tagged-primitives", false); /** Last value of each key used in presets, used for prefilling corresponding fields */ static final Map<String, String> LAST_VALUES = new HashMap<>(); /** This specifies the property key that will be modified by the item. */ public String key; // NOSONAR /** The text to display */ public String text; // NOSONAR /** The context used for translating {@link #text} */ public String text_context; // NOSONAR /** * Allows to change the matching process, i.e., determining whether the tags of an OSM object fit into this preset. * If a preset fits then it is linked in the Tags/Membership dialog.<ul> * <li>none: neutral, i.e., do not consider this item for matching</li> * <li>key: positive if key matches, neutral otherwise</li> * <li>key!: positive if key matches, negative otherwise</li> * <li>keyvalue: positive if key and value matches, neutral otherwise</li> * <li>keyvalue!: positive if key and value matches, negative otherwise</li></ul> * Note that for a match, at least one positive and no negative is required. * Default is "keyvalue!" for {@link Key} and "none" for {@link Text}, {@link Combo}, {@link MultiSelect} and {@link Check}. */ public String match = getDefaultMatch().getValue(); // NOSONAR /** * Enum denoting how a match (see {@link TaggingPresetItem#matches}) is performed. */ protected enum MatchType { /** Neutral, i.e., do not consider this item for matching. */ NONE("none"), /** Positive if key matches, neutral otherwise. */ KEY("key"), /** Positive if key matches, negative otherwise. */ KEY_REQUIRED("key!"), /** Positive if key and value matches, neutral otherwise. */ KEY_VALUE("keyvalue"), /** Positive if key and value matches, negative otherwise. */ KEY_VALUE_REQUIRED("keyvalue!"); private final String value; MatchType(String value) { this.value = value; } /** * Replies the associated textual value. * @return the associated textual value */ public String getValue() { return value; } /** * Determines the {@code MatchType} for the given textual value. * @param type the textual value * @return the {@code MatchType} for the given textual value */ public static MatchType ofString(String type) { for (MatchType i : EnumSet.allOf(MatchType.class)) { if (i.getValue().equals(type)) return i; } throw new IllegalArgumentException(type + " is not allowed"); } } /** * Usage information on a key */ protected static class Usage { /** * A set of values that were used for this key. */ public final SortedSet<String> values = new TreeSet<>();; // NOSONAR private boolean hadKeys; private boolean hadEmpty; /** * Check if there is exactly one value for this key. * @return <code>true</code> if there was exactly one value. */ public boolean hasUniqueValue() { return values.size() == 1 && !hadEmpty; } /** * Check if this key was not used in any primitive * @return <code>true</code> if it was unused. */ public boolean unused() { return values.isEmpty(); } /** * Get the first value available. * @return The first value * @throws NoSuchElementException if there is no such value. */ public String getFirst() { return values.first(); } /** * Check if we encountered any primitive that had any keys * @return <code>true</code> if any of the primtives had any tags. */ public boolean hadKeys() { return hadKeys; } } protected static Usage determineTextUsage(Collection<OsmPrimitive> sel, String key) { Usage returnValue = new Usage(); for (OsmPrimitive s : sel) { String v = s.get(key); if (v != null) { returnValue.values.add(v); } else { returnValue.hadEmpty = true; } if (s.hasKeys()) { returnValue.hadKeys = true; } } return returnValue; } protected static Usage determineBooleanUsage(Collection<OsmPrimitive> sel, String key) { Usage returnValue = new Usage(); for (OsmPrimitive s : sel) { String booleanValue = OsmUtils.getNamedOsmBoolean(s.get(key)); if (booleanValue != null) { returnValue.values.add(booleanValue); } } return returnValue; } /** * Returns the default match. * @return the default match */ public abstract MatchType getDefaultMatch(); /** * Returns the list of values. * @return the list of values */ public abstract Collection<String> getValues(); protected String getKeyTooltipText() { return tr("This corresponds to the key ''{0}''", key); } @Override protected Boolean matches(Map<String, String> tags) { switch (MatchType.ofString(match)) { case NONE: return null; case KEY: return tags.containsKey(key) ? Boolean.TRUE : null; case KEY_REQUIRED: return tags.containsKey(key); case KEY_VALUE: return tags.containsKey(key) && getValues().contains(tags.get(key)) ? Boolean.TRUE : null; case KEY_VALUE_REQUIRED: return tags.containsKey(key) && getValues().contains(tags.get(key)); default: throw new IllegalStateException(); } } @Override public String toString() { return "KeyedItem [key=" + key + ", text=" + text + ", text_context=" + text_context + ", match=" + match + ']'; } }
Running PMD through: Ant
Error
Exception applying rule DataClass on file org\openstreetmap\josm\gui\tagging\presets\items\KeyedItem.java, continuing with next rule
java.lang.NullPointerException
at net.sourceforge.pmd.lang.ast.AbstractNode.jjtGetChild(AbstractNode.java:104)
at net.sourceforge.pmd.lang.java.metrics.impl.AbstractJavaClassMetric.getDeclarationsOfType(AbstractJavaClassMetric.java:116)
at net.sourceforge.pmd.lang.java.metrics.impl.AbstractJavaClassMetric.getMethodsAndConstructors(AbstractJavaClassMetric.java:94)
at net.sourceforge.pmd.lang.java.metrics.impl.AbstractJavaClassMetric.countMatchingOpSigs(AbstractJavaClassMetric.java:52)
at net.sourceforge.pmd.lang.java.metrics.impl.WocMetric.computeFor(WocMetric.java:35)
at net.sourceforge.pmd.lang.java.metrics.impl.WocMetric.computeFor(WocMetric.java:20)
at net.sourceforge.pmd.lang.metrics.AbstractMetricsComputer.computeForType(AbstractMetricsComputer.java:35)
at net.sourceforge.pmd.lang.metrics.AbstractMetricsFacade.computeForType(AbstractMetricsFacade.java:67)
at net.sourceforge.pmd.lang.java.metrics.JavaMetrics.get(JavaMetrics.java:55)
at net.sourceforge.pmd.lang.java.rule.design.DataClassRule.interfaceRevealsData(DataClassRule.java:48)
at net.sourceforge.pmd.lang.java.rule.design.DataClassRule.visit(DataClassRule.java:30)
at net.sourceforge.pmd.lang.java.rule.AbstractJavaMetricsRule.visit(AbstractJavaMetricsRule.java:25)
at net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration.jjtAccept(ASTClassOrInterfaceDeclaration.java:33)
at net.sourceforge.pmd.lang.java.ast.AbstractJavaNode.childrenAccept(AbstractJavaNode.java:56)
at net.sourceforge.pmd.lang.java.rule.AbstractJavaRule.visit(AbstractJavaRule.java:83)
at net.sourceforge.pmd.lang.java.rule.AbstractJavaRule.visit(AbstractJavaRule.java:120)
at net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration.jjtAccept(ASTClassOrInterfaceBodyDeclaration.java:41)
at net.sourceforge.pmd.lang.java.ast.AbstractJavaNode.childrenAccept(AbstractJavaNode.java:56)
at net.sourceforge.pmd.lang.java.rule.AbstractJavaRule.visit(AbstractJavaRule.java:83)
at net.sourceforge.pmd.lang.java.rule.AbstractJavaRule.visit(AbstractJavaRule.java:116)
at net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody.jjtAccept(ASTClassOrInterfaceBody.java:21)
at net.sourceforge.pmd.lang.java.ast.AbstractJavaNode.childrenAccept(AbstractJavaNode.java:56)
at net.sourceforge.pmd.lang.java.rule.AbstractJavaRule.visit(AbstractJavaRule.java:83)
at net.sourceforge.pmd.lang.java.rule.AbstractJavaMetricsRule.visit(AbstractJavaMetricsRule.java:42)
at net.sourceforge.pmd.lang.java.rule.design.DataClassRule.visit(DataClassRule.java:43)
at net.sourceforge.pmd.lang.java.rule.AbstractJavaMetricsRule.visit(AbstractJavaMetricsRule.java:25)
at net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration.jjtAccept(ASTClassOrInterfaceDeclaration.java:33)
at net.sourceforge.pmd.lang.java.ast.AbstractJavaNode.childrenAccept(AbstractJavaNode.java:56)
at net.sourceforge.pmd.lang.java.rule.AbstractJavaRule.visit(AbstractJavaRule.java:83)
at net.sourceforge.pmd.lang.java.rule.AbstractJavaRule.visit(AbstractJavaRule.java:232)
at net.sourceforge.pmd.lang.java.ast.ASTTypeDeclaration.jjtAccept(ASTTypeDeclaration.java:35)
at net.sourceforge.pmd.lang.java.ast.AbstractJavaNode.childrenAccept(AbstractJavaNode.java:56)
at net.sourceforge.pmd.lang.java.rule.AbstractJavaRule.visit(AbstractJavaRule.java:83)
at net.sourceforge.pmd.lang.java.rule.AbstractJavaRule.visit(AbstractJavaRule.java:212)
at net.sourceforge.pmd.lang.java.rule.AbstractJavaRule.visitAll(AbstractJavaRule.java:39)
at net.sourceforge.pmd.lang.java.rule.AbstractJavaRule.apply(AbstractJavaRule.java:27)
at net.sourceforge.pmd.lang.rule.AbstractDelegateRule.apply(AbstractDelegateRule.java:321)
at net.sourceforge.pmd.RuleSet.apply(RuleSet.java:502)
at net.sourceforge.pmd.RuleSets.apply(RuleSets.java:143)
at net.sourceforge.pmd.SourceCodeProcessor.processSource(SourceCodeProcessor.java:181)
at net.sourceforge.pmd.SourceCodeProcessor.processSourceCode(SourceCodeProcessor.java:95)
at net.sourceforge.pmd.SourceCodeProcessor.processSourceCode(SourceCodeProcessor.java:50)
at net.sourceforge.pmd.processor.PmdRunnable.call(PmdRunnable.java:75)
at net.sourceforge.pmd.processor.MonoThreadProcessor.runAnalysis(MonoThreadProcessor.java:29)
at net.sourceforge.pmd.processor.AbstractPMDProcessor.processFiles(AbstractPMDProcessor.java:111)
at net.sourceforge.pmd.PMD.processFiles(PMD.java:315)
at net.sourceforge.pmd.ant.internal.PMDTaskImpl.doTask(PMDTaskImpl.java:192)
at net.sourceforge.pmd.ant.internal.PMDTaskImpl.execute(PMDTaskImpl.java:276)
at net.sourceforge.pmd.ant.PMDTask.execute(PMDTask.java:49)
at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:293)
at sun.reflect.GeneratedMethodAccessor4.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
at org.apache.tools.ant.Task.perform(Task.java:348)
at org.apache.tools.ant.Target.execute(Target.java:435)
at org.apache.tools.ant.Target.performTasks(Target.java:456)
at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1405)
at org.apache.tools.ant.Project.executeTarget(Project.java:1376)
at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
at org.eclipse.ant.internal.launching.remote.EclipseDefaultExecutor.executeTargets(EclipseDefaultExecutor.java:36)
at org.apache.tools.ant.Project.executeTargets(Project.java:1260)
at org.eclipse.ant.internal.launching.remote.InternalAntRunner.run(InternalAntRunner.java:460)
at org.eclipse.ant.internal.launching.remote.InternalAntRunner.main(InternalAntRunner.java:142)
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4