diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AbstractNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AbstractNode.java index df223b717b..8e6c1de943 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AbstractNode.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AbstractNode.java @@ -26,6 +26,9 @@ import net.sourceforge.pmd.lang.ast.xpath.Attribute; import net.sourceforge.pmd.lang.ast.xpath.AttributeAxisIterator; import net.sourceforge.pmd.lang.ast.xpath.DocumentNavigator; import net.sourceforge.pmd.lang.dfa.DataFlowNode; +import net.sourceforge.pmd.util.DataMap; +import net.sourceforge.pmd.util.DataMap.DataKey; +import net.sourceforge.pmd.util.DataMap.SimpleDataKey; /** @@ -42,6 +45,10 @@ public abstract class AbstractNode implements Node { private static final Logger LOG = Logger.getLogger(AbstractNode.class.getName()); + private static final SimpleDataKey LEGACY_USER_DATA = DataMap.simpleDataKey("legacy user data"); + + private final DataMap> userData = DataMap.newDataMap(); + /** * @deprecated Use {@link #getParent()} */ @@ -73,7 +80,6 @@ public abstract class AbstractNode implements Node { @Deprecated protected GenericToken lastToken; private DataFlowNode dataFlowNode; - private Object userData; // @Deprecated? private String image; @@ -522,12 +528,17 @@ public abstract class AbstractNode implements Node { @Override public Object getUserData() { - return userData; + return userData.get(LEGACY_USER_DATA); } @Override public void setUserData(final Object userData) { - this.userData = userData; + this.userData.set(LEGACY_USER_DATA, userData); + } + + @Override + public DataMap> getUserMap() { + return userData; } /** 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 e1571e8a5f..03a7bcbcfc 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 @@ -14,6 +14,8 @@ 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; +import net.sourceforge.pmd.util.DataMap; +import net.sourceforge.pmd.util.DataMap.DataKey; /** * Root interface for all AST nodes. This interface provides only the API @@ -413,6 +415,14 @@ public interface Node { void removeChildAtIndex(int childIndex); + /** + * Returns a data map used to store additional information on this node. + * This replaces the legacy {@link #getUserData()}/{@link #setUserData(Object)}. + * + * @return The user data map of this node + */ + DataMap> getUserMap(); + /** * Returns the parent of this node, or null if this is the {@linkplain RootNode root} * of the tree. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/DataMap.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/DataMap.java new file mode 100644 index 0000000000..7a38e975bc --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/DataMap.java @@ -0,0 +1,94 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util; + +import java.util.IdentityHashMap; +import java.util.Map; + +/** + * An opaque, strongly typed heterogeneous data container. + * + * @param Type of keys in this map + */ +public class DataMap { + + private final Map, Object> map = new IdentityHashMap<>(); + + private DataMap() { + + } + + /** + * Set the mapping to the given data. + * + * @param key Key + * @param data Data mapped to the key + * @param Type of the data + * + * @return Previous value associated with the key (nullable) + */ + @SuppressWarnings("unchecked") + public T set(DataKey key, T data) { + return (T) map.put(key, data); + } + + /** + * Retrieves the data currently mapped to the key. + * + * @param key Key + * @param Type of the data + * + * @return Value associated with the key (nullable) + */ + @SuppressWarnings("unchecked") + public T get(DataKey key) { + return (T) map.get(key); + } + + /** + * Returns true if the given key has a non-null value in the map. + * + * @param key Key + * + * @return True if some value is set + */ + public boolean isSet(DataKey key) { + return map.containsKey(key); + } + + public static DataMap newDataMap() { + return new DataMap<>(); + } + + public static SimpleDataKey simpleDataKey(final String name) { + return new SimpleDataKey<>(name); + } + + /** + * A key for type-safe access into a {@link DataMap}. Data keys use + * reference identity and are only compared by reference within + * {@link DataMap}. + * + * @param Type of the family of keys this is a part of + * @param Type of the addressed data + */ + public interface DataKey, T> { + + } + + public static class SimpleDataKey implements DataKey, T> { + + private final String name; + + SimpleDataKey(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + } +}