/
githubmirror
/
RxJava
Обзор
Документация
Войти
/
githubmirror
/
RxJava
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
4.x
src/main/java/io/reactivex/rxjava4/exceptions/CompositeException.java
290 строк
11 KB
David Karnok
4.x: Java Migration; records, newer API, newer syntax (#8187)
26 июн 2026, 19:06
Не верифицирован
26 июн 2026, 19:06
ebdb8a9
Код
Авторство
О чём код?
/* * Copyright (c) 2016-present, RxJava Contributors. * * 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 * * http://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 io.reactivex.rxjava4.exceptions; import java.io.*; import java.util.*; import io.reactivex.rxjava4.annotations.NonNull; /** * Represents an exception that is a composite of one or more other exceptions. A {@code CompositeException} * does not modify the structure of any exception it wraps, but at print-time it iterates through the list of * Throwables contained in the composite in order to print them all. * * Its invariant is to contain an immutable, ordered (by insertion order), unique list of non-composite * exceptions. You can retrieve individual exceptions in this list with {@link #getExceptions()}. * * The {@link #printStackTrace()} implementation handles the StackTrace in a customized way instead of using * {@code getCause()} so that it can avoid circular references. * * If you invoke {@link #getCause()}, it will lazily create the causal chain but will stop if it finds any * Throwable in the chain that it has already seen. */ public final class CompositeException extends RuntimeException { @Serial private static final long serialVersionUID = 3026362227162912146L; private final List<Throwable> exceptions; private final String message; private Throwable cause; /** * Constructs a CompositeException with the given array of Throwables as the * list of suppressed exceptions. * @param exceptions the Throwables to have as initially suppressed exceptions * * @throws IllegalArgumentException if <code>exceptions</code> is empty. */ public CompositeException(@NonNull Throwable... exceptions) { this(exceptions == null ? Collections.singletonList(new NullPointerException("exceptions was null")) : Arrays.asList(exceptions)); } /** * Constructs a CompositeException with the given array of Throwables as the * list of suppressed exceptions. * @param errors the Throwables to have as initially suppressed exceptions * * @throws IllegalArgumentException if <code>errors</code> is empty. */ public CompositeException(@NonNull Iterable<? extends Throwable> errors) { Set<Throwable> deDupedExceptions = new LinkedHashSet<>(); if (errors != null) { for (Throwable ex : errors) { if (ex instanceof CompositeException ce) { deDupedExceptions.addAll(ce.getExceptions()); } else { deDupedExceptions.add(Objects.requireNonNullElseGet(ex, () -> new NullPointerException("Throwable was null!"))); } } } else { deDupedExceptions.add(new NullPointerException("errors was null")); } if (deDupedExceptions.isEmpty()) { throw new IllegalArgumentException("errors is empty"); } List<Throwable> localExceptions = new ArrayList<>(deDupedExceptions); this.exceptions = Collections.unmodifiableList(localExceptions); this.message = exceptions.size() + " exceptions occurred. "; } /** * Retrieves the list of exceptions that make up the {@code CompositeException}. * * @return the exceptions that make up the {@code CompositeException}, as a {@link List} of {@link Throwable}s */ @NonNull public List<Throwable> getExceptions() { return exceptions; } @Override @NonNull public String getMessage() { return message; } @Override @NonNull public synchronized Throwable getCause() { // NOPMD if (cause == null) { String separator = System.getProperty("line.separator"); if (exceptions.size() > 1) { Map<Throwable, Boolean> seenCauses = new IdentityHashMap<>(); StringBuilder aggregateMessage = new StringBuilder(); aggregateMessage.append("Multiple exceptions (").append(exceptions.size()).append(")").append(separator); for (Throwable inner : exceptions) { int depth = 0; while (inner != null) { aggregateMessage.repeat(" ", Math.max(0, depth)); aggregateMessage.append("|-- "); aggregateMessage.append(inner.getClass().getCanonicalName()).append(": "); String innerMessage = inner.getMessage(); if (innerMessage != null && innerMessage.contains(separator)) { aggregateMessage.append(separator); for (String line : innerMessage.split(separator)) { aggregateMessage.repeat(" ", Math.max(0, depth + 2)); aggregateMessage.append(line).append(separator); } } else { aggregateMessage.append(innerMessage); aggregateMessage.append(separator); } aggregateMessage.repeat(" ", Math.max(0, depth + 2)); StackTraceElement[] st = inner.getStackTrace(); if (st.length > 0) { aggregateMessage.append("at ").append(st[0]).append(separator); } if (!seenCauses.containsKey(inner)) { seenCauses.put(inner, true); inner = inner.getCause(); depth++; } else { inner = inner.getCause(); if (inner != null) { aggregateMessage.repeat(" ", Math.max(0, depth + 2)); aggregateMessage.append("|-- "); aggregateMessage.append("(cause not expanded again) "); aggregateMessage.append(inner.getClass().getCanonicalName()).append(": "); aggregateMessage.append(inner.getMessage()); aggregateMessage.append(separator); } break; } } } cause = new ExceptionOverview(aggregateMessage.toString().trim()); } else { cause = exceptions.getFirst(); } } return cause; } /** * All of the following {@code printStackTrace} functionality is derived from JDK {@link Throwable} * {@code printStackTrace}. In particular, the {@code PrintStreamOrWriter} abstraction is copied wholesale. * * Changes from the official JDK implementation:<ul> * <li>no infinite loop detection</li> * <li>smaller critical section holding {@link PrintStream} lock</li> * <li>explicit knowledge about the exceptions {@link List} that this loops through</li> * </ul> */ @Override public void printStackTrace() { printStackTrace(System.err); } @Override public void printStackTrace(PrintStream s) { printStackTrace(new WrappedPrintStream(s)); } @Override public void printStackTrace(PrintWriter s) { printStackTrace(new WrappedPrintWriter(s)); } /** * Special handling for printing out a {@code CompositeException}. * Loops through all inner exceptions and prints them out. * * @param output * stream to print to */ private void printStackTrace(PrintStreamOrWriter output) { output.append(this).append("\n"); for (StackTraceElement myStackElement : getStackTrace()) { output.append("\tat ").append(myStackElement).append("\n"); } int i = 1; for (Throwable ex : exceptions) { output.append(" ComposedException ").append(i).append(" :\n"); appendStackTrace(output, ex, "\t"); i++; } output.append("\n"); } private void appendStackTrace(PrintStreamOrWriter output, Throwable ex, String prefix) { output.append(prefix).append(ex).append('\n'); for (StackTraceElement stackElement : ex.getStackTrace()) { output.append("\t\tat ").append(stackElement).append('\n'); } if (ex.getCause() != null) { output.append("\tCaused by: "); appendStackTrace(output, ex.getCause(), ""); } } abstract static class PrintStreamOrWriter { /** * Prints the object's string representation via the underlying PrintStream or PrintWriter. * @param o the object to print * @return this */ abstract PrintStreamOrWriter append(Object o); } /** * Same abstraction and implementation as in JDK to allow PrintStream and PrintWriter to share implementation. */ static final class WrappedPrintStream extends PrintStreamOrWriter { private final PrintStream printStream; WrappedPrintStream(PrintStream printStream) { this.printStream = printStream; } @Override WrappedPrintStream append(Object o) { printStream.print(o); return this; } } /** * Same abstraction and implementation as in JDK to allow PrintStream and PrintWriter to share implementation. */ static final class WrappedPrintWriter extends PrintStreamOrWriter { private final PrintWriter printWriter; WrappedPrintWriter(PrintWriter printWriter) { this.printWriter = printWriter; } @Override WrappedPrintWriter append(Object o) { printWriter.print(o); return this; } } /** * Contains a formatted message with a simplified representation of the exception graph * contained within the CompositeException. */ static final class ExceptionOverview extends RuntimeException { @Serial private static final long serialVersionUID = 3875212506787802066L; ExceptionOverview(String message) { super(message); } @Override public synchronized Throwable fillInStackTrace() { return this; } } /** * Returns the number of suppressed exceptions. * @return the number of suppressed exceptions */ public int size() { return exceptions.size(); } }