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

com.landawn.abacus.util.ExceptionUtil Maven / Gradle / Ivy

Go to download

A general programming library in Java/Android. It's easy to learn and simple to use with concise and powerful APIs.

The newest version!
/*
 * Copyright (C) 2018 HaiYang Li
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.landawn.abacus.util;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;

import com.landawn.abacus.annotation.Beta;
import com.landawn.abacus.exception.UncheckedException;
import com.landawn.abacus.exception.UncheckedIOException;
import com.landawn.abacus.exception.UncheckedInterruptedException;
import com.landawn.abacus.exception.UncheckedParseException;
import com.landawn.abacus.exception.UncheckedReflectiveOperationException;
import com.landawn.abacus.exception.UncheckedSQLException;
import com.landawn.abacus.util.u.Optional;

/**
 * Note: This class contains the methods copied from Apache Commons and Google Guava under Apache License v2.
 *
 */
public final class ExceptionUtil {

    private static final int MAX_DEPTH_FOR_LOOP_CAUSE = 100;

    private ExceptionUtil() {
        // singleton
    }

    private static final Map, Function> toRuntimeExceptionFuncMap = new ConcurrentHashMap<>();

    static {
        toRuntimeExceptionFuncMap.put(RuntimeException.class, e -> (RuntimeException) e);

        toRuntimeExceptionFuncMap.put(Error.class, RuntimeException::new); // right or not?

        toRuntimeExceptionFuncMap.put(IOException.class, e -> new UncheckedIOException((IOException) e));

        toRuntimeExceptionFuncMap.put(SQLException.class, e -> new UncheckedSQLException((SQLException) e));

        toRuntimeExceptionFuncMap.put(ReflectiveOperationException.class, e -> new UncheckedReflectiveOperationException((ReflectiveOperationException) e));

        toRuntimeExceptionFuncMap.put(ParseException.class, e -> new UncheckedParseException((ParseException) e));

        toRuntimeExceptionFuncMap.put(InterruptedException.class, e -> new UncheckedInterruptedException((InterruptedException) e));

        toRuntimeExceptionFuncMap.put(ExecutionException.class, e -> e.getCause() == null ? new UncheckedException(e) : toRuntimeException(e.getCause()));

        toRuntimeExceptionFuncMap.put(InvocationTargetException.class,
                e -> e.getCause() == null ? new UncheckedException(e) : toRuntimeException(e.getCause()));

        toRuntimeExceptionFuncMap.put(UndeclaredThrowableException.class,
                e -> e.getCause() == null ? (UndeclaredThrowableException) e : toRuntimeException(e.getCause()));
    }

    private static final Function RUNTIME_FUNC = e -> (RuntimeException) e;

    private static final Function CHECKED_FUNC = UncheckedException::new;

    private static final String UncheckedSQLExceptionClassName = UncheckedSQLException.class.getSimpleName();

    private static final String UncheckedIOExceptionClassName = UncheckedIOException.class.getSimpleName();

    /**
     *
     * @param 
     * @param exceptionClass
     * @param runtimeExceptionMapper
     */
    public static  void registerRuntimeExceptionMapper(final Class exceptionClass,
            final Function runtimeExceptionMapper) {
        registerRuntimeExceptionMapper(exceptionClass, runtimeExceptionMapper, false);
    }

    /**
     *
     * @param 
     * @param exceptionClass
     * @param runtimeExceptionMapper
     * @param force
     * @throws IllegalArgumentException
     */
    @SuppressWarnings("rawtypes")
    public static  void registerRuntimeExceptionMapper(final Class exceptionClass,
            final Function runtimeExceptionMapper, final boolean force) throws IllegalArgumentException {
        N.checkArgNotNull(exceptionClass, cs.exceptionClass);
        N.checkArgNotNull(runtimeExceptionMapper, cs.runtimeExceptionMapper);

        if (N.isBuiltinClass(exceptionClass)) {
            throw new IllegalArgumentException("Can't register Exception class with package starting with \"java.\", \"javax.\", \"com.landawn.abacus\": "
                    + exceptionClass.getPackage().getName());
        }

        if (toRuntimeExceptionFuncMap.containsKey(exceptionClass) && !force) {
            throw new IllegalArgumentException("Exception class: " + ClassUtil.getCanonicalClassName(exceptionClass) + " has already been registered");
        }

        toRuntimeExceptionFuncMap.put(exceptionClass, (Function) runtimeExceptionMapper);
    }

