diff --git a/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/Miscellaneous.kt b/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/Miscellaneous.kt new file mode 100644 index 000000000..9d0c817d3 --- /dev/null +++ b/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/Miscellaneous.kt @@ -0,0 +1,46 @@ +/* + * STAROPENSOURCE ENGINE SOURCE FILE + * Copyright (c) 2024 The StarOpenSource Engine Authors + * Licensed under the GNU Affero General Public License v3 + * with an exception allowing classpath linking. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.staropensource.engine.base.utility.misc + +/** + * REALLY miscellaneous methods are in here. + * + * @since v1-alpha10 + */ +class Miscellaneous private constructor() { + /** + * Companion object of [Miscellaneous]. + * + * @since v1-alpha10 + */ + companion object { + /** + * Returns if currently + * running on the main thread. + * + * @return `true` if running on the main thread, `false` if not, `null` if not supported (e.g. not running on the JVM) + * @since v1-alpha10 + */ + fun onMainThread(): Boolean? { + return Thread.currentThread().threadId() == 1L + } + } +} diff --git a/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/StackTraceUtils.kt b/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/StackTraceUtils.kt new file mode 100644 index 000000000..2bfabdaa6 --- /dev/null +++ b/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/StackTraceUtils.kt @@ -0,0 +1,165 @@ +/* + * STAROPENSOURCE ENGINE SOURCE FILE + * Copyright (c) 2024 The StarOpenSource Engine Authors + * Licensed under the GNU Affero General Public License v3 + * with an exception allowing classpath linking. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.staropensource.engine.base.utility.misc + +import de.staropensource.engine.base.type.Origin +import java.lang.reflect.InvocationTargetException + +/** + * Utility methods for stack trace + * and exception analysis. + * + * @since v1-alpha10 + */ +class StackTraceUtils private constructor() { + /** + * Companion object of [StackTraceUtils]. + * + * @since v1-alpha10 + */ + companion object { + /** + * Returns the method caller. + * + * @param depth how deep to go into the stack trace + * @return deepest method caller + * @since v1-alpha10 + */ + fun getMethodCaller(depth: UInt = 0u): Origin { + val stacktrace: Array = Throwable().stackTrace + var element: StackTraceElement? = null + + // Get wanted element + element = if (depth.plus(2u).toInt() > stacktrace.size) + stacktrace[stacktrace.size - 1] + else + stacktrace[depth.plus(2u).toInt()] + + // Return origin + return Origin( + packageName = element.className.substringBeforeLast('.'), + className = element.className.substringAfterLast('.'), + methodName = element.methodName, + lineNumber = element.lineNumber.toUInt(), + ) + } + + + /** + * Returns the `Caused by: ` header + * usually found in JVM stacktraces. + * + * @param throwable [Throwable] to use + * @return stacktrace header + * @since v1-alpha10 + */ + fun stacktraceHeader(throwable: Throwable): String { + return "Caused by: ${throwable.javaClass.name}${if (throwable.message == null) "" else ": ${throwable.message}"}" + } + + /** + * Returns the body of the stacktrace + * as it is usually found in JVM + * stacktraces. + * + * @param throwable [Throwable] to use + * @param indent if to add a tab character (`\t`) as indentation + * @return stacktrace body + * @since v1-alpha10 + */ + fun stacktraceBody(throwable: Throwable, indent: Boolean = true): String = buildString { + for (element: StackTraceElement in throwable.stackTrace) { + if (!isEmpty()) + append("\n") + if (indent) + append("\t") + + append("at ") + append(element) + } + } + + /** + * Returns a recursively resolved + * collection of stacktraces, all + * derived from the passed [throwable]. + * + * Use this method if you intend to + * print an exception's stacktrace or + * want to display it nicely elsewhere. + * + * + * @param throwable [Throwable] to use + * @param indent if to add a tab character (`\t`) as indentation + * @param includeHeader if to include the `Caused by` stacktrace header (see [stacktraceHeader]) + * @return recursively resolved stacktrace + * @since v1-alpha10 + */ + fun stacktraceRecursive(throwable: Throwable, indent: Boolean = true, includeHeader: Boolean = true, depth: UInt = 10u): String = buildString { + // Append header + if (includeHeader) { + append(stacktraceHeader(throwable)) + append("\n") + } + + // Append body + append(stacktraceBody(throwable, indent = indent)) + + // Recurse downwards + if (throwable.cause != null) { + append("\n") + if (depth == 0u) + append("...") + else + append(stacktraceRecursive(throwable.cause!!, indent = indent, includeHeader = includeHeader, depth = depth.minus(1u))) + } + + if (throwable is ClassNotFoundException) { + val exception: ClassNotFoundException = throwable + if (exception.exception != null) { + append("\n") + if (depth == 0u) + append("...") + else + append(stacktraceRecursive(exception.exception, indent = indent, includeHeader = includeHeader, depth = depth.minus(1u))) + } + } else if (throwable is ExceptionInInitializerError) { + val exception: ExceptionInInitializerError = throwable + if (exception.exception != null) { + append("\n") + if (depth == 0u) + append("...") + else + append(stacktraceRecursive(exception.exception, indent = indent, includeHeader = includeHeader, depth = depth.minus(1u))) + } + } else if (throwable is InvocationTargetException) { + val exception: InvocationTargetException = throwable + if (exception.targetException != null) { + append("\n") + if (depth == 0u) + append("...") + else + append(stacktraceRecursive(exception.targetException, indent = indent, includeHeader = includeHeader, depth = depth.minus(1u))) + } + } + } + } +} diff --git a/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/package-info.kt b/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/package-info.kt new file mode 100644 index 000000000..0bc45d362 --- /dev/null +++ b/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/package-info.kt @@ -0,0 +1,26 @@ +/* + * STAROPENSOURCE ENGINE SOURCE FILE + * Copyright (c) 2024 The StarOpenSource Engine Authors + * Licensed under the GNU Affero General Public License v3 + * with an exception allowing classpath linking. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * Small, miscellaneous utility classes. + * + * @since v1-alpha10 + */ +package de.staropensource.engine.base.utility.misc