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.commons.svl; 018 019/** 020 * Thrown when an SVL <c>#{...}</c> expression cannot be lexed or parsed. 021 * 022 * <p> 023 * The message reads <c>message at position N:</c>, followed by the source line containing the position and a caret line 024 * (tabs copied, other characters as spaces) under the position. 025 */ 026@SuppressWarnings({ 027 "java:S110" // Exception hierarchy mirrors the resolver exception layering 028}) 029public class ExprException extends VarResolverException { 030 private static final long serialVersionUID = 1L; 031 032 private final String baseMessage; 033 private final String source; 034 private final int pos; 035 036 /** 037 * Constructor. 038 * 039 * @param message The plain (not printf-style) description. 040 * @param source The expression source text. 041 * @param pos The zero-based character offset of the problem. 042 */ 043 public ExprException(String message, String source, int pos) { 044 this(message, source, pos, null); 045 } 046 047 /** 048 * Constructor. 049 * 050 * @param message The plain (not printf-style) description. 051 * @param source The expression source text. 052 * @param pos The zero-based character offset of the problem. 053 * @param cause The underlying failure. Can be <jk>null</jk>. 054 */ 055 public ExprException(String message, String source, int pos, Throwable cause) { 056 super(cause, "%s", render(message, source, pos)); 057 this.baseMessage = message; 058 this.source = source; 059 this.pos = pos; 060 } 061 062 private static String render(String message, String source, int pos) { 063 int p = Math.max(0, Math.min(pos, source.length())); 064 int ls = source.lastIndexOf('\n', p - 1) + 1; 065 int le = source.indexOf('\n', p); 066 if (le < 0) le = source.length(); 067 var line = source.substring(ls, le); 068 var pad = new StringBuilder(); 069 for (int i = ls; i < p; i++) 070 pad.append(source.charAt(i) == '\t' ? '\t' : ' '); 071 return message + " at position " + pos + ":\n" + line + "\n" + pad + "^"; 072 } 073 074 /** @return The description without position or source. */ 075 public String getBaseMessage() { return baseMessage; } 076 077 /** @return The expression source text. */ 078 public String getSource() { return source; } 079 080 /** @return The zero-based character offset of the problem. */ 081 public int getPos() { return pos; } 082}