LibreTube/app/src/main/java/com/github/libretube/util/HtmlParser.kt

135 lines
4.0 KiB
Kotlin
Raw Normal View History

2023-01-16 21:36:23 +05:30
package com.github.libretube.util
import android.text.Editable
import android.text.Html
import android.text.Spanned
import androidx.core.text.HtmlCompat
import org.xml.sax.Attributes
import org.xml.sax.ContentHandler
import org.xml.sax.Locator
import org.xml.sax.SAXException
import org.xml.sax.XMLReader
class HtmlParser private constructor(private val handler: TagHandler) :
Html.TagHandler,
ContentHandler {
2023-01-16 21:36:23 +05:30
private val tagStatus = ArrayDeque<Boolean>()
private var wrapped: ContentHandler? = null
private var text: Editable? = null
override fun handleTag(opening: Boolean, tag: String, output: Editable, xmlReader: XMLReader) {
if (wrapped == null) {
// record result object
text = output
// record current content handler
wrapped = xmlReader.contentHandler
// replace content handler with our own that forwards to calls to original when needed
xmlReader.contentHandler = this
// handle endElement() callback for <inject/> tag
tagStatus.addLast(java.lang.Boolean.FALSE)
}
}
@Throws(SAXException::class)
override fun startElement(
uri: String,
localName: String,
qName: String,
attributes: Attributes
) {
2023-01-16 21:36:23 +05:30
val isHandled = handler.handleTag(true, localName, text, attributes)
tagStatus.addLast(isHandled)
if (!isHandled) {
wrapped?.startElement(uri, localName, qName, attributes)
}
}
@Throws(SAXException::class)
override fun endElement(uri: String, localName: String, qName: String) {
2023-01-16 21:36:23 +05:30
if (!tagStatus.removeLast()) {
wrapped?.endElement(uri, localName, qName)
}
handler.handleTag(false, localName, text, null)
}
override fun setDocumentLocator(locator: Locator) {
wrapped?.setDocumentLocator(locator)
}
@Throws(SAXException::class)
override fun startDocument() {
2023-01-16 21:36:23 +05:30
wrapped?.startDocument()
}
@Throws(SAXException::class)
override fun endDocument() {
2023-01-16 21:36:23 +05:30
wrapped?.endDocument()
}
@Throws(SAXException::class)
override fun startPrefixMapping(prefix: String, uri: String) {
2023-01-16 21:36:23 +05:30
wrapped?.startPrefixMapping(prefix, uri)
}
@Throws(SAXException::class)
override fun endPrefixMapping(prefix: String) {
2023-01-16 21:36:23 +05:30
wrapped?.endPrefixMapping(prefix)
}
@Throws(SAXException::class)
override fun characters(ch: CharArray, start: Int, length: Int) {
2023-01-16 21:36:23 +05:30
wrapped?.characters(ch, start, length)
}
@Throws(SAXException::class)
override fun ignorableWhitespace(ch: CharArray, start: Int, length: Int) {
2023-01-16 21:36:23 +05:30
wrapped?.ignorableWhitespace(ch, start, length)
}
@Throws(SAXException::class)
override fun processingInstruction(target: String, data: String) {
2023-01-16 21:36:23 +05:30
wrapped?.processingInstruction(target, data)
}
@Throws(SAXException::class)
override fun skippedEntity(name: String) {
2023-01-16 21:36:23 +05:30
wrapped?.skippedEntity(name)
}
interface TagHandler {
fun handleTag(
opening: Boolean,
tag: String?,
output: Editable?,
attributes: Attributes?
): Boolean
2023-01-16 21:36:23 +05:30
}
companion object {
fun createSpannedText(html: String, handler: TagHandler): Spanned {
// add a tag at the start that is not handled by default,
// allowing custom tag handler to replace xmlReader contentHandler
return HtmlCompat.fromHtml(
html,
HtmlCompat.FROM_HTML_MODE_LEGACY,
null,
HtmlParser(handler)
)
2023-01-16 21:36:23 +05:30
}
@JvmStatic fun getValue(attributes: Attributes, name: String): String? {
var i = 0
val n = attributes.length
while (i < n) {
if (name.equals(attributes.getLocalName(i), ignoreCase = true)) {
return attributes.getValue(i)
}
i++
}
return null
}
}
}