Skip to content

Instantly share code, notes, and snippets.

@vy
Last active September 16, 2021 11:16
Show Gist options
  • Save vy/2add907accfee6a0980cd5f1a3882cbf to your computer and use it in GitHub Desktop.
Save vy/2add907accfee6a0980cd5f1a3882cbf to your computer and use it in GitHub Desktop.
Utility method to stream exception causal chains in Java.
/*
* Copyright © 2021 Volkan Yazıcı ([email protected])
*
* 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.
*/
import java.util.Iterator;
import java.util.Objects;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public final class Throwables {
private Throwables() {}
/**
* Streams the causal chain of an exception with loop detection support.
*
* @param error the root error the causal chain will be constructed from
* @return a stream of {@link Throwable}s starting from the given root {@code error} and iterating through all its causes
* @throws IllegalArgumentException if a loop gets detected
*/
public static Stream<Throwable> streamCausalChain(Throwable error) {
// Check arguments.
Objects.requireNonNull(error, "error");
// Use a slow pointer to detect loops.
Throwable[] earlierCause = {error};
boolean[] advanceEarlierCause = {false};
// Encapsulate the `while` loop in an iterator.
Throwable[] cause = {error};
Iterator<Throwable> iterator = new Iterator<Throwable>() {
@Override
public boolean hasNext() {
return cause[0] != null;
}
@Override
public Throwable next() {
// Advance the cause pointer.
Throwable lastCause = cause[0];
cause[0] = cause[0].getCause();
// Advance the earlier cause pointer.
if (earlierCause[0] == cause[0]) {
throw new IllegalArgumentException("loop detected");
}
if (advanceEarlierCause[0]) {
earlierCause[0] = earlierCause[0].getCause();
}
advanceEarlierCause[0] = !advanceEarlierCause[0];
// Return the cause pointer referenced at start.
return lastCause;
}
};
// Create a stream out of the iterator.
Iterable<Throwable> iterable = () -> iterator;
return StreamSupport.stream(iterable.spliterator(), false);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment