forked from ReactiveX/RxJava
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathExceptions.java
More file actions
151 lines (141 loc) · 5.42 KB
/
Exceptions.java
File metadata and controls
151 lines (141 loc) · 5.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/**
* Copyright 2014 Netflix, Inc.
*
* 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 rx.exceptions;
import java.util.HashSet;
import java.util.Set;
/**
* @warn javadoc class description missing
*/
public class Exceptions {
private Exceptions() {
}
/**
* @warn javadoc missing
* @return
*/
public static RuntimeException propagate(Throwable t) {
/*
* The return type of RuntimeException is a trick for code to be like this:
*
* throw Exceptions.propagate(e);
*
* Even though nothing will return and throw via that 'throw', it allows the code to look like it
* so it's easy to read and understand that it will always result in a throw.
*/
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else if (t instanceof Error) {
throw (Error) t;
} else {
throw new RuntimeException(t);
}
}
/**
* Throws a particular {@code Throwable} only if it belongs to a set of "fatal" error varieties. These
* varieties are as follows:
* <ul>
* <li>{@link OnErrorNotImplementedException}</li>
* <li>{@link OnErrorFailedException}</li>
* <li>{@code StackOverflowError}</li>
* <li>{@code VirtualMachineError}</li>
* <li>{@code ThreadDeath}</li>
* <li>{@code LinkageError}</li>
* </ul>
* This can be useful if you are writing an operator that calls user-supplied code, and you want to
* notify subscribers of errors encountered in that code by calling their {@code onError} methods, but only
* if the errors are not so catastrophic that such a call would be futile, in which case you simply want to
* rethrow the error.
*
* @param t
* the {@code Throwable} to test and perhaps throw
* @see <a href="https://github.com/Netflix/RxJava/issues/748#issuecomment-32471495">RxJava: StackOverflowError is swallowed (Issue #748)</a>
*/
public static void throwIfFatal(Throwable t) {
if (t instanceof OnErrorNotImplementedException) {
throw (OnErrorNotImplementedException) t;
} else if (t instanceof OnErrorFailedException) {
Throwable cause = ((OnErrorFailedException) t).getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else {
throw (OnErrorFailedException) t;
}
}
// values here derived from https://github.com/Netflix/RxJava/issues/748#issuecomment-32471495
else if (t instanceof StackOverflowError) {
throw (StackOverflowError) t;
} else if (t instanceof VirtualMachineError) {
throw (VirtualMachineError) t;
} else if (t instanceof ThreadDeath) {
throw (ThreadDeath) t;
} else if (t instanceof LinkageError) {
throw (LinkageError) t;
}
}
private static final int MAX_DEPTH = 25;
/**
* Adds a {@code Throwable} to a causality-chain of Throwables, as an additional cause (if it does not
* already appear in the chain among the causes).
*
* @param e
* the {@code Throwable} at the head of the causality chain
* @param cause
* the {@code Throwable} you want to add as a cause of the chain
*/
public static void addCause(Throwable e, Throwable cause) {
Set<Throwable> seenCauses = new HashSet<Throwable>();
int i = 0;
while (e.getCause() != null) {
if (i++ >= MAX_DEPTH) {
// stack too deep to associate cause
return;
}
e = e.getCause();
if (seenCauses.contains(e.getCause())) {
break;
} else {
seenCauses.add(e.getCause());
}
}
// we now have 'e' as the last in the chain
try {
e.initCause(cause);
} catch (Throwable t) {
// ignore
// the javadocs say that some Throwables (depending on how they're made) will never
// let me call initCause without blowing up even if it returns null
}
}
/**
* Get the {@code Throwable} at the end of the causality-chain for a particular {@code Throwable}
*
* @param e
* the {@code Throwable} whose final cause you are curious about
* @return the last {@code Throwable} in the causality-chain of {@code e} (or a "Stack too deep to get
* final cause" {@code RuntimeException} if the chain is too long to traverse)
*/
public static Throwable getFinalCause(Throwable e) {
int i = 0;
while (e.getCause() != null) {
if (i++ >= MAX_DEPTH) {
// stack too deep to get final cause
return new RuntimeException("Stack too deep to get final cause");
}
e = e.getCause();
}
return e;
}
}