001/* 002 * Configurate 003 * Copyright (C) zml and Configurate contributors 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * 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.spongepowered.configurate.loader; 018 019import org.checkerframework.checker.nullness.qual.Nullable; 020import org.spongepowered.configurate.ConfigurateException; 021import org.spongepowered.configurate.ConfigurationNode; 022 023import java.io.IOException; 024import java.util.Arrays; 025 026/** 027 * Indicates an error that occurred while parsing the configuration. 028 * 029 * <p>These exceptions can include a specific position reference 030 * within a file.</p> 031 * 032 * @since 4.0.0 033 */ 034public class ParsingException extends ConfigurateException { 035 036 /** 037 * Indicates a line or column is unknown. 038 */ 039 public static final int UNKNOWN_POS = -1; 040 041 private static final char POSITION_MARKER = '^'; 042 private static final long serialVersionUID = 8379206111053602577L; 043 044 /** 045 * Given an unknown {@link IOException}, return it as a Configurate type. 046 * 047 * <p>If the input {@code ex} is already a {@link ParsingException}, 048 * this method returns the input value.</p> 049 * 050 * @param source node where the source exception was thrown 051 * @param ex the source exception 052 * @return an exception, either casted or wrapped 053 * @since 4.0.0 054 */ 055 public static ParsingException wrap(final ConfigurationNode source, final IOException ex) { 056 if (ex instanceof ParsingException) { 057 return (ParsingException) ex; 058 } else { 059 return new ParsingException(source, -1, -1, null, null, ex); 060 } 061 } 062 063 private final int line; 064 private final int column; 065 private final @Nullable String context; 066 067 /** 068 * Create a new parsing exception. 069 * 070 * @param position position in the node structure where the error occurred 071 * @param line line with issue 072 * @param column column in the line 073 * @param context the line in a file where the error occurred 074 * @param message message describing the error 075 * @since 4.0.0 076 */ 077 public ParsingException( 078 final ConfigurationNode position, 079 final int line, 080 final int column, 081 final String context, 082 final @Nullable String message 083 ) { 084 this(position, line, column, context, message, null); 085 } 086 087 /** 088 * Create a new parsing exception. 089 * 090 * @param line line with issue 091 * @param column column in the line 092 * @param context the line in a file where the error occurred 093 * @param message message describing the error 094 * @param cause direct cause 095 * @since 4.0.0 096 */ 097 public ParsingException( 098 final int line, 099 final int column, 100 final @Nullable String context, 101 final @Nullable String message, 102 final @Nullable Throwable cause 103 ) { 104 super(message, cause); 105 this.line = line; 106 this.column = column; 107 this.context = context; 108 } 109 110 /** 111 * Create a new parsing exception. 112 * 113 * @param position position in the node structure where the error occurred 114 * @param line line with issue 115 * @param column column in the line 116 * @param context the line in a file where the error occurred 117 * @param message message describing the error 118 * @param cause direct cause 119 * @since 4.0.0 120 */ 121 public ParsingException( 122 final ConfigurationNode position, 123 final int line, 124 final int column, 125 final @Nullable String context, 126 final @Nullable String message, 127 final @Nullable Throwable cause 128 ) { 129 super(position, message, cause); 130 this.line = line; 131 this.column = column; 132 this.context = context; 133 } 134 135 /** 136 * Line most closely associated with this error. 137 * 138 * @return line, or {@code -1} for unknown 139 * @since 4.0.0 140 */ 141 public int line() { 142 return this.line; 143 } 144 145 /** 146 * Column most closely associated with the error. 147 * 148 * @return column, or {@code -1} for unknown 149 * @since 4.0.0 150 */ 151 public int column() { 152 return this.column; 153 } 154 155 /** 156 * A context line from the source, if available. 157 * 158 * @return context line 159 * @since 4.0.0 160 */ 161 public @Nullable String context() { 162 return this.context; 163 } 164 165 @Override 166 public @Nullable String getMessage() { 167 if (this.line == UNKNOWN_POS || this.column == UNKNOWN_POS) { 168 return super.getMessage(); 169 } 170 final @Nullable String rawMessage = this.rawMessage(); 171 final StringBuilder message = new StringBuilder(rawMessage == null ? 0 : (rawMessage.length() + 20)); 172 message.append(this.path()) 173 .append("@(line ").append(this.line).append(", col ").append(this.column).append("): ") 174 .append(rawMessage); 175 176 if (this.context != null) { 177 message.append(System.lineSeparator()).append(this.context); 178 if (this.column >= 0 && this.column < this.context.length()) { 179 message.append(System.lineSeparator()); 180 if (this.column > 0) { 181 final char[] spaces = new char[this.column - 1]; 182 Arrays.fill(spaces, ' '); 183 message.append(spaces); 184 } 185 186 message.append(POSITION_MARKER); 187 } 188 } 189 190 return message.toString(); 191 } 192 193}