package org.nate.internal.dom4j.cssselectors.internal; import java.util.LinkedHashSet; import java.util.Set; import org.dom4j.Branch; import org.nate.internal.dom4j.cssselectors.DOMHelper; import se.fishtank.css.selectors.NodeSelectorException; import se.fishtank.css.selectors.specifier.PseudoNthSpecifier; import se.fishtank.css.util.Assert; /** * Simple port of Christer Sandberg's CSS selectors to Dom4j (https://github.com/chrsan/css-selectors) */ public class PseudoNthSpecifierChecker implements NodeTraversalChecker { /** The {@code nth-*} pseudo-class specifier to check against. */ private final PseudoNthSpecifier specifier; /** The set of nodes to check. */ private Set<Branch> nodes; /** The result of the checks. */ private Set<Branch> result; public PseudoNthSpecifierChecker(PseudoNthSpecifier specifier) { Assert.notNull(specifier, "specifier is null!"); this.specifier = specifier; } @Override public Set<Branch> check(Set<Branch> nodes) throws NodeSelectorException { Assert.notNull(nodes, "nodes is null!"); this.nodes = nodes; result = new LinkedHashSet<Branch>(); String value = specifier.getValue(); if ("nth-child".equals(value)) { getNthChild(); } else if ("nth-last-child".equals(value)) { getNthLastChild(); } else if ("nth-of-type".equals(value)) { getNthOfType(); } else if ("nth-last-of-type".equals(value)) { getNthLastOfType(); } else { throw new NodeSelectorException("Unknown pseudo nth class: " + value); } return result; } /** * Get the {@code :nth-child} elements. * * @see <a href="http://www.w3.org/TR/css3-selectors/#nth-child-pseudo"><code>:nth-child</code> pseudo-class</a> */ private void getNthChild() { for (Branch node : nodes) { int count = 1; Branch n = DOMHelper.getPreviousSiblingElement(node); while (n != null) { count++; n = DOMHelper.getPreviousSiblingElement(n); } if (specifier.isMatch(count)) { result.add(node); } } } /** * Get {@code :nth-last-child} elements. * * @see <a href="http://www.w3.org/TR/css3-selectors/#nth-last-child-pseudo"><code>:nth-last-child</code> pseudo-class</a> */ private void getNthLastChild() { for (Branch node : nodes) { int count = 1; Branch n = DOMHelper.getNextSiblingElement(node); while (n != null) { count++; n = DOMHelper.getNextSiblingElement(n); } if (specifier.isMatch(count)) { result.add(node); } } } /** * Get {@code :nth-of-type} elements. * * @see <a href="http://www.w3.org/TR/css3-selectors/#nth-of-type-pseudo"><code>:nth-of-type</code> pseudo-class</a> */ private void getNthOfType() { for (Branch node : nodes) { int count = 1; Branch n = DOMHelper.getPreviousSiblingElement(node); while (n != null) { if (n.getName().equals(node.getName())) { count++; } n = DOMHelper.getPreviousSiblingElement(n); } if (specifier.isMatch(count)) { result.add(node); } } } /** * Get {@code nth-last-of-type} elements. * * @see <a href="http://www.w3.org/TR/css3-selectors/#nth-last-of-type-pseudo"><code>:nth-last-of-type</code> pseudo-class</a> */ private void getNthLastOfType() { for (Branch node : nodes) { int count = 1; Branch n = DOMHelper.getNextSiblingElement(node); while (n != null) { if (n.getName().equals(node.getName())) { count++; } n = DOMHelper.getNextSiblingElement(n); } if (specifier.isMatch(count)) { result.add(node); } } } }