diff --git a/app/src/main/java/com/arthenica/smartexception/AbstractExceptions.java b/app/src/main/java/com/arthenica/smartexception/AbstractExceptions.java new file mode 100755 index 000000000..f0759d34d --- /dev/null +++ b/app/src/main/java/com/arthenica/smartexception/AbstractExceptions.java @@ -0,0 +1,732 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2020, Taner Sener + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.arthenica.smartexception; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + *

Abstract class that includes common variables and methods for different Exceptions implementations. + * + * @author Taner Sener + * @since 0.1.0 + */ +public abstract class AbstractExceptions { + + /** + *

Default max depth used in methods which do not have a maxDepth argument. + */ + public static final int DEFAULT_MAX_DEPTH = 10; + + /** + *

Default value for ignoring all causes when stack trace elements are printed or converted to string. + */ + public static final boolean DEFAULT_IGNORE_ALL_CAUSES = false; + + /** + *

Stores global root package names. + */ + public static final Set rootPackageSet = Collections.synchronizedSet(new HashSet()); + + /** + *

Stores global group package names. + */ + public static final Set groupPackageSet = Collections.synchronizedSet(new HashSet()); + + /** + *

Stores global ignore package names. + */ + public static final Set ignorePackageSet = Collections.synchronizedSet(new HashSet()); + + /** + *

Stores global ignore cause package names. + */ + public static final Set ignoreCausePackageSet = Collections.synchronizedSet(new HashSet()); + + /** + *

Stores the value of global ignore all causes option. + */ + public static boolean ignoreAllCauses = DEFAULT_IGNORE_ALL_CAUSES; + + /** + *

Stores the global stack trace serializer implementation. + */ + public static StackTraceElementSerializer stackTraceElementSerializer; + + /** + *

Registers a new root package. + * + * @param packageString root package name to register + */ + public static void registerRootPackage(final String packageString) { + rootPackageSet.add(packageString); + } + + /** + *

Clears previously registered root packages. + */ + public static void clearRootPackages() { + rootPackageSet.clear(); + } + + /** + *

Registers a new group package. + * + * @param packageString group package name to register + */ + public static void registerGroupPackage(final String packageString) { + groupPackageSet.add(packageString); + } + + /** + *

Clears previously registered group packages. + */ + public static void clearGroupPackages() { + groupPackageSet.clear(); + } + + /** + *

Returns the global stack trace serializer implementation which is used to serialize {@link StackTraceElement} + * objects in getStackTraceString methods. + * + * @return current stack trace serializer implementation + */ + public static StackTraceElementSerializer getStackTraceElementSerializer() { + return stackTraceElementSerializer; + } + + /** + *

Sets the global stack trace serializer implementation which is used to serialize {@link StackTraceElement} + * objects in getStackTraceString methods. + * + * @param stackTraceElementSerializer new stack trace serializer implementation + */ + public static void setStackTraceElementSerializer(final StackTraceElementSerializer stackTraceElementSerializer) { + AbstractExceptions.stackTraceElementSerializer = stackTraceElementSerializer; + } + + /** + *

Registers a new ignore package. + * + * @param packageString ignore package name to register + * @param ignoreCauseClasses ignore cause classes from this package too + */ + public static void registerIgnorePackage(final String packageString, final boolean ignoreCauseClasses) { + ignorePackageSet.add(packageString); + if (ignoreCauseClasses) { + ignoreCausePackageSet.add(packageString); + } + } + + /** + *

Clears previously registered ignore packages. + */ + public static void clearIgnorePackages() { + ignorePackageSet.clear(); + ignoreCausePackageSet.clear(); + } + + /** + *

Returns the value of ignore all causes option. + * + * @return the value of global ignore all causes option. If value is true then stack trace elements printed or + * converted to string will not include causes. If value is false causes will be appended to the stack trace of + * the main throwable. + */ + public static boolean getIgnoreAllCauses() { + return ignoreAllCauses; + } + + /** + *

Sets the value of ignore all causes option. + * + * @param ignoreAllCauses new global ignore all causes option. If value is true then stack trace elements printed + * or converted to string will not include causes. If value is false causes will be appended to the stack trace of + * the main throwable. + */ + public static void setIgnoreAllCauses(final boolean ignoreAllCauses) { + AbstractExceptions.ignoreAllCauses = ignoreAllCauses; + } + + /** + *

Returns the smart stack trace for the given throwable. + * + *

This method uses root packages registered by {@link #registerRootPackage(String)}, group packages registered + * by {@link #registerGroupPackage(String)} and ignore packages registered by + * {@link #registerIgnorePackage(String, boolean)} to build the smart stack trace. + * + * @param throwable parent throwable + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable) { + return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses); + } + + /** + *

Returns the smart stack trace for the given throwable. + * + *

This method uses root packages registered by {@link #registerRootPackage(String)}, group packages registered + * by {@link #registerGroupPackage(String)} and ignore packages registered by + * {@link #registerIgnorePackage(String, boolean)} to build the smart stack trace. + * + * @param throwable parent throwable + * @param ignoreAllCauses ignore all causes in the exception chain + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final boolean ignoreAllCauses) { + return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses); + } + + /** + *

Returns the smart stack trace for the given throwable using packages provided. + * + * @param throwable parent throwable + * @param rootPackageSet root packages to use for building the stack trace + * @param groupPackageSet group packages to use for building the stack trace + * @param ignorePackageSet ignore packages to use for building the stack trace + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final Set rootPackageSet, final Set groupPackageSet, final Set ignorePackageSet) { + return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses); + } + + /** + *

Returns the smart stack trace for the given throwable using packages provided. + * + * @param throwable parent throwable + * @param rootPackageSet root packages to use for building the stack trace + * @param groupPackageSet group packages to use for building the stack trace + * @param ignorePackageSet ignore packages to use for building the stack trace + * @param ignoreAllCauses ignore all causes in the exception chain + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final Set rootPackageSet, final Set groupPackageSet, final Set ignorePackageSet, final boolean ignoreAllCauses) { + return getStackTraceString(throwable, false, rootPackageSet, groupPackageSet, ignorePackageSet, 0, ignoreAllCauses); + } + + /** + *

Returns the smart stack trace for the given throwable using root package provided. + * + * @param throwable parent throwable + * @param rootPackage root package to use for building the stack trace + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final String rootPackage) { + return getStackTraceString(throwable, false, Collections.singleton(rootPackage), new HashSet(), new HashSet(), 0, ignoreAllCauses); + } + + /** + *

Returns the smart stack trace for the given throwable using root package provided. + * + * @param throwable parent throwable + * @param rootPackage root package to use for building the stack trace + * @param groupPackage group package to use for building the stack trace + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final String rootPackage, final String groupPackage) { + return getStackTraceString(throwable, false, Collections.singleton(rootPackage), Collections.singleton(groupPackage), new HashSet(), 0, ignoreAllCauses); + } + + /** + *

Returns the smart stack trace for the given throwable using elements found until the maxDepth. + * + * @param throwable parent throwable + * @param maxDepth max depth in exception chain that will be used + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final int maxDepth) { + return getStackTraceString(throwable, false, new HashSet(), new HashSet(), new HashSet(), maxDepth, ignoreAllCauses); + } + + /** + *

Returns the smart stack trace for the given throwable using elements found until the maxDepth. + * + * @param throwable parent throwable + * @param maxDepth max depth in exception chain that will be used + * @param ignoreAllCauses ignore all causes in the exception chain + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final int maxDepth, final boolean ignoreAllCauses) { + return getStackTraceString(throwable, false, new HashSet(), new HashSet(), new HashSet(), maxDepth, ignoreAllCauses); + } + + /** + *

Returns the smart stack trace for the given throwable using parameters provided. + * + * @param throwable parent throwable + * @param isCause throwable is a cause or not + * @param rootPackageSet root packages to use for building the stack trace + * @param groupPackageSet group packages to use for building the stack trace + * @param ignorePackageSet ignore packages to use for building the stack trace + * @param maxDepth max depth in exception chain that will be used + * @param ignoreAllCauses ignore all causes in the exception chain + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final boolean isCause, final Set rootPackageSet, final Set groupPackageSet, final Set ignorePackageSet, final int maxDepth, final boolean ignoreAllCauses) { + final StringBuilder builder = new StringBuilder(); + + if (throwable == null) { + return ""; + } + + final String className = throwable.getClass().getName(); + + final StackTraceElement[] stackTraceElements; + if (maxDepth > 0) { + stackTraceElements = getStackTrace(throwable, maxDepth); + } else { + stackTraceElements = getStackTrace(throwable, rootPackageSet, ignorePackageSet); + } + String message = throwable.getLocalizedMessage(); + if (isEmpty(message)) { + message = throwable.getMessage(); + } + + /* PRINT MESSAGE */ + if (isCause) { + builder.append(System.lineSeparator()); + builder.append("Caused by: "); + builder.append(className); + if (!isEmpty(message)) { + builder.append(": "); + builder.append(message); + } + } else { + builder.append(System.lineSeparator()); + builder.append(className); + if (!isEmpty(message)) { + builder.append(": "); + builder.append(message); + } + } + + /* PRINT SHORT STACK TRACE */ + String currentGroupPackage = null; + StackTraceElement firstStackTraceElementInTheGroup = null; + int currentGroupCount = 0; + for (StackTraceElement traceElement : stackTraceElements) { + String traceElementClassName = traceElement.getClassName(); + String groupPackageMatch = getContainingPackage(traceElementClassName, groupPackageSet); + + if (groupPackageMatch != null) { + if (!groupPackageMatch.equals(currentGroupPackage)) { + appendStackTraceGroupElement(builder, currentGroupPackage, currentGroupCount, firstStackTraceElementInTheGroup); + + builder.append(System.lineSeparator()); + builder.append("\tat "); + + currentGroupPackage = groupPackageMatch; + firstStackTraceElementInTheGroup = traceElement; + currentGroupCount = 1; + } else { + currentGroupCount++; + } + } else { + currentGroupCount = appendStackTraceGroupElement(builder, currentGroupPackage, currentGroupCount, firstStackTraceElementInTheGroup); + + builder.append(System.lineSeparator()); + builder.append("\tat "); + if (stackTraceElementSerializer == null) { + throw new IllegalArgumentException("Stack trace element serializer not initialized."); + } else { + builder.append(stackTraceElementSerializer.toString(traceElement)); + } + currentGroupPackage = null; + } + } + + appendStackTraceGroupElement(builder, currentGroupPackage, currentGroupCount, firstStackTraceElementInTheGroup); + + final Throwable cause = throwable.getCause(); + if (cause != null && !containsPackage(className, ignoreCausePackageSet) && !ignoreAllCauses) { + builder.append(getStackTraceString(cause, true, rootPackageSet, groupPackageSet, ignorePackageSet, maxDepth, ignoreAllCauses)); + } + + return builder.toString(); + } + + /** + *

Appends stack trace group information for the given parameters. + * + * @param stringBuilder string builder that group stack trace information will be appended + * @param currentGroupPackage package name of the current group + * @param numberOfElementsInTheCurrentGroup number of elements in the current group + * @param firstStackTraceElementInTheGroup first stack trace element of this group + * @return new value for the group element count + */ + public static int appendStackTraceGroupElement(final StringBuilder stringBuilder, final String currentGroupPackage, final int numberOfElementsInTheCurrentGroup, final StackTraceElement firstStackTraceElementInTheGroup) { + if (numberOfElementsInTheCurrentGroup > 0) { + if (stackTraceElementSerializer == null) { + throw new IllegalArgumentException("Stack trace element serializer not initialized."); + } else { + stringBuilder.append((numberOfElementsInTheCurrentGroup == 1) ? stackTraceElementSerializer.toString(firstStackTraceElementInTheGroup) : String.format("%s%s ... %d more", stackTraceElementSerializer.getModuleName(firstStackTraceElementInTheGroup), currentGroupPackage, (numberOfElementsInTheCurrentGroup - 1))); + } + } + + return 0; + } + + /** + *

Returns true if package of the given class name matches one of package names provided in + * packageSet. + * + * @param fullClassName fully qualified class name + * @param packageSet set containing package names + * @return true if package of the given class name matches one of package names provided in packageSet, + * false otherwise + */ + public static boolean containsPackage(final String fullClassName, final Set packageSet) { + return getContainingPackage(fullClassName, packageSet) != null; + } + + /** + *

Checks if one of the package names in the packageSet matches the package of the given class name + * and returns the matching package name. + * + * @param fullClassName fully qualified class name + * @param packageSet set containing package names + * @return one of the package names in the packageSet that matches the package of the given class name + * or null if no matching package name is found in the packageSet + */ + public static String getContainingPackage(final String fullClassName, final Set packageSet) { + for (final String parentExceptionPackage : packageSet) { + if (fullClassName.startsWith(parentExceptionPackage)) { + return parentExceptionPackage; + } + } + + return null; + } + + /** + *

Returns all messages found in the exception chain of the throwable as a single string. + * + * @param throwable parent throwable + * @return a string containing all messages found in the exception chain + */ + public static String getAllMessages(final Throwable throwable) { + final StringBuilder messageBuilder = new StringBuilder(); + getAllMessages(throwable, messageBuilder); + return messageBuilder.toString(); + } + + /** + *

Adds all messages found in the exception chain of the throwable into the given message builder. + * + * @param throwable parent throwable + * @param messageBuilder message builder + */ + public static void getAllMessages(final Throwable throwable, final StringBuilder messageBuilder) { + if (throwable != null) { + final String message = throwable.getMessage(); + if (!isEmpty(message)) { + if (messageBuilder.length() != 0) { + messageBuilder.append(System.lineSeparator()); + messageBuilder.append(" - Caused by: "); + } + messageBuilder.append(message); + } + getAllMessages(throwable.getCause(), messageBuilder); + } + } + + /** + *

Builds a smart stack trace for the given throwable using elements found until the maxDepth and + * elements of it. + * + * @param throwable parent throwable + * @param maxDepth max depth in exception chain that will be returned + * @return an array containing stack trace elements + */ + public static StackTraceElement[] getStackTrace(final Throwable throwable, final int maxDepth) { + final ArrayList list = new ArrayList(); + + if (throwable != null) { + StackTraceElement[] stackTrace = throwable.getStackTrace(); + for (int i = 0; (i < stackTrace.length) && (i < maxDepth); i++) { + list.add(stackTrace[i]); + } + } + + return list.toArray(new StackTraceElement[0]); + } + + /** + *

Builds a smart stack trace for the given throwable using packages provided and returns elements + * of it. + * + * @param throwable parent throwable + * @param rootPackageSet root packages to use for building the stack trace + * @param ignorePackageSet ignore packages to use for building the stack trace + * @return an array containing stack trace elements + */ + public static StackTraceElement[] getStackTrace(final Throwable throwable, final Set rootPackageSet, final Set ignorePackageSet) { + final ArrayList list = new ArrayList(); + final ArrayList partialList = new ArrayList(); + + if (throwable != null) { + for (StackTraceElement stackTraceElement : throwable.getStackTrace()) { + final String className = stackTraceElement.getClassName(); + if (!isEmpty(className)) { + if (containsPackage(className, rootPackageSet)) { + list.addAll(partialList); + list.add(stackTraceElement); + } else if (!containsPackage(className, ignorePackageSet)) { + partialList.add(stackTraceElement); + } + } + } + } + + if (list.isEmpty()) { + list.addAll(partialList); + } + + return list.toArray(new StackTraceElement[0]); + } + + /** + *

Returns true if the given cause class is found in the exception chain of the throwable. + *
+ *
+ *

Search starts from the throwable itself and goes through the exception chain up until + * {@link #DEFAULT_MAX_DEPTH}. + * + * @param throwable parent throwable + * @param causeClass class to search for + * @return true if the given cause class is found in the exception chain of the throwable, false + * otherwise + */ + public static boolean containsCause(final Throwable throwable, final Class causeClass) { + return containsCause(throwable, causeClass, null); + } + + /** + *

Returns true if the given cause class and cause message is found in the exception chain of the + * throwable. + *
+ *
+ *

Search starts from the throwable itself and goes through the exception chain up until + * {@link #DEFAULT_MAX_DEPTH}. + * + * @param throwable parent throwable + * @param causeClass class to search for + * @param causeMessage message to search for + * @return true if the given cause class and cause message is found in the exception chain of the + * throwable, false otherwise + */ + public static boolean containsCause(final Throwable throwable, final Class causeClass, final String causeMessage) { + return (searchCause(throwable, causeClass, causeMessage, DEFAULT_MAX_DEPTH) != null); + } + + /** + *

Returns the cause of the throwable by walking through the exception chain. + * + * @param throwable parent throwable + * @return the cause of the throwable found or null if throwable does not have a cause + */ + public static Throwable getCause(final Throwable throwable) { + return getCause(throwable, DEFAULT_MAX_DEPTH); + } + + /** + *

Returns the cause of the throwable by walking through the exception chain. + *
+ *
+ *

Note that this method walks through the exception chain up to given maxDepth. If exception chain + * includes more items than maxDepth, cause found at maxDepth level is returned. + * + * @param throwable parent throwable + * @param maxDepth max depth in exception chain that will be searched + * @return the cause of the throwable found until maxDepth is reached in the exception + * chain or null if throwable does not have a cause + */ + public static Throwable getCause(final Throwable throwable, final int maxDepth) { + if (throwable == null) { + return null; + } + + if (maxDepth <= 0) { + return throwable; + } + + final Throwable cause = throwable.getCause(); + if (cause == null) { + return throwable; + } else { + return getCause(cause, maxDepth - 1); + } + } + + /** + *

Searches for the given cause class in the exception chain of the throwable. + *
+ *
+ *

Search starts from the throwable itself and goes through the exception chain up until + * {@link #DEFAULT_MAX_DEPTH}. + * + * @param throwable parent throwable + * @param causeClass class to search for + * @return the throwable found or null if no class in exception chain matches the given cause class + */ + public static Throwable searchCause(final Throwable throwable, final Class causeClass) { + return searchCause(throwable, causeClass, null, DEFAULT_MAX_DEPTH); + } + + /** + *

Searches for the given cause class and cause message in the exception chain of the throwable. + *
+ *
+ *

Search starts from the throwable itself and goes through the exception chain up until + * {@link #DEFAULT_MAX_DEPTH}. + * + * @param throwable parent throwable + * @param causeClass class to search for + * @param causeMessage message to search for + * @return the throwable found or null if no class in exception chain matches the given cause class + * and cause message + */ + public static Throwable searchCause(final Throwable throwable, final Class causeClass, final String causeMessage) { + return searchCause(throwable, causeClass, causeMessage, DEFAULT_MAX_DEPTH); + } + + /** + *

Searches for the given cause class and cause message in the exception chain of the throwable. + *
+ *
+ *

Search starts from the throwable itself and goes through the exception chain up until the given + * maxDepth. + * + * @param throwable parent throwable + * @param causeClass class to search for + * @param causeMessage message to search for + * @param maxDepth max depth in exception chain that will be searched + * @return the throwable found or null if no class in exception chain matches the given cause class + * and cause message + */ + public static Throwable searchCause(final Throwable throwable, final Class causeClass, final String causeMessage, final int maxDepth) { + if (throwable == null) { + return null; + } + + if (isEmpty(causeMessage)) { + if (throwable.getClass().equals(causeClass)) { + return throwable; + } + } else { + if (throwable.getClass().equals(causeClass) && getAllMessages(throwable).toLowerCase().contains(causeMessage.toLowerCase())) { + return throwable; + } + } + + if (maxDepth <= 0) { + return null; + } + + final Throwable cause = throwable.getCause(); + if (cause == null) { + return null; + } else { + return searchCause(cause, causeClass, causeMessage, maxDepth - 1); + } + } + + /** + *

Searches for the given cause class in the exception chain of the throwable. + *
+ *
+ *

Search starts from the throwable itself and goes through the exception chain up until the given + * maxDepth. + * + * @param throwable parent throwable + * @param causeClass class to search for + * @param maxDepth max depth in exception chain that will be searched + * @return the throwable found or null if no class in exception chain matches the given cause class + */ + public static Throwable searchCause(final Throwable throwable, final Class causeClass, final int maxDepth) { + if (throwable == null) { + return null; + } + + if (throwable.getClass().equals(causeClass)) { + return throwable; + } + + if (maxDepth <= 0) { + return null; + } + + final Throwable cause = throwable.getCause(); + if (cause == null) { + return null; + } else { + return searchCause(cause, causeClass, maxDepth - 1); + } + } + + /** + *

Checks if given string is empty or not. + * + * @param value string to check + * @return true if given string is null or has zero length after trim(), false otherwise + */ + public static boolean isEmpty(final String value) { + if (value == null) { + return true; + } + + return value.trim().length() == 0; + } + + /** + *

Returns the package name of this class. + * + * @param className fully qualified class name + * @return name of the package + */ + public static String packageName(final String className) { + if (className == null) { + return ""; + } + + int index = className.lastIndexOf("."); + if (index >= 0) { + return className.substring(0, index); + } else { + return ""; + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/arthenica/smartexception/StackTraceElementSerializer.java b/app/src/main/java/com/arthenica/smartexception/StackTraceElementSerializer.java new file mode 100755 index 000000000..9e1d548da --- /dev/null +++ b/app/src/main/java/com/arthenica/smartexception/StackTraceElementSerializer.java @@ -0,0 +1,74 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2020, Taner Sener + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.arthenica.smartexception; + +/** + *

Base interface for classes which can serialize {@link StackTraceElement} objects. + * + * @author Taner Sener + * @since 0.1.0 + */ +public interface StackTraceElementSerializer { + + /** + *

Converts the given stackTraceElement into a string. + * + * @param stackTraceElement stack trace element to convert + * @return string representing the given stackTraceElement + */ + String toString(final StackTraceElement stackTraceElement); + + /** + *

Returns module name for the given stackTraceElement. + * + * @param stackTraceElement stack trace element to use + * @return module name of the given stackTraceElement or an empty string if implementation does not + * support printing module names + */ + String getModuleName(final StackTraceElement stackTraceElement); + + /** + *

Returns string definition used to print native methods. + * + * @return string definition used to print native methods + */ + String getNativeMethodDefinition(); + + /** + *

Returns string definition used to print methods with unknown source. + * + * @return string definition used to print methods with unknown source + */ + String getUnknownSourceDefinition(); + +} \ No newline at end of file diff --git a/app/src/main/java/com/arthenica/smartexception/java/Exceptions.java b/app/src/main/java/com/arthenica/smartexception/java/Exceptions.java new file mode 100755 index 000000000..a94458842 --- /dev/null +++ b/app/src/main/java/com/arthenica/smartexception/java/Exceptions.java @@ -0,0 +1,423 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2020, Taner Sener + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.arthenica.smartexception.java; + +import com.arthenica.smartexception.AbstractExceptions; +import com.arthenica.smartexception.StackTraceElementSerializer; + +import java.util.Set; + +/** + *

Utility class to handle Throwable objects and stack traces. This implementation is compatible with all Java and + * Android versions. + * + * @author Taner Sener + * @since 0.1.0 + */ +public class Exceptions { + + static { + AbstractExceptions.setStackTraceElementSerializer(new StackTraceElementSerializer() { + + @Override + public String toString(StackTraceElement stackTraceElement) { + final StringBuilder stringBuilder = new StringBuilder(); + + stringBuilder.append(stackTraceElement.getClassName()); + stringBuilder.append("."); + stringBuilder.append(stackTraceElement.getMethodName()); + + if (stackTraceElement.isNativeMethod()) { + stringBuilder.append("(Native Method)"); + } else if ((stackTraceElement.getFileName() != null) && (stackTraceElement.getFileName().length() > 0)) { + stringBuilder.append("("); + stringBuilder.append(stackTraceElement.getFileName()); + if (stackTraceElement.getLineNumber() >= 0) { + stringBuilder.append(":"); + stringBuilder.append(stackTraceElement.getLineNumber()); + } + stringBuilder.append(")"); + } else { + stringBuilder.append("(Unknown Source)"); + } + + return stringBuilder.toString(); + } + + @Override + public String getModuleName(StackTraceElement stackTraceElement) { + return ""; + } + + @Override + public String getNativeMethodDefinition() { + return "(Native Method)"; + } + + @Override + public String getUnknownSourceDefinition() { + return "(Unknown Source)"; + } + }); + } + + /** + *

Registers a new root package. + * + * @param packageString root package name to register + */ + public static void registerRootPackage(final String packageString) { + AbstractExceptions.registerRootPackage(packageString); + } + + /** + *

Clears previously registered root packages. + */ + public static void clearRootPackages() { + AbstractExceptions.clearRootPackages(); + } + + /** + *

Registers a new group package. + * + * @param packageString group package name to register + */ + public static void registerGroupPackage(final String packageString) { + AbstractExceptions.registerGroupPackage(packageString); + } + + /** + *

Clears previously registered group packages. + */ + public static void clearGroupPackages() { + AbstractExceptions.clearGroupPackages(); + } + + /** + *

Returns the global stack trace serializer implementation which is used to serialize {@link StackTraceElement} + * objects in getStackTraceString methods. + * + * @return current stack trace serializer implementation + */ + public static StackTraceElementSerializer getStackTraceElementSerializer() { + return AbstractExceptions.getStackTraceElementSerializer(); + } + + /** + *

Sets the global stack trace serializer implementation which is used to serialize {@link StackTraceElement} + * objects in getStackTraceString methods. + * + * @param stackTraceElementSerializer new stack trace serializer implementation + */ + public static void setStackTraceElementSerializer(final StackTraceElementSerializer stackTraceElementSerializer) { + AbstractExceptions.setStackTraceElementSerializer(stackTraceElementSerializer); + } + + /** + *

Registers a new ignore package. + * + * @param packageString ignore package name to register + * @param ignoreCauseClasses ignore cause classes from this package too + */ + public static void registerIgnorePackage(final String packageString, final boolean ignoreCauseClasses) { + AbstractExceptions.registerIgnorePackage(packageString, ignoreCauseClasses); + } + + /** + *

Clears previously registered ignore packages. + */ + public static void clearIgnorePackages() { + AbstractExceptions.clearIgnorePackages(); + } + + /** + *

Returns the value of ignore all causes option. + * + * @return the value of global ignore all causes option. If value is true then stack trace elements printed or + * converted to string will not include causes. If value is false causes will be appended to the stack trace of + * the main throwable. + */ + public static boolean getIgnoreAllCauses() { + return AbstractExceptions.getIgnoreAllCauses(); + } + + /** + *

Sets the value of ignore all causes option. + * + * @param ignoreAllCauses new global ignore all causes option. If value is true then stack trace elements printed + * or converted to string will not include causes. If value is false causes will be appended to the stack trace of + * the main throwable. + */ + public static void setIgnoreAllCauses(final boolean ignoreAllCauses) { + AbstractExceptions.setIgnoreAllCauses(ignoreAllCauses); + } + + /** + *

Returns the smart stack trace for the given throwable. + * + *

This method uses root packages registered by {@link #registerRootPackage(String)}, group packages registered + * by {@link #registerGroupPackage(String)} and ignore packages registered by + * {@link #registerIgnorePackage(String, boolean)} to build the smart stack trace. + * + * @param throwable parent throwable + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable) { + return AbstractExceptions.getStackTraceString(throwable); + } + + /** + *

Returns the smart stack trace for the given throwable. + * + *

This method uses root packages registered by {@link #registerRootPackage(String)}, group packages registered + * by {@link #registerGroupPackage(String)} and ignore packages registered by + * {@link #registerIgnorePackage(String, boolean)} to build the smart stack trace. + * + * @param throwable parent throwable + * @param ignoreAllCauses ignore all causes in the exception chain + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final boolean ignoreAllCauses) { + return AbstractExceptions.getStackTraceString(throwable, ignoreAllCauses); + } + + /** + *

Returns the smart stack trace for the given throwable using packages provided. + * + * @param throwable parent throwable + * @param rootPackageSet root packages to use for building the stack trace + * @param groupPackageSet group packages to use for building the stack trace + * @param ignorePackageSet ignore packages to use for building the stack trace + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final Set rootPackageSet, final Set groupPackageSet, final Set ignorePackageSet) { + return AbstractExceptions.getStackTraceString(throwable, rootPackageSet, groupPackageSet, ignorePackageSet); + } + + /** + *

Returns the smart stack trace for the given throwable using packages provided. + * + * @param throwable parent throwable + * @param rootPackageSet root packages to use for building the stack trace + * @param groupPackageSet group packages to use for building the stack trace + * @param ignorePackageSet ignore packages to use for building the stack trace + * @param ignoreAllCauses ignore all causes in the exception chain + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final Set rootPackageSet, final Set groupPackageSet, final Set ignorePackageSet, final boolean ignoreAllCauses) { + return AbstractExceptions.getStackTraceString(throwable, rootPackageSet, groupPackageSet, ignorePackageSet, ignoreAllCauses); + } + + /** + *

Returns the smart stack trace for the given throwable using root package provided. + * + * @param throwable parent throwable + * @param rootPackage root package to use for building the stack trace + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final String rootPackage) { + return AbstractExceptions.getStackTraceString(throwable, rootPackage); + } + + /** + *

Returns the smart stack trace for the given throwable using root package provided. + * + * @param throwable parent throwable + * @param rootPackage root package to use for building the stack trace + * @param groupPackage group package to use for building the stack trace + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final String rootPackage, final String groupPackage) { + return AbstractExceptions.getStackTraceString(throwable, rootPackage, groupPackage); + } + + /** + *

Returns the smart stack trace for the given throwable using elements found until the maxDepth. + * + * @param throwable parent throwable + * @param maxDepth max depth in exception chain that will be used + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final int maxDepth) { + return AbstractExceptions.getStackTraceString(throwable, maxDepth); + } + + /** + *

Returns the smart stack trace for the given throwable using elements found until the maxDepth. + * + * @param throwable parent throwable + * @param maxDepth max depth in exception chain that will be used + * @param ignoreAllCauses ignore all causes in the exception chain + * @return a string containing the smart stack trace for the given throwable + */ + public static String getStackTraceString(final Throwable throwable, final int maxDepth, final boolean ignoreAllCauses) { + return AbstractExceptions.getStackTraceString(throwable, maxDepth, ignoreAllCauses); + } + + /** + *

Returns all messages found in the exception chain of the throwable as a single string. + * + * @param throwable parent throwable + * @return a string containing all messages found in the exception chain + */ + public static String getAllMessages(final Throwable throwable) { + return AbstractExceptions.getAllMessages(throwable); + } + + /** + *

Returns true if the given cause class is found in the exception chain of the throwable. + *
+ *
+ *

Search starts from the throwable itself and goes through the exception chain up until + * {@link AbstractExceptions#DEFAULT_MAX_DEPTH}. + * + * @param throwable parent throwable + * @param causeClass class to search for + * @return true if the given cause class is found in the exception chain of the throwable, false + * otherwise + */ + public static boolean containsCause(final Throwable throwable, final Class causeClass) { + return AbstractExceptions.containsCause(throwable, causeClass); + } + + /** + *

Returns true if the given cause class and cause message is found in the exception chain of the + * throwable. + *
+ *
+ *

Search starts from the throwable itself and goes through the exception chain up until + * {@link AbstractExceptions#DEFAULT_MAX_DEPTH}. + * + * @param throwable parent throwable + * @param causeClass class to search for + * @param causeMessage message to search for + * @return true if the given cause class and cause message is found in the exception chain of the + * throwable, false otherwise + */ + public static boolean containsCause(final Throwable throwable, final Class causeClass, final String causeMessage) { + return AbstractExceptions.containsCause(throwable, causeClass, causeMessage); + } + + /** + *

Returns the cause of the throwable by walking through the exception chain. + * + * @param throwable parent throwable + * @return the cause of the throwable found or null if throwable does not have a cause + */ + public static Throwable getCause(final Throwable throwable) { + return AbstractExceptions.getCause(throwable); + } + + /** + *

Returns the cause of the throwable by walking through the exception chain. + *
+ *
+ *

Note that this method walks through the exception chain up to given maxDepth. If exception chain + * includes more items than maxDepth, cause found at maxDepth level is returned. + * + * @param throwable parent throwable + * @param maxDepth max depth in exception chain that will be searched + * @return the cause of the throwable found until maxDepth is reached in the exception + * chain or null if throwable does not have a cause + */ + public static Throwable getCause(final Throwable throwable, final int maxDepth) { + return AbstractExceptions.getCause(throwable, maxDepth); + } + + /** + *

Searches for the given cause class in the exception chain of the throwable. + *
+ *
+ *

Search starts from the throwable itself and goes through the exception chain up until + * {@link AbstractExceptions#DEFAULT_MAX_DEPTH}. + * + * @param throwable parent throwable + * @param causeClass class to search for + * @return the throwable found or null if no class in exception chain matches the given cause class + */ + public static Throwable searchCause(final Throwable throwable, final Class causeClass) { + return AbstractExceptions.searchCause(throwable, causeClass); + } + + /** + *

Searches for the given cause class and cause message in the exception chain of the throwable. + *
+ *
+ *

Search starts from the throwable itself and goes through the exception chain up until + * {@link AbstractExceptions#DEFAULT_MAX_DEPTH}. + * + * @param throwable parent throwable + * @param causeClass class to search for + * @param causeMessage message to search for + * @return the throwable found or null if no class in exception chain matches the given cause class + * and cause message + */ + public static Throwable searchCause(final Throwable throwable, final Class causeClass, final String causeMessage) { + return AbstractExceptions.searchCause(throwable, causeClass, causeMessage); + } + + /** + *

Searches for the given cause class and cause message in the exception chain of the throwable. + *
+ *
+ *

Search starts from the throwable itself and goes through the exception chain up until the given + * maxDepth. + * + * @param throwable parent throwable + * @param causeClass class to search for + * @param causeMessage message to search for + * @param maxDepth max depth in exception chain that will be searched + * @return the throwable found or null if no class in exception chain matches the given cause class + * and cause message + */ + public static Throwable searchCause(final Throwable throwable, final Class causeClass, final String causeMessage, final int maxDepth) { + return AbstractExceptions.searchCause(throwable, causeClass, causeMessage, maxDepth); + } + + /** + *

Searches for the given cause class in the exception chain of the throwable. + *
+ *
+ *

Search starts from the throwable itself and goes through the exception chain up until the given + * maxDepth. + * + * @param throwable parent throwable + * @param causeClass class to search for + * @param maxDepth max depth in exception chain that will be searched + * @return the throwable found or null if no class in exception chain matches the given cause class + */ + public static Throwable searchCause(final Throwable throwable, final Class causeClass, final int maxDepth) { + return AbstractExceptions.searchCause(throwable, causeClass, maxDepth); + } + +} \ No newline at end of file diff --git a/app/src/main/java/ru/vtosters/lite/music/FFMpeg.java b/app/src/main/java/ru/vtosters/lite/music/FFMpeg.java index b7ee15ea2..d16184c8e 100644 --- a/app/src/main/java/ru/vtosters/lite/music/FFMpeg.java +++ b/app/src/main/java/ru/vtosters/lite/music/FFMpeg.java @@ -1,6 +1,5 @@ package ru.vtosters.lite.music; -import android.os.Environment; import android.util.Log; import com.arthenica.ffmpegkit.FFmpegKit; @@ -21,11 +20,11 @@ public class FFMpeg { try { String[] files = Globals.getContext().getAssets().list("ffmpeg"); // log all files - Log.d("INIT", "files: " + Arrays.toString(files)); + Log.d("FFMpeg", "files: " + Arrays.toString(files)); for (String file : files) { if (file.endsWith(".so")) { InputStream in = Globals.getContext().getAssets().open("ffmpeg" + File.separator + file); - FileOutputStream out = new FileOutputStream(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/" + file); + FileOutputStream out = new FileOutputStream(Globals.getContext().getFilesDir() + File.separator + file); byte[] buffer = new byte[BUFFER_SIZE]; int read; while ((read = in.read(buffer)) != -1) { @@ -41,7 +40,7 @@ public class FFMpeg { } private static void checkFFMpegLibs() { - var lib = new File(Globals.getContext().getCacheDir(), "libffmpegkit_abidetect.so"); + var lib = new File(Globals.getContext().getFilesDir(), "libffmpegkit_abidetect.so"); if (!lib.exists()) { ToastUtils.a("Копирование библиотеки ffmpegkit"); copyAssets(); @@ -60,7 +59,4 @@ public class FFMpeg { return false; } } - - - }