diff --git a/src/main/java/org/jsoup/select/Evaluator.java b/src/main/java/org/jsoup/select/Evaluator.java index 3303684783..2f31c12103 100644 --- a/src/main/java/org/jsoup/select/Evaluator.java +++ b/src/main/java/org/jsoup/select/Evaluator.java @@ -80,6 +80,27 @@ public String toString() { } } + /** + * Evaluator for element namespace + */ + public static final class Namespace extends Evaluator { + private final String nameSpace; + + public Namespace(String nameSpace) { + this.nameSpace = nameSpace; + } + + @Override + public boolean matches(Element root, Element element) { + return (element.normalName().startsWith(nameSpace)); + } + + @Override + public String toString() { + return String.format("%s", nameSpace); + } + } + /** * Evaluator for element id */ diff --git a/src/main/java/org/jsoup/select/QueryParser.java b/src/main/java/org/jsoup/select/QueryParser.java index b049df3d74..ed47527883 100644 --- a/src/main/java/org/jsoup/select/QueryParser.java +++ b/src/main/java/org/jsoup/select/QueryParser.java @@ -162,6 +162,8 @@ private void findElements() { byId(); else if (tq.matchChomp(".")) byClass(); + else if (tq.toString().endsWith("|*")) + byNamespace(); else if (tq.matchesWord() || tq.matches("*|")) byTag(); else if (tq.matches("[")) @@ -262,6 +264,12 @@ private void byTag() { } } + private void byNamespace() { + String nameSpace = normalize(tq.consumeElementSelector()); + Validate.notEmpty(nameSpace); + evals.add(new Evaluator.Namespace(nameSpace.replace("|", ":"))); + } + private void byAttribute() { TokenQueue cq = new TokenQueue(tq.chompBalanced('[', ']')); // content queue String key = cq.consumeToAny(AttributeEvals); // eq, not, start, end, contain, match, (no val) @@ -312,7 +320,7 @@ private void indexGreaterThan() { private void indexEquals() { evals.add(new Evaluator.IndexEquals(consumeIndex())); } - + //pseudo selectors :first-child, :last-child, :nth-child, ... private static final Pattern NTH_AB = Pattern.compile("(([+-])?(\\d+)?)n(\\s*([+-])?\\s*\\d+)?", Pattern.CASE_INSENSITIVE); private static final Pattern NTH_B = Pattern.compile("([+-])?(\\d+)"); diff --git a/src/main/java/org/jsoup/select/Selector.java b/src/main/java/org/jsoup/select/Selector.java index 71d0bc2a19..cf3adc9ce1 100644 --- a/src/main/java/org/jsoup/select/Selector.java +++ b/src/main/java/org/jsoup/select/Selector.java @@ -27,6 +27,7 @@ * tagelements with the given tag namediv * *|Eelements of type E in any namespace (including non-namespaced)*|name finds <fb:name> and <name> elements * ns|Eelements of type E in the namespace nsfb|name finds <fb:name> elements + * ns|*all elements in the namespace nsfb|* finds <fb:name> and <fb:school> elements * #idelements with attribute ID of "id"div#wrap, #logo * .classelements with a class name of "class"div.left, .result * [attr]elements with an attribute named "attr" (with any value)a[href], [title] diff --git a/src/test/java/org/jsoup/nodes/ElementTest.java b/src/test/java/org/jsoup/nodes/ElementTest.java index 5ff3041563..57414d105a 100644 --- a/src/test/java/org/jsoup/nodes/ElementTest.java +++ b/src/test/java/org/jsoup/nodes/ElementTest.java @@ -1399,6 +1399,16 @@ public void testNamespacedElements() { assertEquals("html > body > fb|comments", els.get(0).cssSelector()); } + @Test + public void testSelectByNamespace() { + String html = "p in namespace nsimg in namespace ns"; + Document doc = Jsoup.parse(html); + Elements els = doc.select("ns|*"); + assertEquals(2, els.size()); + assertEquals("html > body > ns|p", els.get(0).cssSelector()); + assertEquals("html > body > ns|img", els.get(1).cssSelector()); + } + @Test public void testChainedRemoveAttributes() { String html = "Text";