package org.nate.internal.dom4j.cssselectors.internal;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import org.dom4j.Branch;
import org.dom4j.Node;
import org.nate.internal.dom4j.cssselectors.DOMHelper;
import se.fishtank.css.selectors.NodeSelectorException;
import se.fishtank.css.selectors.specifier.PseudoClassSpecifier;
import se.fishtank.css.util.Assert;
/**
* Simple port of Christer Sandberg's CSS selectors to Dom4j (https://github.com/chrsan/css-selectors)
*/
public class PseudoClassSpecifierChecker implements NodeTraversalChecker {
private final PseudoClassSpecifier specifier;
/** The set of nodes to check. */
private Set<Branch> nodes;
/** The result of the checks. */
private Set<Branch> result;
public PseudoClassSpecifierChecker(PseudoClassSpecifier 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 ("empty".equals(value)) {
getEmptyElements();
} else if ("first-child".equals(value)) {
getFirstChildElements();
} else if ("first-of-type".equals(value)) {
getFirstOfType();
} else if ("last-child".equals(value)) {
getLastChildElements();
} else if ("last-of-type".equals(value)) {
getLastOfType();
} else if ("only-child".equals(value)) {
getOnlyChildElements();
} else if ("only-of-type".equals(value)) {
getOnlyOfTypeElements();
} else {
throw new NodeSelectorException("Unknown pseudo class: " + value);
}
return result;
}
/**
* Get {@code :empty} elements.
*
* @see <a href="http://www.w3.org/TR/css3-selectors/#empty-pseudo"><code>:empty</code> pseudo-class</a>
*/
private void getEmptyElements() {
for (Branch node : nodes) {
if (!(node instanceof Branch)) {
continue;
}
Iterator<Node> childrenIter = ((Branch) node).nodeIterator();
boolean empty = true;
while (childrenIter.hasNext()) {
Node n = childrenIter.next();
if (n.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
empty = false;
break;
} else if (n.getNodeType() == org.w3c.dom.Node.TEXT_NODE) {
// TODO: Should we trim the text and see if it's length 0?
String value = n.getText();
if (value.length() > 0) {
empty = false;
break;
}
}
}
if (empty) {
result.add(node);
}
}
}
/**
* Get {@code :first-child} elements.
*
* @see <a href="http://www.w3.org/TR/css3-selectors/#first-child-pseudo"><code>:first-child</code> pseudo-class</a>
*/
private void getFirstChildElements() {
for (Branch node : nodes) {
if (DOMHelper.getPreviousSiblingElement(node) == null) {
result.add(node);
}
}
}
/**
* Get {@code :first-of-type} elements.
*
* @see <a href="http://www.w3.org/TR/css3-selectors/#first-of-type-pseudo"><code>:first-of-type</code> pseudo-class</a>
*/
private void getFirstOfType() {
for (Branch node : nodes) {
Branch n = DOMHelper.getPreviousSiblingElement(node);
while (n != null) {
if (n.getName().equals(node.getName())) {
break;
}
n = DOMHelper.getPreviousSiblingElement(n);
}
if (n == null) {
result.add(node);
}
}
}
/**
* Get {@code :last-child} elements.
*
* @see <a href="http://www.w3.org/TR/css3-selectors/#last-child-pseudo"><code>:last-child</code> pseudo-class</a>
*/
private void getLastChildElements() {
for (Branch node : nodes) {
if (DOMHelper.getNextSiblingElement(node) == null) {
result.add(node);
}
}
}
/**
* Get {@code :last-of-type} elements.
*
* @see <a href="http://www.w3.org/TR/css3-selectors/#last-of-type-pseudo"><code>:last-of-type</code> pseudo-class</a>
*/
private void getLastOfType() {
for (Branch node : nodes) {
Branch n = DOMHelper.getNextSiblingElement(node);
while (n != null) {
if (n.getName().equals(node.getName())) {
break;
}
n = DOMHelper.getNextSiblingElement(n);
}
if (n == null) {
result.add(node);
}
}
}
/**
* Get {@code :only-child} elements.
*
* @see <a href="http://www.w3.org/TR/css3-selectors/#only-child-pseudo"><code>:only-child</code> pseudo-class</a>
*/
private void getOnlyChildElements() {
for (Branch node : nodes) {
if (DOMHelper.getPreviousSiblingElement(node) == null &&
DOMHelper.getNextSiblingElement(node) == null) {
result.add(node);
}
}
}
/**
* Get {@code :only-of-type} elements.
*
* @see <a href="http://www.w3.org/TR/css3-selectors/#only-of-type-pseudo"><code>:only-of-type</code> pseudo-class</a>
*/
private void getOnlyOfTypeElements() {
for (Branch node : nodes) {
Branch n = DOMHelper.getPreviousSiblingElement(node);
while (n != null) {
if (n.getName().equals(node.getName())) {
break;
}
n = DOMHelper.getPreviousSiblingElement(n);
}
if (n == null) {
n = DOMHelper.getNextSiblingElement(node);
while (n != null) {
if (n.getName().equals(node.getName())) {
break;
}
n = DOMHelper.getNextSiblingElement(n);
}
if (n == null) {
result.add(node);
}
}
}
}
}