All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.fleeksoft.ksoup.nodes.TextNode.kt Maven / Gradle / Ivy

package com.fleeksoft.ksoup.nodes

import com.fleeksoft.ksoup.helper.Validate
import com.fleeksoft.ksoup.internal.StringUtil
import okio.IOException

/**
 * A text node.
 *
 * @author Sabeeh, [email protected]
 */
public open class TextNode(text: String) : LeafNode() {
    /**
     * Create a new TextNode representing the supplied (unencoded) text).
     *
     * @param text raw text
     * @see .createFromEncoded
     */
    init {
        value = text
    }

    override fun nodeName(): String {
        return "#text"
    }

    /**
     * Get the text content of this text node.
     * @return Unencoded, normalised text.
     * @see TextNode.getWholeText
     */
    public open fun text(): String {
        return StringUtil.normaliseWhitespace(getWholeText())
    }

    /**
     * Set the text content of this text node.
     * @param text unencoded text
     * @return this, for chaining
     */
    public fun text(text: String?): TextNode {
        coreValue(text)
        return this
    }

    public fun getWholeText(): String = coreValue()

    public fun isBlank(): Boolean = StringUtil.isBlank(coreValue())

    /**
     * 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 fun splitText(offset: Int): TextNode {
        val text: String = coreValue()
        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",
        )
        val head = text.substring(0, offset)
        val tail = text.substring(offset)
        text(head)
        val tailNode = TextNode(tail)
        if (parentNode != null) parentNode!!.addChildren(siblingIndex() + 1, tailNode)
        return tailNode
    }

    @Throws(IOException::class)
    override fun outerHtmlHead(
        accum: Appendable,
        depth: Int,
        out: Document.OutputSettings,
    ) {
        val prettyPrint: Boolean = out.prettyPrint()
        val parent: Element? = if (parentNode is Element) parentNode as Element? else null
        val normaliseWhite = prettyPrint && !Element.preserveWhitespace(parentNode)
        val trimLikeBlock = parent != null && (parent.tag().isBlock || parent.tag().formatAsBlock())
        var trimLeading = false
        var trimTrailing = false
        if (normaliseWhite) {
            trimLeading = trimLikeBlock && siblingIndex == 0 || parentNode is Document
            trimTrailing = trimLikeBlock && nextSibling() == null

            // if this text is just whitespace, and the next node will cause an indent, skip this text:
            val next: Node? = nextSibling()
            val prev: Node? = previousSibling()
            val isBlank = isBlank()
            val couldSkip =
                next is Element && next.shouldIndent(out) || next is TextNode && next.isBlank() || prev is Element && (
                    prev.isBlock() || prev.nameIs("br")
                ) // br is a bit special - make sure we don't get a dangling blank line, but not a block otherwise wraps in head
            if (couldSkip && isBlank) return
            if (
                (prev == null && parent != null && parent.tag().formatAsBlock() && !isBlank) ||
                (out.outline() && siblingNodes().isNotEmpty() && !isBlank) ||
                (prev != null && prev.nameIs("br")) // special case wrap on inline 
- doesn't make sense as a block tag ) { indent(accum, depth, out) } } Entities.escape(accum, coreValue(), out, false, normaliseWhite, trimLeading, trimTrailing) } @Throws(IOException::class) override fun outerHtmlTail( accum: Appendable, depth: Int, out: Document.OutputSettings, ) { } override fun toString(): String { return outerHtml() } override fun createClone(): Node { val clone = TextNode("") clone.value = this.value return clone } override fun clone(): TextNode { return super.clone() as TextNode } public companion object { /** * 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 fun createFromEncoded(encodedText: String): TextNode { val text: String = Entities.unescape(encodedText) return TextNode(text) } internal fun normaliseWhitespace(text: String): String { return StringUtil.normaliseWhitespace(text) } internal fun stripLeadingWhitespace(text: String): String { return text.replaceFirst("^\\s+".toRegex(), "") } internal fun lastCharIsWhitespace(sb: StringBuilder): Boolean { return sb.isNotEmpty() && sb[sb.length - 1] == ' ' } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy