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