001package org.jsoup.nodes; 002 003import org.jsoup.helper.Validate; 004import org.jsoup.internal.QuietAppendable; 005import org.jsoup.internal.StringUtil; 006 007 008/** 009 A text node. 010 011 @author Jonathan Hedley, jonathan@hedley.net */ 012public class TextNode extends LeafNode { 013 /** 014 Create a new TextNode representing the supplied (unencoded) text). 015 016 @param text raw text 017 @see #createFromEncoded(String) 018 */ 019 public TextNode(String text) { 020 super(text); 021 } 022 023 @Override public String nodeName() { 024 return "#text"; 025 } 026 027 /** 028 * Get the text content of this text node. 029 * @return Unencoded, normalised text. 030 * @see TextNode#getWholeText() 031 */ 032 public String text() { 033 return StringUtil.normaliseWhitespace(getWholeText()); 034 } 035 036 /** 037 * Set the text content of this text node. 038 * @param text unencoded text 039 * @return this, for chaining 040 */ 041 public TextNode text(String text) { 042 coreValue(text); 043 return this; 044 } 045 046 /** 047 Get the (unencoded) text of this text node, including any newlines and spaces present in the original. 048 @return text 049 */ 050 public String getWholeText() { 051 return coreValue(); 052 } 053 054 /** 055 Test if this text node is blank -- that is, empty or only whitespace (including newlines). 056 @return true if this document is empty or only whitespace, false if it contains any text content. 057 */ 058 public boolean isBlank() { 059 return StringUtil.isBlank(coreValue()); 060 } 061 062 /** 063 * Split this text node into two nodes at the specified string offset. After splitting, this node will contain the 064 * original text up to the offset, and will have a new text node sibling containing the text after the offset. 065 * @param offset string offset point to split node at. 066 * @return the newly created text node containing the text after the offset. 067 */ 068 public TextNode splitText(int offset) { 069 final String text = coreValue(); 070 Validate.isTrue(offset >= 0, "Split offset must be not be negative"); 071 Validate.isTrue(offset < text.length(), "Split offset must not be greater than current text length"); 072 073 String head = text.substring(0, offset); 074 String tail = text.substring(offset); 075 text(head); 076 TextNode tailNode = new TextNode(tail); 077 if (parentNode != null) 078 parentNode.addChildren(siblingIndex()+1, tailNode); 079 080 return tailNode; 081 } 082 083 @Override 084 void outerHtmlHead(QuietAppendable accum, Document.OutputSettings out) { 085 Entities.escape(accum, coreValue(), out, Entities.ForText); 086 } 087 088 @Override 089 public String toString() { 090 return outerHtml(); 091 } 092 093 @Override 094 public TextNode clone() { 095 return (TextNode) super.clone(); 096 } 097 098 /** 099 * Create a new TextNode from HTML encoded (aka escaped) data. 100 * @param encodedText Text containing encoded HTML (e.g. {@code <}) 101 * @return TextNode containing unencoded data (e.g. {@code <}) 102 */ 103 public static TextNode createFromEncoded(String encodedText) { 104 String text = Entities.unescape(encodedText); 105 return new TextNode(text); 106 } 107 108 static String normaliseWhitespace(String text) { 109 text = StringUtil.normaliseWhitespace(text); 110 return text; 111 } 112 113 static String stripLeadingWhitespace(String text) { 114 return text.replaceFirst("^\\s+", ""); 115 } 116 117 static boolean lastCharIsWhitespace(StringBuilder sb) { 118 return sb.length() != 0 && sb.charAt(sb.length() - 1) == ' '; 119 } 120}