001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.parser;
014
015import static org.apache.juneau.common.internal.StringUtils.*;
016
017import java.lang.reflect.*;
018import java.text.*;
019import org.apache.juneau.*;
020import org.apache.juneau.collections.*;
021import org.apache.juneau.serializer.*;
022
023/**
024 * Exception that indicates invalid syntax encountered during parsing.
025 *
026 * <h5 class='section'>See Also:</h5><ul>
027 *    <li class='link'><a class="doclink" href="../../../../index.html#jm.SerializersAndParsers">Serializers and Parsers</a>
028
029 * </ul>
030 *
031 * @serial exclude
032 */
033public class ParseException extends BasicRuntimeException {
034
035   private static final long serialVersionUID = 1L;
036
037   /**
038    * Creator method.
039    *
040    * <p>
041    * If the throwable is already a {@link ParseException}, we simply return that exception as-is.
042    * If the throwable is an {@link InvocationTargetException}, we unwrap the thrown exception.
043    * Otherwise we create a new {@link ParseException}.
044    *
045    * @param e The exception being wrapped or unwrapped.
046    * @return A new {@link SerializeException}.
047    */
048   public static ParseException create(Throwable e) {
049      if (e instanceof InvocationTargetException)
050         e = ((InvocationTargetException)e).getCause();
051      if (e instanceof ParseException)
052         return (ParseException)e;
053      return new ParseException(e);
054   }
055
056   /**
057    * Constructor.
058    *
059    * @param message The {@link MessageFormat}-style message.
060    * @param args Optional {@link MessageFormat}-style arguments.
061    */
062   public ParseException(String message, Object...args) {
063      super(message, args);
064   }
065
066   /**
067    * Constructor.
068    *
069    * @param causedBy The cause of this exception.
070    * @param message The {@link MessageFormat}-style message.
071    * @param args Optional {@link MessageFormat}-style arguments.
072    */
073   public ParseException(Throwable causedBy, String message, Object...args) {
074      super(causedBy, message, args);
075   }
076
077   /**
078    * Constructor.
079    *
080    * @param causedBy The cause of this exception.
081    */
082   public ParseException(Throwable causedBy) {
083      super(causedBy);
084   }
085
086   /**
087    * Constructor.
088    *
089    * @param session The parser session.
090    * @param message The exception message containing {@link MessageFormat}-style arguments.
091    * @param args Optional {@link MessageFormat}-style arguments.
092    */
093   public ParseException(ParserSession session, String message, Object...args) {
094      super(getMessage(session, message, args));
095   }
096
097   /**
098    * Constructor.
099    *
100    * @param session The parser session.
101    * @param causedBy The cause of this exception.
102    * @param message The exception message containing {@link MessageFormat}-style arguments.
103    * @param args Optional {@link MessageFormat}-style arguments.
104    */
105   public ParseException(ParserSession session, Throwable causedBy, String message, Object...args) {
106      super(causedBy, getMessage(session, message, args));
107   }
108
109   /**
110    * Constructor.
111    *
112    * @param session The parser session.
113    * @param causedBy The inner exception.
114    */
115   public ParseException(ParserSession session, Exception causedBy) {
116      super(causedBy, getMessage(session, causedBy.getMessage()));
117   }
118
119
120   private static String getMessage(ParserSession session, String msg, Object... args) {
121      if (args.length != 0)
122         msg = format(msg, args);
123
124      if (session != null) {
125         Position p = session.getPosition();
126         StringBuilder sb = new StringBuilder(msg);
127
128         sb.append("\n\tAt: ").append(p);
129
130         JsonMap lastLocation = session.getLastLocation();
131         if (lastLocation != null) {
132            sb.append("\n\tWhile parsing into: ");
133            lastLocation.forEach((k,v) -> sb.append("\n\t\t").append(k).append(": ").append(v));
134         }
135
136         String lines = session.getInputAsString();
137         if (lines == null)
138            sb.append("\n\tUse BEAN_debug setting to display content.");
139         else {
140            int numLines = session.getDebugOutputLines();
141            int start = p.line - numLines, end = p.line + numLines;
142            sb.append("\n---start--\n").append(getNumberedLines(lines, start, end)).append("---end---");
143         }
144
145         msg = sb.toString();
146      }
147      return msg;
148   }
149
150   /**
151    * Returns the highest-level <c>ParseException</c> in the stack trace.
152    *
153    * <p>
154    * Useful for JUnit testing of error conditions.
155    *
156    * @return The root parse exception, or this exception if there isn't one.
157    */
158   public ParseException getRootCause() {
159      ParseException t = this;
160      while (! (t.getCause() == null || ! (t.getCause() instanceof ParseException)))
161         t = (ParseException)t.getCause();
162      return t;
163   }
164}