From ca324995b36874ba74ced8f00a455ab6a0b5912a Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Wed, 23 Oct 2024 15:02:55 +0800 Subject: [PATCH] Clean up ExceptionUtils with `Either.catch` --- .../ehviewer/client/parser/ForumsParser.kt | 6 +- .../client/parser/GalleryDetailParser.kt | 227 +++++++++--------- .../parser/GalleryNotAvailableParser.kt | 6 +- .../client/parser/GalleryTokenApiParser.kt | 6 +- .../ehviewer/client/parser/ProfileParser.kt | 9 +- .../com/hippo/ehviewer/util/AppHelper.kt | 5 +- .../com/hippo/ehviewer/util/ExceptionUtils.kt | 37 +-- 7 files changed, 125 insertions(+), 171 deletions(-) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/client/parser/ForumsParser.kt b/app/src/main/kotlin/com/hippo/ehviewer/client/parser/ForumsParser.kt index 1aea9f6d7..d6a572b88 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/client/parser/ForumsParser.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/client/parser/ForumsParser.kt @@ -15,19 +15,19 @@ */ package com.hippo.ehviewer.client.parser +import arrow.core.Either +import arrow.core.getOrElse import com.hippo.ehviewer.client.EhUrl import com.hippo.ehviewer.client.exception.NotLoggedInException -import com.hippo.ehviewer.util.ExceptionUtils import org.jsoup.Jsoup object ForumsParser { - fun parse(body: String): String = runCatching { + fun parse(body: String): String = Either.catch { val d = Jsoup.parse(body, EhUrl.URL_FORUMS) val userlinks = d.getElementById("userlinks") val child = userlinks!!.child(0).child(0).child(0) child.attr("href") }.getOrElse { - ExceptionUtils.throwIfFatal(it) throw NotLoggedInException() } } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/client/parser/GalleryDetailParser.kt b/app/src/main/kotlin/com/hippo/ehviewer/client/parser/GalleryDetailParser.kt index d59984f69..71aac79fe 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/client/parser/GalleryDetailParser.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/client/parser/GalleryDetailParser.kt @@ -15,12 +15,13 @@ */ package com.hippo.ehviewer.client.parser +import arrow.core.Either +import arrow.core.getOrElse import arrow.core.unzip import com.hippo.ehviewer.EhDB import com.hippo.ehviewer.Settings import com.hippo.ehviewer.client.EhFilter import com.hippo.ehviewer.client.EhUtils -import com.hippo.ehviewer.client.EhUtils.getCategory import com.hippo.ehviewer.client.data.BaseGalleryInfo import com.hippo.ehviewer.client.data.GalleryComment import com.hippo.ehviewer.client.data.GalleryCommentList @@ -37,7 +38,6 @@ import com.hippo.ehviewer.client.exception.ParseException import com.hippo.ehviewer.client.exception.PiningException import com.hippo.ehviewer.client.getNormalPreviewKey import com.hippo.ehviewer.client.getThumbKey -import com.hippo.ehviewer.util.ExceptionUtils import com.hippo.ehviewer.util.toEpochMillis import com.hippo.ehviewer.util.toFloatOrDefault import com.hippo.ehviewer.util.toIntOrDefault @@ -136,7 +136,7 @@ object GalleryDetailParser { PATTERN_ARCHIVE.find(body)?.run { gd.archiveUrl = groupValues[1].trim().unescapeXml() } - try { + Either.catch { val gm = d.getElementsByClass("gm")[0] // Thumb url gm.getElementById("gd1")?.child(0)?.attr("style")?.trim()?.let { @@ -147,16 +147,15 @@ object GalleryDetailParser { gd.titleJpn = gm.getElementById("gj")?.text()?.trim() // Category - try { + gd.category = Either.catch { val gdc = gm.getElementById("gdc")!! var ce = gdc.getElementsByClass("cn").first() if (ce == null) { ce = gdc.getElementsByClass("cs").first() } - gd.category = getCategory(ce!!.text()) - } catch (e: Throwable) { - ExceptionUtils.throwIfFatal(e) - gd.category = EhUtils.UNKNOWN + EhUtils.getCategory(ce!!.text()) + }.getOrElse { + EhUtils.UNKNOWN } // Uploader @@ -219,8 +218,7 @@ object GalleryDetailParser { if (gd.favoriteSlot == NOT_FAVORITED && EhDB.containLocalFavorites(gd.gid)) { gd.favoriteSlot = LOCAL_FAVORITED } - } catch (e: Throwable) { - ExceptionUtils.throwIfFatal(e) + }.getOrElse { throw ParseException("Can't parse gallery detail") } @@ -279,7 +277,7 @@ object GalleryDetailParser { } } - private fun parseTagGroup(element: Element): GalleryTagGroup? = try { + private fun parseTagGroup(element: Element): GalleryTagGroup? = Either.catch { var nameSpace = element.child(0).text() // Remove last ':' nameSpace = nameSpace.substring(0, nameSpace.length - 1) @@ -297,143 +295,134 @@ object GalleryDetailParser { } group.add(tag) } - if (group.size > 0) group else null - } catch (e: Throwable) { - ExceptionUtils.throwIfFatal(e) - logcat(e) + group.takeIf { it.isNotEmpty() } + }.getOrElse { + logcat(it) null } /** * Parse tag groups with html parser */ - private fun parseTagGroups(document: Document): List = try { + private fun parseTagGroups(document: Document): List = Either.catch { val taglist = document.getElementById("taglist")!! val tagGroups = taglist.child(0).child(0).children() parseTagGroups(tagGroups) - } catch (e: Throwable) { - ExceptionUtils.throwIfFatal(e) - logcat(e) + }.getOrElse { + logcat(it) emptyList() } - private fun parseTagGroups(trs: Elements): List = try { + private fun parseTagGroups(trs: Elements): List = Either.catch { trs.mapNotNull { parseTagGroup(it) } - } catch (e: Throwable) { - ExceptionUtils.throwIfFatal(e) - logcat(e) + }.getOrElse { + logcat(it) emptyList() } - private suspend fun parseComment(element: Element): GalleryComment? { - return try { - // Id - val a = element.previousElementSibling() - val name = a!!.attr("name") - val id = name trimAnd { substring(1).toInt().toLong() } - // Editable, vote up and vote down - val c4 = element.getElementsByClass("c4").first() - var voteUpAble = false - var voteUpEd = false - var voteDownAble = false - var voteDownEd = false - var editable = false - var uploader = false - if (null != c4) { - if ("Uploader Comment" == c4.text()) { - uploader = true - } - for (e in c4.children()) { - when (e.text()) { - "Vote+" -> { - voteUpAble = true - voteUpEd = e.attr("style").trim().isNotEmpty() - } - - "Vote-" -> { - voteDownAble = true - voteDownEd = e.attr("style").trim().isNotEmpty() - } - - "Edit" -> editable = true + private suspend fun parseComment(element: Element): GalleryComment? = Either.catch { + // Id + val a = element.previousElementSibling() + val name = a!!.attr("name") + val id = name trimAnd { substring(1).toInt().toLong() } + // Editable, vote up and vote down + val c4 = element.getElementsByClass("c4").first() + var voteUpAble = false + var voteUpEd = false + var voteDownAble = false + var voteDownEd = false + var editable = false + var uploader = false + if (null != c4) { + if ("Uploader Comment" == c4.text()) { + uploader = true + } + for (e in c4.children()) { + when (e.text()) { + "Vote+" -> { + voteUpAble = true + voteUpEd = e.attr("style").trim().isNotEmpty() } + + "Vote-" -> { + voteDownAble = true + voteDownEd = e.attr("style").trim().isNotEmpty() + } + + "Edit" -> editable = true } } - // Vote state - val c7 = element.getElementsByClass("c7").first() - val voteState = c7?.text()?.trim() - // Score - val c5 = element.getElementsByClass("c5").first() - val score = c5?.children()?.first()?.text()?.trim()?.toIntOrDefault(0) ?: 0 - // time - val c3 = element.getElementsByClass("c3").first() - val temp = c3!!.ownText() - val (timeStr, user) = if (temp.endsWith(':')) { - temp.substring("Posted on ".length, temp.length - " by:".length) to c3.child(0).text() - } else { - temp.substring("Posted on ".length) to null - } - val time = WEB_COMMENT_DATE_FORMAT.parse(timeStr).toEpochMillis() - // comment - val c6 = element.getElementsByClass("c6").first() - // fix underline support - for (e in c6!!.children()) { - if ("span" == e.tagName() && "text-decoration:underline;" == e.attr("style")) { - e.tagName("u") - } - } - val commentHtml = c6.html() - // filter comment - if (!uploader) { - val sEhFilter = EhFilter - if (score <= Settings.commentThreshold || sEhFilter.filterCommenter(user.orEmpty()) || sEhFilter.filterComment(commentHtml)) { - return null - } - } - // last edited - val c8 = element.getElementsByClass("c8").first() - val lastEdited = c8?.children()?.first()?.run { WEB_COMMENT_DATE_FORMAT.parse(text()).toEpochMillis() } ?: 0 - GalleryComment( - id, score, editable, voteUpAble, voteUpEd, voteDownAble, - voteDownEd, uploader, voteState, time, user, commentHtml, lastEdited, - ) - } catch (e: Throwable) { - ExceptionUtils.throwIfFatal(e) - logcat(e) - null } + // Vote state + val c7 = element.getElementsByClass("c7").first() + val voteState = c7?.text()?.trim() + // Score + val c5 = element.getElementsByClass("c5").first() + val score = c5?.children()?.first()?.text()?.trim()?.toIntOrDefault(0) ?: 0 + // time + val c3 = element.getElementsByClass("c3").first() + val temp = c3!!.ownText() + val (timeStr, user) = if (temp.endsWith(':')) { + temp.substring("Posted on ".length, temp.length - " by:".length) to c3.child(0).text() + } else { + temp.substring("Posted on ".length) to null + } + val time = WEB_COMMENT_DATE_FORMAT.parse(timeStr).toEpochMillis() + // comment + val c6 = element.getElementsByClass("c6").first() + // fix underline support + for (e in c6!!.children()) { + if ("span" == e.tagName() && "text-decoration:underline;" == e.attr("style")) { + e.tagName("u") + } + } + val commentHtml = c6.html() + // filter comment + if (!uploader) { + val sEhFilter = EhFilter + if (score <= Settings.commentThreshold || sEhFilter.filterCommenter(user.orEmpty()) || sEhFilter.filterComment(commentHtml)) { + return null + } + } + // last edited + val c8 = element.getElementsByClass("c8").first() + val lastEdited = c8?.children()?.first()?.run { WEB_COMMENT_DATE_FORMAT.parse(text()).toEpochMillis() } ?: 0 + GalleryComment( + id, score, editable, voteUpAble, voteUpEd, voteDownAble, + voteDownEd, uploader, voteState, time, user, commentHtml, lastEdited, + ) + }.getOrElse { + logcat(it) + null } /** * Parse comments with html parser */ - suspend fun parseComments(document: Document): GalleryCommentList { + suspend fun parseComments(document: Document): GalleryCommentList = Either.catch { // Disable pretty print to get comments in raw html document.outputSettings().prettyPrint(false) - return try { - val cdiv = document.getElementById("cdiv")!! - val c1s = cdiv.getElementsByClass("c1") - val list = c1s.mapNotNull { parseComment(it) } - val chd = cdiv.getElementById("chd") - var hasMore = false - NodeTraversor.traverse( - object : NodeVisitor { - override fun head(node: Node, depth: Int) { - if (node is Element && node.text() == "click to show all") { - hasMore = true - } + val cdiv = document.getElementById("cdiv")!! + val c1s = cdiv.getElementsByClass("c1") + val list = c1s.mapNotNull { parseComment(it) } + val chd = cdiv.getElementById("chd") + var hasMore = false + NodeTraversor.traverse( + object : NodeVisitor { + override fun head(node: Node, depth: Int) { + if (node is Element && node.text() == "click to show all") { + hasMore = true } + } - override fun tail(node: Node, depth: Int) {} - }, - chd!!, - ) - GalleryCommentList(list, hasMore) - } catch (e: Throwable) { - ExceptionUtils.throwIfFatal(e) - logcat(e) - EMPTY_GALLERY_COMMENT_LIST - } + override fun tail(node: Node, depth: Int) {} + }, + chd!!, + ) + GalleryCommentList(list, hasMore) + }.getOrElse { + logcat(it) + EMPTY_GALLERY_COMMENT_LIST } /** diff --git a/app/src/main/kotlin/com/hippo/ehviewer/client/parser/GalleryNotAvailableParser.kt b/app/src/main/kotlin/com/hippo/ehviewer/client/parser/GalleryNotAvailableParser.kt index 484c0c05f..e6855c933 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/client/parser/GalleryNotAvailableParser.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/client/parser/GalleryNotAvailableParser.kt @@ -17,16 +17,16 @@ */ package com.hippo.ehviewer.client.parser -import com.hippo.ehviewer.util.ExceptionUtils +import arrow.core.Either +import arrow.core.getOrElse import eu.kanade.tachiyomi.util.system.logcat import org.jsoup.Jsoup object GalleryNotAvailableParser { - fun parse(body: String): String? = runCatching { + fun parse(body: String): String? = Either.catch { val e = Jsoup.parse(body).getElementsByClass("d").first() e!!.child(0).html().replace("
", "\n") }.getOrElse { - ExceptionUtils.throwIfFatal(it) logcat(it) null } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/client/parser/GalleryTokenApiParser.kt b/app/src/main/kotlin/com/hippo/ehviewer/client/parser/GalleryTokenApiParser.kt index 52be057ad..b80784f7e 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/client/parser/GalleryTokenApiParser.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/client/parser/GalleryTokenApiParser.kt @@ -15,9 +15,10 @@ */ package com.hippo.ehviewer.client.parser +import arrow.core.Either +import arrow.core.getOrElse import com.hippo.ehviewer.client.exception.EhException import com.hippo.ehviewer.client.parseAs -import com.hippo.ehviewer.util.ExceptionUtils import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -32,10 +33,9 @@ object GalleryTokenApiParser { * ] * } */ - fun parse(body: String): String = runCatching { + fun parse(body: String): String = Either.catch { body.parseAs().tokenList[0].token }.getOrElse { - ExceptionUtils.throwIfFatal(it) throw EhException(body.parseAs().error) } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/client/parser/ProfileParser.kt b/app/src/main/kotlin/com/hippo/ehviewer/client/parser/ProfileParser.kt index ede3a094c..f76c4b406 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/client/parser/ProfileParser.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/client/parser/ProfileParser.kt @@ -15,18 +15,19 @@ */ package com.hippo.ehviewer.client.parser +import arrow.core.Either +import arrow.core.getOrElse import com.hippo.ehviewer.client.EhUrl import com.hippo.ehviewer.client.exception.ParseException -import com.hippo.ehviewer.util.ExceptionUtils import eu.kanade.tachiyomi.util.system.logcat import org.jsoup.Jsoup object ProfileParser { - fun parse(body: String): Result = runCatching { + fun parse(body: String): Result = Either.catch { val d = Jsoup.parse(body) val profilename = d.getElementById("profilename") val displayName = profilename!!.child(0).text() - val avatar = runCatching { + val avatar = Either.catch { val avatar = profilename.nextElementSibling()!!.nextElementSibling()!!.child(0).attr("src") if (avatar.isEmpty()) { @@ -37,13 +38,11 @@ object ProfileParser { avatar } }.getOrElse { - ExceptionUtils.throwIfFatal(it) logcat { "No avatar" } null } Result(displayName, avatar) }.getOrElse { - ExceptionUtils.throwIfFatal(it) throw ParseException("Parse forums error") } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/util/AppHelper.kt b/app/src/main/kotlin/com/hippo/ehviewer/util/AppHelper.kt index 55bc4db4d..0862c1342 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/util/AppHelper.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/util/AppHelper.kt @@ -18,6 +18,8 @@ package com.hippo.ehviewer.util import android.app.Activity import android.content.Intent import android.widget.Toast +import arrow.core.Either +import arrow.core.getOrElse import com.hippo.ehviewer.R object AppHelper { @@ -27,11 +29,10 @@ object AppHelper { sendIntent.putExtra(Intent.EXTRA_TEXT, text) sendIntent.type = "text/plain" val chooser = Intent.createChooser(sendIntent, from.getString(R.string.share)) - return runCatching { + return Either.catch { from.startActivity(chooser) true }.getOrElse { - ExceptionUtils.throwIfFatal(it) Toast.makeText(from, R.string.error_cant_find_activity, Toast.LENGTH_SHORT).show() false } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/util/ExceptionUtils.kt b/app/src/main/kotlin/com/hippo/ehviewer/util/ExceptionUtils.kt index 548c96077..a2c339ca2 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/util/ExceptionUtils.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/util/ExceptionUtils.kt @@ -17,44 +17,9 @@ package com.hippo.ehviewer.util import com.hippo.ehviewer.R import eu.kanade.tachiyomi.util.system.logcat -import java.io.IOException -import java.net.MalformedURLException -import java.net.ProtocolException -import java.net.SocketException -import java.net.SocketTimeoutException -import java.net.UnknownHostException -import javax.net.ssl.SSLException import splitties.init.appCtx fun Throwable.displayString(): String { logcat(this) - val cause = cause - return when { - cause != null && this is IOException -> getReadableStringInternal(cause) - else -> getReadableStringInternal(this) - } -} - -private fun getReadableStringInternal(e: Throwable) = when (e) { - is MalformedURLException -> appCtx.getString(R.string.error_invalid_url) - is SocketTimeoutException -> appCtx.getString(R.string.error_timeout) - is UnknownHostException -> appCtx.getString(R.string.error_unknown_host) - is ProtocolException -> if (e.message!!.startsWith("Too many follow-up requests:")) { - appCtx.getString(R.string.error_redirection) - } else { - appCtx.getString(R.string.error_socket) - } - is SocketException, is SSLException -> appCtx.getString(R.string.error_socket) - else -> e.message ?: appCtx.getString(R.string.error_unknown) -} - -object ExceptionUtils { - fun throwIfFatal(t: Throwable) { - // values here derived from https://github.com/ReactiveX/RxJava/issues/748#issuecomment-32471495 - when (t) { - is VirtualMachineError, is ThreadDeath, is LinkageError -> { - throw t - } - } - } + return message ?: appCtx.getString(R.string.error_unknown) }