mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2025-12-06 14:00:55 +03:00
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)
This commit is contained in:
@@ -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.
|
||||
* <p>
|
||||
* The default implementations of these methods do nothing. Subclasses may override only those
|
||||
* methods they are interested in. They can use <code>getLocation()</code> to access the current
|
||||
* character position of the parser at any point. The <code>start*</code> methods will be called
|
||||
* while the location points to the first character of the parsed element. The <code>end*</code>
|
||||
* methods will be called while the location points to the character position that directly follows
|
||||
* the last character of the parsed element. Example:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* ["lorem ipsum"]
|
||||
* ^ ^
|
||||
* startString endString
|
||||
* </pre>
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*
|
||||
* @param <A>
|
||||
* The type of handlers used for JSON arrays
|
||||
* @param <O>
|
||||
* The type of handlers used for JSON objects
|
||||
* @see JsonParser
|
||||
*/
|
||||
abstract class JsonHandler<A, O> {
|
||||
|
||||
JsonParser parser;
|
||||
|
||||
/**
|
||||
* Returns the current parser location.
|
||||
*
|
||||
* @return the current parser location
|
||||
*/
|
||||
protected Location getLocation() {
|
||||
return parser.getLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the beginning of a <code>null</code> 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 <code>null</code> 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 (<code>true</code> or <code>false</code>) 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 (<code>true</code> or <code>false</code>) 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 (<code>'"'</code>).
|
||||
*/
|
||||
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 (<code>'"'</code>).
|
||||
*
|
||||
* @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 (<code>'['</code>).
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*
|
||||
* @return a handler for this array, or <code>null</code> 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 (<code>']'</code>).
|
||||
*
|
||||
* @param array
|
||||
* the array handler returned from {@link #startArray()}, or <code>null</code> 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 <code>start</code>
|
||||
* method for the specific element type ({@link #startString()}, {@link #startNumber()}, etc.).
|
||||
*
|
||||
* @param array
|
||||
* the array handler returned from {@link #startArray()}, or <code>null</code> 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 <code>end</code> 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 <code>null</code> 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 (<code>'{'</code>).
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*
|
||||
* @return a handler for this object, or <code>null</code> 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 (<code>'}'</code>).
|
||||
*
|
||||
* @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 <code>null</code> 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 (<code>'"'</code>) 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 <code>null</code> 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 <code>end</code> 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) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Object, Object> 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<Object, Object>)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.
|
||||
* <p>
|
||||
* Characters are read in chunks into a default-sized input buffer. Hence, wrapping a reader in an
|
||||
* additional <code>BufferedReader</code> likely won't improve reading performance.
|
||||
* </p>
|
||||
*
|
||||
* @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.
|
||||
* <p>
|
||||
* Characters are read in chunks into an input buffer of the given size. Hence, wrapping a reader
|
||||
* in an additional <code>BufferedReader</code> likely won't improve reading performance.
|
||||
* </p>
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user