commit
7d102cb509
8 files changed
+225
-28
No files matched your search
@@ -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();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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
|
||||
|
||||
+14
-1
@@ -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
|
||||
|
||||
+14
-18
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user