package com.smartandroid.sa.tag.nodes;
import com.smartandroid.sa.tag.helper.StringUtil;
import com.smartandroid.sa.tag.helper.Validate;
/**
* A text node.
*
* @author Jonathan Hedley, jonathan@hedley.net
*/
public class TextNode extends Node {
/*
* TextNode is a node, and so by default comes with attributes and children.
* The attributes are seldom used, but use memory, and the child nodes are
* never used. So we don't have them, and override accessors to attributes
* to create them as needed on the fly.
*/
private static final String TEXT_KEY = "text";
String text;
/**
* Create a new TextNode representing the supplied (unencoded) text).
*
* @param text
* raw text
* @param baseUri
* base uri
* @see #createFromEncoded(String, String)
*/
public TextNode(String text, String baseUri) {
this.baseUri = baseUri;
this.text = text;
}
public String nodeName() {
return "#text";
}
/**
* Get the text content of this text node.
*
* @return Unencoded, normalised text.
* @see TextNode#getWholeText()
*/
public String text() {
return normaliseWhitespace(getWholeText());
}
/**
* Set the text content of this text node.
*
* @param text
* unencoded text
* @return this, for chaining
*/
public TextNode text(String text) {
this.text = text;
if (attributes != null)
attributes.put(TEXT_KEY, text);
return this;
}
/**
* Get the (unencoded) text of this text node, including any newlines and
* spaces present in the original.
*
* @return text
*/
public String getWholeText() {
return attributes == null ? text : attributes.get(TEXT_KEY);
}
/**
* Test if this text node is blank -- that is, empty or only whitespace
* (including newlines).
*
* @return true if this document is empty or only whitespace, false if it
* contains any text content.
*/
public boolean isBlank() {
return StringUtil.isBlank(getWholeText());
}
/**
* Split this text node into two nodes at the specified string offset. After
* splitting, this node will contain the original text up to the offset, and
* will have a new text node sibling containing the text after the offset.
*
* @param offset
* string offset point to split node at.
* @return the newly created text node containing the text after the offset.
*/
public TextNode splitText(int offset) {
Validate.isTrue(offset >= 0, "Split offset must be not be negative");
Validate.isTrue(offset < text.length(),
"Split offset must not be greater than current text length");
String head = getWholeText().substring(0, offset);
String tail = getWholeText().substring(offset);
text(head);
TextNode tailNode = new TextNode(tail, this.baseUri());
if (parent() != null)
parent().addChildren(siblingIndex() + 1, tailNode);
return tailNode;
}
void outerHtmlHead(StringBuilder accum, int depth,
Document.OutputSettings out) {
if (out.prettyPrint()
&& ((siblingIndex() == 0 && parentNode instanceof Element
&& ((Element) parentNode).tag().formatAsBlock() && !isBlank()) || (out
.outline() && siblingNodes().size() > 0 && !isBlank())))
indent(accum, depth, out);
boolean normaliseWhite = out.prettyPrint()
&& parent() instanceof Element
&& !Element.preserveWhitespace((Element) parent());
Entities.escape(accum, getWholeText(), out, false, normaliseWhite,
false);
}
void outerHtmlTail(StringBuilder accum, int depth,
Document.OutputSettings out) {
}
public String toString() {
return outerHtml();
}
/**
* Create a new TextNode from HTML encoded (aka escaped) data.
*
* @param encodedText
* Text containing encoded HTML (e.g. <)
* @return TextNode containing unencoded data (e.g. <)
*/
public static TextNode createFromEncoded(String encodedText, String baseUri) {
String text = Entities.unescape(encodedText);
return new TextNode(text, baseUri);
}
static String normaliseWhitespace(String text) {
text = StringUtil.normaliseWhitespace(text);
return text;
}
static String stripLeadingWhitespace(String text) {
return text.replaceFirst("^\\s+", "");
}
static boolean lastCharIsWhitespace(StringBuilder sb) {
return sb.length() != 0 && sb.charAt(sb.length() - 1) == ' ';
}
// attribute fiddling. create on first access.
private void ensureAttributes() {
if (attributes == null) {
attributes = new Attributes();
attributes.put(TEXT_KEY, text);
}
}
@Override
public String attr(String attributeKey) {
ensureAttributes();
return super.attr(attributeKey);
}
@Override
public Attributes attributes() {
ensureAttributes();
return super.attributes();
}
@Override
public Node attr(String attributeKey, String attributeValue) {
ensureAttributes();
return super.attr(attributeKey, attributeValue);
}
@Override
public boolean hasAttr(String attributeKey) {
ensureAttributes();
return super.hasAttr(attributeKey);
}
@Override
public Node removeAttr(String attributeKey) {
ensureAttributes();
return super.removeAttr(attributeKey);
}
@Override
public String absUrl(String attributeKey) {
ensureAttributes();
return super.absUrl(attributeKey);
}
}