Merge branch 'deprecate-find-xx-of-any-type' into 7.0.x

Refs #2886
This commit is contained in:
Clément Fournier committed 2020-12-10 20:40:06 +01:00
commit 7d102cb509
8 files changed
+225 -28

No files matched your search

+19
View File
@@ -46,6 +46,25 @@ Support for XPath versions 1.0, 1.0-compatibility was removed, support for XPath
* The deprecated support for sequence-valued attributes is removed. Sequence-valued properties are still supported.
* Refer to [the Saxonica documentation](https://www.saxonica.com/html/documentation/expressions/xpath31new.html) for an introduction to new features in XPath 3.1.
#### Node stream API
This version includes a powerful API to navigate trees, similar in usage to the Java 8 Stream API:
```java
node.descendants(ASTMethodCall.class)
.filter(m -> "toString".equals(m.getMethodName()))
.map(m -> m.getQualifier())
.filter(q -> TypeTestUtil.isA(String.class, q))
.foreach(System.out::println);
```
A pipeline like shown here traverses the tree lazily, which is more efficient than traversing eagerly to put all descendants in a list. It is also much easier to change than the old imperative way.
To make this API as accessible as possible, the {% jdoc core::lang.ast.Node %} interface has been fitted with new methods producing node streams. Those methods replace previous tree traversal methods like `Node#findDescendantsOfType`. In all cases, they should be more efficient and more convenient.
See {% jdoc core::lang.ast.NodeStream %} for more details.
#### JavaScript support
The JS specific parser options have been removed. The parser now always retains comments and uses version ES6.
File diff suppressed because it is too large. Load diff
@@ -183,4 +183,20 @@ public abstract class AbstractNode<B extends AbstractNode<B, N>, N extends Gener
return getXPathNodeName();
}
@Override
@SuppressWarnings("unchecked")
public final <R extends Node> @Nullable R firstChild(Class<? extends R> rClass) {
// This operation is extremely common so we give it an optimal
// implementation, based directly on the array. This will never
// create a node stream object, and array bounds are not checked.
// It's final so it can be inlined.
for (Node child : children) {
if (rClass.isInstance(child)) {
// rClass.cast(child) is more expensive than this
// unchecked cast, which we know is safe.
return (R) child;
}
}
return null;
}
}
@@ -83,4 +83,14 @@ public interface GenericNode<N extends GenericNode<N>> extends Node {
default NodeStream<N> ancestors() {
return (NodeStream<N>) Node.super.ancestors();
}
@Override
default @Nullable N getPreviousSibling() {
return (N) Node.super.getPreviousSibling();
}
@Override
default @Nullable N getNextSibling() {
return (N) Node.super.getNextSibling();
}
}
@@ -162,7 +162,7 @@ final class SingletonNodeStream<T extends Node> extends IteratorBasedNStream<T>
@Override
public <R extends Node> NodeStream<R> firstChild(Class<? extends R> rClass) {
return NodeStream.of(TraversalUtils.getFirstChildMatching(node, Filtermap.isInstance(rClass), 0, node.getNumChildren()));
return NodeStream.of(node.firstChild(rClass));
}
@Override
@@ -39,7 +39,20 @@ public class AttributeAxisIterator implements Iterator<Attribute> {
Long.TYPE, Character.TYPE, Float.TYPE));
private static final Set<String> FILTERED_OUT_NAMES
= new HashSet<>(Arrays.asList("toString", "getNumChildren", "getIndexInParent", "getParent", "getClass", "getRuleIndex", "getXPathNodeName", "altNumber", "toStringTree", "getTypeNameNode", "hashCode", "getImportedNameNode", "getScope"));
= new HashSet<>(Arrays.asList("toString",
"getNumChildren",
"getIndexInParent",
"getParent",
"getClass",
"isFindBoundary",
"getRuleIndex",
"getXPathNodeName",
"altNumber",
"toStringTree",
"getTypeNameNode",
"hashCode",
"getImportedNameNode",
"getScope"));
/* Iteration variables */
private final Iterator<MethodWrapper> iterator;
@@ -294,6 +294,23 @@ public final class CollectionUtil {
return newM;
}
/**
* Returns an unmodifiable set containing the set union of the collection,
* and the new elements.
*/
@SafeVarargs
@SuppressWarnings("unchecked")
public static <V> Set<V> setUnion(Collection<? extends V> set, V first, V... newElements) {
if (set instanceof PSet) {
return ((PSet<V>) set).plus(first).plusAll(Arrays.asList(newElements));
}
Set<V> newSet = new LinkedHashSet<>(set.size() + 1 + newElements.length);
newSet.addAll(set);
newSet.add(first);
Collections.addAll(newSet, newElements);
return Collections.unmodifiableSet(newSet);
}
/**
* Returns a map associating each key in the first list to its
@@ -5,21 +5,22 @@
package net.sourceforge.pmd.lang.rule.xpath.impl;
import static net.sourceforge.pmd.util.CollectionUtil.setOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Test;
import net.sourceforge.pmd.lang.ast.DummyNode;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.xpath.Attribute;
import net.sourceforge.pmd.util.CollectionUtil;
/**
@@ -27,6 +28,8 @@ import net.sourceforge.pmd.lang.rule.xpath.Attribute;
*/
public class AttributeAxisIteratorTest {
private static final Set<String> DEFAULT_ATTRS = setOf("BeginColumn", "BeginLine", "Image", "EndColumn", "EndLine");
/**
* Test hasNext and next.
*/
@@ -36,14 +39,8 @@ public class AttributeAxisIteratorTest {
dummyNode.setCoords(1, 1, 2, 2);
AttributeAxisIterator it = new AttributeAxisIterator(dummyNode);
Map<String, Attribute> atts = toMap(it);
assertEquals(6, atts.size());
assertTrue(atts.containsKey("BeginColumn"));
assertTrue(atts.containsKey("BeginLine"));
assertTrue(atts.containsKey("FindBoundary"));
assertTrue(atts.containsKey("Image"));
assertTrue(atts.containsKey("EndColumn"));
assertTrue(atts.containsKey("EndLine"));
assertEquals(DEFAULT_ATTRS, toMap(it).keySet());
}
@Test
@@ -51,21 +48,20 @@ public class AttributeAxisIteratorTest {
DummyNodeWithEnum dummyNode = new DummyNodeWithEnum();
AttributeAxisIterator it = new AttributeAxisIterator(dummyNode);
Map<String, Attribute> atts = toMap(it);
assertEquals(7, atts.size());
assertTrue(atts.containsKey("Enum"));
assertEquals(DummyNodeWithEnum.MyEnum.FOO, atts.get("Enum").getValue());
Set<String> expected = CollectionUtil.setUnion(DEFAULT_ATTRS, "Enum");
assertEquals(expected, toMap(it).keySet());
}
@Test
public void testAttributeAxisIteratorWithList() {
// list attributes are not supported anymore
DummyNodeWithList dummyNode = new DummyNodeWithList();
AttributeAxisIterator it = new AttributeAxisIterator(dummyNode);
Map<String, Attribute> atts = toMap(it);
assertEquals(6, atts.size());
assertFalse(atts.containsKey("List"));
assertFalse(atts.containsKey("NodeList"));
assertEquals(DEFAULT_ATTRS, toMap(it).keySet());
}
private Map<String, Attribute> toMap(AttributeAxisIterator it) {