32 @SuppressWarnings("serial")
33 final class TranslatedException extends Exception {
34
35 private TranslatedException(String message) {
36 super(message);
37 }
38
39 private TranslatedException(String message, Throwable cause) {
40 super(message, cause);
41 }
42
43 /**
44 * No need to record an initial stack trace since it will be manually overwritten.
45 */
46 @SuppressWarnings("sync-override")
47 @Override
48 public Throwable fillInStackTrace() {
49 return this;
50 }
51
52 @Override
53 public String toString() {
54 return getMessage();
55 }
56
57 private static TranslatedException create(String className, String message) {
58 if (className.equals(TranslatedException.class.getName())) {
59 // Chop the class name when boxing another TranslatedException
60 return new TranslatedException(message);
61 }
62 if (message == null) {
63 return new TranslatedException(className);
64 }
65 return new TranslatedException(className + ": " + message);
66 }
67
68 private static String encodedString(String value) {
69 return Objects.toString(value, "").replace('|', '_');
70 }
71
72 /**
73 * Encodes {@code throwable} including its stack and causes as a string. The encoding format of
74 * a single exception with its cause is:
75 *
76 * <pre>
77 * <exception class name> '|' <exception message> '|' <stack size> '|' [<class> '|' <method> '|' <file> '|' <line> '|' ]*
78 * </pre>
79 *
80 * Each cause is appended after the exception is it the cause of.
81 */
130
131 /**
132 * Decodes {@code encodedThrowable} into a {@link TranslatedException}.
133 *
134 * @param encodedThrowable an encoded exception in the format specified by
135 * {@link #encodeThrowable}
136 */
137 @VMEntryPoint
138 static Throwable decodeThrowable(String encodedThrowable) {
139 try {
140 int i = 0;
141 String[] parts = encodedThrowable.split("\\|");
142 Throwable parent = null;
143 Throwable result = null;
144 while (i != parts.length) {
145 String exceptionClassName = parts[i++];
146 String exceptionMessage = parts[i++];
147 Throwable throwable = create(exceptionClassName, exceptionMessage);
148 int stackTraceDepth = Integer.parseInt(parts[i++]);
149
150 StackTraceElement[] suffix = parent == null ? new StackTraceElement[0] : getStackTraceSuffix();
151 StackTraceElement[] stackTrace = new StackTraceElement[stackTraceDepth + suffix.length];
152 for (int j = 0; j < stackTraceDepth; j++) {
153 String className = parts[i++];
154 String methodName = parts[i++];
155 String fileName = parts[i++];
156 int lineNumber = Integer.parseInt(parts[i++]);
157 if (fileName.isEmpty()) {
158 fileName = null;
159 }
160 stackTrace[j] = new StackTraceElement(className, methodName, fileName, lineNumber);
161 }
162 System.arraycopy(suffix, 0, stackTrace, stackTraceDepth, suffix.length);
163 throwable.setStackTrace(stackTrace);
164 if (parent != null) {
165 parent.initCause(throwable);
166 } else {
167 result = throwable;
168 }
169 parent = throwable;
170 }
|
32 @SuppressWarnings("serial")
33 final class TranslatedException extends Exception {
34
35 private TranslatedException(String message) {
36 super(message);
37 }
38
39 private TranslatedException(String message, Throwable cause) {
40 super(message, cause);
41 }
42
43 /**
44 * No need to record an initial stack trace since it will be manually overwritten.
45 */
46 @SuppressWarnings("sync-override")
47 @Override
48 public Throwable fillInStackTrace() {
49 return this;
50 }
51
52 private static Throwable create(String className, String message) {
53 // Try create with reflection first.
54 try {
55 Class<?> cls = Class.forName(className);
56 if (message == null) {
57 return (Throwable) cls.getConstructor().newInstance();
58 }
59 cls.getDeclaredConstructor(String.class);
60 return (Throwable) cls.getConstructor(String.class).newInstance(message);
61 } catch (Throwable ignore) {
62 }
63
64 if (className.equals(TranslatedException.class.getName())) {
65 // Chop the class name when boxing another TranslatedException
66 return new TranslatedException(message);
67 }
68
69 if (message == null) {
70 return new TranslatedException(className);
71 }
72 return new TranslatedException(className + ": " + message);
73 }
74
75 private static String encodedString(String value) {
76 return Objects.toString(value, "").replace('|', '_');
77 }
78
79 /**
80 * Encodes {@code throwable} including its stack and causes as a string. The encoding format of
81 * a single exception with its cause is:
82 *
83 * <pre>
84 * <exception class name> '|' <exception message> '|' <stack size> '|' [<class> '|' <method> '|' <file> '|' <line> '|' ]*
85 * </pre>
86 *
87 * Each cause is appended after the exception is it the cause of.
88 */
137
138 /**
139 * Decodes {@code encodedThrowable} into a {@link TranslatedException}.
140 *
141 * @param encodedThrowable an encoded exception in the format specified by
142 * {@link #encodeThrowable}
143 */
144 @VMEntryPoint
145 static Throwable decodeThrowable(String encodedThrowable) {
146 try {
147 int i = 0;
148 String[] parts = encodedThrowable.split("\\|");
149 Throwable parent = null;
150 Throwable result = null;
151 while (i != parts.length) {
152 String exceptionClassName = parts[i++];
153 String exceptionMessage = parts[i++];
154 Throwable throwable = create(exceptionClassName, exceptionMessage);
155 int stackTraceDepth = Integer.parseInt(parts[i++]);
156
157 StackTraceElement[] suffix = getStackTraceSuffix();
158 StackTraceElement[] stackTrace = new StackTraceElement[stackTraceDepth + suffix.length];
159 for (int j = 0; j < stackTraceDepth; j++) {
160 String className = parts[i++];
161 String methodName = parts[i++];
162 String fileName = parts[i++];
163 int lineNumber = Integer.parseInt(parts[i++]);
164 if (fileName.isEmpty()) {
165 fileName = null;
166 }
167 stackTrace[j] = new StackTraceElement(className, methodName, fileName, lineNumber);
168 }
169 System.arraycopy(suffix, 0, stackTrace, stackTraceDepth, suffix.length);
170 throwable.setStackTrace(stackTrace);
171 if (parent != null) {
172 parent.initCause(throwable);
173 } else {
174 result = throwable;
175 }
176 parent = throwable;
177 }
|