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

jvmMain.com.prof18.rssparser.internal.JvmXmlParser.kt Maven / Gradle / Ivy

package com.prof18.rssparser.internal

import com.prof18.rssparser.exception.RssParsingException
import com.prof18.rssparser.internal.atom.AtomFeedHandler
import com.prof18.rssparser.internal.rss.RssFeedHandler
import com.prof18.rssparser.model.RssChannel
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import okhttp3.internal.closeQuietly
import org.xml.sax.Attributes
import org.xml.sax.InputSource
import org.xml.sax.SAXParseException
import org.xml.sax.helpers.DefaultHandler
import java.nio.charset.Charset
import javax.xml.parsers.SAXParserFactory

internal class JvmXmlParser(
    private val charset: Charset? = null,
    private val dispatcher: CoroutineDispatcher,
) : XmlParser {
    override suspend fun parseXML(input: ParserInput): RssChannel = withContext(dispatcher) {
        try {
            val parser = SAXParserFactory.newInstance().newSAXParser()
            val handler = SaxFeedHandler()

            if (charset != null) {
                val inputSource = InputSource(input.inputStream).apply {
                    encoding = charset.toString()
                }
                parser.parse(inputSource, handler)
            } else {
                parser.parse(input.inputStream, handler)
            }

            return@withContext handler.getChannel()
        } catch (exception: SAXParseException) {
            throw RssParsingException(
                message = "Something went wrong during the parsing of the feed. Please double check if the XML is valid",
                cause = exception
            )
        } finally {
            input.inputStream.closeQuietly()
        }
    }
}

private class SaxFeedHandler : DefaultHandler() {
    private var feedHandler: FeedHandler? = null
    private val textBuilder: StringBuilder = StringBuilder()

    fun getChannel(): RssChannel =
        requireNotNull(feedHandler).buildRssChannel()

    override fun startElement(
        uri: String?,
        localName: String?,
        qName: String?,
        attributes: Attributes?,
    ) {
        textBuilder.setLength(0)

        when (qName) {
            RssKeyword.Rss.value -> {
                feedHandler = RssFeedHandler()
            }
            AtomKeyword.Atom.value -> {
                feedHandler = AtomFeedHandler()
            }
            else -> feedHandler?.onStartRssElement(qName, attributes)
        }
    }

    override fun endElement(
        uri: String?,
        localName: String?,
        qName: String?,
    ) {
        val text = textBuilder.toString().trim()
        feedHandler?.endElement(qName, text)
    }

    override fun characters(ch: CharArray, start: Int, length: Int) {
        textBuilder.append(String(ch, start, length))
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy