package org.nate.internal.jsoup;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.select.Selector.SelectorParseException;
import org.nate.encoder.NateDocument;
import org.nate.encoder.NateNode;
import org.nate.exception.BadCssExpressionException;
public class JsoupBackedNateElement extends JsoupBackedAbstractNode {
private Element element;
public JsoupBackedNateElement(Element element) {
this.element = element;
}
@Override
public NateNode copy() {
verifyState();
return new JsoupBackedNateElement(element.clone());
}
@Override
public void removeAttribute(String attributeName) {
verifyState();
element.removeAttr(attributeName);
}
@Override
public String render() {
verifyState();
return element.outerHtml();
}
@Override
public void replaceChildren(NateDocument newChildrenSource) {
verifyState();
removeChildren();
if (!(newChildrenSource instanceof JsoupBackedNateDocumentFragment)) {
throw new IllegalStateException(
"Internal Error. Expected JsoupBackedNateDocumentFragment, but got: " + newChildrenSource);
}
Collection<Node> newChildren = ((JsoupBackedAbstractNode)newChildrenSource).getJsoupNodes();
for (Node node : newChildren) {
this.element.appendChild(node.clone());
}
}
@Override
public void replaceWith(List<NateNode> newNodes) {
verifyState();
Element parent = this.element.parent();
List<Node> newFamily = new ArrayList<Node>();
for (Node sibling : parent.childNodes()) {
if (sibling == this.element) {
newFamily.addAll(cloneAll(jsoupNodesOf(newNodes)));
} else {
newFamily.add(sibling);
}
}
removeChildrenFrom(parent);
for (Node node : newFamily) {
parent.appendChild(node);
}
// This element has been removed, and so no further operations will be valid.
invalidate();
}
private static Collection<Node> cloneAll(Collection<Node> originals) {
Collection<Node> clones = new ArrayList<Node>(originals.size());
for (Node node : originals) {
clones.add(node.clone());
}
return clones;
}
private static List<Node> jsoupNodesOf(List<NateNode> nateNodes) {
List<Node> jsoupNodes = new ArrayList<Node>(nateNodes.size());
for (NateNode nateNode : nateNodes) {
jsoupNodes.addAll(((JsoupBackedAbstractNode) nateNode).getJsoupNodes());
}
return jsoupNodes;
}
@Override
public void setAttribute(String name, String value) {
verifyState();
element.attr(name, value);
}
@Override
public void setTextContent(String text) {
verifyState();
removeChildren();
element.appendText(text);
}
private void removeChildren() {
removeChildrenFrom(element);
}
private static void removeChildrenFrom(Element element) {
// TODO: Measure performance. I bet this is quadratic in number of children!!!!
// Copy into new list to avoid ConcurrentModificationException
List<Node> childNodes = new ArrayList<Node>(element.childNodes());
for (Node childNode : childNodes) {
childNode.remove();
}
}
@Override
protected List<Element> findMatchingElements(String selector) {
try {
return element.select(selector);
} catch (SelectorParseException e) {
throw new BadCssExpressionException(e);
}
}
@Override
Collection<Node> getJsoupNodes() {
return Collections.singleton((Node) element);
}
}