View Javadoc
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 }