From ebe1cd33679e2c2153740410c567d51f31723a18 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Mon, 11 Nov 2019 13:46:36 +0100 Subject: [PATCH] IntelliJ Themes: added internal json parser for parsing IntelliJ .theme.json files this is a partly copy of https://github.com/ralfstx/minimal-json (license is MIT) --- .../com/formdev/flatlaf/json/JsonHandler.java | 266 +++++++++ .../com/formdev/flatlaf/json/JsonParser.java | 514 ++++++++++++++++++ .../com/formdev/flatlaf/json/Location.java | 79 +++ .../formdev/flatlaf/json/ParseException.java | 83 +++ 4 files changed, 942 insertions(+) create mode 100644 flatlaf-core/src/main/java/com/formdev/flatlaf/json/JsonHandler.java create mode 100644 flatlaf-core/src/main/java/com/formdev/flatlaf/json/JsonParser.java create mode 100644 flatlaf-core/src/main/java/com/formdev/flatlaf/json/Location.java create mode 100644 flatlaf-core/src/main/java/com/formdev/flatlaf/json/ParseException.java diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/json/JsonHandler.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/json/JsonHandler.java new file mode 100644 index 00000000..9200709a --- /dev/null +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/json/JsonHandler.java @@ -0,0 +1,266 @@ +/******************************************************************************* + * Copyright (c) 2016 EclipseSource. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ******************************************************************************/ + +// from https://github.com/ralfstx/minimal-json + +package com.formdev.flatlaf.json; + + +/** + * A handler for parser events. Instances of this class can be given to a {@link JsonParser}. The + * parser will then call the methods of the given handler while reading the input. + *

+ * The default implementations of these methods do nothing. Subclasses may override only those + * methods they are interested in. They can use getLocation() to access the current + * character position of the parser at any point. The start* methods will be called + * while the location points to the first character of the parsed element. The end* + * methods will be called while the location points to the character position that directly follows + * the last character of the parsed element. Example: + *

+ * + *
+ * ["lorem ipsum"]
+ *  ^            ^
+ *  startString  endString
+ * 
+ *