    /**
     * Converts the specified {@code Exception} to a {@code RuntimeException} if it's a checked {@code exception}. Otherwise, returns itself.
     *
     * @param e the exception to convert
     * @return the converted runtime exception
     */
    public static RuntimeException toRuntimeException(final Exception e) {
        return toRuntimeException(e, false, false);
    }

    /**
     * Converts the specified {@code Exception} to a {@code RuntimeException} if it's a checked {@code exception}. Otherwise, returns itself.
     *
     * @param e the exception to convert
     * @param callInterrupt whether to call {@code Thread.currentThread().interrupt()} if the exception is an {@code InterruptedException}
     * @return the converted runtime exception
     */
    public static RuntimeException toRuntimeException(final Exception e, final boolean callInterrupt) {
        return toRuntimeException(e, callInterrupt, false);
    }

    /**
     * Converts the specified {@code Throwable} to a {@code RuntimeException} if it's a checked {@code exception}.
     * Otherwise, returns itself.
     *
     * @param e the throwable to convert
     * @return the converted runtime exception
     */
    public static RuntimeException toRuntimeException(final Throwable e) {
        return toRuntimeException(e, false, false);
    }

    /**
     * Converts the specified {@code Throwable} to a {@code RuntimeException} if it's a checked {@code exception},
     * or throw it if it's an {@code Error}. Otherwise, returns itself.
     *
     * @param e the throwable to convert
     * @param callInterrupt whether to call {@code Thread.currentThread().interrupt()} if the exception is an {@code InterruptedException}
     * @return the converted runtime exception
     */
    public static RuntimeException toRuntimeException(final Throwable e, final boolean callInterrupt) {
        return toRuntimeException(e, callInterrupt, false);
    }

    /**
     * Converts the specified {@code Throwable} to a {@code RuntimeException} if it's a checked {@code exception},
     * or throws it if it's an {@code Error}. Otherwise, returns itself.
     *
     * @param e the throwable to convert
     * @param callInterrupt whether to call {@code Thread.currentThread().interrupt()} if the exception is an {@code InterruptedException}
     * @param throwIfItIsError whether to throw the throwable if it is an {@code Error}
     * @return the converted runtime exception
     */
    public static RuntimeException toRuntimeException(final Throwable e, final boolean callInterrupt, final boolean throwIfItIsError) {
        if (throwIfItIsError && e instanceof Error) {
            throw (Error) e;
        }

        if (callInterrupt && e instanceof InterruptedException) {
            Thread.currentThread().interrupt();
        }

        final Class cls = (Class) e.getClass();
        Function func = toRuntimeExceptionFuncMap.get(cls);

        Map.Entry, Function> candidate = null;

        if (func == null) {
            for (final Map.Entry, Function> entry : toRuntimeExceptionFuncMap.entrySet()) { //NOSONAR
                if (entry.getKey().isAssignableFrom(cls) && (candidate == null || candidate.getKey().isAssignableFrom(entry.getKey()))) {
                    candidate = entry;
                }
            }

            if (candidate == null) {
                if (e instanceof RuntimeException) {
                    func = RUNTIME_FUNC;
                } else {
                    func = CHECKED_FUNC;
                }
            } else {
                func = candidate.getValue();
            }

            toRuntimeExceptionFuncMap.put(cls, func);
        }

        return func.apply(e);
    }

    static final Predicate uncheckedExceptionNameTester = Pattern.compile("Unchecked[a-zA-Z0-9]*Exception").asPredicate();
    static final Map, Class> runtimeToCheckedExceptionClassMap = new ConcurrentHashMap<>();

