/*
 * Decompiled with CFR 0.152.
 */
package org.apache.logging.log4j.core.impl;

import java.io.Serializable;
import java.net.URL;
import java.security.CodeSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.impl.ExtendedClassInfo;
import org.apache.logging.log4j.core.impl.ExtendedStackTraceElement;
import org.apache.logging.log4j.core.impl.ReflectiveCallerClassUtility;
import org.apache.logging.log4j.core.util.Loader;
import org.apache.logging.log4j.core.util.Throwables;
import org.apache.logging.log4j.status.StatusLogger;

public class ThrowableProxy
implements Serializable {
    private static final ThrowableProxy[] EMPTY_THROWABLE_PROXY_ARRAY = new ThrowableProxy[0];
    private static final char EOL = '\n';
    private static final Logger LOGGER = StatusLogger.getLogger();
    private static final PrivateSecurityManager SECURITY_MANAGER;
    private static final long serialVersionUID = -2752771578252251910L;
    private final ThrowableProxy causeProxy;
    private int commonElementCount;
    private final ExtendedStackTraceElement[] extendedStackTrace;
    private final String localizedMessage;
    private final String message;
    private final String name;
    private final ThrowableProxy[] suppressedProxies;
    private final transient Throwable throwable;

    private ThrowableProxy() {
        this.throwable = null;
        this.name = null;
        this.extendedStackTrace = null;
        this.causeProxy = null;
        this.message = null;
        this.localizedMessage = null;
        this.suppressedProxies = EMPTY_THROWABLE_PROXY_ARRAY;
    }

    public ThrowableProxy(Throwable throwable) {
        this.throwable = throwable;
        this.name = throwable.getClass().getName();
        this.message = throwable.getMessage();
        this.localizedMessage = throwable.getLocalizedMessage();
        HashMap<String, CacheEntry> map = new HashMap<String, CacheEntry>();
        Stack<Class<?>> stack = this.getCurrentStack();
        this.extendedStackTrace = this.toExtendedStackTrace(stack, map, null, throwable.getStackTrace());
        Throwable throwableCause = throwable.getCause();
        this.causeProxy = throwableCause == null ? null : new ThrowableProxy(throwable, stack, map, throwableCause);
        this.suppressedProxies = this.toSuppressedProxies(throwable);
    }

    private ThrowableProxy(Throwable parent, Stack<Class<?>> stack, Map<String, CacheEntry> map, Throwable cause) {
        this.throwable = cause;
        this.name = cause.getClass().getName();
        this.message = this.throwable.getMessage();
        this.localizedMessage = this.throwable.getLocalizedMessage();
        this.extendedStackTrace = this.toExtendedStackTrace(stack, map, parent.getStackTrace(), cause.getStackTrace());
        this.causeProxy = cause.getCause() == null ? null : new ThrowableProxy(parent, stack, map, cause.getCause());
        this.suppressedProxies = this.toSuppressedProxies(cause);
    }

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (this.getClass() != obj.getClass()) {
            return false;
        }
        ThrowableProxy other = (ThrowableProxy)obj;
        if (this.causeProxy == null ? other.causeProxy != null : !this.causeProxy.equals(other.causeProxy)) {
            return false;
        }
        if (this.commonElementCount != other.commonElementCount) {
            return false;
        }
        if (this.name == null ? other.name != null : !this.name.equals(other.name)) {
            return false;
        }
        if (!Arrays.equals(this.extendedStackTrace, other.extendedStackTrace)) {
            return false;
        }
        return Arrays.equals(this.suppressedProxies, other.suppressedProxies);
    }

    private void formatCause(StringBuilder sb, ThrowableProxy cause, List<String> ignorePackages) {
        sb.append("Caused by: ").append(cause).append('\n');
        this.formatElements(sb, cause.commonElementCount, cause.getThrowable().getStackTrace(), cause.extendedStackTrace, ignorePackages);
        if (cause.getCauseProxy() != null) {
            this.formatCause(sb, cause.causeProxy, ignorePackages);
        }
    }

    private void formatElements(StringBuilder sb, int commonCount, StackTraceElement[] causedTrace, ExtendedStackTraceElement[] extStackTrace, List<String> ignorePackages) {
        if (ignorePackages == null || ignorePackages.isEmpty()) {
            for (int i = 0; i < extStackTrace.length; ++i) {
                this.formatEntry(causedTrace[i], extStackTrace[i], sb);
            }
        } else {
            int count = 0;
            for (int i = 0; i < extStackTrace.length; ++i) {
                if (!this.ignoreElement(causedTrace[i], ignorePackages)) {
                    if (count > 0) {
                        if (count == 1) {
                            sb.append("\t....\n");
                        } else {
                            sb.append("\t... suppressed ").append(count).append(" lines\n");
                        }
                        count = 0;
                    }
                    this.formatEntry(causedTrace[i], extStackTrace[i], sb);
                    continue;
                }
                ++count;
            }
            if (count > 0) {
                if (count == 1) {
                    sb.append("\t...\n");
                } else {
                    sb.append("\t... suppressed ").append(count).append(" lines\n");
                }
            }
        }
        if (commonCount != 0) {
            sb.append("\t... ").append(commonCount).append(" more").append('\n');
        }
    }

    private void formatEntry(StackTraceElement element, ExtendedStackTraceElement extStackTraceElement, StringBuilder sb) {
        sb.append("\tat ");
        sb.append(extStackTraceElement);
        sb.append('\n');
    }

    public void formatWrapper(StringBuilder sb, ThrowableProxy cause) {
        this.formatWrapper(sb, cause, null);
    }

    public void formatWrapper(StringBuilder sb, ThrowableProxy cause, List<String> packages) {
        Throwable caused;
        Throwable throwable = caused = cause.getCauseProxy() != null ? cause.getCauseProxy().getThrowable() : null;
        if (caused != null) {
            this.formatWrapper(sb, cause.causeProxy);
            sb.append("Wrapped by: ");
        }
        sb.append(cause).append('\n');
        this.formatElements(sb, cause.commonElementCount, cause.getThrowable().getStackTrace(), cause.extendedStackTrace, packages);
    }

    public ThrowableProxy getCauseProxy() {
        return this.causeProxy;
    }

    public String getCauseStackTraceAsString() {
        return this.getCauseStackTraceAsString(null);
    }

    public String getCauseStackTraceAsString(List<String> packages) {
        StringBuilder sb = new StringBuilder();
        if (this.causeProxy != null) {
            this.formatWrapper(sb, this.causeProxy);
            sb.append("Wrapped by: ");
        }
        sb.append(this.toString());
        sb.append('\n');
        this.formatElements(sb, 0, this.throwable.getStackTrace(), this.extendedStackTrace, packages);
        return sb.toString();
    }

    public int getCommonElementCount() {
        return this.commonElementCount;
    }

    private Stack<Class<?>> getCurrentStack() {
        if (ReflectiveCallerClassUtility.isSupported()) {
            Stack classes = new Stack();
            int index = 1;
            Class<?> clazz = ReflectiveCallerClassUtility.getCaller(index);
            while (clazz != null) {
                classes.push(clazz);
                clazz = ReflectiveCallerClassUtility.getCaller(++index);
            }
            return classes;
        }
        if (SECURITY_MANAGER != null) {
            Class<?>[] array = SECURITY_MANAGER.getClasses();
            Stack classes = new Stack();
            for (Class<?> clazz : array) {
                classes.push(clazz);
            }
            return classes;
        }
        return new Stack();
    }

    public ExtendedStackTraceElement[] getExtendedStackTrace() {
        return this.extendedStackTrace;
    }

    public String getExtendedStackTraceAsString() {
        return this.getExtendedStackTraceAsString(null);
    }

    public String getExtendedStackTraceAsString(List<String> ignorePackages) {
        StringBuilder sb = new StringBuilder(this.name);
        String msg = this.message;
        if (msg != null) {
            sb.append(": ").append(msg);
        }
        sb.append('\n');
        this.formatElements(sb, 0, this.throwable.getStackTrace(), this.extendedStackTrace, ignorePackages);
        if (this.causeProxy != null) {
            this.formatCause(sb, this.causeProxy, ignorePackages);
        }
        return sb.toString();
    }

    public String getLocalizedMessage() {
        return this.localizedMessage;
    }

    public String getMessage() {
        return this.message;
    }

    public String getName() {
        return this.name;
    }

    public StackTraceElement[] getStackTrace() {
        return this.throwable == null ? null : this.throwable.getStackTrace();
    }

    public ThrowableProxy[] getSuppressedProxies() {
        return this.suppressedProxies;
    }

    public String getSuppressedStackTrace() {
        ThrowableProxy[] suppressed = this.getSuppressedProxies();
        if (suppressed == null || suppressed.length == 0) {
            return "";
        }
        StringBuilder sb = new StringBuilder("Suppressed Stack Trace Elements:\n");
        for (ThrowableProxy proxy : suppressed) {
            sb.append(proxy.getExtendedStackTraceAsString());
        }
        return sb.toString();
    }

    public Throwable getThrowable() {
        return this.throwable;
    }

    public int hashCode() {
        int prime = 31;
        int result = 1;
        result = 31 * result + (this.causeProxy == null ? 0 : this.causeProxy.hashCode());
        result = 31 * result + this.commonElementCount;
        result = 31 * result + (this.extendedStackTrace == null ? 0 : Arrays.hashCode(this.extendedStackTrace));
        result = 31 * result + (this.suppressedProxies == null ? 0 : Arrays.hashCode(this.suppressedProxies));
        result = 31 * result + (this.name == null ? 0 : this.name.hashCode());
        return result;
    }

    private boolean ignoreElement(StackTraceElement element, List<String> ignorePackages) {
        String className = element.getClassName();
        for (String pkg : ignorePackages) {
            if (!className.startsWith(pkg)) continue;
            return true;
        }
        return false;
    }

    private Class<?> loadClass(ClassLoader lastLoader, String className) {
        Class<?> clazz;
        if (lastLoader != null) {
            try {
                clazz = Loader.initializeClass(className, lastLoader);
                if (clazz != null) {
                    return clazz;
                }
            }
            catch (Exception ignore) {
                // empty catch block
            }
        }
        try {
            clazz = Loader.loadClass(className);
        }
        catch (ClassNotFoundException ignored) {
            try {
                clazz = Loader.initializeClass(className, this.getClass().getClassLoader());
            }
            catch (ClassNotFoundException ignore) {
                return null;
            }
        }
        return clazz;
    }

    private CacheEntry toCacheEntry(StackTraceElement stackTraceElement, Class<?> callerClass, boolean exact) {
        String location = "?";
        String version = "?";
        ClassLoader lastLoader = null;
        if (callerClass != null) {
            String ver;
            try {
                URL locationURL;
                CodeSource source = callerClass.getProtectionDomain().getCodeSource();
                if (source != null && (locationURL = source.getLocation()) != null) {
                    String str = locationURL.toString().replace('\\', '/');
                    int index = str.lastIndexOf("/");
                    if (index >= 0 && index == str.length() - 1) {
                        index = str.lastIndexOf("/", index - 1);
                        location = str.substring(index + 1);
                    } else {
                        location = str.substring(index + 1);
                    }
                }
            }
            catch (Exception ex) {
                // empty catch block
            }
            Package pkg = callerClass.getPackage();
            if (pkg != null && (ver = pkg.getImplementationVersion()) != null) {
                version = ver;
            }
            lastLoader = callerClass.getClassLoader();
        }
        return new CacheEntry(new ExtendedClassInfo(exact, location, version), lastLoader);
    }

    ExtendedStackTraceElement[] toExtendedStackTrace(Stack<Class<?>> stack, Map<String, CacheEntry> map, StackTraceElement[] rootTrace, StackTraceElement[] stackTrace) {
        int stackLength;
        if (rootTrace != null) {
            int stackIndex;
            int rootIndex = rootTrace.length - 1;
            for (stackIndex = stackTrace.length - 1; rootIndex >= 0 && stackIndex >= 0 && rootTrace[rootIndex].equals(stackTrace[stackIndex]); --rootIndex, --stackIndex) {
            }
            this.commonElementCount = stackTrace.length - 1 - stackIndex;
            stackLength = stackIndex + 1;
        } else {
            this.commonElementCount = 0;
            stackLength = stackTrace.length;
        }
        ExtendedStackTraceElement[] extStackTrace = new ExtendedStackTraceElement[stackLength];
        Class<?> clazz = stack.isEmpty() ? null : stack.peek();
        ClassLoader lastLoader = null;
        for (int i = stackLength - 1; i >= 0; --i) {
            ExtendedClassInfo extClassInfo;
            CacheEntry entry;
            StackTraceElement stackTraceElement = stackTrace[i];
            String className = stackTraceElement.getClassName();
            if (clazz != null && className.equals(clazz.getName())) {
                entry = this.toCacheEntry(stackTraceElement, clazz, true);
                extClassInfo = entry.element;
                lastLoader = entry.loader;
                stack.pop();
                clazz = stack.isEmpty() ? null : stack.peek();
            } else if (map.containsKey(className)) {
                entry = map.get(className);
                extClassInfo = entry.element;
                if (entry.loader != null) {
                    lastLoader = entry.loader;
                }
            } else {
                entry = this.toCacheEntry(stackTraceElement, this.loadClass(lastLoader, className), false);
                extClassInfo = entry.element;
                map.put(stackTraceElement.toString(), entry);
                if (entry.loader != null) {
                    lastLoader = entry.loader;
                }
            }
            extStackTrace[i] = new ExtendedStackTraceElement(stackTraceElement, extClassInfo);
        }
        return extStackTrace;
    }

    public String toString() {
        String msg = this.message;
        return msg != null ? this.name + ": " + msg : this.name;
    }

    private ThrowableProxy[] toSuppressedProxies(Throwable thrown) {
        try {
            Throwable[] suppressed = Throwables.getSuppressed(thrown);
            if (suppressed == null) {
                return EMPTY_THROWABLE_PROXY_ARRAY;
            }
            ThrowableProxy[] proxies = new ThrowableProxy[suppressed.length];
            for (int i = 0; i < suppressed.length; ++i) {
                proxies[i] = new ThrowableProxy(suppressed[i]);
            }
            return proxies;
        }
        catch (Exception e) {
            StatusLogger.getLogger().error(e);
            return null;
        }
    }

    static {
        if (ReflectiveCallerClassUtility.isSupported()) {
            SECURITY_MANAGER = null;
        } else {
            PrivateSecurityManager securityManager;
            try {
                securityManager = new PrivateSecurityManager();
                if (securityManager.getClasses() == null) {
                    securityManager = null;
                    LOGGER.error("Unable to obtain call stack from security manager.");
                }
            }
            catch (Exception e) {
                securityManager = null;
                LOGGER.debug("Unable to install security manager.", (Throwable)e);
            }
            SECURITY_MANAGER = securityManager;
        }
    }

    private static class PrivateSecurityManager
    extends SecurityManager {
        private PrivateSecurityManager() {
        }

        public Class<?>[] getClasses() {
            return this.getClassContext();
        }
    }

    static class CacheEntry {
        private final ExtendedClassInfo element;
        private final ClassLoader loader;

        public CacheEntry(ExtendedClassInfo element, ClassLoader loader) {
            this.element = element;
            this.loader = loader;
        }
    }
}