+ * Subclasses that build an object representation of the parsed JSON can return arbitrary handler + * objects for JSON arrays and JSON objects in {@link #startArray()} and {@link #startObject()}. + * These handler objects will then be provided in all subsequent parser events for this particular + * array or object. They can be used to keep track the elements of a JSON array or object. + *

+ * + * @param + * The type of handlers used for JSON arrays + * @param + * The type of handlers used for JSON objects + * @see JsonParser + */ +abstract class JsonHandler { + + JsonParser parser; + + /** + * Returns the current parser location. + * + * @return the current parser location + */ + protected Location getLocation() { + return parser.getLocation(); + } + + /** + * Indicates the beginning of a null literal in the JSON input. This method will be + * called when reading the first character of the literal. + */ + public void startNull() { + } + + /** + * Indicates the end of a null literal in the JSON input. This method will be called + * after reading the last character of the literal. + */ + public void endNull() { + } + + /** + * Indicates the beginning of a boolean literal (true or false) in the + * JSON input. This method will be called when reading the first character of the literal. + */ + public void startBoolean() { + } + + /** + * Indicates the end of a boolean literal (true or false) in the JSON + * input. This method will be called after reading the last character of the literal. + * + * @param value + * the parsed boolean value + */ + public void endBoolean(boolean value) { + } + + /** + * Indicates the beginning of a string in the JSON input. This method will be called when reading + * the opening double quote character ('"'). + */ + public void startString() { + } + + /** + * Indicates the end of a string in the JSON input. This method will be called after reading the + * closing double quote character ('"'). + * + * @param string + * the parsed string + */ + public void endString(String string) { + } + + /** + * Indicates the beginning of a number in the JSON input. This method will be called when reading + * the first character of the number. + */ + public void startNumber() { + } + + /** + * Indicates the end of a number in the JSON input. This method will be called after reading the + * last character of the number. + * + * @param string + * the parsed number string + */ + public void endNumber(String string) { + } + + /** + * Indicates the beginning of an array in the JSON input. This method will be called when reading + * the opening square bracket character ('['). + *

+ * This method may return an object to handle subsequent parser events for this array. This array + * handler will then be provided in all calls to {@link #startArrayValue(Object) + * startArrayValue()}, {@link #endArrayValue(Object) endArrayValue()}, and + * {@link #endArray(Object) endArray()} for this array. + *

+ * + * @return a handler for this array, or null if not needed + */ + public A startArray() { + return null; + } + + /** + * Indicates the end of an array in the JSON input. This method will be called after reading the + * closing square bracket character (']'). + * + * @param array + * the array handler returned from {@link #startArray()}, or null if not + * provided + */ + public void endArray(A array) { + } + + /** + * Indicates the beginning of an array element in the JSON input. This method will be called when + * reading the first character of the element, just before the call to the start + * method for the specific element type ({@link #startString()}, {@link #startNumber()}, etc.). + * + * @param array + * the array handler returned from {@link #startArray()}, or null if not + * provided + */ + public void startArrayValue(A array) { + } + + /** + * Indicates the end of an array element in the JSON input. This method will be called after + * reading the last character of the element value, just after the end method for the + * specific element type (like {@link #endString(String) endString()}, {@link #endNumber(String) + * endNumber()}, etc.). + * + * @param array + * the array handler returned from {@link #startArray()}, or null if not + * provided + */ + public void endArrayValue(A array) { + } + + /** + * Indicates the beginning of an object in the JSON input. This method will be called when reading + * the opening curly bracket character ('{'). + *

+ * This method may return an object to handle subsequent parser events for this object. This + * object handler will be provided in all calls to {@link #startObjectName(Object) + * startObjectName()}, {@link #endObjectName(Object, String) endObjectName()}, + * {@link #startObjectValue(Object, String) startObjectValue()}, + * {@link #endObjectValue(Object, String) endObjectValue()}, and {@link #endObject(Object) + * endObject()} for this object. + *

+ * + * @return a handler for this object, or null if not needed + */ + public O startObject() { + return null; + } + + /** + * Indicates the end of an object in the JSON input. This method will be called after reading the + * closing curly bracket character ('}'). + * + * @param object + * the object handler returned from {@link #startObject()}, or null if not provided + */ + public void endObject(O object) { + } + + /** + * Indicates the beginning of the name of an object member in the JSON input. This method will be + * called when reading the opening quote character ('"') of the member name. + * + * @param object + * the object handler returned from {@link #startObject()}, or null if not + * provided + */ + public void startObjectName(O object) { + } + + /** + * Indicates the end of an object member name in the JSON input. This method will be called after + * reading the closing quote character ('"') of the member name. + * + * @param object + * the object handler returned from {@link #startObject()}, or null if not provided + * @param name + * the parsed member name + */ + public void endObjectName(O object, String name) { + } + + /** + * Indicates the beginning of the name of an object member in the JSON input. This method will be + * called when reading the opening quote character ('"') of the member name. + * + * @param object + * the object handler returned from {@link #startObject()}, or null if not + * provided + * @param name + * the member name + */ + public void startObjectValue(O object, String name) { + } + + /** + * Indicates the end of an object member value in the JSON input. This method will be called after + * reading the last character of the member value, just after the end method for the + * specific member type (like {@link #endString(String) endString()}, {@link #endNumber(String) + * endNumber()}, etc.). + * + * @param object + * the object handler returned from {@link #startObject()}, or null if not provided + * @param name + * the parsed member name + */ + public void endObjectValue(O object, String name) { + } + +} diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/json/JsonParser.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/json/JsonParser.java new file mode 100644 index 00000000..b001a79f --- /dev/null +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/json/JsonParser.java @@ -0,0 +1,514 @@ +/******************************************************************************* + * Copyright (c) 2013, 2016 EclipseSource. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ******************************************************************************/ + +// from https://github.com/ralfstx/minimal-json + +package com.formdev.flatlaf.json; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; + + +/** + * A streaming parser for JSON text. The parser reports all events to a given handler. + */ +class JsonParser { + + private static final int MAX_NESTING_LEVEL = 1000; + private static final int MIN_BUFFER_SIZE = 10; + private static final int DEFAULT_BUFFER_SIZE = 1024; + + private final JsonHandler handler; + private Reader reader; + private char[] buffer; + private int bufferOffset; + private int index; + private int fill; + private int line; + private int lineOffset; + private int current; + private StringBuilder captureBuffer; + private int captureStart; + private int nestingLevel; + + /* + * | bufferOffset + * v + * [a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t] < input + * [l|m|n|o|p|q|r|s|t|?|?] < buffer + * ^ ^ + * | index fill + */ + + /** + * Creates a new JsonParser with the given handler. The parser will report all parser events to + * this handler. + * + * @param handler + * the handler to process parser events + */ + @SuppressWarnings("unchecked") + public JsonParser(JsonHandler handler) { + if (handler == null) { + throw new NullPointerException("handler is null"); + } + this.handler = (JsonHandler)handler; + handler.parser = this; + } + + /** + * Parses the given input string. The input must contain a valid JSON value, optionally padded + * with whitespace. + * + * @param string + * the input string, must be valid JSON + * @throws ParseException + * if the input is not valid JSON + */ + public void parse(String string) { + if (string == null) { + throw new NullPointerException("string is null"); + } + int bufferSize = Math.max(MIN_BUFFER_SIZE, Math.min(DEFAULT_BUFFER_SIZE, string.length())); + try { + parse(new StringReader(string), bufferSize); + } catch (IOException exception) { + // StringReader does not throw IOException + throw new RuntimeException(exception); + } + } + + /** + * Reads the entire input from the given reader and parses it as JSON. The input must contain a + * valid JSON value, optionally padded with whitespace. + *

+ * Characters are read in chunks into a default-sized input buffer. Hence, wrapping a reader in an + * additional BufferedReader likely won't improve reading performance. + *

+ * + * @param reader + * the reader to read the input from + * @throws IOException + * if an I/O error occurs in the reader + * @throws ParseException + * if the input is not valid JSON + */ + public void parse(Reader reader) throws IOException { + parse(reader, DEFAULT_BUFFER_SIZE); + } + + /** + * Reads the entire input from the given reader and parses it as JSON. The input must contain a + * valid JSON value, optionally padded with whitespace. + *

+ * Characters are read in chunks into an input buffer of the given size. Hence, wrapping a reader + * in an additional BufferedReader likely won't improve reading performance. + *

+ * + * @param reader + * the reader to read the input from + * @param buffersize + * the size of the input buffer in chars + * @throws IOException + * if an I/O error occurs in the reader + * @throws ParseException + * if the input is not valid JSON + */ + public void parse(Reader reader, int buffersize) throws IOException { + if (reader == null) { + throw new NullPointerException("reader is null"); + } + if (buffersize <= 0) { + throw new IllegalArgumentException("buffersize is zero or negative"); + } + this.reader = reader; + buffer = new char[buffersize]; + bufferOffset = 0; + index = 0; + fill = 0; + line = 1; + lineOffset = 0; + current = 0; + captureStart = -1; + read(); + skipWhiteSpace(); + readValue(); + skipWhiteSpace(); + if (!isEndOfText()) { + throw error("Unexpected character"); + } + } + + private void readValue() throws IOException { + switch (current) { + case 'n': + readNull(); + break; + case 't': + readTrue(); + break; + case 'f': + readFalse(); + break; + case '"': + readString(); + break; + case '[': + readArray(); + break; + case '{': + readObject(); + break; + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + readNumber(); + break; + default: + throw expected("value"); + } + } + + private void readArray() throws IOException { + Object array = handler.startArray(); + read(); + if (++nestingLevel > MAX_NESTING_LEVEL) { + throw error("Nesting too deep"); + } + skipWhiteSpace(); + if (readChar(']')) { + nestingLevel--; + handler.endArray(array); + return; + } + do { + skipWhiteSpace(); + handler.startArrayValue(array); + readValue(); + handler.endArrayValue(array); + skipWhiteSpace(); + } while (readChar(',')); + if (!readChar(']')) { + throw expected("',' or ']'"); + } + nestingLevel--; + handler.endArray(array); + } + + private void readObject() throws IOException { + Object object = handler.startObject(); + read(); + if (++nestingLevel > MAX_NESTING_LEVEL) { + throw error("Nesting too deep"); + } + skipWhiteSpace(); + if (readChar('}')) { + nestingLevel--; + handler.endObject(object); + return; + } + do { + skipWhiteSpace(); + handler.startObjectName(object); + String name = readName(); + handler.endObjectName(object, name); + skipWhiteSpace(); + if (!readChar(':')) { + throw expected("':'"); + } + skipWhiteSpace(); + handler.startObjectValue(object, name); + readValue(); + handler.endObjectValue(object, name); + skipWhiteSpace(); + } while (readChar(',')); + if (!readChar('}')) { + throw expected("',' or '}'"); + } + nestingLevel--; + handler.endObject(object); + } + + private String readName() throws IOException { + if (current != '"') { + throw expected("name"); + } + return readStringInternal(); + } + + private void readNull() throws IOException { + handler.startNull(); + read(); + readRequiredChar('u'); + readRequiredChar('l'); + readRequiredChar('l'); + handler.endNull(); + } + + private void readTrue() throws IOException { + handler.startBoolean(); + read(); + readRequiredChar('r'); + readRequiredChar('u'); + readRequiredChar('e'); + handler.endBoolean(true); + } + + private void readFalse() throws IOException { + handler.startBoolean(); + read(); + readRequiredChar('a'); + readRequiredChar('l'); + readRequiredChar('s'); + readRequiredChar('e'); + handler.endBoolean(false); + } + + private void readRequiredChar(char ch) throws IOException { + if (!readChar(ch)) { + throw expected("'" + ch + "'"); + } + } + + private void readString() throws IOException { + handler.startString(); + handler.endString(readStringInternal()); + } + + private String readStringInternal() throws IOException { + read(); + startCapture(); + while (current != '"') { + if (current == '\\') { + pauseCapture(); + readEscape(); + startCapture(); + } else if (current < 0x20) { + throw expected("valid string character"); + } else { + read(); + } + } + String string = endCapture(); + read(); + return string; + } + + private void readEscape() throws IOException { + read(); + switch (current) { + case '"': + case '/': + case '\\': + captureBuffer.append((char)current); + break; + case 'b': + captureBuffer.append('\b'); + break; + case 'f': + captureBuffer.append('\f'); + break; + case 'n': + captureBuffer.append('\n'); + break; + case 'r': + captureBuffer.append('\r'); + break; + case 't': + captureBuffer.append('\t'); + break; + case 'u': + char[] hexChars = new char[4]; + for (int i = 0; i < 4; i++) { + read(); + if (!isHexDigit()) { + throw expected("hexadecimal digit"); + } + hexChars[i] = (char)current; + } + captureBuffer.append((char)Integer.parseInt(new String(hexChars), 16)); + break; + default: + throw expected("valid escape sequence"); + } + read(); + } + + private void readNumber() throws IOException { + handler.startNumber(); + startCapture(); + readChar('-'); + int firstDigit = current; + if (!readDigit()) { + throw expected("digit"); + } + if (firstDigit != '0') { + while (readDigit()) { + } + } + readFraction(); + readExponent(); + handler.endNumber(endCapture()); + } + + private boolean readFraction() throws IOException { + if (!readChar('.')) { + return false; + } + if (!readDigit()) { + throw expected("digit"); + } + while (readDigit()) { + } + return true; + } + + private boolean readExponent() throws IOException { + if (!readChar('e') && !readChar('E')) { + return false; + } + if (!readChar('+')) { + readChar('-'); + } + if (!readDigit()) { + throw expected("digit"); + } + while (readDigit()) { + } + return true; + } + + private boolean readChar(char ch) throws IOException { + if (current != ch) { + return false; + } + read(); + return true; + } + + private boolean readDigit() throws IOException { + if (!isDigit()) { + return false; + } + read(); + return true; + } + + private void skipWhiteSpace() throws IOException { + while (isWhiteSpace()) { + read(); + } + } + + private void read() throws IOException { + if (index == fill) { + if (captureStart != -1) { + captureBuffer.append(buffer, captureStart, fill - captureStart); + captureStart = 0; + } + bufferOffset += fill; + fill = reader.read(buffer, 0, buffer.length); + index = 0; + if (fill == -1) { + current = -1; + index++; + return; + } + } + if (current == '\n') { + line++; + lineOffset = bufferOffset + index; + } + current = buffer[index++]; + } + + private void startCapture() { + if (captureBuffer == null) { + captureBuffer = new StringBuilder(); + } + captureStart = index - 1; + } + + private void pauseCapture() { + int end = current == -1 ? index : index - 1; + captureBuffer.append(buffer, captureStart, end - captureStart); + captureStart = -1; + } + + private String endCapture() { + int start = captureStart; + int end = index - 1; + captureStart = -1; + if (captureBuffer.length() > 0) { + captureBuffer.append(buffer, start, end - start); + String captured = captureBuffer.toString(); + captureBuffer.setLength(0); + return captured; + } + return new String(buffer, start, end - start); + } + + Location getLocation() { + int offset = bufferOffset + index - 1; + int column = offset - lineOffset + 1; + return new Location(offset, line, column); + } + + private ParseException expected(String expected) { + if (isEndOfText()) { + return error("Unexpected end of input"); + } + return error("Expected " + expected); + } + + private ParseException error(String message) { + return new ParseException(message, getLocation()); + } + + private boolean isWhiteSpace() { + return current == ' ' || current == '\t' || current == '\n' || current == '\r'; + } + + private boolean isDigit() { + return current >= '0' && current <= '9'; + } + + private boolean isHexDigit() { + return current >= '0' && current <= '9' + || current >= 'a' && current <= 'f' + || current >= 'A' && current <= 'F'; + } + + private boolean isEndOfText() { + return current == -1; + } + +} diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/json/Location.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/json/Location.java new file mode 100644 index 00000000..43e913e2 --- /dev/null +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/json/Location.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2016 EclipseSource. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ******************************************************************************/ + +// from https://github.com/ralfstx/minimal-json + +package com.formdev.flatlaf.json; + + +/** + * An immutable object that represents a location in the parsed text. + */ +public class Location { + + /** + * The absolute character index, starting at 0. + */ + public final int offset; + + /** + * The line number, starting at 1. + */ + public final int line; + + /** + * The column number, starting at 1. + */ + public final int column; + + Location(int offset, int line, int column) { + this.offset = offset; + this.column = column; + this.line = line; + } + + @Override + public String toString() { + return line + ":" + column; + } + + @Override + public int hashCode() { + return offset; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Location other = (Location)obj; + return offset == other.offset && column == other.column && line == other.line; + } + +} \ No newline at end of file diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/json/ParseException.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/json/ParseException.java new file mode 100644 index 00000000..4b5f55d5 --- /dev/null +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/json/ParseException.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2013, 2016 EclipseSource. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + ******************************************************************************/ + +// from https://github.com/ralfstx/minimal-json + +package com.formdev.flatlaf.json; + +/** + * An unchecked exception to indicate that an input does not qualify as valid JSON. + */ +public class ParseException extends RuntimeException { + + private final Location location; + + ParseException(String message, Location location) { + super(message + " at " + location); + this.location = location; + } + + /** + * Returns the location at which the error occurred. + * + * @return the error location + */ + public Location getLocation() { + return location; + } + + /** + * Returns the absolute character index at which the error occurred. The offset of the first + * character of a document is 0. + * + * @return the character offset at which the error occurred, will be >= 0 + * @deprecated Use {@link #getLocation()} instead + */ + @Deprecated + public int getOffset() { + return location.offset; + } + + /** + * Returns the line number in which the error occurred. The number of the first line is 1. + * + * @return the line in which the error occurred, will be >= 1 + * @deprecated Use {@link #getLocation()} instead + */ + @Deprecated + public int getLine() { + return location.line; + } + + /** + * Returns the column number at which the error occurred, i.e. the number of the character in its + * line. The number of the first character of a line is 1. + * + * @return the column in which the error occurred, will be >= 1 + * @deprecated Use {@link #getLocation()} instead + */ + @Deprecated + public int getColumn() { + return location.column; + } + +}