    /**
     *
     * @param e
     * @return
     */
    public static Exception tryToGetOriginalCheckedException(final Exception e) {
        return tryToGetOriginalCheckedException(e, MAX_DEPTH_FOR_LOOP_CAUSE);
    }

    private static Exception tryToGetOriginalCheckedException(final Exception e, int loopCount) {
        if (loopCount <= 0) {
            return e;
        }

        if (e instanceof RuntimeException && e.getCause() instanceof Exception && !(e.getCause() instanceof RuntimeException)) {
            if (e instanceof UncheckedException //
                    || (uncheckedExceptionNameTester.test(ClassUtil.getSimpleClassName(e.getClass())))) {
                return (Exception) e.getCause();
            }

            final Throwable cause = e.getCause();

            if (cause.getClass().equals(runtimeToCheckedExceptionClassMap.get(e.getClass()))) {
                return (Exception) cause;
            }

            if (toRuntimeExceptionFuncMap.containsKey(cause.getClass())
                    && toRuntimeExceptionFuncMap.get(cause.getClass()).apply(cause).getClass().equals(e.getClass())) {

                runtimeToCheckedExceptionClassMap.put(e.getClass(), cause.getClass());

                return (Exception) cause;
            }
        } else if (e.getCause() instanceof Exception && (e instanceof InvocationTargetException || e instanceof ExecutionException)) {
            return tryToGetOriginalCheckedException((Exception) e.getCause(), --loopCount);
        }

        return e;
    }

    /**
     *
     * @param e
     * @param targetExceptionType
     * @return
     */
    public static boolean hasCause(final Throwable e, final Class targetExceptionType) {
        int maxDepth = MAX_DEPTH_FOR_LOOP_CAUSE;
        Throwable prevCause = null;
        Throwable cause = e;

        do {
            if (targetExceptionType.isAssignableFrom(cause.getClass())) {
                return true;
            }

            prevCause = cause;
        } while (maxDepth-- > 0 && (cause = cause.getCause()) != null && cause != prevCause);

        return false;
    }

    /**
     *
     * @param e
     * @param targetExceptionTester
     * @return
     */
    public static boolean hasCause(final Throwable e, final Predicate targetExceptionTester) {
        int maxDepth = MAX_DEPTH_FOR_LOOP_CAUSE;
        Throwable prevCause = null;
        Throwable cause = e;

        do {
            if (targetExceptionTester.test(cause)) {
                return true;
            }

            prevCause = cause;
        } while (maxDepth-- > 0 && (cause = cause.getCause()) != null && cause != prevCause);

        return false;
    }

    /**
     *
     * @param e
     * @return
     */
    public static boolean hasSQLCause(final Throwable e) {
        int maxDepth = MAX_DEPTH_FOR_LOOP_CAUSE;
        Throwable prevCause = null;
        Throwable cause = e;

        do {
            if (cause instanceof SQLException || UncheckedSQLExceptionClassName.equals(cause.getClass().getSimpleName())) {
                return true;
            }

            prevCause = cause;
        } while (maxDepth-- > 0 && (cause = cause.getCause()) != null && cause != prevCause);

        return false;
    }

    /**
     *
     * @param e
     * @return
     */
    public static boolean hasIOCause(final Throwable e) {
        int maxDepth = MAX_DEPTH_FOR_LOOP_CAUSE;
        Throwable prevCause = null;
        Throwable cause = e;

        do {
            if (cause instanceof IOException || UncheckedIOExceptionClassName.equals(cause.getClass().getSimpleName())) {
                return true;
            }

            prevCause = cause;
        } while (maxDepth-- > 0 && (cause = cause.getCause()) != null && cause != prevCause);

        return false;
    }

    /**
     *
     * @param e
     * @return
     */
    @Beta
    public static boolean isNullPointerOrIllegalArgumentException(final Throwable e) {
        return e instanceof NullPointerException || e instanceof IllegalArgumentException;
    }

