1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.juneau.parser;
18
19 import static org.apache.juneau.commons.utils.StringUtils.*;
20 import static org.apache.juneau.commons.utils.Utils.*;
21
22 import java.lang.reflect.*;
23 import java.text.*;
24
25 import org.apache.juneau.*;
26 import org.apache.juneau.serializer.*;
27
28 /**
29 * Exception that indicates invalid syntax encountered during parsing.
30 *
31 * <h5 class='section'>See Also:</h5><ul>
32 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a>
33
34 * </ul>
35 *
36 * @serial exclude
37 */
38 public class ParseException extends BasicRuntimeException {
39
40 private static final long serialVersionUID = 1L;
41
42 /**
43 * Creator method.
44 *
45 * <p>
46 * If the throwable is already a {@link ParseException}, we simply return that exception as-is.
47 * If the throwable is an {@link InvocationTargetException}, we unwrap the thrown exception.
48 * Otherwise we create a new {@link ParseException}.
49 *
50 * @param e The exception being wrapped or unwrapped.
51 * @return A new {@link SerializeException}.
52 */
53 public static ParseException create(Throwable e) {
54 if (e instanceof InvocationTargetException e2)
55 e = e2.getCause();
56 if (e instanceof ParseException e3)
57 return e3;
58 return new ParseException(e);
59 }
60
61 private static String getMessage(ParserSession session, String msg, Object...args) {
62 if (args.length != 0)
63 msg = f(msg, args);
64
65 if (nn(session)) {
66 Position p = session.getPosition();
67 var sb = new StringBuilder(msg);
68
69 sb.append("\n\tAt: ").append(p);
70
71 var lastLocation = session.getLastLocation();
72 if (nn(lastLocation)) {
73 sb.append("\n\tWhile parsing into: ");
74 lastLocation.forEach((k, v) -> sb.append("\n\t\t").append(k).append(": ").append(v));
75 }
76
77 String lines = session.getInputAsString();
78 if (lines == null)
79 sb.append("\n\tUse BEAN_debug setting to display content.");
80 else {
81 int numLines = session.getDebugOutputLines();
82 int start = p.line - numLines, end = p.line + numLines;
83 sb.append("\n---start--\n").append(getNumberedLines(lines, start, end)).append("---end---");
84 }
85
86 msg = sb.toString();
87 }
88 return msg;
89 }
90
91 /**
92 * Constructor.
93 *
94 * @param session The parser session.
95 * @param causedBy The inner exception.
96 */
97 public ParseException(ParserSession session, Exception causedBy) {
98 super(causedBy, getMessage(session, causedBy.getMessage()));
99 }
100
101 /**
102 * Constructor.
103 *
104 * @param session The parser session.
105 * @param message The exception message containing {@link MessageFormat}-style arguments.
106 * @param args Optional {@link MessageFormat}-style arguments.
107 */
108 public ParseException(ParserSession session, String message, Object...args) {
109 super(getMessage(session, message, args));
110 }
111
112 /**
113 * Constructor.
114 *
115 * @param session The parser session.
116 * @param causedBy The cause of this exception.
117 * @param message The exception message containing {@link MessageFormat}-style arguments.
118 * @param args Optional {@link MessageFormat}-style arguments.
119 */
120 public ParseException(ParserSession session, Throwable causedBy, String message, Object...args) {
121 super(causedBy, getMessage(session, message, args));
122 }
123
124 /**
125 * Constructor.
126 *
127 * @param message The {@link MessageFormat}-style message.
128 * @param args Optional {@link MessageFormat}-style arguments.
129 */
130 public ParseException(String message, Object...args) {
131 super(message, args);
132 }
133
134 /**
135 * Constructor.
136 *
137 * @param causedBy The cause of this exception.
138 */
139 public ParseException(Throwable causedBy) {
140 super(causedBy);
141 }
142
143 /**
144 * Constructor.
145 *
146 * @param causedBy The cause of this exception.
147 * @param message The {@link MessageFormat}-style message.
148 * @param args Optional {@link MessageFormat}-style arguments.
149 */
150 public ParseException(Throwable causedBy, String message, Object...args) {
151 super(causedBy, message, args);
152 }
153
154 /**
155 * Returns the highest-level <c>ParseException</c> in the stack trace.
156 *
157 * <p>
158 * Useful for JUnit testing of error conditions.
159 *
160 * @return The root parse exception, or this exception if there isn't one.
161 */
162 public ParseException getRootCause() {
163 ParseException t = this;
164 while (! (t.getCause() == null || ! (t.getCause() instanceof ParseException)))
165 t = (ParseException)t.getCause();
166 return t;
167 }
168
169 @Override /* Overridden from BasicRuntimeException */
170 public ParseException setMessage(String message, Object...args) {
171 super.setMessage(message, args);
172 return this;
173 }
174 }