    /**
     *
     * @param e
     * @return
     */
    public static List listCause(final Throwable e) {
        final List list = new ArrayList<>();
        Throwable cause = e;

        while (cause != null && !list.contains(cause)) {
            list.add(cause);
            cause = cause.getCause();
        }

        return list;
    }

    /**
     * Returns the specified {@code throwable} if there is no cause found in it ({@code throwable.getCause() == null}).
     *
     * @param e
     * @return
     */
    public static Throwable firstCause(final Throwable e) {
        int maxDepth = MAX_DEPTH_FOR_LOOP_CAUSE;
        Throwable cause = e;
        Throwable result = e;

        while (maxDepth-- > 0 && (cause = cause.getCause()) != null && cause != result) {
            result = cause;
        }

        return result;
    }

    /**
     *
     * @param 
     * @param e
     * @param targetExceptionType
     * @return
     */
    public static  Optional findCause(final Throwable e, final Class targetExceptionType) {
        int maxDepth = MAX_DEPTH_FOR_LOOP_CAUSE;
        Throwable prevCause = null;
        Throwable cause = e;

        do {
            if (targetExceptionType.isAssignableFrom(cause.getClass())) {
                return Optional.of((E) cause);
            }

            prevCause = cause;
        } while (maxDepth-- > 0 && (cause = cause.getCause()) != null && cause != prevCause);

        return Optional.empty();
    }

    /**
     * @param 
     * @param e
     * @param targetExceptionTester
     * @return
     */
    public static  Optional findCause(final Throwable e, final Predicate targetExceptionTester) {
        int maxDepth = MAX_DEPTH_FOR_LOOP_CAUSE;
        Throwable prevCause = null;
        Throwable cause = e;

        do {
            if (targetExceptionTester.test(cause)) {
                return Optional.of((E) cause);
            }

            prevCause = cause;
        } while (maxDepth-- > 0 && (cause = cause.getCause()) != null && cause != prevCause);

        return Optional.empty();
    }

    //-----------------------------------------------------------------------

    /**
     * Gets the stack trace from a Throwable as a String.
     *
     * 

The result of this method varies by JDK version as this method * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. * On JDK1.3 and earlier, the cause exception will not be shown * unless the specified throwable alters printStackTrace.

* * @param throwable the {@link Throwable} to be examined, which may be null * @return the stack trace as generated by the exception's * {@code printStackTrace(PrintWriter)} method, or an empty String if {@code null} input */ public static String getStackTrace(final Throwable throwable) { if (throwable == null) { return Strings.EMPTY; } final StringWriter sw = new StringWriter(); throwable.printStackTrace(new PrintWriter(sw, true)); return sw.toString(); } // /** // * Gets the error msg. // * // * @param e // * @return // * @deprecated replaced by {@link #getErrorMessage(Throwable, true)} // */ // @Deprecated // @Internal // public static String getMessage(Throwable e) { // return getErrorMessage(e, true); // } /** * * @param e * @return */ public static String getErrorMessage(final Throwable e) { return getErrorMessage(e, false); } /** * * @param e * @param withExceptionClassName * @return */ public static String getErrorMessage(final Throwable e, final boolean withExceptionClassName) { String msg = e.getMessage(); if (Strings.isEmpty(msg) && e.getCause() != null) { int maxDepth = MAX_DEPTH_FOR_LOOP_CAUSE; Throwable prevCause = null; Throwable cause = e; do { msg = cause.getMessage(); if (Strings.isNotEmpty(msg)) { break; } prevCause = cause; } while (maxDepth-- > 0 && (cause = cause.getCause()) != null && cause != prevCause); } if (Strings.isEmpty(msg)) { msg = e.getClass().getCanonicalName(); } if (withExceptionClassName) { if (e instanceof SQLException) { return e.getClass().getSimpleName() + "|" + ((SQLException) e).getErrorCode() + "|" + msg; } else { return e.getClass().getSimpleName() + "|" + msg; } } else { if (e instanceof SQLException) { return ((SQLException) e).getErrorCode() + "|" + msg; } else { return msg; } } } // ---------------------------------------------------------------------------------------------> }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy