From 3c5b4bee3d8e7a90ac7e9fb6ed8b1967655784f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Prante?= Date: Sun, 29 Jan 2023 21:45:02 +0100 Subject: [PATCH] remove JSON parser, use a smaller JSON builder for JSON writing, add creation/modification date methods to MarcRecord --- CREDITS.txt | 8 - src/main/java/org/xbib/marc/MarcField.java | 22 + src/main/java/org/xbib/marc/MarcRecord.java | 4 +- src/main/java/org/xbib/marc/json/Json.java | 279 ------ .../java/org/xbib/marc/json/JsonArray.java | 397 -------- .../java/org/xbib/marc/json/JsonBuilder.java | 261 +++++ .../xbib/marc/json/JsonDefaultHandler.java | 110 --- .../org/xbib/marc/json/JsonException.java | 28 - .../java/org/xbib/marc/json/JsonHandler.java | 196 ---- .../java/org/xbib/marc/json/JsonLiteral.java | 91 -- .../java/org/xbib/marc/json/JsonMapper.java | 108 -- .../java/org/xbib/marc/json/JsonNumber.java | 114 --- .../java/org/xbib/marc/json/JsonObject.java | 785 --------------- .../java/org/xbib/marc/json/JsonReader.java | 460 --------- .../java/org/xbib/marc/json/JsonString.java | 59 -- .../java/org/xbib/marc/json/JsonValue.java | 295 ------ .../java/org/xbib/marc/json/JsonWriter.java | 140 --- .../org/xbib/marc/json/JsonWriterConfig.java | 157 --- .../org/xbib/marc/json/MarcJsonWriter.java | 165 ++-- .../org/xbib/marc/xml/MarcXchangeWriter.java | 14 +- .../org/xbib/marc/json/JsonArrayTest.java | 522 ---------- ...onWriterTest.java => JsonBuilderTest.java} | 77 +- .../org/xbib/marc/json/JsonLiteralTest.java | 126 --- .../org/xbib/marc/json/JsonNumberTest.java | 172 ---- .../org/xbib/marc/json/JsonObjectTest.java | 925 ------------------ .../org/xbib/marc/json/JsonReaderTest.java | 812 --------------- .../org/xbib/marc/json/JsonStringTest.java | 106 -- .../java/org/xbib/marc/json/JsonTest.java | 219 ----- .../xbib/marc/json/MarcJsonWriterTest.java | 3 - .../org/xbib/marc/json/PrettyPrintTest.java | 114 --- .../org/xbib/marc/json/test_ubl.mrc.json | 2 +- 31 files changed, 399 insertions(+), 6372 deletions(-) delete mode 100755 src/main/java/org/xbib/marc/json/Json.java delete mode 100755 src/main/java/org/xbib/marc/json/JsonArray.java create mode 100644 src/main/java/org/xbib/marc/json/JsonBuilder.java delete mode 100644 src/main/java/org/xbib/marc/json/JsonDefaultHandler.java delete mode 100755 src/main/java/org/xbib/marc/json/JsonException.java delete mode 100755 src/main/java/org/xbib/marc/json/JsonHandler.java delete mode 100755 src/main/java/org/xbib/marc/json/JsonLiteral.java delete mode 100644 src/main/java/org/xbib/marc/json/JsonMapper.java delete mode 100755 src/main/java/org/xbib/marc/json/JsonNumber.java delete mode 100755 src/main/java/org/xbib/marc/json/JsonObject.java delete mode 100755 src/main/java/org/xbib/marc/json/JsonReader.java delete mode 100755 src/main/java/org/xbib/marc/json/JsonString.java delete mode 100755 src/main/java/org/xbib/marc/json/JsonValue.java delete mode 100755 src/main/java/org/xbib/marc/json/JsonWriter.java delete mode 100755 src/main/java/org/xbib/marc/json/JsonWriterConfig.java delete mode 100755 src/test/java/org/xbib/marc/json/JsonArrayTest.java rename src/test/java/org/xbib/marc/json/{JsonWriterTest.java => JsonBuilderTest.java} (63%) delete mode 100755 src/test/java/org/xbib/marc/json/JsonLiteralTest.java delete mode 100755 src/test/java/org/xbib/marc/json/JsonNumberTest.java delete mode 100755 src/test/java/org/xbib/marc/json/JsonObjectTest.java delete mode 100755 src/test/java/org/xbib/marc/json/JsonReaderTest.java delete mode 100755 src/test/java/org/xbib/marc/json/JsonStringTest.java delete mode 100755 src/test/java/org/xbib/marc/json/JsonTest.java delete mode 100755 src/test/java/org/xbib/marc/json/PrettyPrintTest.java diff --git a/CREDITS.txt b/CREDITS.txt index 1f6e6d6..5145568 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -47,11 +47,3 @@ https://github.com/marc4j/marc4j/tree/master/test/resources and were donated by libraries for testing purpose. ----------------- - -The JSON reader/writer classes are derived work from - -https://github.com/ralfstx/minimal-json - -The original work is based on the MIT License - ------------------ diff --git a/src/main/java/org/xbib/marc/MarcField.java b/src/main/java/org/xbib/marc/MarcField.java index 7e6996b..7017387 100644 --- a/src/main/java/org/xbib/marc/MarcField.java +++ b/src/main/java/org/xbib/marc/MarcField.java @@ -187,6 +187,28 @@ public class MarcField implements Comparable { return builder.value; } + /** + * Return a "top most" value, useful for recovering a control field value from + * hierarchical structures like JSON formats. + * @return the value + */ + public String recoverControlFieldValue() { + String value = getValue(); + if (value == null || value.isEmpty()) { + // the control field is disguised as a data field, try lookup value in first subfield of "_" + value = getFirstSubfieldValue("_"); + // if no value, maybe blank " "? + if (value == null || value.isEmpty()) { + value = getFirstSubfieldValue(" "); + } + // still no value? Then it is some exotic like MAB with subfield "a"? + if (value == null || value.isEmpty()) { + value = getFirstSubfieldValue("a"); + } + } + return value; + } + /** * Returns if this MARC field is a control field. * @return true if control field, false if not diff --git a/src/main/java/org/xbib/marc/MarcRecord.java b/src/main/java/org/xbib/marc/MarcRecord.java index b84e74a..68c790b 100644 --- a/src/main/java/org/xbib/marc/MarcRecord.java +++ b/src/main/java/org/xbib/marc/MarcRecord.java @@ -174,7 +174,7 @@ public class MarcRecord implements Map { public LocalDate getCreationDate() { if (marcFields != null) { - String value = getFirst("008").getValue(); + String value = getFirst("008").recoverControlFieldValue(); if (value != null && value.length() >= 6) { return LocalDate.parse(value.substring(0, 6), field8DateFormat); } @@ -184,7 +184,7 @@ public class MarcRecord implements Map { public LocalDate getLastModificationDate() { if (marcFields != null) { - String value = getFirst("005").getValue(); + String value = getFirst("005").recoverControlFieldValue(); if (value != null && value.length() >= 8) { return LocalDate.parse(value.substring(0, 8), field5DateFormat); } diff --git a/src/main/java/org/xbib/marc/json/Json.java b/src/main/java/org/xbib/marc/json/Json.java deleted file mode 100755 index d650288..0000000 --- a/src/main/java/org/xbib/marc/json/Json.java +++ /dev/null @@ -1,279 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.util.Objects; - -/** - * This class serves as the entry point to the JSON API. - *

- * To parse a given JSON input, use the parse() methods like in this - * example: - *

- *
- * JsonObject object = Json.parse(string).asObject();
- * 
- *

- * To create a JSON data structure to be serialized, use the methods - * value(), array(), and object(). For example, the following - * snippet will produce the JSON string {"foo": 23, "bar": true}: - *

- *
- * String string = Json.object().add("foo", 23).add("bar", true).toString();
- * 
- *

- * To create a JSON array from a given Java array, you can use one of the array() - * methods with varargs parameters: - *

- *
- * String[] names = ...
- * JsonArray array = Json.array(names);
- * 
- */ -public final class Json { - - private Json() { - // not meant to be instantiated - } - - /** - * Returns a JsonValue instance that represents the given int value. - * - * @param value the value to get a JSON representation for - * @return a JSON value that represents the given value - */ - public static JsonValue of(int value) { - return new JsonNumber(Integer.toString(value, 10)); - } - - /** - * Returns a JsonValue instance that represents the given long value. - * - * @param value the value to get a JSON representation for - * @return a JSON value that represents the given value - */ - public static JsonValue of(long value) { - return new JsonNumber(Long.toString(value, 10)); - } - - /** - * Returns a JsonValue instance that represents the given float value. - * - * @param value the value to get a JSON representation for - * @return a JSON value that represents the given value - */ - public static JsonValue of(float value) { - if (Float.isInfinite(value) || Float.isNaN(value)) { - throw new IllegalArgumentException("Infinite and NaN values not permitted in JSON"); - } - return new JsonNumber(cutOffPointZero(Float.toString(value))); - } - - /** - * Returns a JsonValue instance that represents the given double value. - * - * @param value the value to get a JSON representation for - * @return a JSON value that represents the given value - */ - public static JsonValue of(double value) { - if (Double.isInfinite(value) || Double.isNaN(value)) { - throw new IllegalArgumentException("Infinite and NaN values not permitted in JSON"); - } - return new JsonNumber(cutOffPointZero(Double.toString(value))); - } - - /** - * Returns a JsonValue instance that represents the given string. - * - * @param string the string to get a JSON representation for - * @return a JSON value that represents the given string - */ - public static JsonValue of(String string) { - return string == null ? JsonLiteral.NULL : new JsonString(string); - } - - /** - * Returns a JsonValue instance that represents the given boolean value. - * - * @param value the value to get a JSON representation for - * @return a JSON value that represents the given value - */ - public static JsonValue of(boolean value) { - return value ? JsonLiteral.TRUE : JsonLiteral.FALSE; - } - - /** - * Creates a new empty JsonArray. This is equivalent to creating a new JsonArray using the - * constructor. - * - * @return a new empty JSON array - */ - public static JsonArray array() { - return new JsonArray(); - } - - /** - * Creates a new JsonArray that contains the JSON representations of the given int - * values. - * - * @param values the values to be included in the new JSON array - * @return a new JSON array that contains the given values - */ - public static JsonArray array(int... values) { - Objects.requireNonNull(values); - JsonArray array = new JsonArray(); - for (int value : values) { - array.add(value); - } - return array; - } - - /** - * Creates a new JsonArray that contains the JSON representations of the given long - * values. - * - * @param values the values to be included in the new JSON array - * @return a new JSON array that contains the given values - */ - public static JsonArray array(long... values) { - Objects.requireNonNull(values); - JsonArray array = new JsonArray(); - for (long value : values) { - array.add(value); - } - return array; - } - - /** - * Creates a new JsonArray that contains the JSON representations of the given float - * values. - * - * @param values the values to be included in the new JSON array - * @return a new JSON array that contains the given values - */ - public static JsonArray array(float... values) { - Objects.requireNonNull(values); - JsonArray array = new JsonArray(); - for (float value : values) { - array.add(value); - } - return array; - } - - /** - * Creates a new JsonArray that contains the JSON representations of the given double - * values. - * - * @param values the values to be included in the new JSON array - * @return a new JSON array that contains the given values - */ - public static JsonArray array(double... values) { - Objects.requireNonNull(values); - JsonArray array = new JsonArray(); - for (double value : values) { - array.add(value); - } - return array; - } - - /** - * Creates a new JsonArray that contains the JSON representations of the given - * boolean values. - * - * @param values the values to be included in the new JSON array - * @return a new JSON array that contains the given values - */ - public static JsonArray array(boolean... values) { - Objects.requireNonNull(values); - JsonArray array = new JsonArray(); - for (boolean value : values) { - array.add(value); - } - return array; - } - - /** - * Creates a new JsonArray that contains the JSON representations of the given strings. - * - * @param strings the strings to be included in the new JSON array - * @return a new JSON array that contains the given strings - */ - public static JsonArray array(String... strings) { - Objects.requireNonNull(strings); - JsonArray array = new JsonArray(); - for (String value : strings) { - array.add(value); - } - return array; - } - - /** - * Creates a new empty JsonObject. This is equivalent to creating a new JsonObject using the - * constructor. - * - * @return a new empty JSON object - */ - public static JsonObject object() { - return new JsonObject(); - } - - /** - * Parses the given input string as JSON. The input must contain a valid JSON value, optionally - * padded with whitespace. - * - * @param string the input string, must be valid JSON - * @return a value that represents the parsed JSON - * @throws IOException if the input is not valid JSON - */ - public static JsonValue parse(String string) throws IOException { - Objects.requireNonNull(string); - JsonDefaultHandler handler = new JsonDefaultHandler(); - new JsonReader<>(new StringReader(string), handler).parse(); - return handler.getValue(); - } - - /** - * 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. Hence, wrapping a reader in an additional - * BufferedReader likely won't improve reading performance. - *

- * - * @param reader the reader to read the JSON value from - * @return a value that represents the parsed JSON - * @throws IOException if an I/O error occurs in the reader - * @throws JsonException if the input is not valid JSON - */ - public static JsonValue parse(Reader reader) throws IOException { - JsonDefaultHandler handler = new JsonDefaultHandler(); - try (reader) { - new JsonReader<>(reader, handler).parse(); - } - return handler.getValue(); - } - - private static String cutOffPointZero(String string) { - if (string.endsWith(".0")) { - return string.substring(0, string.length() - 2); - } - return string; - } - -} diff --git a/src/main/java/org/xbib/marc/json/JsonArray.java b/src/main/java/org/xbib/marc/json/JsonArray.java deleted file mode 100755 index 86ab78c..0000000 --- a/src/main/java/org/xbib/marc/json/JsonArray.java +++ /dev/null @@ -1,397 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; - -/** - * Represents a JSON array, an ordered collection of JSON values. - *

- * Elements can be added using the add(...) methods which accept instances of - * {@link JsonValue}, strings, primitive numbers, and boolean values. To replace an element of an - * array, use the set(int, ...) methods. - *

- *

- * Elements can be accessed by their index using {@link #get(int)}. This class also supports - * iterating over the elements in document order using an {@link #iterator()} or an enhanced for - * loop: - *

- *
- * for (JsonValue value : jsonArray) {
- *   ...
- * }
- * 
- *

- * An equivalent {@link List} can be obtained from the method {@link #values()}. - *

- *

- * Note that this class is not thread-safe. If multiple threads access a - * JsonArray instance concurrently, while at least one of these threads modifies the - * contents of this array, access to the instance must be synchronized externally. Failure to do so - * may lead to an inconsistent state. - *

- */ -public class JsonArray extends JsonValue implements Iterable { - - private final List values; - - /** - * Creates a new empty JsonArray. - */ - public JsonArray() { - values = new ArrayList<>(); - } - - /** - * Creates a new JsonArray with the contents of the specified JSON array. - * - * @param array the JsonArray to get the initial contents from, must not be null - */ - public JsonArray(JsonArray array) { - Objects.requireNonNull(array); - values = new ArrayList<>(array.values); - } - - /** - * Appends the JSON representation of the specified int value to the end of this - * array. - * - * @param value the value to add to the array - * @return the array itself, to enable method chaining - */ - public JsonArray add(int value) { - values.add(Json.of(value)); - return this; - } - - /** - * Appends the JSON representation of the specified long value to the end of this - * array. - * - * @param value the value to add to the array - * @return the array itself, to enable method chaining - */ - public JsonArray add(long value) { - values.add(Json.of(value)); - return this; - } - - /** - * Appends the JSON representation of the specified float value to the end of this - * array. - * - * @param value the value to add to the array - * @return the array itself, to enable method chaining - */ - public JsonArray add(float value) { - values.add(Json.of(value)); - return this; - } - - /** - * Appends the JSON representation of the specified double value to the end of this - * array. - * - * @param value the value to add to the array - * @return the array itself, to enable method chaining - */ - public JsonArray add(double value) { - values.add(Json.of(value)); - return this; - } - - /** - * Appends the JSON representation of the specified boolean value to the end of this - * array. - * - * @param value the value to add to the array - * @return the array itself, to enable method chaining - */ - public JsonArray add(boolean value) { - values.add(Json.of(value)); - return this; - } - - /** - * Appends the JSON representation of the specified string to the end of this array. - * - * @param value the string to add to the array - * @return the array itself, to enable method chaining - */ - public JsonArray add(String value) { - values.add(Json.of(value)); - return this; - } - - /** - * Appends the specified JSON value to the end of this array. - * - * @param value the JsonValue to add to the array, must not be null - * @return the array itself, to enable method chaining - */ - public JsonArray add(JsonValue value) { - Objects.requireNonNull(value); - values.add(value); - return this; - } - - /** - * Replaces the element at the specified position in this array with the JSON representation of - * the specified int value. - * - * @param index the index of the array element to replace - * @param value the value to be stored at the specified array position - * @return the array itself, to enable method chaining - * @throws IndexOutOfBoundsException if the index is out of range, i.e. index < 0 or - * index >= size - */ - public JsonArray set(int index, int value) { - values.set(index, Json.of(value)); - return this; - } - - /** - * Replaces the element at the specified position in this array with the JSON representation of - * the specified long value. - * - * @param index the index of the array element to replace - * @param value the value to be stored at the specified array position - * @return the array itself, to enable method chaining - * @throws IndexOutOfBoundsException if the index is out of range, i.e. index < 0 or - * index >= size - */ - public JsonArray set(int index, long value) { - values.set(index, Json.of(value)); - return this; - } - - /** - * Replaces the element at the specified position in this array with the JSON representation of - * the specified float value. - * - * @param index the index of the array element to replace - * @param value the value to be stored at the specified array position - * @return the array itself, to enable method chaining - * @throws IndexOutOfBoundsException if the index is out of range, i.e. index < 0 or - * index >= size - */ - public JsonArray set(int index, float value) { - values.set(index, Json.of(value)); - return this; - } - - /** - * Replaces the element at the specified position in this array with the JSON representation of - * the specified double value. - * - * @param index the index of the array element to replace - * @param value the value to be stored at the specified array position - * @return the array itself, to enable method chaining - * @throws IndexOutOfBoundsException if the index is out of range, i.e. index < 0 or - * index >= size - */ - public JsonArray set(int index, double value) { - values.set(index, Json.of(value)); - return this; - } - - /** - * Replaces the element at the specified position in this array with the JSON representation of - * the specified boolean value. - * - * @param index the index of the array element to replace - * @param value the value to be stored at the specified array position - * @return the array itself, to enable method chaining - * @throws IndexOutOfBoundsException if the index is out of range, i.e. index < 0 or - * index >= size - */ - public JsonArray set(int index, boolean value) { - values.set(index, Json.of(value)); - return this; - } - - /** - * Replaces the element at the specified position in this array with the JSON representation of - * the specified string. - * - * @param index the index of the array element to replace - * @param value the string to be stored at the specified array position - * @return the array itself, to enable method chaining - * @throws IndexOutOfBoundsException if the index is out of range, i.e. index < 0 or - * index >= size - */ - public JsonArray set(int index, String value) { - values.set(index, Json.of(value)); - return this; - } - - /** - * Replaces the element at the specified position in this array with the specified JSON value. - * - * @param index the index of the array element to replace - * @param value the value to be stored at the specified array position, must not be null - * @return the array itself, to enable method chaining - * @throws IndexOutOfBoundsException if the index is out of range, i.e. index < 0 or - * index >= size - */ - public JsonArray set(int index, JsonValue value) { - if (value == null) { - throw new NullPointerException(); - } - values.set(index, value); - return this; - } - - /** - * Removes the element at the specified index from this array. - * - * @param index the index of the element to remove - * @return the array itself, to enable method chaining - * @throws IndexOutOfBoundsException if the index is out of range, i.e. index < 0 or - * index >= size - */ - public JsonArray remove(int index) { - values.remove(index); - return this; - } - - /** - * Returns the number of elements in this array. - * - * @return the number of elements in this array - */ - public int size() { - return values.size(); - } - - /** - * Returns true if this array contains no elements. - * - * @return true if this array contains no elements - */ - public boolean isEmpty() { - return values.isEmpty(); - } - - /** - * Returns the value of the element at the specified position in this array. - * - * @param index the index of the array element to return - * @return the value of the element at the specified position - * @throws IndexOutOfBoundsException if the index is out of range, i.e. index < 0 or - * index >= size - */ - public JsonValue get(int index) { - return values.get(index); - } - - /** - * Returns a list of the values in this array in document order. - * - * @return a list of the values in this array - */ - public List values() { - return values; - } - - /** - * Returns an iterator over the values of this array in document order. The returned iterator - * cannot be used to modify this array. - * - * @return an iterator over the values of this array - */ - @Override - public Iterator iterator() { - final Iterator iterator = values.iterator(); - return new Iterator<>() { - - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - @Override - public JsonValue next() { - return iterator.next(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } - - @Override - void write(JsonWriter writer) throws IOException { - writer.writeArrayOpen(); - Iterator iterator = iterator(); - if (iterator.hasNext()) { - iterator.next().write(writer); - while (iterator.hasNext()) { - writer.writeArraySeparator(); - iterator.next().write(writer); - } - } - writer.writeArrayClose(); - } - - @Override - public boolean isArray() { - return true; - } - - @Override - public JsonArray asArray() { - return this; - } - - @Override - public int hashCode() { - return values.hashCode(); - } - - /** - * Indicates whether a given object is "equal to" this JsonArray. An object is considered equal - * if it is also a JsonArray and both arrays contain the same list of values. - *

- * If two JsonArrays are equal, they will also produce the same JSON output. - *

- * - * @param object the object to be compared with this JsonArray - * @return {@code true} if the specified object is equal to this JsonArray, false - * otherwise - */ - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object == null) { - return false; - } - if (getClass() != object.getClass()) { - return false; - } - JsonArray other = (JsonArray) object; - return values.equals(other.values); - } - -} diff --git a/src/main/java/org/xbib/marc/json/JsonBuilder.java b/src/main/java/org/xbib/marc/json/JsonBuilder.java new file mode 100644 index 0000000..9eb95a1 --- /dev/null +++ b/src/main/java/org/xbib/marc/json/JsonBuilder.java @@ -0,0 +1,261 @@ +/** + * Copyright 2016-2022 Jörg Prante + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * Apache License 2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.xbib.marc.json; + +import java.io.IOException; +import java.time.Instant; +import java.util.Collection; +import java.util.Map; +import java.util.Objects; + +public class JsonBuilder { + + private final Appendable appendable; + + private State state; + + protected JsonBuilder() { + this(new StringBuilder()); + } + + protected JsonBuilder(Appendable appendable) { + this.appendable = appendable; + this.state = new State(null, 0, Structure.DOCSTART, true); + } + + public JsonBuilder beginCollection() throws IOException { + this.state = new State(state, state.level + 1, Structure.COLLECTION, true); + appendable.append('['); + return this; + } + + public JsonBuilder endCollection() throws IOException { + if (state.structure != Structure.COLLECTION) { + throw new IOException("no array to close"); + } + appendable.append(']'); + this.state = state != null ? state.parent : null; + return this; + } + + public JsonBuilder beginMap() throws IOException { + if (state.structure == Structure.COLLECTION) { + beginArrayValue(); + } + this.state = new State(state, state.level + 1, Structure.MAP, true); + appendable.append('{'); + return this; + } + + public JsonBuilder endMap() throws IOException { + if (state.structure != Structure.MAP && state.structure != Structure.KEY) { + throw new IOException("no object to close"); + } + appendable.append('}'); + this.state = state != null ? state.parent : null; + return this; + } + + public JsonBuilder buildMap(Map map) throws IOException { + Objects.requireNonNull(map); + boolean wrap = state.structure != Structure.MAP; + if (wrap) { + beginMap(); + } + for (Map.Entry entry : map.entrySet()) { + buildKey(entry.getKey()); + buildValue(entry.getValue()); + } + if (wrap) { + endMap(); + } + return this; + } + + public JsonBuilder buildCollection(Collection collection) throws IOException { + Objects.requireNonNull(collection); + beginCollection(); + for (Object object : collection) { + buildValue(object); + } + endCollection(); + return this; + } + + @SuppressWarnings("unchecked") + public JsonBuilder buildValue(Object object) throws IOException { + if (object instanceof Map) { + buildMap((Map) object); + return this; + } else if (object instanceof Collection) { + buildCollection((Collection) object); + return this; + } + if (state.structure == Structure.COLLECTION) { + beginArrayValue(); + } + if (object == null) { + buildNull(); + } else if (object instanceof CharSequence) { + buildString((CharSequence) object, true); + } else if (object instanceof Boolean) { + buildBoolean((Boolean) object); + } else if (object instanceof Byte) { + buildNumber((byte) object); + } else if (object instanceof Integer) { + buildNumber((int) object); + } else if (object instanceof Long) { + buildNumber((long) object); + } else if (object instanceof Float) { + buildNumber((float) object); + } else if (object instanceof Double) { + buildNumber((double) object); + } else if (object instanceof Number) { + buildNumber((Number) object); + } else if (object instanceof Instant) { + buildInstant((Instant) object); + } else { + throw new IllegalArgumentException("unable to write object class " + object.getClass()); + } + return this; + } + + public JsonBuilder buildKey(CharSequence string) throws IOException { + if (state.structure == Structure.COLLECTION) { + beginArrayValue(); + } else if (state.structure == Structure.MAP || state.structure == Structure.KEY) { + beginKey(string != null ? string.toString() : null); + } + buildString(string, true); + if (state.structure == Structure.MAP || state.structure == Structure.KEY) { + endKey(string != null ? string.toString() : null); + } + state.structure = Structure.KEY; + return this; + } + + public JsonBuilder buildNull() throws IOException { + buildString("null", false); + return this; + } + + public String build() { + return appendable.toString(); + } + + private void beginKey(String k) throws IOException { + if (state.first) { + state.first = false; + } else { + appendable.append(","); + } + } + + private void endKey(String k) throws IOException { + appendable.append(":"); + } + + private void beginArrayValue() throws IOException { + if (state.first) { + state.first = false; + } else { + appendable.append(","); + } + } + + private void buildBoolean(boolean bool) throws IOException { + buildString(bool ? "true" : "false", false); + } + + private void buildNumber(Number number) throws IOException { + buildString(number != null ? number.toString() : null, false); + } + + private void buildInstant(Instant instant) throws IOException { + buildString(instant.toString(), true); + } + + private void buildString(CharSequence string, boolean escape) throws IOException { + appendable.append(escape ? escapeString(string) : string); + } + + private CharSequence escapeString(CharSequence string) { + StringBuilder sb = new StringBuilder(); + sb.append('"'); + int start = 0; + int l = string.length(); + for (int i = 0; i < l; i++) { + char c = string.charAt(i); + // In JavaScript, U+2028 and U+2029 characters count as line endings and must be encoded. + // http://stackoverflow.com/questions/2965293/javascript-parse-error-on-u2028-unicode-character + if (c == '"' || c == '\\' || c < 32 || c == '\u2028' || c == '\u2029') { + if (i > start) { + sb.append(string, start, i); + } + start = i + 1; + sb.append(escapeCharacter(c)); + } + } + if (l > start) { + sb.append(string, start, l); + } + sb.append('"'); + return sb; + } + + private static String escapeCharacter(char c) { + switch (c) { + case '\n' -> { + return "\\n"; + } + case '\r' -> { + return "\\r"; + } + case '\t' -> { + return "\\t"; + } + case '\\' -> { + return "\\\\"; + } + case '\'' -> { + return "\\'"; + } + case '\"' -> { + return "\\\""; + } + } + String hex = Integer.toHexString(c); + return "\\u0000".substring(0, 6 - hex.length()) + hex; + } + + private enum Structure { + DOCSTART, MAP, KEY, COLLECTION + } + + private static class State { + State parent; + int level; + Structure structure; + boolean first; + + State(State parent, int level, Structure structure, boolean first) { + this.parent = parent; + this.level = level; + this.structure = structure; + this.first = first; + } + } +} diff --git a/src/main/java/org/xbib/marc/json/JsonDefaultHandler.java b/src/main/java/org/xbib/marc/json/JsonDefaultHandler.java deleted file mode 100644 index 58ed90d..0000000 --- a/src/main/java/org/xbib/marc/json/JsonDefaultHandler.java +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -public class JsonDefaultHandler implements JsonHandler { - - protected JsonValue value; - - public JsonDefaultHandler() { - } - - public JsonValue getValue() { - return value; - } - - @Override - public JsonArray startArray() { - return new JsonArray(); - } - - @Override - public JsonObject startObject() { - return new JsonObject(); - } - - @Override - public void startNull() { - } - - @Override - public void endNull() { - value = JsonLiteral.NULL; - } - - @Override - public void startBoolean() { - } - - @Override - public void endBoolean(boolean bool) { - value = bool ? JsonLiteral.TRUE : JsonLiteral.FALSE; - } - - @Override - public void startString() { - } - - @Override - public void endString(String string) { - value = new JsonString(string); - } - - @Override - public void startNumber() { - } - - @Override - public void endNumber(String string) { - value = new JsonNumber(string); - } - - @Override - public void endArray(JsonArray array) { - value = array; - } - - @Override - public void startArrayValue(JsonArray array) { - } - - @Override - public void endObject(JsonObject object) { - value = object; - } - - @Override - public void startObjectName(JsonObject object) { - } - - @Override - public void endObjectName(JsonObject object, String name) { - } - - @Override - public void startObjectValue(JsonObject object, String name) { - } - - @Override - public void endArrayValue(JsonArray array) { - array.add(value); - } - - @Override - public void endObjectValue(JsonObject object, String name) { - object.add(name, value); - } -} diff --git a/src/main/java/org/xbib/marc/json/JsonException.java b/src/main/java/org/xbib/marc/json/JsonException.java deleted file mode 100755 index abbe2bf..0000000 --- a/src/main/java/org/xbib/marc/json/JsonException.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -@SuppressWarnings("serial") -public class JsonException extends RuntimeException { - - JsonException(Throwable throwable) { - super(throwable); - } - - JsonException(String message) { - super(message); - } -} diff --git a/src/main/java/org/xbib/marc/json/JsonHandler.java b/src/main/java/org/xbib/marc/json/JsonHandler.java deleted file mode 100755 index 484bba5..0000000 --- a/src/main/java/org/xbib/marc/json/JsonHandler.java +++ /dev/null @@ -1,196 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -/** - * An interface for parser events. A {@link JsonHandler} can be given to a {@link JsonReader}. The - * parser will then call the methods of the given handler while reading the input. - - *

- * Implementations 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 JsonReader - */ -public interface JsonHandler { - - /** - * Indicates the beginning of a null literal in the JSON input. This method will be - * called when reading the first character of the literal. - */ - 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. - */ - 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. - */ - 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 - */ - 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 ('"'). - */ - 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 - */ - 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. - */ - 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 - */ - 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 - */ - A startArray(); - - /** - * 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 - */ - 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 - */ - 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 - */ - 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 - */ - O startObject(); - - /** - * 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 - */ - 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 - */ - 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 - */ - 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 - */ - 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 - */ - void endObjectValue(O object, String name); - -} diff --git a/src/main/java/org/xbib/marc/json/JsonLiteral.java b/src/main/java/org/xbib/marc/json/JsonLiteral.java deleted file mode 100755 index 35fbe60..0000000 --- a/src/main/java/org/xbib/marc/json/JsonLiteral.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - *
Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import java.io.IOException; - -/** - * A JSON literal. - */ -class JsonLiteral extends JsonValue { - - public static final JsonLiteral NULL = new JsonLiteral("null"); - - public static final JsonLiteral TRUE = new JsonLiteral("true"); - - public static final JsonLiteral FALSE = new JsonLiteral("false"); - - private final String value; - - private final boolean isNull; - - private final boolean isTrue; - - private final boolean isFalse; - - JsonLiteral(String value) { - this.value = value; - isNull = "null".equals(value); - isTrue = "true".equals(value); - isFalse = "false".equals(value); - } - - @Override - void write(JsonWriter writer) throws IOException { - writer.writeLiteral(value); - } - - @Override - public String toString() { - return value; - } - - @Override - public int hashCode() { - return value.hashCode(); - } - - @Override - public boolean isNull() { - return isNull; - } - - @Override - public boolean isTrue() { - return isTrue; - } - - @Override - public boolean isFalse() { - return isFalse; - } - - @Override - public boolean isBoolean() { - return isTrue || isFalse; - } - - @Override - public boolean asBoolean() { - return isNull ? super.asBoolean() : isTrue; - } - - @Override - public boolean equals(Object object) { - return this == object || object != null && getClass() == object.getClass() && value.equals(((JsonLiteral) object).value); - } - -} diff --git a/src/main/java/org/xbib/marc/json/JsonMapper.java b/src/main/java/org/xbib/marc/json/JsonMapper.java deleted file mode 100644 index 0c3855c..0000000 --- a/src/main/java/org/xbib/marc/json/JsonMapper.java +++ /dev/null @@ -1,108 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class JsonMapper { - - private JsonMapper() { - // do not instantiate this class - } - - public static Object asObject(JsonValue value) { - if (value.isBoolean()) { - return value.asBoolean(); - } else if (value.isInt()) { - return value.asInt(); - } else if (value.isLong()) { - return value.asLong(); - } else if (value.isFloat()) { - return value.asFloat(); - } else if (value.isDouble()) { - return value.asDouble(); - } else if (value.isString()) { - return value.asString(); - } else if (value.isArray()) { - return asList(value.asArray()); - } else if (value.isObject()) { - return asMap(value.asObject()); - } else { - return null; - } - } - - public static List asList(JsonArray array) { - List list = new ArrayList<>(array.size()); - for (JsonValue element : array) { - list.add(asObject(element)); - } - return list; - } - - public static Map asMap(JsonObject object) { - Map map = new HashMap<>(object.size(), 1.f); - for (JsonObject.Member member : object) { - map.put(member.getName(), asObject(member.getValue())); - } - return map; - } - - public static JsonValue asJsonValue(Object object) { - if (object == null) { - return JsonLiteral.NULL; - } else if (object instanceof Boolean) { - return Json.of((Boolean) object); - } else if (object instanceof Integer) { - return Json.of((Integer) object); - } else if (object instanceof Long) { - return Json.of((Long) object); - } else if (object instanceof Float) { - return Json.of((Float) object); - } else if (object instanceof Double) { - return Json.of((Double) object); - } else if (object instanceof String) { - return Json.of((String) object); - } else if (object instanceof Collection) { - return asJsonArray((Collection) object); - } else if (object instanceof Map) { - return asJsonObject((Map) object); - } else { - return null; - } - } - - public static JsonArray asJsonArray(Collection collection) { - JsonArray array = new JsonArray(); - for (Object element : collection) { - array.add(asJsonValue(element)); - } - return array; - } - - public static JsonObject asJsonObject(Map map) { - JsonObject object = new JsonObject(); - for (Map.Entry entry : map.entrySet()) { - object.add(String.valueOf(entry.getKey()), - asJsonValue(entry.getValue())); - } - return object; - } -} diff --git a/src/main/java/org/xbib/marc/json/JsonNumber.java b/src/main/java/org/xbib/marc/json/JsonNumber.java deleted file mode 100755 index b267b47..0000000 --- a/src/main/java/org/xbib/marc/json/JsonNumber.java +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import java.io.IOException; -import java.util.Objects; - -/** - * - */ -class JsonNumber extends JsonValue { - - private final String string; - - JsonNumber(String string) { - Objects.requireNonNull(string); - this.string = string; - } - - @Override - public String toString() { - return string; - } - - @Override - void write(JsonWriter writer) throws IOException { - writer.writeNumber(string); - } - - @Override - public boolean isInt() { - try { - asInt(); - } catch (NumberFormatException e) { - return false; - } - return true; - } - - @Override - public boolean isLong() { - try { - asLong(); - } catch (NumberFormatException e) { - return false; - } - return true; - } - - @Override - public boolean isFloat() { - try { - asFloat(); - } catch (NumberFormatException e) { - return false; - } - return true; - } - - @Override - public boolean isDouble() { - try { - asDouble(); - } catch (NumberFormatException e) { - return false; - } - return true; - } - - @Override - public int asInt() { - return Integer.parseInt(string, 10); - } - - @Override - public long asLong() { - return Long.parseLong(string, 10); - } - - @Override - public float asFloat() { - return Float.parseFloat(string); - } - - @Override - public double asDouble() { - return Double.parseDouble(string); - } - - @Override - public int hashCode() { - return string.hashCode(); - } - - @Override - public boolean equals(Object object) { - return this == object || object != null && getClass() == object.getClass() - && string.equals(((JsonNumber) object).string); - } - -} diff --git a/src/main/java/org/xbib/marc/json/JsonObject.java b/src/main/java/org/xbib/marc/json/JsonObject.java deleted file mode 100755 index 8733f77..0000000 --- a/src/main/java/org/xbib/marc/json/JsonObject.java +++ /dev/null @@ -1,785 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; - -/** - * Represents a JSON object, a set of name/value pairs, where the names are strings and the values - * are JSON values. - *

- * Members can be added using the add(String, ...) methods which accept instances of - * {@link JsonValue}, strings, primitive numbers, and boolean values. To modify certain values of an - * object, use the set(String, ...) methods. Please note that the add - * methods are faster than set as they do not search for existing members. On the other - * hand, the add methods do not prevent adding multiple members with the same name. - * Duplicate names are discouraged but not prohibited by JSON. - *

- *

- * Members can be accessed by their name using {@link #get(String)}. A list of all names can be - * obtained from the method {@link #names()}. This class also supports iterating over the members in - * document order using an {@link #iterator()} or an enhanced for loop: - *

- *
- * for (Member member : jsonObject) {
- *   String name = member.getName();
- *   JsonValue value = member.getValue();
- *   ...
- * }
- * 
- *

- * Even though JSON objects are unordered by definition, instances of this class preserve the order - * of members to allow processing in document order and to guarantee a predictable output. - *

- *

- * Note that this class is not thread-safe. If multiple threads access a - * JsonObject instance concurrently, while at least one of these threads modifies the - * contents of this object, access to the instance must be synchronized externally. Failure to do so - * may lead to an inconsistent state. - *

- */ -public class JsonObject extends JsonValue implements Iterable { - - private final List names; - - private final List values; - - private HashIndexTable table; - - /** - * Creates a new empty JsonObject. - */ - public JsonObject() { - names = new ArrayList<>(); - values = new ArrayList<>(); - table = new HashIndexTable(); - } - - /** - * Creates a new JsonObject, initialized with the contents of the specified JSON object. - * - * @param object the JSON object to get the initial contents from, must not be null - */ - public JsonObject(JsonObject object) { - Objects.requireNonNull(object); - names = new ArrayList<>(object.names); - values = new ArrayList<>(object.values); - table = new HashIndexTable(); - updateHashIndex(); - } - - /** - * Appends a new member to the end of this object, with the specified name and the JSON - * representation of the specified int value. - *

- * This method does not prevent duplicate names. Calling this method with a name - * that already exists in the object will append another member with the same name. In order to - * replace existing members, use the method set(name, value) instead. However, - * add is much faster than set (because it does not need to - * search for existing members). Therefore add should be preferred when constructing new - * objects. - *

- * - * @param name the name of the member to add - * @param value the value of the member to add - * @return the object itself, to enable method chaining - */ - public JsonObject add(String name, int value) { - add(name, Json.of(value)); - return this; - } - - /** - * Appends a new member to the end of this object, with the specified name and the JSON - * representation of the specified long value. - *

- * This method does not prevent duplicate names. Calling this method with a name - * that already exists in the object will append another member with the same name. In order to - * replace existing members, use the method set(name, value) instead. However, - * add is much faster than set (because it does not need to - * search for existing members). Therefore add should be preferred when constructing new - * objects. - *

- * - * @param name the name of the member to add - * @param value the value of the member to add - * @return the object itself, to enable method chaining - */ - public JsonObject add(String name, long value) { - add(name, Json.of(value)); - return this; - } - - /** - * Appends a new member to the end of this object, with the specified name and the JSON - * representation of the specified float value. - *

- * This method does not prevent duplicate names. Calling this method with a name - * that already exists in the object will append another member with the same name. In order to - * replace existing members, use the method set(name, value) instead. However, - * add is much faster than set (because it does not need to - * search for existing members). Therefore add should be preferred when constructing new - * objects. - *

- * - * @param name the name of the member to add - * @param value the value of the member to add - * @return the object itself, to enable method chaining - */ - public JsonObject add(String name, float value) { - add(name, Json.of(value)); - return this; - } - - /** - * Appends a new member to the end of this object, with the specified name and the JSON - * representation of the specified double value. - *

- * This method does not prevent duplicate names. Calling this method with a name - * that already exists in the object will append another member with the same name. In order to - * replace existing members, use the method set(name, value) instead. However, - * add is much faster than set (because it does not need to - * search for existing members). Therefore add should be preferred when constructing new - * objects. - *

- * - * @param name the name of the member to add - * @param value the value of the member to add - * @return the object itself, to enable method chaining - */ - public JsonObject add(String name, double value) { - add(name, Json.of(value)); - return this; - } - - /** - * Appends a new member to the end of this object, with the specified name and the JSON - * representation of the specified boolean value. - *

- * This method does not prevent duplicate names. Calling this method with a name - * that already exists in the object will append another member with the same name. In order to - * replace existing members, use the method set(name, value) instead. However, - * add is much faster than set (because it does not need to - * search for existing members). Therefore add should be preferred when constructing new - * objects. - *

- * - * @param name the name of the member to add - * @param value the value of the member to add - * @return the object itself, to enable method chaining - */ - public JsonObject add(String name, boolean value) { - add(name, Json.of(value)); - return this; - } - - /** - * Appends a new member to the end of this object, with the specified name and the JSON - * representation of the specified string. - *

- * This method does not prevent duplicate names. Calling this method with a name - * that already exists in the object will append another member with the same name. In order to - * replace existing members, use the method set(name, value) instead. However, - * add is much faster than set (because it does not need to - * search for existing members). Therefore add should be preferred when constructing new - * objects. - *

- * - * @param name the name of the member to add - * @param value the value of the member to add - * @return the object itself, to enable method chaining - */ - public JsonObject add(String name, String value) { - add(name, Json.of(value)); - return this; - } - - /** - * Appends a new member to the end of this object, with the specified name and the specified JSON - * value. - *

- * This method does not prevent duplicate names. Calling this method with a name - * that already exists in the object will append another member with the same name. In order to - * replace existing members, use the method set(name, value) instead. However, - * add is much faster than set (because it does not need to - * search for existing members). Therefore add should be preferred when constructing new - * objects. - *

- * - * @param name the name of the member to add - * @param value the value of the member to add, must not be null - * @return the object itself, to enable method chaining - */ - public JsonObject add(String name, JsonValue value) { - if (name == null) { - throw new NullPointerException("name is null"); - } - if (value == null) { - throw new NullPointerException("value is null"); - } - table.add(name, names.size()); - names.add(name); - values.add(value); - return this; - } - - /** - * Sets the value of the member with the specified name to the JSON representation of the - * specified int value. If this object does not contain a member with this name, a - * new member is added at the end of the object. If this object contains multiple members with - * this name, only the last one is changed. - *

- * This method should only be used to modify existing objects. To fill a new - * object with members, the method add(name, value) should be preferred which is much - * faster (as it does not need to search for existing members). - *

- * - * @param name the name of the member to replace - * @param value the value to set to the member - * @return the object itself, to enable method chaining - */ - public JsonObject set(String name, int value) { - set(name, Json.of(value)); - return this; - } - - /** - * Sets the value of the member with the specified name to the JSON representation of the - * specified long value. If this object does not contain a member with this name, a - * new member is added at the end of the object. If this object contains multiple members with - * this name, only the last one is changed. - *

- * This method should only be used to modify existing objects. To fill a new - * object with members, the method add(name, value) should be preferred which is much - * faster (as it does not need to search for existing members). - *

- * - * @param name the name of the member to replace - * @param value the value to set to the member - * @return the object itself, to enable method chaining - */ - public JsonObject set(String name, long value) { - set(name, Json.of(value)); - return this; - } - - /** - * Sets the value of the member with the specified name to the JSON representation of the - * specified float value. If this object does not contain a member with this name, a - * new member is added at the end of the object. If this object contains multiple members with - * this name, only the last one is changed. - *

- * This method should only be used to modify existing objects. To fill a new - * object with members, the method add(name, value) should be preferred which is much - * faster (as it does not need to search for existing members). - *

- * - * @param name the name of the member to add - * @param value the value of the member to add - * @return the object itself, to enable method chaining - */ - public JsonObject set(String name, float value) { - set(name, Json.of(value)); - return this; - } - - /** - * Sets the value of the member with the specified name to the JSON representation of the - * specified double value. If this object does not contain a member with this name, a - * new member is added at the end of the object. If this object contains multiple members with - * this name, only the last one is changed. - *

- * This method should only be used to modify existing objects. To fill a new - * object with members, the method add(name, value) should be preferred which is much - * faster (as it does not need to search for existing members). - *

- * - * @param name the name of the member to add - * @param value the value of the member to add - * @return the object itself, to enable method chaining - */ - public JsonObject set(String name, double value) { - set(name, Json.of(value)); - return this; - } - - /** - * Sets the value of the member with the specified name to the JSON representation of the - * specified boolean value. If this object does not contain a member with this name, - * a new member is added at the end of the object. If this object contains multiple members with - * this name, only the last one is changed. - *

- * This method should only be used to modify existing objects. To fill a new - * object with members, the method add(name, value) should be preferred which is much - * faster (as it does not need to search for existing members). - *

- * - * @param name the name of the member to add - * @param value the value of the member to add - * @return the object itself, to enable method chaining - */ - public JsonObject set(String name, boolean value) { - set(name, Json.of(value)); - return this; - } - - /** - * Sets the value of the member with the specified name to the JSON representation of the - * specified string. If this object does not contain a member with this name, a new member is - * added at the end of the object. If this object contains multiple members with this name, only - * the last one is changed. - *

- * This method should only be used to modify existing objects. To fill a new - * object with members, the method add(name, value) should be preferred which is much - * faster (as it does not need to search for existing members). - *

- * - * @param name the name of the member to add - * @param value the value of the member to add - * @return the object itself, to enable method chaining - */ - public JsonObject set(String name, String value) { - set(name, Json.of(value)); - return this; - } - - /** - * Sets the value of the member with the specified name to the specified JSON value. If this - * object does not contain a member with this name, a new member is added at the end of the - * object. If this object contains multiple members with this name, only the last one is changed. - *

- * This method should only be used to modify existing objects. To fill a new - * object with members, the method add(name, value) should be preferred which is much - * faster (as it does not need to search for existing members). - *

- * - * @param name the name of the member to add - * @param value the value of the member to add, must not be null - * @return the object itself, to enable method chaining - */ - public JsonObject set(String name, JsonValue value) { - if (name == null) { - throw new NullPointerException("name is null"); - } - if (value == null) { - throw new NullPointerException("value is null"); - } - int index = indexOf(name); - if (index != -1) { - values.set(index, value); - } else { - table.add(name, names.size()); - names.add(name); - values.add(value); - } - return this; - } - - /** - * Removes a member with the specified name from this object. If this object contains multiple - * members with the given name, only the last one is removed. If this object does not contain a - * member with the specified name, the object is not modified. - * - * @param name the name of the member to remove - * @return the object itself, to enable method chaining - */ - public JsonObject remove(String name) { - Objects.requireNonNull(name); - int index = indexOf(name); - if (index != -1) { - table.remove(index); - names.remove(index); - values.remove(index); - } - return this; - } - - /** - * Copies all members of the specified object into this object. When the specified object contains - * members with names that also exist in this object, the existing values in this object will be - * replaced by the corresponding values in the specified object. - * - * @param object the object to merge - * @return the object itself, to enable method chaining - */ - public JsonObject merge(JsonObject object) { - Objects.requireNonNull(object); - for (Member member : object) { - this.set(member.name, member.value); - } - return this; - } - - /** - * Returns the value of the member with the specified name in this object. If this object contains - * multiple members with the given name, this method will return the last one. - * - * @param name the name of the member whose value is to be returned - * @return the value of the last member with the specified name, or null if this - * object does not contain a member with that name - */ - public JsonValue get(String name) { - Objects.requireNonNull(name); - int index = indexOf(name); - return index != -1 ? values.get(index) : null; - } - - /** - * Returns the int value of the member with the specified name in this object. If - * this object does not contain a member with this name, the given default value is returned. If - * this object contains multiple members with the given name, the last one will be picked. If this - * member's value does not represent a JSON number or if it cannot be interpreted as Java - * int, an exception is thrown. - * - * @param name the name of the member whose value is to be returned - * @param defaultValue the value to be returned if the requested member is missing - * @return the value of the last member with the specified name, or the given default value if - * this object does not contain a member with that name - */ - public int getInt(String name, int defaultValue) { - JsonValue value = get(name); - return value != null ? value.asInt() : defaultValue; - } - - /** - * Returns the long value of the member with the specified name in this object. If - * this object does not contain a member with this name, the given default value is returned. If - * this object contains multiple members with the given name, the last one will be picked. If this - * member's value does not represent a JSON number or if it cannot be interpreted as Java - * long, an exception is thrown. - * - * @param name the name of the member whose value is to be returned - * @param defaultValue the value to be returned if the requested member is missing - * @return the value of the last member with the specified name, or the given default value if - * this object does not contain a member with that name - */ - public long getLong(String name, long defaultValue) { - JsonValue value = get(name); - return value != null ? value.asLong() : defaultValue; - } - - /** - * Returns the float value of the member with the specified name in this object. If - * this object does not contain a member with this name, the given default value is returned. If - * this object contains multiple members with the given name, the last one will be picked. If this - * member's value does not represent a JSON number or if it cannot be interpreted as Java - * float, an exception is thrown. - * - * @param name the name of the member whose value is to be returned - * @param defaultValue the value to be returned if the requested member is missing - * @return the value of the last member with the specified name, or the given default value if - * this object does not contain a member with that name - */ - public float getFloat(String name, float defaultValue) { - JsonValue value = get(name); - return value != null ? value.asFloat() : defaultValue; - } - - /** - * Returns the double value of the member with the specified name in this object. If - * this object does not contain a member with this name, the given default value is returned. If - * this object contains multiple members with the given name, the last one will be picked. If this - * member's value does not represent a JSON number or if it cannot be interpreted as Java - * double, an exception is thrown. - * - * @param name the name of the member whose value is to be returned - * @param defaultValue the value to be returned if the requested member is missing - * @return the value of the last member with the specified name, or the given default value if - * this object does not contain a member with that name - */ - public double getDouble(String name, double defaultValue) { - JsonValue value = get(name); - return value != null ? value.asDouble() : defaultValue; - } - - /** - * Returns the boolean value of the member with the specified name in this object. If - * this object does not contain a member with this name, the given default value is returned. If - * this object contains multiple members with the given name, the last one will be picked. If this - * member's value does not represent a JSON true or false value, an - * exception is thrown. - * - * @param name the name of the member whose value is to be returned - * @param defaultValue the value to be returned if the requested member is missing - * @return the value of the last member with the specified name, or the given default value if - * this object does not contain a member with that name - */ - public boolean getBoolean(String name, boolean defaultValue) { - JsonValue value = get(name); - return value != null ? value.asBoolean() : defaultValue; - } - - /** - * Returns the String value of the member with the specified name in this object. If - * this object does not contain a member with this name, the given default value is returned. If - * this object contains multiple members with the given name, the last one is picked. If this - * member's value does not represent a JSON string, an exception is thrown. - * - * @param name the name of the member whose value is to be returned - * @param defaultValue the value to be returned if the requested member is missing - * @return the value of the last member with the specified name, or the given default value if - * this object does not contain a member with that name - */ - public String getString(String name, String defaultValue) { - JsonValue value = get(name); - return value != null ? value.asString() : defaultValue; - } - - /** - * Returns the number of members (name/value pairs) in this object. - * - * @return the number of members in this object - */ - public int size() { - return names.size(); - } - - /** - * Returns true if this object contains no members. - * - * @return true if this object contains no members - */ - public boolean isEmpty() { - return names.isEmpty(); - } - - /** - * Returns a list of the names in this object in document order. The returned list is backed by - * this object and will reflect subsequent changes. It cannot be used to modify this object. - * Attempts to modify the returned list will result in an exception. - * - * @return a list of the names in this object - */ - public List names() { - return Collections.unmodifiableList(names); - } - - /** - * Returns an iterator over the members of this object in document order. The returned iterator - * cannot be used to modify this object. - * - * @return an iterator over the members of this object - */ - @Override - public Iterator iterator() { - final Iterator namesIterator = names.iterator(); - final Iterator valuesIterator = values.iterator(); - return new Iterator() { - @Override - public boolean hasNext() { - return namesIterator.hasNext(); - } - - @Override - public Member next() { - String name = namesIterator.next(); - JsonValue value = valuesIterator.next(); - return new Member(name, value); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - }; - } - - @Override - void write(JsonWriter writer) throws IOException { - writer.writeObjectOpen(); - Iterator namesIterator = names.iterator(); - Iterator valuesIterator = values.iterator(); - if (namesIterator.hasNext()) { - writer.writeMemberName(namesIterator.next()); - writer.writeMemberSeparator(); - valuesIterator.next().write(writer); - while (namesIterator.hasNext()) { - writer.writeObjectSeparator(); - writer.writeMemberName(namesIterator.next()); - writer.writeMemberSeparator(); - valuesIterator.next().write(writer); - } - } - writer.writeObjectClose(); - } - - @Override - public boolean isObject() { - return true; - } - - @Override - public JsonObject asObject() { - return this; - } - - @Override - public int hashCode() { - int result = 1; - result = 31 * result + names.hashCode(); - result = 31 * result + values.hashCode(); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - JsonObject other = (JsonObject) obj; - return names.equals(other.names) && values.equals(other.values); - } - - int indexOf(String name) { - int index = table.get(name); - if (index != -1 && name.equals(names.get(index))) { - return index; - } - return names.lastIndexOf(name); - } - - private void updateHashIndex() { - int size = names.size(); - for (int i = 0; i < size; i++) { - table.add(names.get(i), i); - } - } - - /** - * Represents a member of a JSON object, a pair of a name and a value. - */ - public static class Member { - - private final String name; - private final JsonValue value; - - public Member(String name, JsonValue value) { - this.name = name; - this.value = value; - } - - /** - * Returns the name of this member. - * - * @return the name of this member, never null - */ - public String getName() { - return name; - } - - /** - * Returns the value of this member. - * - * @return the value of this member, never null - */ - public JsonValue getValue() { - return value; - } - - @Override - public int hashCode() { - int result = 1; - result = 31 * result + name.hashCode(); - result = 31 * result + value.hashCode(); - return result; - } - - /** - * Indicates whether a given object is "equal to" this JsonObject. An object is considered equal - * if it is also a JsonObject and both objects contain the same members in - * the same order. - *

- * If two JsonObjects are equal, they will also produce the same JSON output. - *

- * - * @param object the object to be compared with this JsonObject - * @return true if the specified object is equal to this JsonObject, false - * otherwise - */ - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object == null) { - return false; - } - if (getClass() != object.getClass()) { - return false; - } - Member other = (Member) object; - return name.equals(other.name) && value.equals(other.value); - } - - } - - /** - * - */ - static class HashIndexTable { - - private final byte[] hashTable = new byte[32]; // must be a power of two - - HashIndexTable() { - } - - HashIndexTable(HashIndexTable original) { - System.arraycopy(original.hashTable, 0, hashTable, 0, hashTable.length); - } - - void add(String name, int index) { - int slot = hashSlotFor(name); - if (index < 0xff) { - // increment by 1, 0 stands for empty - hashTable[slot] = (byte) (index + 1); - } else { - hashTable[slot] = 0; - } - } - - void remove(int index) { - for (int i = 0; i < hashTable.length; i++) { - if (hashTable[i] == index + 1) { - hashTable[i] = 0; - } else if (hashTable[i] > index + 1) { - hashTable[i]--; - } - } - } - - int get(Object name) { - int slot = hashSlotFor(name); - // subtract 1, 0 stands for empty - return (hashTable[slot] & 0xff) - 1; - } - - private int hashSlotFor(Object element) { - return element.hashCode() & hashTable.length - 1; - } - } -} diff --git a/src/main/java/org/xbib/marc/json/JsonReader.java b/src/main/java/org/xbib/marc/json/JsonReader.java deleted file mode 100755 index 5a8fe2c..0000000 --- a/src/main/java/org/xbib/marc/json/JsonReader.java +++ /dev/null @@ -1,460 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import java.io.IOException; -import java.io.Reader; -import java.util.Objects; - -/** - * A streaming parser for JSON text. The parser reports all events to a given handler. - * - * @param the JSON array type - * @param the JSON object type - */ -public class JsonReader { - - private static final int MAX_NESTING_LEVEL = 1000; - - private static final int DEFAULT_BUFFER_SIZE = 1024; - - private final Reader reader; - - private final JsonHandler handler; - - private char[] buffer; - - private int index; - - private int fill; - - private int current; - - private StringBuilder captureBuffer; - - private int captureStart; - - private int nestingLevel; - - /** - * Creates a new JsonParser with the given handler. The parser will report all parser events to - * this handler. - * @param reader the reader - * @param handler the handler to process parser events - */ - public JsonReader(Reader reader, JsonHandler handler) { - Objects.requireNonNull(handler); - this.handler = handler; - this.reader = reader; - } - - /** - * 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. - *

- * - * @throws IOException if an I/O error occurs in the reader - * @throws JsonException if the input is not valid JSON - */ - public void parse() throws IOException { - parse(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 buffersize the size of the input buffer in chars - * @throws IOException if an I/O error occurs in the reader - * @throws JsonException if the input is not valid JSON - */ - public void parse(int buffersize) throws IOException { - if (reader == null) { - throw new NullPointerException("reader is null"); - } - if (buffersize <= 0) { - throw new IllegalArgumentException("buffersize is zero or negative"); - } - buffer = new char[buffersize]; - index = 0; - fill = 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 { - A 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 { - O 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 (true) { - if (!readDigit()) { - break; - } - } - } - readFraction(); - readExponent(); - handler.endNumber(endCapture()); - } - - private void readFraction() throws IOException { - if (!readChar('.')) { - return; - } - if (!readDigit()) { - throw expected("digit"); - } - while (true) { - if (!readDigit()) { - break; - } - } - } - - private void readExponent() throws IOException { - if (!readChar('e') && !readChar('E')) { - return; - } - if (!readChar('+')) { - readChar('-'); - } - if (!readDigit()) { - throw expected("digit"); - } - while (true) { - if (!readDigit()) { - break; - } - } - } - - 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; - } - fill = reader.read(buffer, 0, buffer.length); - index = 0; - if (fill == -1) { - current = -1; - index++; - return; - } - } - 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); - } - - private JsonException expected(String expected) { - if (isEndOfText()) { - return error("Unexpected end of input"); - } - return error("Expected " + expected); - } - - private JsonException error(String message) { - return new JsonException(message); - } - - 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/src/main/java/org/xbib/marc/json/JsonString.java b/src/main/java/org/xbib/marc/json/JsonString.java deleted file mode 100755 index a407ce3..0000000 --- a/src/main/java/org/xbib/marc/json/JsonString.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - *
Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import java.io.IOException; -import java.util.Objects; - -/** - * - */ -class JsonString extends JsonValue { - - private final String string; - - JsonString(String string) { - Objects.requireNonNull(string); - this.string = string; - } - - @Override - void write(JsonWriter writer) throws IOException { - writer.writeString(string); - } - - @Override - public boolean isString() { - return true; - } - - @Override - public String asString() { - return string; - } - - @Override - public int hashCode() { - return string.hashCode(); - } - - @Override - public boolean equals(Object object) { - return this == object || object != null && getClass() == object.getClass() - && string.equals(((JsonString) object).string); - } - -} diff --git a/src/main/java/org/xbib/marc/json/JsonValue.java b/src/main/java/org/xbib/marc/json/JsonValue.java deleted file mode 100755 index a506745..0000000 --- a/src/main/java/org/xbib/marc/json/JsonValue.java +++ /dev/null @@ -1,295 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import java.io.IOException; -import java.io.StringWriter; - -/** - * Represents a JSON value. This can be a JSON object, an array, - * a number, a string, or one of the literals - * true, false, and null. - *

- * The literals true, false, and null are - * represented by the constants - * {@code JsonLiteral.NULL}, {@code JsonLiteral.FALSE}, and {@code JsonLiteral.NULL}. - *

- *

- * JSON objects and arrays are represented by the subtypes - * {@link JsonObject} and {@link JsonArray}. Instances of these types can be created using the - * public constructors of these classes. - *

- *

- * Instances that represent JSON numbers, strings and - * boolean values can be created using the static factory methods - * {@code JSON.parse(String)}, {@code valueOf(long)}, {@code valueOf(double)}, etc. - *

- *

- * In order to find out whether an instance of this class is of a certain type, the methods - * {@link #isObject()}, {@link #isArray()}, {@link #isString()}, {@link #isInt()} etc. can be - * used. - *

- *

- * If the type of a JSON value is known, the methods {@link #asObject()}, {@link #asArray()}, - * {@link #asString()}, {@link #asInt()}, etc. can be used to get this value directly in the - * appropriate target type. - *

- */ -public abstract class JsonValue { - - public JsonValue() { - } - - /** - * Detects whether this value represents a JSON object. If this is the case, this value is an - * instance of {@link JsonObject}. - * - * @return true if this value is an instance of JsonObject - */ - public boolean isObject() { - return false; - } - - /** - * Detects whether this value represents a JSON array. If this is the case, this value is an - * instance of {@link JsonArray}. - * - * @return true if this value is an instance of JsonArray - */ - public boolean isArray() { - return false; - } - - /** - * Detects whether this value represents a JSON number that is an integer. - * - * @return true if this value represents a JSON number that is an integer - */ - public boolean isInt() { - return false; - } - - /** - * Detects whether this value represents a JSON number that is an long. - * - * @return true if this value represents a JSON number that is an long - */ - public boolean isLong() { - return false; - } - - /** - * Detects whether this value represents a JSON number that is an float. - * - * @return true if this value represents a JSON number that is an float - */ - public boolean isFloat() { - return false; - } - - /** - * Detects whether this value represents a JSON number that is an double. - * - * @return true if this value represents a JSON number that is an double - */ - public boolean isDouble() { - return false; - } - - /** - * Detects whether this value represents a JSON string. - * - * @return true if this value represents a JSON string - */ - public boolean isString() { - return false; - } - - /** - * Detects whether this value represents a boolean value. - * - * @return true if this value represents either the JSON literal true or - * false - */ - public boolean isBoolean() { - return false; - } - - /** - * Detects whether this value represents the JSON literal true. - * - * @return true if this value represents the JSON literal true - */ - public boolean isTrue() { - return false; - } - - /** - * Detects whether this value represents the JSON literal false. - * - * @return true if this value represents the JSON literal false - */ - public boolean isFalse() { - return false; - } - - /** - * Detects whether this value represents the JSON literal null. - * - * @return true if this value represents the JSON literal null - */ - public boolean isNull() { - return false; - } - - /** - * Returns this JSON value as {@link JsonObject}, assuming that this value represents a JSON - * object. If this is not the case, an exception is thrown. - * - * @return a JSONObject for this value - * @throws UnsupportedOperationException if this value is not a JSON object - */ - public JsonObject asObject() { - throw new UnsupportedOperationException("Not an object: " + toString()); - } - - /** - * Returns this JSON value as {@link JsonArray}, assuming that this value represents a JSON array. - * If this is not the case, an exception is thrown. - * - * @return a JSONArray for this value - * @throws UnsupportedOperationException if this value is not a JSON array - */ - public JsonArray asArray() { - throw new UnsupportedOperationException("Not an array: " + toString()); - } - - /** - * Returns this JSON value as an int value, assuming that this value represents a - * JSON number that can be interpreted as Java int. If this is not the case, an - * exception is thrown. - *

- * To be interpreted as Java int, the JSON number must neither contain an exponent - * nor a fraction part. Moreover, the number must be in the Integer range. - *

- * - * @return this value as int - * @throws UnsupportedOperationException if this value is not a JSON number - * @throws NumberFormatException if this JSON number can not be interpreted as int value - */ - public int asInt() { - throw new UnsupportedOperationException("Not a number: " + toString()); - } - - /** - * Returns this JSON value as a long value, assuming that this value represents a - * JSON number that can be interpreted as Java long. If this is not the case, an - * exception is thrown. - *

- * To be interpreted as Java long, the JSON number must neither contain an exponent - * nor a fraction part. Moreover, the number must be in the Long range. - *

- * - * @return this value as long - * @throws UnsupportedOperationException if this value is not a JSON number - * @throws NumberFormatException if this JSON number can not be interpreted as long value - */ - public long asLong() { - throw new UnsupportedOperationException(); - } - - /** - * Returns this JSON value as a float value, assuming that this value represents a - * JSON number. If this is not the case, an exception is thrown. - *

- * If the JSON number is out of the Float range, {@link Float#POSITIVE_INFINITY} or - * {@link Float#NEGATIVE_INFINITY} is returned. - *

- * - * @return this value as float - * @throws UnsupportedOperationException if this value is not a JSON number - */ - public float asFloat() { - throw new UnsupportedOperationException(); - } - - /** - * Returns this JSON value as a double value, assuming that this value represents a - * JSON number. If this is not the case, an exception is thrown. - *

- * If the JSON number is out of the Double range, {@link Double#POSITIVE_INFINITY} or - * {@link Double#NEGATIVE_INFINITY} is returned. - *

- * - * @return this value as double - * @throws UnsupportedOperationException if this value is not a JSON number - */ - public double asDouble() { - throw new UnsupportedOperationException(); - } - - /** - * Returns this JSON value as String, assuming that this value represents a JSON string. If this - * is not the case, an exception is thrown. - * - * @return the string represented by this value - * @throws UnsupportedOperationException if this value is not a JSON string - */ - public String asString() { - throw new UnsupportedOperationException(); - } - - /** - * Returns this JSON value as a boolean value, assuming that this value is either - * true or false. If this is not the case, an exception is thrown. - * - * @return this value as boolean - * @throws UnsupportedOperationException if this value is neither true or false - */ - public boolean asBoolean() { - throw new UnsupportedOperationException(); - } - - /** - * Returns the JSON string for this value in its minimal form, without any additional whitespace. - * - * @return a JSON string that represents this value - */ - @Override - public String toString() { - return toString(JsonWriterConfig.minimal()); - } - - /** - * Returns the JSON string for this value using the given formatting. - * - * @param config a configuration that controls the formatting or null for the minimal form - * @return a JSON string that represents this value - */ - public String toString(JsonWriterConfig config) { - StringWriter writer = new StringWriter(); - try { - write(config.createWriter(writer)); - } catch (IOException exception) { - // StringWriter does not throw IOException, so this is impossible - throw new JsonException(exception); - } - return writer.toString(); - } - - abstract void write(JsonWriter writer) throws IOException; - -} diff --git a/src/main/java/org/xbib/marc/json/JsonWriter.java b/src/main/java/org/xbib/marc/json/JsonWriter.java deleted file mode 100755 index d50beb4..0000000 --- a/src/main/java/org/xbib/marc/json/JsonWriter.java +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import java.io.IOException; -import java.io.Writer; - -/** - * - */ -public class JsonWriter { - - private static final int CONTROL_CHARACTERS_END = 0x001f; - - private static final char[] QUOT_CHARS = {'\\', '"'}; - private static final char[] BS_CHARS = {'\\', '\\'}; - private static final char[] LF_CHARS = {'\\', 'n'}; - private static final char[] CR_CHARS = {'\\', 'r'}; - private static final char[] TAB_CHARS = {'\\', 't'}; - // In JavaScript, U+2028 and U+2029 characters count as line endings and must be encoded. - // http://stackoverflow.com/questions/2965293/javascript-parse-error-on-u2028-unicode-character - private static final char[] UNICODE_2028_CHARS = {'\\', 'u', '2', '0', '2', '8'}; - private static final char[] UNICODE_2029_CHARS = {'\\', 'u', '2', '0', '2', '9'}; - private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'a', 'b', 'c', 'd', 'e', 'f'}; - - protected final Writer writer; - - public JsonWriter(Writer writer) { - this.writer = writer; - } - - protected void writeLiteral(String value) throws IOException { - writer.write(value); - } - - protected void writeNumber(String string) throws IOException { - writer.write(string); - } - - protected void writeString(String string) throws IOException { - writer.write('"'); - writeJsonString(string); - writer.write('"'); - } - - protected void writeArrayOpen() throws IOException { - writer.write('['); - } - - protected void writeArrayClose() throws IOException { - writer.write(']'); - } - - protected void writeArraySeparator() throws IOException { - writer.write(','); - } - - protected void writeObjectOpen() throws IOException { - writer.write('{'); - } - - protected void writeObjectClose() throws IOException { - writer.write('}'); - } - - protected void writeMemberName(String name) throws IOException { - writer.write('"'); - writeJsonString(name); - writer.write('"'); - } - - protected void writeMemberSeparator() throws IOException { - writer.write(':'); - } - - protected void writeObjectSeparator() throws IOException { - writer.write(','); - } - - private void writeJsonString(String string) throws IOException { - int length = string.length(); - int start = 0; - for (int index = 0; index < length; index++) { - char[] replacement = getReplacementChars(string.charAt(index)); - if (replacement != null) { - writer.write(string, start, index - start); - writer.write(replacement); - start = index + 1; - } - } - writer.write(string, start, length - start); - } - - private static char[] getReplacementChars(char ch) { - if (ch > '\\') { - if (ch < '\u2028' || ch > '\u2029') { - // The lower range contains 'a' .. 'z'. Only 2 checks required. - return null; - } - return ch == '\u2028' ? UNICODE_2028_CHARS : UNICODE_2029_CHARS; - } - if (ch == '\\') { - return BS_CHARS; - } - if (ch > '"') { - // This range contains '0' .. '9' and 'A' .. 'Z'. Need 3 checks to get here. - return null; - } - if (ch == '"') { - return QUOT_CHARS; - } - if (ch > CONTROL_CHARACTERS_END) { - return null; - } - if (ch == '\n') { - return LF_CHARS; - } - if (ch == '\r') { - return CR_CHARS; - } - if (ch == '\t') { - return TAB_CHARS; - } - return new char[]{'\\', 'u', '0', '0', HEX_DIGITS[ch >> 4 & 0x000f], HEX_DIGITS[ch & 0x000f]}; - } -} diff --git a/src/main/java/org/xbib/marc/json/JsonWriterConfig.java b/src/main/java/org/xbib/marc/json/JsonWriterConfig.java deleted file mode 100755 index 342eb2b..0000000 --- a/src/main/java/org/xbib/marc/json/JsonWriterConfig.java +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import java.io.IOException; -import java.io.Writer; -import java.util.Arrays; - -/** - * Controls the formatting of the JSON output. Use one of the available constants. - */ -@FunctionalInterface -public interface JsonWriterConfig { - - JsonWriter createWriter(Writer writer); - - /** - * Write JSON in its minimal form, without any additional whitespace. This is the default. - */ - static JsonWriterConfig minimal() { - return JsonWriter::new; - } - - /** - * Write JSON in pretty-print, with each value on a separate line and an indentation of two - * spaces. - */ - static JsonWriterConfig prettyPrint(int n) { - return new PrettyPrint(n); - } - - /** - * Enables human readable JSON output by inserting whitespace between values.after commas and - * colons. Example: - * - *
-     * jsonValue.writeTo(writer, WriterConfig.prettyPrint());
-     * 
- */ - class PrettyPrint implements JsonWriterConfig { - - private final char[] indentChars; - - PrettyPrint(char[] indentChars) { - this.indentChars = indentChars; - } - - /** - * Print every value on a separate line. Use the given number of spaces for indentation. - * - * @param number the number of spaces to use - */ - PrettyPrint(int number) { - this(fillChars(number)); - } - - private static char[] fillChars(int number) { - if (number < 0) { - throw new IllegalArgumentException("number is negative"); - } - char[] chars = new char[number]; - Arrays.fill(chars, ' '); - return chars; - } - - @Override - public JsonWriter createWriter(Writer writer) { - return new PrettyPrintWriter(writer, indentChars); - } - } - - class PrettyPrintWriter extends JsonWriter { - - private final char[] indentChars; - private int indent; - - private PrettyPrintWriter(Writer writer, char[] indentChars) { - super(writer); - this.indentChars = indentChars; - } - - @Override - protected void writeArrayOpen() throws IOException { - indent++; - writer.write('['); - writeNewLine(); - } - - @Override - protected void writeArrayClose() throws IOException { - indent--; - writeNewLine(); - writer.write(']'); - } - - @Override - protected void writeArraySeparator() throws IOException { - writer.write(','); - if (!writeNewLine()) { - writer.write(' '); - } - } - - @Override - protected void writeObjectOpen() throws IOException { - indent++; - writer.write('{'); - writeNewLine(); - } - - @Override - protected void writeObjectClose() throws IOException { - indent--; - writeNewLine(); - writer.write('}'); - } - - @Override - protected void writeMemberSeparator() throws IOException { - writer.write(':'); - writer.write(' '); - } - - @Override - protected void writeObjectSeparator() throws IOException { - writer.write(','); - if (!writeNewLine()) { - writer.write(' '); - } - } - - private boolean writeNewLine() throws IOException { - if (indentChars == null) { - return false; - } - writer.write('\n'); - for (int i = 0; i < indent; i++) { - writer.write(indentChars); - } - return true; - } - - } -} diff --git a/src/main/java/org/xbib/marc/json/MarcJsonWriter.java b/src/main/java/org/xbib/marc/json/MarcJsonWriter.java index 32c4129..1b7cc9e 100644 --- a/src/main/java/org/xbib/marc/json/MarcJsonWriter.java +++ b/src/main/java/org/xbib/marc/json/MarcJsonWriter.java @@ -67,7 +67,7 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo private Writer writer; - private JsonWriter jsonWriter; + private JsonBuilder jsonBuilder; private Marc.Builder builder; @@ -90,6 +90,7 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo private String index; private String indexType; + /** * Flag for indicating if writer is at top of file. */ @@ -111,7 +112,7 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo public MarcJsonWriter(Writer writer, int bufferSize) { this.writer = new BufferedWriter(writer, bufferSize); - this.jsonWriter = new JsonWriter(this.writer); + this.jsonBuilder = new JsonBuilder(this.writer); this.bufferSize = bufferSize; this.lock = new ReentrantLock(); this.builder = Marc.builder(); @@ -143,6 +144,10 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo return this; } + public JsonBuilder getJsonBuilder() { + return jsonBuilder; + } + public MarcJsonWriter setIndex(String index, String indexType) { this.index = index; this.indexType = indexType; @@ -182,14 +187,20 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo @Override public void startDocument() { - // nothing to do here + if (style.contains(Style.EMBEDDED_RECORD)) { + try { + jsonBuilder.beginMap(); + } catch (IOException e) { + handleException(e); + } + } } @Override public void beginCollection() { if (style.contains(Style.ARRAY)) { try { - jsonWriter.writeArrayOpen(); + jsonBuilder.beginCollection(); } catch (IOException e) { handleException(e); } @@ -257,13 +268,13 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo public void endCollection() { if (style.contains(Style.ARRAY)) { try { - jsonWriter.writeArrayClose(); + jsonBuilder.endCollection(); } catch (IOException e) { handleException(e); } } if (style.contains(Style.ELASTICSEARCH_BULK)) { - // finish with line-feed "\n", not with System.lineSeparator() + // finish with line-feed "\n", not with System.lineSeparator() !!! try { writer.write("\n"); } catch (IOException e) { @@ -279,13 +290,25 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo @Override public void endDocument() { - try { - flush(); - } catch (IOException e) { - handleException(e); + if (style.contains(Style.EMBEDDED_RECORD)) { + try { + jsonBuilder.endMap(); + } catch (IOException e) { + handleException(e); + } + } else { + try { + flush(); + } catch (IOException e) { + handleException(e); + } } } + public void write(Map map) { + + } + /** * Write MARC record using fields, indicators, and subfield structures, * therefore allowing duplicate keys in the output. @@ -303,67 +326,47 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo } } else { if (style.contains(Style.ARRAY)) { - jsonWriter.writeArraySeparator(); + writer.write(","); } else if (style.contains(Style.LINES)) { - jsonWriter.writeLiteral("\n"); + writer.append(System.lineSeparator()); } else if (style.contains(Style.ELASTICSEARCH_BULK)) { - jsonWriter.writeLiteral("\n"); + writer.append(System.lineSeparator()); writeMetaDataLine(marcRecord); } } - jsonWriter.writeObjectOpen(); + if (!style.contains(Style.EMBEDDED_RECORD)) { + jsonBuilder.beginMap(); + } if (marcRecord.getFormat() != null) { - jsonWriter.writeMemberName(FORMAT_TAG); - jsonWriter.writeMemberSeparator(); - jsonWriter.writeString(marcRecord.getFormat()); - jsonWriter.writeObjectSeparator(); + jsonBuilder.buildKey(FORMAT_TAG).buildValue(marcRecord.getFormat()); } if (marcRecord.getType() != null) { - jsonWriter.writeMemberName(TYPE_TAG); - jsonWriter.writeMemberSeparator(); - jsonWriter.writeString(marcRecord.getType()); - jsonWriter.writeObjectSeparator(); + jsonBuilder.buildKey(TYPE_TAG).buildValue(marcRecord.getType()); } if (!RecordLabel.EMPTY.equals(marcRecord.getRecordLabel())) { - jsonWriter.writeMemberName(LEADER_TAG); - jsonWriter.writeMemberSeparator(); - jsonWriter.writeString(marcRecord.getRecordLabel().toString()); - jsonWriter.writeObjectSeparator(); + jsonBuilder.buildKey(LEADER_TAG).buildValue(marcRecord.getRecordLabel().toString()); } - boolean fieldseparator = false; for (MarcField marcField : marcRecord.getFields()) { - if (fieldseparator) { - jsonWriter.writeObjectSeparator(); - } - jsonWriter.writeMemberName(marcField.getTag()); - jsonWriter.writeMemberSeparator(); + jsonBuilder.buildKey(marcField.getTag()); if (marcField.isControl()) { - jsonWriter.writeArrayOpen(); - jsonWriter.writeString(marcField.getValue()); - jsonWriter.writeArrayClose(); + jsonBuilder.buildValue(marcField.recoverControlFieldValue()); } else { - jsonWriter.writeObjectOpen(); - jsonWriter.writeMemberName(marcField.getIndicator()); - jsonWriter.writeMemberSeparator(); - jsonWriter.writeArrayOpen(); - boolean subfieldseparator = false; + jsonBuilder.beginMap(); + jsonBuilder.buildKey(marcField.getIndicator()); + jsonBuilder.beginCollection(); for (MarcField.Subfield subfield : marcField.getSubfields()) { - if (subfieldseparator) { - jsonWriter.writeObjectSeparator(); - } - jsonWriter.writeObjectOpen(); - jsonWriter.writeMemberName(subfield.getId()); - jsonWriter.writeMemberSeparator(); - jsonWriter.writeString(subfield.getValue()); - jsonWriter.writeObjectClose(); - subfieldseparator = true; + jsonBuilder.beginMap(); + jsonBuilder.buildKey(subfield.getId()); + jsonBuilder.buildValue(subfield.getValue()); + jsonBuilder.endMap(); } - jsonWriter.writeArrayClose(); - jsonWriter.writeObjectClose(); + jsonBuilder.endCollection(); + jsonBuilder.endMap(); } - fieldseparator = true; } - jsonWriter.writeObjectClose(); + if (!style.contains(Style.EMBEDDED_RECORD)) { + jsonBuilder.endMap(); + } } /** @@ -385,14 +388,16 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo if (style.contains(Style.ARRAY)) { writer.write(","); } else if (style.contains(Style.LINES)) { - writer.write("\n"); + writer.write(System.lineSeparator()); } else if (style.contains(Style.ELASTICSEARCH_BULK)) { - writer.write("\n"); + writer.write(System.lineSeparator()); writeMetaDataLine(marcRecord); } } StringBuilder sb = new StringBuilder(); - sb.append("{"); + if (!style.contains(Style.EMBEDDED_RECORD)) { + sb.append("{"); + } int c0 = 0; for (Map.Entry tags : marcRecord.entrySet()) { if (c0 > 0) { @@ -514,7 +519,9 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo } c0++; } - sb.append('}'); + if (!style.contains(Style.EMBEDDED_RECORD)) { + sb.append('}'); + } writer.write(sb.toString()); } @@ -561,7 +568,8 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo writer = new OutputStreamWriter(compress ? new CompressedOutputStream(out, bufferSize) : new BufferedOutputStream(out, bufferSize), StandardCharsets.UTF_8); - jsonWriter = new JsonWriter(writer); + //jsonWriter = new JsonWriter(writer); + jsonBuilder = new JsonBuilder(writer); } @SuppressWarnings("unchecked") @@ -582,7 +590,7 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo "\"_index\":\"" + index + "\"," + "\"_type\":\"" + indexType + "\"," + "\"_id\":\"" + id + "\"}}" + - "\n"); + System.lineSeparator()); } catch (IOException e) { handleException(e); } @@ -601,34 +609,20 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); switch (c) { - case '"': - sb.append("\\\""); - break; - case '\\': - sb.append("\\\\"); - break; - case '\b': - sb.append("\\b"); - break; - case '\f': - sb.append("\\f"); - break; - case '\n': - sb.append("\\n"); - break; - case '\r': - sb.append("\\r"); - break; - case '\t': - sb.append("\\t"); - break; - default: + case '"' -> sb.append("\\\""); + case '\\' -> sb.append("\\\\"); + case '\b' -> sb.append("\\b"); + case '\f' -> sb.append("\\f"); + case '\n' -> sb.append("\\n"); + case '\r' -> sb.append("\\r"); + case '\t' -> sb.append("\\t"); + default -> { if (c < 0x1f) { sb.append("\\u").append(String.format("%04x", (int) c)); } else { sb.append(c); } - break; + } } } return sb.toString(); @@ -638,7 +632,11 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo * */ public enum Style { - ARRAY, LINES, ELASTICSEARCH_BULK, ALLOW_DUPLICATES + ARRAY, + LINES, + ELASTICSEARCH_BULK, + ALLOW_DUPLICATES, + EMBEDDED_RECORD } /** @@ -651,5 +649,4 @@ public class MarcJsonWriter extends MarcContentHandler implements Flushable, Clo def.setLevel(Deflater.BEST_COMPRESSION); } } - } diff --git a/src/main/java/org/xbib/marc/xml/MarcXchangeWriter.java b/src/main/java/org/xbib/marc/xml/MarcXchangeWriter.java index 0c3a67f..5fc4014 100644 --- a/src/main/java/org/xbib/marc/xml/MarcXchangeWriter.java +++ b/src/main/java/org/xbib/marc/xml/MarcXchangeWriter.java @@ -350,19 +350,7 @@ public class MarcXchangeWriter extends MarcContentHandler implements Flushable, return; } if (field.isControl()) { - String value = field.getValue(); - if (value == null || value.isEmpty()) { - // the control field is disguised as a data field, try lookup value in first subfield of "_" - value = field.getFirstSubfieldValue("_"); - // if no value, maybe " "? - if (value == null || value.isEmpty()) { - value = field.getFirstSubfieldValue(" "); - } - // still no value? Then it is some exotic like MAB with subfield "a"? - if (value == null || value.isEmpty()) { - value = field.getFirstSubfieldValue("a"); - } - } + String value = field.recoverControlFieldValue(); if (value != null && !value.isEmpty()) { Iterator attrs = Collections.singletonList(eventFactory.createAttribute(TAG_ATTRIBUTE, transform(field.getTag()))).iterator(); diff --git a/src/test/java/org/xbib/marc/json/JsonArrayTest.java b/src/test/java/org/xbib/marc/json/JsonArrayTest.java deleted file mode 100755 index 1f44a17..0000000 --- a/src/test/java/org/xbib/marc/json/JsonArrayTest.java +++ /dev/null @@ -1,522 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.InOrder; -import java.io.IOException; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.List; - -public class JsonArrayTest { - - private JsonArray array; - - private static JsonArray array(String... values) { - JsonArray array = new JsonArray(); - for (String value : values) { - array.add(value); - } - return array; - } - - @BeforeEach - public void setUp() { - array = new JsonArray(); - } - - @Test - public void copyConstructorFailsWithNull() { - Assertions.assertThrows(NullPointerException.class, () -> new JsonArray(null)); - } - - @Test - public void copyConstructorHasSameValues() { - array.add(23); - JsonArray copy = new JsonArray(array); - assertEquals(array.values(), copy.values()); - } - - @Test - public void copyConstructorworksOnSafeCopy() { - JsonArray copy = new JsonArray(array); - array.add(23); - assertTrue(copy.isEmpty()); - } - - @Test - public void isEmptyisTrueAfterCreation() { - assertTrue(array.isEmpty()); - } - - @Test - public void isEmptyisFalseAfterAdd() { - array.add(true); - assertFalse(array.isEmpty()); - } - - @Test - public void sizeisZeroAfterCreation() { - assertEquals(0, array.size()); - } - - @Test - public void sizeisOneAfterAdd() { - array.add(true); - assertEquals(1, array.size()); - } - - @Test - public void iteratorisEmptyAfterCreation() { - assertFalse(array.iterator().hasNext()); - } - - @Test - public void iteratorhasNextAfterAdd() { - array.add(true); - Iterator iterator = array.iterator(); - assertTrue(iterator.hasNext()); - assertEquals(JsonLiteral.TRUE, iterator.next()); - assertFalse(iterator.hasNext()); - } - - @Test - public void iteratordoesNotAllowModification() { - Assertions.assertThrows(UnsupportedOperationException.class, () -> { - array.add(23); - Iterator iterator = array.iterator(); - iterator.next(); - iterator.remove(); - }); - } - - @Test - public void iteratordetectsConcurrentModification() { - Assertions.assertThrows(ConcurrentModificationException.class, () -> { - Iterator iterator = array.iterator(); - array.add(23); - iterator.next(); - }); - } - - @Test - public void valuesisEmptyAfterCreation() { - assertTrue(array.values().isEmpty()); - } - - @Test - public void valuescontainsValueAfterAdd() { - array.add(true); - assertEquals(1, array.values().size()); - assertEquals(JsonLiteral.TRUE, array.values().get(0)); - } - - @Test - public void valuesReflectsChanges() { - List values = array.values(); - array.add(true); - assertEquals(array.values(), values); - } - - @Test - public void valuesPreventsModification() { - List values = array.values(); - values.add(JsonLiteral.TRUE); - } - - @Test - public void getreturnsValue() { - array.add(23); - JsonValue value = array.get(0); - assertEquals(Json.of(23), value); - } - - @Test - public void getfailsWithInvalidIndex() { - Assertions.assertThrows(IndexOutOfBoundsException.class, () -> array.get(0)); - } - - @Test - public void addint() { - array.add(23); - assertEquals("[23]", array.toString()); - } - - @Test - public void addintenablesChaining() { - assertSame(array, array.add(23)); - } - - @Test - public void addlong() { - array.add(23L); - assertEquals("[23]", array.toString()); - } - - @Test - public void addlongenablesChaining() { - assertSame(array, array.add(23L)); - } - - @Test - public void addfloat() { - array.add(3.14f); - assertEquals("[3.14]", array.toString()); - } - - @Test - public void addfloatenablesChaining() { - assertSame(array, array.add(3.14f)); - } - - @Test - public void adddouble() { - array.add(3.14d); - assertEquals("[3.14]", array.toString()); - } - - @Test - public void adddoubleenablesChaining() { - assertSame(array, array.add(3.14d)); - } - - @Test - public void addboolean() { - array.add(true); - assertEquals("[true]", array.toString()); - } - - @Test - public void addbooleanenablesChaining() { - assertSame(array, array.add(true)); - } - - @Test - public void addstring() { - array.add("foo"); - assertEquals("[\"foo\"]", array.toString()); - } - - @Test - public void addstringenablesChaining() { - assertSame(array, array.add("foo")); - } - - @Test - public void addstringtoleratesNull() { - array.add((String) null); - assertEquals("[null]", array.toString()); - } - - @Test - public void addjsonNull() { - array.add(JsonLiteral.NULL); - assertEquals("[null]", array.toString()); - } - - @Test - public void addjsonArray() { - array.add(new JsonArray()); - assertEquals("[[]]", array.toString()); - } - - @Test - public void addjsonObject() { - array.add(new JsonObject()); - assertEquals("[{}]", array.toString()); - } - - @Test - public void addjsonenablesChaining() { - assertSame(array, array.add(JsonLiteral.NULL)); - } - - @Test - public void addjsonfailsWithNull() { - Assertions.assertThrows(NullPointerException.class, () -> array.add((JsonValue) null)); - } - - @Test - public void addjsonnestedArray() { - JsonArray innerArray = new JsonArray(); - innerArray.add(23); - array.add(innerArray); - assertEquals("[[23]]", array.toString()); - } - - @Test - public void addjsonnestedArraymodifiedAfterAdd() { - JsonArray innerArray = new JsonArray(); - array.add(innerArray); - innerArray.add(23); - assertEquals("[[23]]", array.toString()); - } - - @Test - public void addjsonnestedObject() { - JsonObject innerObject = new JsonObject(); - innerObject.add("a", 23); - array.add(innerObject); - assertEquals("[{\"a\":23}]", array.toString()); - } - - @Test - public void addjsonnestedObjectmodifiedAfterAdd() { - JsonObject innerObject = new JsonObject(); - array.add(innerObject); - innerObject.add("a", 23); - assertEquals("[{\"a\":23}]", array.toString()); - } - - @Test - public void setint() { - array.add(false); - array.set(0, 23); - assertEquals("[23]", array.toString()); - } - - @Test - public void setintenablesChaining() { - array.add(false); - assertSame(array, array.set(0, 23)); - } - - @Test - public void setlong() { - array.add(false); - array.set(0, 23L); - assertEquals("[23]", array.toString()); - } - - @Test - public void setlongenablesChaining() { - array.add(false); - assertSame(array, array.set(0, 23L)); - } - - @Test - public void setfloat() { - array.add(false); - array.set(0, 3.14f); - assertEquals("[3.14]", array.toString()); - } - - @Test - public void setfloatenablesChaining() { - array.add(false); - assertSame(array, array.set(0, 3.14f)); - } - - @Test - public void setdouble() { - array.add(false); - array.set(0, 3.14d); - assertEquals("[3.14]", array.toString()); - } - - @Test - public void setdoubleenablesChaining() { - array.add(false); - assertSame(array, array.set(0, 3.14d)); - } - - @Test - public void setboolean() { - array.add(false); - array.set(0, true); - assertEquals("[true]", array.toString()); - } - - @Test - public void setbooleanenablesChaining() { - array.add(false); - assertSame(array, array.set(0, true)); - } - - @Test - public void setstring() { - array.add(false); - array.set(0, "foo"); - assertEquals("[\"foo\"]", array.toString()); - } - - @Test - public void setstringenablesChaining() { - array.add(false); - assertSame(array, array.set(0, "foo")); - } - - @Test - public void setjsonNull() { - array.add(false); - array.set(0, JsonLiteral.NULL); - assertEquals("[null]", array.toString()); - } - - @Test - public void setjsonArray() { - array.add(false); - array.set(0, new JsonArray()); - assertEquals("[[]]", array.toString()); - } - - @Test - public void setjsonObject() { - array.add(false); - array.set(0, new JsonObject()); - assertEquals("[{}]", array.toString()); - } - - @Test - public void setJsonFailsWithNull() { - array.add(false); - Assertions.assertThrows(NullPointerException.class, () -> - array.set(0, (JsonValue) null)); - } - - @Test - public void setjsonfailsWithInvalidIndex() { - Assertions.assertThrows(IndexOutOfBoundsException.class, () -> - array.set(0, JsonLiteral.NULL)); - } - - @Test - public void setjsonenablesChaining() { - array.add(false); - assertSame(array, array.set(0, JsonLiteral.NULL)); - } - - @Test - public void setjsonreplacesDifferntArrayElements() { - array.add(3).add(6).add(9); - array.set(1, 4).set(2, 5); - assertEquals("[3,4,5]", array.toString()); - } - - @Test - public void removefailsWithInvalidIndex() { - Assertions.assertThrows(IndexOutOfBoundsException.class, () -> - array.remove(0) - ); - } - - @Test - public void removeremovesElement() { - array.add(23); - array.remove(0); - assertEquals("[]", array.toString()); - } - - @Test - public void removekeepsOtherElements() { - array.add("a").add("b").add("c"); - array.remove(1); - assertEquals("[\"a\",\"c\"]", array.toString()); - } - - @Test - public void writeempty() throws IOException { - JsonWriter writer = mock(JsonWriter.class); - array.write(writer); - InOrder inOrder = inOrder(writer); - inOrder.verify(writer).writeArrayOpen(); - inOrder.verify(writer).writeArrayClose(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void writewithSingleValue() throws IOException { - JsonWriter writer = mock(JsonWriter.class); - array.add(23); - array.write(writer); - InOrder inOrder = inOrder(writer); - inOrder.verify(writer).writeArrayOpen(); - inOrder.verify(writer).writeNumber("23"); - inOrder.verify(writer).writeArrayClose(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void writewithMultipleValues() throws IOException { - JsonWriter writer = mock(JsonWriter.class); - array.add(23).add("foo").add(false); - array.write(writer); - InOrder inOrder = inOrder(writer); - inOrder.verify(writer).writeArrayOpen(); - inOrder.verify(writer).writeNumber("23"); - inOrder.verify(writer).writeArraySeparator(); - inOrder.verify(writer).writeString("foo"); - inOrder.verify(writer).writeArraySeparator(); - inOrder.verify(writer).writeLiteral("false"); - inOrder.verify(writer).writeArrayClose(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void isArray() { - assertTrue(array.isArray()); - } - - @Test - public void asArray() { - assertSame(array, array.asArray()); - } - - @Test - public void equalstrueForEqualArrays() { - assertEquals(array(), array()); - assertEquals(array("foo", "bar"), array("foo", "bar")); - } - - @Test - public void equalsfalseForDifferentArrays() { - assertNotEquals(array("foo", "bar"), array("foo", "bar", "baz")); - assertNotEquals(array("foo", "bar"), array("bar", "foo")); - } - - @Test - public void equalsfalseForNull() { - assertNotEquals(null, array); - } - - @Test - public void equalsfalseForSubclass() { - assertNotEquals(array, new JsonArray(array) { - }); - } - - @Test - public void hashCodeequalsForEqualArrays() { - assertEquals(array().hashCode(), array().hashCode()); - assertEquals(array("foo").hashCode(), array("foo").hashCode()); - } - - @Test - public void hashCodediffersForDifferentArrays() { - assertNotEquals(array().hashCode(), array("bar").hashCode()); - assertNotEquals(array("foo").hashCode(), array("bar").hashCode()); - } -} diff --git a/src/test/java/org/xbib/marc/json/JsonWriterTest.java b/src/test/java/org/xbib/marc/json/JsonBuilderTest.java similarity index 63% rename from src/test/java/org/xbib/marc/json/JsonWriterTest.java rename to src/test/java/org/xbib/marc/json/JsonBuilderTest.java index 28b5204..d991624 100755 --- a/src/test/java/org/xbib/marc/json/JsonWriterTest.java +++ b/src/test/java/org/xbib/marc/json/JsonBuilderTest.java @@ -15,17 +15,17 @@ */ package org.xbib.marc.json; -import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.StringWriter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; -public class JsonWriterTest { +public class JsonBuilderTest { private StringWriter output; - private JsonWriter writer; + private JsonBuilder jsonBuilder; private static String string(char... chars) { return String.valueOf(chars); @@ -34,125 +34,110 @@ public class JsonWriterTest { @BeforeEach public void setUp() { output = new StringWriter(); - writer = new JsonWriter(output); + jsonBuilder = new JsonBuilder(output); } @Test public void writeLiteral() throws IOException { - writer.writeLiteral("foo"); - assertEquals("foo", output.toString()); + jsonBuilder.buildValue("foo"); + assertEquals("\"foo\"", output.toString()); } @Test public void writeNumber() throws IOException { - writer.writeNumber("23"); + jsonBuilder.buildValue(23); assertEquals("23", output.toString()); } @Test - public void writeStringempty() throws IOException { - writer.writeString(""); + public void writeStringEmpty() throws IOException { + jsonBuilder.buildValue(""); assertEquals("\"\"", output.toString()); } @Test public void writeStingescapesBackslashes() throws IOException { - writer.writeString("foo\\bar"); + jsonBuilder.buildValue("foo\\bar"); assertEquals("\"foo\\\\bar\"", output.toString()); } @Test - public void writeArrayParts() throws IOException { - writer.writeArrayOpen(); - writer.writeArraySeparator(); - writer.writeArrayClose(); - assertEquals("[,]", output.toString()); + public void writeEmptyArray() throws IOException { + jsonBuilder.beginCollection(); + jsonBuilder.endCollection(); + assertEquals("[]", output.toString()); } @Test - public void writeObjectParts() throws IOException { - writer.writeObjectOpen(); - writer.writeMemberSeparator(); - writer.writeObjectSeparator(); - writer.writeObjectClose(); - assertEquals("{:,}", output.toString()); - } - - @Test - public void writeMemberNameempty() throws IOException { - writer.writeMemberName(""); - assertEquals("\"\"", output.toString()); - } - - @Test - public void writeMemberNameescapesBackslashes() throws IOException { - writer.writeMemberName("foo\\bar"); - assertEquals("\"foo\\\\bar\"", output.toString()); + public void writeEmptyObject() throws IOException { + jsonBuilder.beginMap(); + jsonBuilder.endMap(); + assertEquals("{}", output.toString()); } @Test public void escapesQuotes() throws IOException { - writer.writeString("a\"b"); + jsonBuilder.buildValue("a\"b"); assertEquals("\"a\\\"b\"", output.toString()); } @Test public void escapesEscapedQuotes() throws IOException { - writer.writeString("foo\\\"bar"); + jsonBuilder.buildValue("foo\\\"bar"); assertEquals("\"foo\\\\\\\"bar\"", output.toString()); } @Test public void escapesNewLine() throws IOException { - writer.writeString("foo\nbar"); + jsonBuilder.buildValue("foo\nbar"); assertEquals("\"foo\\nbar\"", output.toString()); } @Test public void escapesWindowsNewLine() throws IOException { - writer.writeString("foo\r\nbar"); + jsonBuilder.buildValue("foo\r\nbar"); assertEquals("\"foo\\r\\nbar\"", output.toString()); } @Test public void escapesTabs() throws IOException { - writer.writeString("foo\tbar"); + jsonBuilder.buildValue("foo\tbar"); assertEquals("\"foo\\tbar\"", output.toString()); } @Test public void escapesSpecialCharacters() throws IOException { - writer.writeString("foo\u2028bar\u2029"); + jsonBuilder.buildValue("foo\u2028bar\u2029"); assertEquals("\"foo\\u2028bar\\u2029\"", output.toString()); } @Test public void escapesZeroCharacter() throws IOException { - writer.writeString(string('f', 'o', 'o', (char) 0, 'b', 'a', 'r')); + jsonBuilder.buildValue(string('f', 'o', 'o', (char) 0, 'b', 'a', 'r')); assertEquals("\"foo\\u0000bar\"", output.toString()); } @Test public void escapesEscapeCharacter() throws IOException { - writer.writeString(string('f', 'o', 'o', (char) 27, 'b', 'a', 'r')); + jsonBuilder.buildValue(string('f', 'o', 'o', (char) 27, 'b', 'a', 'r')); assertEquals("\"foo\\u001bbar\"", output.toString()); } @Test public void escapesControlCharacters() throws IOException { - writer.writeString(string((char) 1, (char) 8, (char) 15, (char) 16, (char) 31)); + jsonBuilder.buildValue(string((char) 1, (char) 8, (char) 15, (char) 16, (char) 31)); assertEquals("\"\\u0001\\u0008\\u000f\\u0010\\u001f\"", output.toString()); } @Test public void escapesFirstChar() throws IOException { - writer.writeString(string('\\', 'x')); + jsonBuilder.buildValue(string('\\', 'x')); assertEquals("\"\\\\x\"", output.toString()); } @Test public void escapesLastChar() throws IOException { - writer.writeString(string('x', '\\')); + jsonBuilder.buildValue(string('x', '\\')); assertEquals("\"x\\\\\"", output.toString()); } } diff --git a/src/test/java/org/xbib/marc/json/JsonLiteralTest.java b/src/test/java/org/xbib/marc/json/JsonLiteralTest.java deleted file mode 100755 index c8d5d0b..0000000 --- a/src/test/java/org/xbib/marc/json/JsonLiteralTest.java +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.xbib.marc.json.JsonLiteral.FALSE; -import static org.xbib.marc.json.JsonLiteral.NULL; -import static org.xbib.marc.json.JsonLiteral.TRUE; -import org.junit.jupiter.api.Test; -import java.io.IOException; - -public class JsonLiteralTest { - - @Test - public void isNull() { - assertTrue(NULL.isNull()); - assertFalse(TRUE.isNull()); - assertFalse(FALSE.isNull()); - } - - @Test - public void isTrue() { - assertTrue(TRUE.isTrue()); - assertFalse(NULL.isTrue()); - assertFalse(FALSE.isTrue()); - } - - @Test - public void isFalse() { - assertTrue(FALSE.isFalse()); - assertFalse(NULL.isFalse()); - assertFalse(TRUE.isFalse()); - } - - @Test - public void isBoolean() { - assertTrue(TRUE.isBoolean()); - assertTrue(FALSE.isBoolean()); - assertFalse(NULL.isBoolean()); - } - - @Test - public void nullwrite() throws IOException { - JsonWriter writer = mock(JsonWriter.class); - NULL.write(writer); - verify(writer).writeLiteral("null"); - verifyNoMoreInteractions(writer); - } - - @Test - public void truewrite() throws IOException { - JsonWriter writer = mock(JsonWriter.class); - TRUE.write(writer); - verify(writer).writeLiteral("true"); - verifyNoMoreInteractions(writer); - } - - @Test - public void falsewrite() throws IOException { - JsonWriter writer = mock(JsonWriter.class); - FALSE.write(writer); - verify(writer).writeLiteral("false"); - verifyNoMoreInteractions(writer); - } - - @Test - public void nulltoString() { - assertEquals("null", NULL.toString()); - } - - @Test - public void truetoString() { - assertEquals("true", TRUE.toString()); - } - - @Test - public void falsetoString() { - assertEquals("false", FALSE.toString()); - } - - @Test - public void nullequals() { - assertEquals(NULL, NULL); - assertNotEquals(null, NULL); - assertNotEquals(NULL, TRUE); - assertNotEquals(NULL, FALSE); - assertNotEquals(NULL, Json.of("null")); - } - - @Test - public void trueequals() { - assertEquals(TRUE, TRUE); - assertNotEquals(null, TRUE); - assertNotEquals(TRUE, FALSE); - assertNotEquals(TRUE, Boolean.TRUE); - assertNotEquals(NULL, Json.of("true")); - } - - @Test - public void falseequals() { - assertEquals(FALSE, FALSE); - assertNotEquals(null, FALSE); - assertNotEquals(FALSE, TRUE); - assertNotEquals(FALSE, Boolean.FALSE); - assertNotEquals(NULL, Json.of("false")); - } -} diff --git a/src/test/java/org/xbib/marc/json/JsonNumberTest.java b/src/test/java/org/xbib/marc/json/JsonNumberTest.java deleted file mode 100755 index 5962344..0000000 --- a/src/test/java/org/xbib/marc/json/JsonNumberTest.java +++ /dev/null @@ -1,172 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import java.io.IOException; -import java.io.StringWriter; - -public class JsonNumberTest { - - private StringWriter output; - - private JsonWriter writer; - - @BeforeEach - public void setUp() { - output = new StringWriter(); - writer = new JsonWriter(output); - } - - @Test - public void constructorfailsWithNull() { - Assertions.assertThrows(NullPointerException.class, () -> new JsonNumber(null)); - } - - @Test - public void write() throws IOException { - new JsonNumber("23").write(writer); - assertEquals("23", output.toString()); - } - - @Test - public void toStringreturnsInputString() { - assertEquals("foo", new JsonNumber("foo").toString()); - } - - @Test - public void isInt() { - assertTrue(new JsonNumber("23").isInt()); - } - - @Test - public void asInt() { - assertEquals(23, new JsonNumber("23").asInt()); - } - - @Test - public void asIntfailsWithExceedingValues() { - Assertions.assertThrows(NumberFormatException.class, () -> { - new JsonNumber("10000000000").asInt(); - }); - } - - @Test - public void asIntfailsWithExponent() { - Assertions.assertThrows(NumberFormatException.class, () -> { - new JsonNumber("1e5").asInt(); - }); - } - - @Test - public void asIntfailsWithFractional() { - Assertions.assertThrows(NumberFormatException.class, () -> { - new JsonNumber("23.5").asInt(); - }); - } - - @Test - public void asLong() { - assertEquals(23L, new JsonNumber("23").asLong()); - } - - @Test - public void asLongfailsWithExceedingValues() { - Assertions.assertThrows(NumberFormatException.class, () -> { - new JsonNumber("10000000000000000000").asLong(); - }); - } - - @Test - public void asLongfailsWithExponent() { - Assertions.assertThrows(NumberFormatException.class, () -> { - new JsonNumber("1e5").asLong(); - }); - } - - @Test - public void asLongfailsWithFractional() { - Assertions.assertThrows(NumberFormatException.class, () -> { - new JsonNumber("23.5").asLong(); - }); - } - - @Test - public void asFloat() { - assertEquals(23.05f, new JsonNumber("23.05").asFloat(), 0); - } - - @Test - public void asFloatreturnsInfinityForExceedingValues() { - assertEquals(Float.POSITIVE_INFINITY, new JsonNumber("1e50").asFloat(), 0); - assertEquals(Float.NEGATIVE_INFINITY, new JsonNumber("-1e50").asFloat(), 0); - } - - @Test - public void asDouble() { - double result = new JsonNumber("23.05").asDouble(); - assertEquals(23.05, result, 0); - } - - @Test - public void asDoublereturnsInfinityForExceedingValues() { - assertEquals(Double.POSITIVE_INFINITY, new JsonNumber("1e500").asDouble(), 0); - assertEquals(Double.NEGATIVE_INFINITY, new JsonNumber("-1e500").asDouble(), 0); - } - - @Test - public void equalstrueForSameInstance() { - JsonNumber number = new JsonNumber("23"); - assertEquals(number, number); - } - - @Test - public void equalstrueForEqualNumberStrings() { - assertEquals(new JsonNumber("23"), new JsonNumber("23")); - } - - @Test - public void equalsfalseForDifferentNumberStrings() { - assertNotEquals(new JsonNumber("23"), new JsonNumber("42")); - assertNotEquals(new JsonNumber("1e+5"), new JsonNumber("1e5")); - } - - @Test - public void equalsfalseForNull() { - assertNotEquals(null, new JsonNumber("23")); - } - - @Test - public void equalsfalseForSubclass() { - assertNotEquals(new JsonNumber("23"), new JsonNumber("23") { - }); - } - - @Test - public void hashCodeequalsForEqualStrings() { - assertEquals(new JsonNumber("23").hashCode(), new JsonNumber("23").hashCode()); - } - - @Test - public void hashCodediffersForDifferentStrings() { - assertNotEquals(new JsonNumber("23").hashCode(), new JsonNumber("42").hashCode()); - } -} diff --git a/src/test/java/org/xbib/marc/json/JsonObjectTest.java b/src/test/java/org/xbib/marc/json/JsonObjectTest.java deleted file mode 100755 index f1afa1e..0000000 --- a/src/test/java/org/xbib/marc/json/JsonObjectTest.java +++ /dev/null @@ -1,925 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.InOrder; -import java.io.IOException; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.List; -import java.util.NoSuchElementException; - -public class JsonObjectTest { - - private JsonObject object; - - private static JsonObject object(String... namesAndValues) { - JsonObject object = new JsonObject(); - for (int i = 0; i < namesAndValues.length; i += 2) { - object.add(namesAndValues[i], namesAndValues[i + 1]); - } - return object; - } - - @BeforeEach - public void setUp() { - object = new JsonObject(); - } - - @Test - public void copyConstructorfailsWithNull() { - Assertions.assertThrows(NullPointerException.class, () -> new JsonObject(null)); - } - - @Test - public void copyConstructorhasSameValues() { - object.add("foo", 23); - JsonObject copy = new JsonObject(object); - assertEquals(object.names(), copy.names()); - assertSame(object.get("foo"), copy.get("foo")); - } - - @Test - public void copyConstructorworksOnSafeCopy() { - JsonObject copy = new JsonObject(object); - object.add("foo", 23); - assertTrue(copy.isEmpty()); - } - - @Test - public void isEmptytrueAfterCreation() { - assertTrue(object.isEmpty()); - } - - @Test - public void isEmptyfalseAfterAdd() { - object.add("a", true); - assertFalse(object.isEmpty()); - } - - @Test - public void sizezeroAfterCreation() { - assertEquals(0, object.size()); - } - - @Test - public void sizeoneAfterAdd() { - object.add("a", true); - assertEquals(1, object.size()); - } - - @Test - public void keyRepetitionallowsMultipleEntries() { - object.add("a", true); - object.add("a", "value"); - assertEquals(2, object.size()); - } - - @Test - public void keyRepetitiongetsLastEntry() { - object.add("a", true); - object.add("a", "value"); - assertEquals("value", object.getString("a", "missing")); - } - - @Test - public void keyRepetitionequalityConsidersRepetitions() { - object.add("a", true); - object.add("a", "value"); - JsonObject onlyFirstProperty = new JsonObject(); - onlyFirstProperty.add("a", true); - assertNotEquals(onlyFirstProperty, object); - JsonObject bothProperties = new JsonObject(); - bothProperties.add("a", true); - bothProperties.add("a", "value"); - assertEquals(bothProperties, object); - } - - @Test - public void namesemptyAfterCreation() { - assertTrue(object.names().isEmpty()); - } - - @Test - public void namescontainsNameAfterAdd() { - object.add("foo", true); - List names = object.names(); - assertEquals(1, names.size()); - assertEquals("foo", names.get(0)); - } - - @Test - public void namesreflectsChanges() { - List names = object.names(); - object.add("foo", true); - assertEquals(1, names.size()); - assertEquals("foo", names.get(0)); - } - - @Test - public void namespreventsModification() { - Assertions.assertThrows(UnsupportedOperationException.class, () -> { - List names = object.names(); - names.add("foo"); - }); - } - - @Test - public void iteratorisEmptyAfterCreation() { - assertFalse(object.iterator().hasNext()); - } - - @Test - public void iteratorhasNextAfterAdd() { - object.add("a", true); - Iterator iterator = object.iterator(); - assertTrue(iterator.hasNext()); - } - - @Test - public void iteratornextReturnsActualValue() { - object.add("a", true); - Iterator iterator = object.iterator(); - assertEquals(new JsonObject.Member("a", JsonLiteral.TRUE), iterator.next()); - } - - @Test - public void iteratornextProgressesToNextValue() { - object.add("a", true); - object.add("b", false); - Iterator iterator = object.iterator(); - iterator.next(); - assertTrue(iterator.hasNext()); - assertEquals(new JsonObject.Member("b", JsonLiteral.FALSE), iterator.next()); - } - - @Test - public void iteratornextFailsAtEnd() { - Assertions.assertThrows(NoSuchElementException.class, () -> { - Iterator iterator = object.iterator(); - iterator.next(); - - }); - } - - @Test - public void iteratordoesNotAllowModification() { - Assertions.assertThrows(UnsupportedOperationException.class, () -> { - object.add("a", 23); - Iterator iterator = object.iterator(); - iterator.next(); - iterator.remove(); - }); - } - - @Test - public void iteratordetectsConcurrentModification() { - Assertions.assertThrows(ConcurrentModificationException.class, () -> { - Iterator iterator = object.iterator(); - object.add("a", 23); - iterator.next(); - }); - } - - @Test - public void getfailsWithNullName() { - Assertions.assertThrows(NullPointerException.class, () -> object.get(null)); - } - - @Test - public void getreturnsNullForNonExistingMember() { - assertNull(object.get("foo")); - } - - @Test - public void getreturnsValueForName() { - object.add("foo", true); - assertEquals(JsonLiteral.TRUE, object.get("foo")); - } - - @Test - public void getreturnsLastValueForName() { - object.add("foo", false).add("foo", true); - assertEquals(JsonLiteral.TRUE, object.get("foo")); - } - - @Test - public void getintreturnsValueFromMember() { - object.add("foo", 23); - - assertEquals(23, object.getInt("foo", 42)); - } - - @Test - public void getintreturnsDefaultForMissingMember() { - assertEquals(23, object.getInt("foo", 23)); - } - - @Test - public void getlongreturnsValueFromMember() { - object.add("foo", 23L); - - assertEquals(23L, object.getLong("foo", 42L)); - } - - @Test - public void getlongreturnsDefaultForMissingMember() { - assertEquals(23L, object.getLong("foo", 23L)); - } - - @Test - public void getfloatreturnsValueFromMember() { - object.add("foo", 3.14f); - - assertEquals(3.14f, object.getFloat("foo", 1.41f), 0); - } - - @Test - public void getfloatreturnsDefaultForMissingMember() { - assertEquals(3.14f, object.getFloat("foo", 3.14f), 0); - } - - @Test - public void getdoublereturnsValueFromMember() { - object.add("foo", 3.14); - - assertEquals(3.14, object.getDouble("foo", 1.41), 0); - } - - @Test - public void getdoublereturnsDefaultForMissingMember() { - assertEquals(3.14, object.getDouble("foo", 3.14), 0); - } - - @Test - public void getbooleanreturnsValueFromMember() { - object.add("foo", true); - - assertTrue(object.getBoolean("foo", false)); - } - - @Test - public void getbooleanreturnsDefaultForMissingMember() { - assertFalse(object.getBoolean("foo", false)); - } - - @Test - public void getstringreturnsValueFromMember() { - object.add("foo", "bar"); - - assertEquals("bar", object.getString("foo", "default")); - } - - @Test - public void getstringreturnsDefaultForMissingMember() { - assertEquals("default", object.getString("foo", "default")); - } - - @Test - public void addfailsWithNullName() { - Assertions.assertThrows(NullPointerException.class, () -> object.add(null, 23)); - } - - @Test - public void addint() { - object.add("a", 23); - assertEquals("{\"a\":23}", object.toString()); - } - - @Test - public void addintenablesChaining() { - assertSame(object, object.add("a", 23)); - } - - @Test - public void addlong() { - object.add("a", 23L); - assertEquals("{\"a\":23}", object.toString()); - } - - @Test - public void addlongenablesChaining() { - assertSame(object, object.add("a", 23L)); - } - - @Test - public void addfloat() { - object.add("a", 3.14f); - assertEquals("{\"a\":3.14}", object.toString()); - } - - @Test - public void addfloatenablesChaining() { - assertSame(object, object.add("a", 3.14f)); - } - - @Test - public void adddouble() { - object.add("a", 3.14d); - assertEquals("{\"a\":3.14}", object.toString()); - } - - @Test - public void adddoubleenablesChaining() { - assertSame(object, object.add("a", 3.14d)); - } - - @Test - public void addboolean() { - object.add("a", true); - assertEquals("{\"a\":true}", object.toString()); - } - - @Test - public void addbooleanenablesChaining() { - assertSame(object, object.add("a", true)); - } - - @Test - public void addstring() { - object.add("a", "foo"); - assertEquals("{\"a\":\"foo\"}", object.toString()); - } - - @Test - public void addstringtoleratesNull() { - object.add("a", (String) null); - assertEquals("{\"a\":null}", object.toString()); - } - - @Test - public void addstringenablesChaining() { - assertSame(object, object.add("a", "foo")); - } - - @Test - public void addjsonNull() { - object.add("a", JsonLiteral.NULL); - assertEquals("{\"a\":null}", object.toString()); - } - - @Test - public void addjsonArray() { - object.add("a", new JsonArray()); - assertEquals("{\"a\":[]}", object.toString()); - } - - @Test - public void addjsonObject() { - object.add("a", new JsonObject()); - assertEquals("{\"a\":{}}", object.toString()); - } - - @Test - public void addjsonenablesChaining() { - assertSame(object, object.add("a", JsonLiteral.NULL)); - } - - @Test - public void addjsonfailsWithNull() { - Assertions.assertThrows(NullPointerException.class, () -> - object.add("a", (JsonValue) null)); - } - - @Test - public void addjsonnestedArray() { - JsonArray innerArray = new JsonArray(); - innerArray.add(23); - object.add("a", innerArray); - assertEquals("{\"a\":[23]}", object.toString()); - } - - @Test - public void addjsonnestedArraymodifiedAfterAdd() { - JsonArray innerArray = new JsonArray(); - object.add("a", innerArray); - innerArray.add(23); - assertEquals("{\"a\":[23]}", object.toString()); - } - - @Test - public void addjsonnestedObject() { - JsonObject innerObject = new JsonObject(); - innerObject.add("a", 23); - object.add("a", innerObject); - assertEquals("{\"a\":{\"a\":23}}", object.toString()); - } - - @Test - public void addjsonnestedObjectmodifiedAfterAdd() { - JsonObject innerObject = new JsonObject(); - object.add("a", innerObject); - innerObject.add("a", 23); - assertEquals("{\"a\":{\"a\":23}}", object.toString()); - } - - @Test - public void setint() { - object.set("a", 23); - assertEquals("{\"a\":23}", object.toString()); - } - - @Test - public void setintenablesChaining() { - assertSame(object, object.set("a", 23)); - } - - @Test - public void setlong() { - object.set("a", 23L); - assertEquals("{\"a\":23}", object.toString()); - } - - @Test - public void setlongenablesChaining() { - assertSame(object, object.set("a", 23L)); - } - - @Test - public void setfloat() { - object.set("a", 3.14f); - - assertEquals("{\"a\":3.14}", object.toString()); - } - - @Test - public void setfloatenablesChaining() { - assertSame(object, object.set("a", 3.14f)); - } - - @Test - public void setdouble() { - object.set("a", 3.14d); - - assertEquals("{\"a\":3.14}", object.toString()); - } - - @Test - public void setdoubleenablesChaining() { - assertSame(object, object.set("a", 3.14d)); - } - - @Test - public void setboolean() { - object.set("a", true); - - assertEquals("{\"a\":true}", object.toString()); - } - - @Test - public void setbooleanenablesChaining() { - assertSame(object, object.set("a", true)); - } - - @Test - public void setstring() { - object.set("a", "foo"); - - assertEquals("{\"a\":\"foo\"}", object.toString()); - } - - @Test - public void setstringenablesChaining() { - assertSame(object, object.set("a", "foo")); - } - - @Test - public void setjsonNull() { - object.set("a", JsonLiteral.NULL); - - assertEquals("{\"a\":null}", object.toString()); - } - - @Test - public void setjsonArray() { - object.set("a", new JsonArray()); - - assertEquals("{\"a\":[]}", object.toString()); - } - - @Test - public void setjsonObject() { - object.set("a", new JsonObject()); - - assertEquals("{\"a\":{}}", object.toString()); - } - - @Test - public void setjsonenablesChaining() { - assertSame(object, object.set("a", JsonLiteral.NULL)); - } - - @Test - public void setaddsElementIfMissing() { - object.set("a", JsonLiteral.TRUE); - - assertEquals("{\"a\":true}", object.toString()); - } - - @Test - public void setmodifiesElementIfExisting() { - object.add("a", JsonLiteral.TRUE); - - object.set("a", JsonLiteral.FALSE); - - assertEquals("{\"a\":false}", object.toString()); - } - - @Test - public void setmodifiesLastElementIfMultipleExisting() { - object.add("a", 1); - object.add("a", 2); - - object.set("a", JsonLiteral.TRUE); - - assertEquals("{\"a\":1,\"a\":true}", object.toString()); - } - - @Test - public void removefailsWithNullName() { - Assertions.assertThrows(NullPointerException.class, () -> object.remove(null)); - } - - @Test - public void removeremovesMatchingMember() { - object.add("a", 23); - - object.remove("a"); - - assertEquals("{}", object.toString()); - } - - @Test - public void removeremovesOnlyMatchingMember() { - object.add("a", 23); - object.add("b", 42); - object.add("c", true); - - object.remove("b"); - - assertEquals("{\"a\":23,\"c\":true}", object.toString()); - } - - @Test - public void removeremovesOnlyLastMatchingMember() { - object.add("a", 23); - object.add("a", 42); - - object.remove("a"); - - assertEquals("{\"a\":23}", object.toString()); - } - - @Test - public void removeremovesOnlyLastMatchingMemberafterRemove() { - object.add("a", 23); - object.remove("a"); - object.add("a", 42); - object.add("a", 47); - - object.remove("a"); - - assertEquals("{\"a\":42}", object.toString()); - } - - @Test - public void removedoesNotModifyObjectWithoutMatchingMember() { - object.add("a", 23); - - object.remove("b"); - - assertEquals("{\"a\":23}", object.toString()); - } - - @Test - public void mergefailsWithNull() { - Assertions.assertThrows(NullPointerException.class, () -> object.merge(null)); - } - - @Test - public void mergeappendsMembers() { - object.add("a", 1).add("b", 1); - object.merge(Json.object().add("c", 2).add("d", 2)); - - assertEquals(Json.object().add("a", 1).add("b", 1).add("c", 2).add("d", 2), object); - } - - @Test - public void mergereplacesMembers() { - object.add("a", 1).add("b", 1).add("c", 1); - object.merge(Json.object().add("b", 2).add("d", 2)); - - assertEquals(Json.object().add("a", 1).add("b", 2).add("c", 1).add("d", 2), object); - } - - @Test - public void writeempty() throws IOException { - JsonWriter writer = mock(JsonWriter.class); - object.write(writer); - - InOrder inOrder = inOrder(writer); - inOrder.verify(writer).writeObjectOpen(); - inOrder.verify(writer).writeObjectClose(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void writewithSingleValue() throws IOException { - JsonWriter writer = mock(JsonWriter.class); - object.add("a", 23); - - object.write(writer); - - InOrder inOrder = inOrder(writer); - inOrder.verify(writer).writeObjectOpen(); - inOrder.verify(writer).writeMemberName("a"); - inOrder.verify(writer).writeMemberSeparator(); - inOrder.verify(writer).writeNumber("23"); - inOrder.verify(writer).writeObjectClose(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void writewithMultipleValues() throws IOException { - JsonWriter writer = mock(JsonWriter.class); - object.add("a", 23); - object.add("b", 3.14f); - object.add("c", "foo"); - object.add("d", true); - object.add("e", (String) null); - - object.write(writer); - - InOrder inOrder = inOrder(writer); - inOrder.verify(writer).writeObjectOpen(); - inOrder.verify(writer).writeMemberName("a"); - inOrder.verify(writer).writeMemberSeparator(); - inOrder.verify(writer).writeNumber("23"); - inOrder.verify(writer).writeObjectSeparator(); - inOrder.verify(writer).writeMemberName("b"); - inOrder.verify(writer).writeMemberSeparator(); - inOrder.verify(writer).writeNumber("3.14"); - inOrder.verify(writer).writeObjectSeparator(); - inOrder.verify(writer).writeMemberName("c"); - inOrder.verify(writer).writeMemberSeparator(); - inOrder.verify(writer).writeString("foo"); - inOrder.verify(writer).writeObjectSeparator(); - inOrder.verify(writer).writeMemberName("d"); - inOrder.verify(writer).writeMemberSeparator(); - inOrder.verify(writer).writeLiteral("true"); - inOrder.verify(writer).writeObjectSeparator(); - inOrder.verify(writer).writeMemberName("e"); - inOrder.verify(writer).writeMemberSeparator(); - inOrder.verify(writer).writeLiteral("null"); - inOrder.verify(writer).writeObjectClose(); - inOrder.verifyNoMoreInteractions(); - } - - @Test - public void isObject() { - assertTrue(object.isObject()); - } - - @Test - public void asObject() { - assertSame(object, object.asObject()); - } - - @Test - public void equalstrueForSameInstance() { - assertEquals(object, object); - } - - @Test - public void equalstrueForEqualObjects() { - assertEquals(object(), object()); - assertEquals(object("a", "1", "b", "2"), object("a", "1", "b", "2")); - } - - @Test - public void equalsfalseForDifferentObjects() { - assertNotEquals(object("a", "1"), object("a", "2")); - assertNotEquals(object("a", "1"), object("b", "1")); - assertNotEquals(object("a", "1", "b", "2"), object("b", "2", "a", "1")); - } - - @Test - public void equalsfalseForNull() { - assertNotEquals(null, new JsonObject()); - } - - @Test - public void equalsfalseForSubclass() { - JsonObject jsonObject = new JsonObject(); - - assertNotEquals(jsonObject, new JsonObject(jsonObject) { - }); - } - - @Test - public void hashCodeequalsForEqualObjects() { - assertEquals(object().hashCode(), object().hashCode()); - assertEquals(object("a", "1").hashCode(), object("a", "1").hashCode()); - } - - @Test - public void hashCodediffersForDifferentObjects() { - assertNotEquals(object().hashCode(), object("a", "1").hashCode()); - assertNotEquals(object("a", "1").hashCode(), object("a", "2").hashCode()); - assertNotEquals(object("a", "1").hashCode(), object("b", "1").hashCode()); - } - - @Test - public void indexOfreturnsNoIndexIfEmpty() { - assertEquals(-1, object.indexOf("a")); - } - - @Test - public void indexOfreturnsIndexOfMember() { - object.add("a", true); - - assertEquals(0, object.indexOf("a")); - } - - @Test - public void indexOfreturnsIndexOfLastMember() { - object.add("a", true); - object.add("a", true); - - assertEquals(1, object.indexOf("a")); - } - - @Test - public void indexOfreturnsIndexOfLastMemberafterRemove() { - object.add("a", true); - object.add("a", true); - object.remove("a"); - - assertEquals(0, object.indexOf("a")); - } - - @Test - public void indexOfreturnsUpdatedIndexAfterRemove() { - // See issue #16 - object.add("a", true); - object.add("b", true); - object.remove("a"); - - assertEquals(0, object.indexOf("b")); - } - - @Test - public void indexOfreturnsIndexOfLastMemberforBigObject() { - object.add("a", true); - // for indexes above 255, the hash index table does not return a value - for (int i = 0; i < 256; i++) { - object.add("x-" + i, 0); - } - object.add("a", true); - - assertEquals(257, object.indexOf("a")); - } - - @Test - public void hashIndexTablecopyConstructor() { - JsonObject.HashIndexTable original = new JsonObject.HashIndexTable(); - original.add("name", 23); - JsonObject.HashIndexTable copy = new JsonObject.HashIndexTable(original); - assertEquals(23, copy.get("name")); - } - - @Test - public void hashIndexTableadd() { - JsonObject.HashIndexTable indexTable = new JsonObject.HashIndexTable(); - - indexTable.add("name-0", 0); - indexTable.add("name-1", 1); - indexTable.add("name-fe", 0xfe); - indexTable.add("name-ff", 0xff); - - assertEquals(0, indexTable.get("name-0")); - assertEquals(1, indexTable.get("name-1")); - assertEquals(0xfe, indexTable.get("name-fe")); - assertEquals(-1, indexTable.get("name-ff")); - } - - @Test - public void hashIndexTableaddoverwritesPreviousValue() { - JsonObject.HashIndexTable indexTable = new JsonObject.HashIndexTable(); - indexTable.add("name", 23); - indexTable.add("name", 42); - assertEquals(42, indexTable.get("name")); - } - - @Test - public void hashIndexTableaddclearsPreviousValueIfIndexExceeds0xff() { - JsonObject.HashIndexTable indexTable = new JsonObject.HashIndexTable(); - indexTable.add("name", 23); - indexTable.add("name", 300); - assertEquals(-1, indexTable.get("name")); - } - - @Test - public void hashIndexTableremove() { - JsonObject.HashIndexTable indexTable = new JsonObject.HashIndexTable(); - indexTable.add("name", 23); - indexTable.remove(23); - assertEquals(-1, indexTable.get("name")); - } - - @Test - public void hashIndexTableremoveupdatesSubsequentElements() { - JsonObject.HashIndexTable indexTable = new JsonObject.HashIndexTable(); - indexTable.add("foo", 23); - indexTable.add("bar", 42); - indexTable.remove(23); - assertEquals(41, indexTable.get("bar")); - } - - @Test - public void hashIndexTableremovedoesNotChangePrecedingElements() { - JsonObject.HashIndexTable indexTable = new JsonObject.HashIndexTable(); - indexTable.add("foo", 23); - indexTable.add("bar", 42); - indexTable.remove(42); - assertEquals(23, indexTable.get("foo")); - } - - @Test - public void memberreturnsNameAndValue() { - JsonObject.Member member = new JsonObject.Member("a", JsonLiteral.TRUE); - assertEquals("a", member.getName()); - assertEquals(JsonLiteral.TRUE, member.getValue()); - } - - @Test - public void memberequalstrueForSameInstance() { - JsonObject.Member member = new JsonObject.Member("a", JsonLiteral.TRUE); - assertEquals(member, member); - } - - @Test - public void memberequalstrueForEqualObjects() { - JsonObject.Member member = new JsonObject.Member("a", JsonLiteral.TRUE); - assertEquals(member, new JsonObject.Member("a", JsonLiteral.TRUE)); - } - - @Test - public void memberequalsfalseForDifferingObjects() { - JsonObject.Member member = new JsonObject.Member("a", JsonLiteral.TRUE); - assertNotEquals(member, new JsonObject.Member("b", JsonLiteral.TRUE)); - assertNotEquals(member, new JsonObject.Member("a", JsonLiteral.FALSE)); - } - - @Test - public void memberequalsfalseForNull() { - JsonObject.Member member = new JsonObject.Member("a", JsonLiteral.TRUE); - assertNotNull(member); - } - - @Test - public void memberequalsfalseForSubclass() { - JsonObject.Member member = new JsonObject.Member("a", JsonLiteral.TRUE); - assertNotEquals(member, new JsonObject.Member("a", JsonLiteral.TRUE) { - }); - } - - @Test - public void memberhashCodeequalsForEqualObjects() { - JsonObject.Member member = new JsonObject.Member("a", JsonLiteral.TRUE); - assertEquals(member.hashCode(), new JsonObject.Member("a", JsonLiteral.TRUE).hashCode()); - } - - @Test - public void memberhashCodediffersForDifferingobjects() { - JsonObject.Member member = new JsonObject.Member("a", JsonLiteral.TRUE); - assertNotEquals(member.hashCode(), new JsonObject.Member("b", JsonLiteral.TRUE).hashCode()); - assertNotEquals(member.hashCode(), new JsonObject.Member("a", JsonLiteral.FALSE).hashCode()); - } - -} diff --git a/src/test/java/org/xbib/marc/json/JsonReaderTest.java b/src/test/java/org/xbib/marc/json/JsonReaderTest.java deleted file mode 100755 index 24f4293..0000000 --- a/src/test/java/org/xbib/marc/json/JsonReaderTest.java +++ /dev/null @@ -1,812 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.StringStartsWith.startsWith; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.xbib.marc.json.Json.parse; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import java.io.IOException; -import java.io.StringReader; - -public class JsonReaderTest { - - private static String join(String... strings) { - StringBuilder builder = new StringBuilder(); - for (String string : strings) { - builder.append(string).append('\n'); - } - return builder.toString(); - } - - @Test - public void constructorRejectsNullHandler() { - Assertions.assertThrows(NullPointerException.class, () -> { - new JsonReader<>(null, null); - }); - } - - @Test - public void parseStringrRejectsNull() { - Assertions.assertThrows(NullPointerException.class, () -> { - JsonReader reader = new JsonReader<>(new StringReader(null), new TestHandler()); - reader.parse(); - }); - } - - @Test - public void parseReaderRejectsNull() { - Assertions.assertThrows(NullPointerException.class, () -> { - JsonReader reader = new JsonReader<>(null, new TestHandler()); - reader.parse(); - }); - } - - @Test - public void parseReaderRejectsNegativeBufferSize() { - Assertions.assertThrows(IllegalArgumentException.class, () -> { - JsonReader reader = new JsonReader<>(new StringReader("[]"), new TestHandler()); - reader.parse(-1); - }); - } - - @Test - public void parseStringRejectsEmpty() { - assertParseException(0, "Unexpected end of input", ""); - } - - @Test - public void parseReaderRejectsEmpty() { - JsonReader reader = new JsonReader<>(new StringReader(""), new TestHandler()); - JsonException exception = Assertions.assertThrows(JsonException.class, reader::parse); - assertThat(exception.getMessage(), startsWith("Unexpected end of input")); - } - - @Test - public void parseNull() throws IOException { - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader("null"), handler); - reader.parse(); - assertEquals(join("startNull ", - "endNull "), - handler.getLog()); - } - - @Test - public void parseTrue() throws IOException { - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader("true"), handler); - reader.parse(); - assertEquals(join("startBoolean ", - "endBoolean true "), - handler.getLog()); - } - - @Test - public void parseFalse() throws IOException { - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader("false"), handler); - reader.parse(); - assertEquals(join("startBoolean ", - "endBoolean false "), - handler.getLog()); - } - - @Test - public void parseString() throws IOException { - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader("\"foo\""), handler); - reader.parse(); - assertEquals(join("startString ", - "endString foo "), - handler.getLog()); - } - - @Test - public void parseStringEmpty() throws IOException { - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader("\"\""), handler); - reader.parse(); - assertEquals(join("startString ", - "endString "), - handler.getLog()); - } - - @Test - public void parseNumber() throws IOException { - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader("23"), handler); - reader.parse(); - assertEquals(join("startNumber ", - "endNumber 23 "), - handler.getLog()); - } - - @Test - public void parseNumberNegative() throws IOException { - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader("-23"), handler); - reader.parse(); - assertEquals(join("startNumber ", - "endNumber -23 "), - handler.getLog()); - } - - @Test - public void parsenumbernegativeexponent() throws IOException { - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader("-2.3e-12"), handler); - reader.parse(); - assertEquals(join("startNumber ", - "endNumber -2.3e-12 "), - handler.getLog()); - } - - @Test - public void parsearray() throws IOException { - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader("[23]"), handler); - reader.parse(); - assertEquals(join("startArray ", - "startArrayValue a1 ", - "startNumber ", - "endNumber 23 ", - "endArrayValue a1 ", - "endArray a1 "), - handler.getLog()); - } - - @Test - public void parsearrayempty() throws IOException { - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader("[]"), handler); - reader.parse(); - assertEquals(join("startArray ", - "endArray a1 "), - handler.getLog()); - } - - @Test - public void parseobject() throws IOException { - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader("{\"foo\": 23}"), handler); - reader.parse(); - assertEquals(join("startObject ", - "startObjectName o1 ", - "endObjectName o1 foo ", - "startObjectValue o1 foo ", - "startNumber ", - "endNumber 23 ", - "endObjectValue o1 foo ", - "endObject o1 "), - handler.getLog()); - } - - @Test - public void parseobjectempty() throws IOException { - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader("{}"), handler); - reader.parse(); - assertEquals(join("startObject ", - "endObject o1 "), - handler.getLog()); - } - - @Test - public void parsestripsPadding() throws IOException { - assertEquals(new JsonArray(), parse(" [ ] ")); - } - - @Test - public void parseignoresAllWhiteSpace() throws IOException { - assertEquals(new JsonArray(), parse("\t\r\n [\t\r\n ]\t\r\n ")); - } - - @Test - public void parsefailsWithUnterminatedString() { - assertParseException(5, "Unexpected end of input", "[\"foo"); - } - - @Test - public void parsehandlesInputsThatExceedBufferSize() throws IOException { - String input = "[ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47 ]"; - JsonDefaultHandler defHandler = new JsonDefaultHandler(); - JsonReader reader = new JsonReader<>(new StringReader(input), defHandler); - reader.parse(3); - assertEquals("[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47]", defHandler.getValue().toString()); - } - - @Test - public void parsehandlesStringsThatExceedBufferSize() throws IOException { - String input = "[ \"lorem ipsum dolor sit amet\" ]"; - JsonDefaultHandler defHandler = new JsonDefaultHandler(); - JsonReader reader = new JsonReader<>(new StringReader(input), defHandler); - reader.parse(3); - assertEquals("[\"lorem ipsum dolor sit amet\"]", defHandler.getValue().toString()); - } - - @Test - public void parsehandlesNumbersThatExceedBufferSize() throws IOException { - String input = "[ 3.141592653589 ]"; - JsonDefaultHandler defHandler = new JsonDefaultHandler(); - JsonReader reader = new JsonReader<>(new StringReader(input), defHandler); - reader.parse(3); - assertEquals("[3.141592653589]", defHandler.getValue().toString()); - } - - @Test - public void parsehandlesPositionsCorrectlyWhenInputExceedsBufferSize() { - final String input = "{\n \"a\": 23,\n \"b\": 42,\n}"; - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader(input), handler); - Assertions.assertThrows(JsonException.class, () -> reader.parse(3)); - } - - @Test - public void parsefailsOnTooDeeplyNestedArray() { - JsonArray array = new JsonArray(); - for (int i = 0; i < 1001; i++) { - array = new JsonArray().add(array); - } - final String input = array.toString(); - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader(input), handler); - JsonException exception = Assertions.assertThrows(JsonException.class, reader::parse); - assertEquals("Nesting too deep", exception.getMessage()); - } - - @Test - public void parsefailsOnTooDeeplyNestedObject() { - JsonObject object = new JsonObject(); - for (int i = 0; i < 1001; i++) { - object = new JsonObject().add("foo", object); - } - final String input = object.toString(); - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader(input), handler); - JsonException exception = Assertions.assertThrows(JsonException.class, reader::parse); - assertEquals("Nesting too deep", exception.getMessage()); - } - - @Test - public void parsefailsOnTooDeeplyNestedMixedObject() { - JsonValue value = new JsonObject(); - for (int i = 0; i < 1001; i++) { - value = i % 2 == 0 ? new JsonArray().add(value) : new JsonObject().add("foo", value); - } - final String input = value.toString(); - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader(input), handler); - JsonException exception = Assertions.assertThrows(JsonException.class, reader::parse); - assertEquals("Nesting too deep", exception.getMessage()); - } - - @Test - public void parsedoesNotFailWithManyArrays() throws IOException { - JsonArray array = new JsonArray(); - for (int i = 0; i < 1001; i++) { - array.add(new JsonArray().add(7)); - } - final String input = array.toString(); - JsonValue result = parse(input); - assertTrue(result.isArray()); - } - - @Test - public void parsedoesNotFailWithManyEmptyArrays() throws IOException { - JsonArray array = new JsonArray(); - for (int i = 0; i < 1001; i++) { - array.add(new JsonArray()); - } - final String input = array.toString(); - JsonValue result = parse(input); - assertTrue(result.isArray()); - } - - @Test - public void parsedoesNotFailWithManyObjects() throws IOException { - JsonArray array = new JsonArray(); - for (int i = 0; i < 1001; i++) { - array.add(new JsonObject().add("a", 7)); - } - final String input = array.toString(); - JsonValue result = parse(input); - assertTrue(result.isArray()); - } - - @Test - public void parsedoesNotFailWithManyEmptyObjects() throws IOException { - JsonArray array = new JsonArray(); - for (int i = 0; i < 1001; i++) { - array.add(new JsonObject()); - } - final String input = array.toString(); - JsonValue result = parse(input); - assertTrue(result.isArray()); - } - - @Test - public void parseCanBeCalledOnce() throws IOException { - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader("[23]"), handler); - reader.parse(); - assertEquals(join( - "startArray ", - "startArrayValue a1 ", - "startNumber ", - "endNumber 23 ", - "endArrayValue a1 ", - "endArray a1 "), - handler.getLog()); - handler = new TestHandler(); - reader = new JsonReader<>(new StringReader("[42]"), handler); - reader.parse(); - assertEquals(join( - "startArray ", - "startArrayValue a1 ", - "startNumber ", - "endNumber 42 ", - "endArrayValue a1 ", - "endArray a1 "), - handler.getLog()); - } - - @Test - public void arraysempty() throws IOException { - assertEquals("[]", parse("[]").toString()); - } - - @Test - public void arrayssingleValue() throws IOException { - assertEquals("[23]", parse("[23]").toString()); - } - - @Test - public void arraysmultipleValues() throws IOException { - assertEquals("[23,42]", parse("[23,42]").toString()); - } - - @Test - public void arrayswithWhitespaces() throws IOException { - assertEquals("[23,42]", parse("[ 23 , 42 ]").toString()); - } - - @Test - public void arraysnested() throws IOException { - assertEquals("[[23]]", parse("[[23]]").toString()); - assertEquals("[[[]]]", parse("[[[]]]").toString()); - assertEquals("[[23],42]", parse("[[23],42]").toString()); - assertEquals("[[23],[42]]", parse("[[23],[42]]").toString()); - assertEquals("[[23],[42]]", parse("[[23],[42]]").toString()); - assertEquals("[{\"foo\":[23]},{\"bar\":[42]}]", - parse("[{\"foo\":[23]},{\"bar\":[42]}]").toString()); - } - - @Test - public void arraysillegalSyntax() { - assertParseException(1, "Expected value", "[,]"); - assertParseException(4, "Expected ',' or ']'", "[23 42]"); - assertParseException(4, "Expected value", "[23,]"); - } - - @Test - public void arraysincomplete() { - assertParseException(1, "Unexpected end of input", "["); - assertParseException(2, "Unexpected end of input", "[ "); - assertParseException(3, "Unexpected end of input", "[23"); - assertParseException(4, "Unexpected end of input", "[23 "); - assertParseException(4, "Unexpected end of input", "[23,"); - assertParseException(5, "Unexpected end of input", "[23, "); - } - - @Test - public void objectsempty() throws IOException { - assertEquals("{}", parse("{}").toString()); - } - - @Test - public void objectssingleValue() throws IOException { - assertEquals("{\"foo\":23}", parse("{\"foo\":23}").toString()); - } - - @Test - public void objectsmultipleValues() throws IOException { - assertEquals("{\"foo\":23,\"bar\":42}", parse("{\"foo\":23,\"bar\":42}").toString()); - } - - @Test - public void objectswhitespace() throws IOException { - assertEquals("{\"foo\":23,\"bar\":42}", parse("{ \"foo\" : 23, \"bar\" : 42 }").toString()); - } - - @Test - public void objectsnested() throws IOException { - assertEquals("{\"foo\":{}}", parse("{\"foo\":{}}").toString()); - assertEquals("{\"foo\":{\"bar\":42}}", parse("{\"foo\":{\"bar\": 42}}").toString()); - assertEquals("{\"foo\":{\"bar\":{\"baz\":42}}}", - parse("{\"foo\":{\"bar\": {\"baz\": 42}}}").toString()); - assertEquals("{\"foo\":[{\"bar\":{\"baz\":[[42]]}}]}", - parse("{\"foo\":[{\"bar\": {\"baz\": [[42]]}}]}").toString()); - } - - @Test - public void objectsillegalSyntax() { - assertParseException(1, "Expected name", "{,}"); - assertParseException(1, "Expected name", "{:}"); - assertParseException(1, "Expected name", "{23}"); - assertParseException(4, "Expected ':'", "{\"a\"}"); - assertParseException(5, "Expected ':'", "{\"a\" \"b\"}"); - assertParseException(5, "Expected value", "{\"a\":}"); - assertParseException(8, "Expected name", "{\"a\":23,}"); - assertParseException(8, "Expected name", "{\"a\":23,42"); - } - - @Test - public void objectsincomplete() { - assertParseException(1, "Unexpected end of input", "{"); - assertParseException(2, "Unexpected end of input", "{ "); - assertParseException(2, "Unexpected end of input", "{\""); - assertParseException(4, "Unexpected end of input", "{\"a\""); - assertParseException(5, "Unexpected end of input", "{\"a\" "); - assertParseException(5, "Unexpected end of input", "{\"a\":"); - assertParseException(6, "Unexpected end of input", "{\"a\": "); - assertParseException(7, "Unexpected end of input", "{\"a\":23"); - assertParseException(8, "Unexpected end of input", "{\"a\":23 "); - assertParseException(8, "Unexpected end of input", "{\"a\":23,"); - assertParseException(9, "Unexpected end of input", "{\"a\":23, "); - } - - @Test - public void stringsemptyStringisAccepted() throws IOException { - assertEquals("", parse("\"\"").asString()); - } - - @Test - public void stringsasciiCharactersareAccepted() throws IOException { - assertEquals(" ", parse("\" \"").asString()); - assertEquals("a", parse("\"a\"").asString()); - assertEquals("foo", parse("\"foo\"").asString()); - assertEquals("A2-D2", parse("\"A2-D2\"").asString()); - assertEquals("\u007f", parse("\"\u007f\"").asString()); - } - - @Test - public void stringsnonAsciiCharactersareAccepted() throws IOException { - assertEquals("Русский", parse("\"Русский\"").asString()); - assertEquals("العربية", parse("\"العربية\"").asString()); - assertEquals("日本語", parse("\"日本語\"").asString()); - } - - @Test - public void stringscontrolCharactersareRejected() { - // JSON string must not contain characters < 0x20 - assertParseException(3, "Expected valid string character", "\"--\n--\""); - assertParseException(3, "Expected valid string character", "\"--\r\n--\""); - assertParseException(3, "Expected valid string character", "\"--\t--\""); - assertParseException(3, "Expected valid string character", "\"--\u0000--\""); - assertParseException(3, "Expected valid string character", "\"--\u001f--\""); - } - - @Test - public void stringsvalidEscapesareAccepted() throws IOException { - // valid escapes are \" \\ \/ \b \f \n \r \t and unicode escapes - assertEquals(" \" ", parse("\" \\\" \"").asString()); - assertEquals(" \\ ", parse("\" \\\\ \"").asString()); - assertEquals(" / ", parse("\" \\/ \"").asString()); - assertEquals(" \u0008 ", parse("\" \\b \"").asString()); - assertEquals(" \u000c ", parse("\" \\f \"").asString()); - assertEquals(" \r ", parse("\" \\r \"").asString()); - assertEquals(" \n ", parse("\" \\n \"").asString()); - assertEquals(" \t ", parse("\" \\t \"").asString()); - } - - @Test - public void stringsescapeatStart() throws IOException { - assertEquals("\\x", parse("\"\\\\x\"").asString()); - } - - @Test - public void stringsescapeatEnd() throws IOException { - assertEquals("x\\", parse("\"x\\\\\"").asString()); - } - - @Test - public void stringsillegalEscapesareRejected() { - assertParseException(2, "Expected valid escape sequence", "\"\\a\""); - assertParseException(2, "Expected valid escape sequence", "\"\\x\""); - assertParseException(2, "Expected valid escape sequence", "\"\\000\""); - } - - @Test - public void stringsvalidUnicodeEscapesareAccepted() throws IOException { - assertEquals("\u0021", parse("\"\\u0021\"").asString()); - assertEquals("\u4711", parse("\"\\u4711\"").asString()); - assertEquals("\uffff", parse("\"\\uffff\"").asString()); - assertEquals("\uabcdx", parse("\"\\uabcdx\"").asString()); - } - - @Test - public void stringsillegalUnicodeEscapesareRejected() { - assertParseException(3, "Expected hexadecimal digit", "\"\\u \""); - assertParseException(3, "Expected hexadecimal digit", "\"\\ux\""); - assertParseException(5, "Expected hexadecimal digit", "\"\\u20 \""); - assertParseException(6, "Expected hexadecimal digit", "\"\\u000x\""); - } - - @Test - public void stringsincompleteStringsareRejected() { - assertParseException(1, "Unexpected end of input", "\""); - assertParseException(4, "Unexpected end of input", "\"foo"); - assertParseException(5, "Unexpected end of input", "\"foo\\"); - assertParseException(6, "Unexpected end of input", "\"foo\\n"); - assertParseException(6, "Unexpected end of input", "\"foo\\u"); - assertParseException(7, "Unexpected end of input", "\"foo\\u0"); - assertParseException(9, "Unexpected end of input", "\"foo\\u000"); - assertParseException(10, "Unexpected end of input", "\"foo\\u0000"); - } - - @Test - public void numbersinteger() throws IOException { - assertEquals(new JsonNumber("0"), parse("0")); - assertEquals(new JsonNumber("-0"), parse("-0")); - assertEquals(new JsonNumber("1"), parse("1")); - assertEquals(new JsonNumber("-1"), parse("-1")); - assertEquals(new JsonNumber("23"), parse("23")); - assertEquals(new JsonNumber("-23"), parse("-23")); - assertEquals(new JsonNumber("1234567890"), parse("1234567890")); - assertEquals(new JsonNumber("123456789012345678901234567890"), - parse("123456789012345678901234567890")); - } - - @Test - public void numbersminusZero() throws IOException { - // allowed by JSON, allowed by Java - JsonValue value = parse("-0"); - assertEquals(0, value.asInt()); - assertEquals(0L, value.asLong()); - assertEquals(0f, value.asFloat(), 0); - assertEquals(0d, value.asDouble(), 0); - } - - @Test - public void numbersdecimal() throws IOException { - assertEquals(new JsonNumber("0.23"), parse("0.23")); - assertEquals(new JsonNumber("-0.23"), parse("-0.23")); - assertEquals(new JsonNumber("1234567890.12345678901234567890"), - parse("1234567890.12345678901234567890")); - } - - @Test - public void numberswithExponent() throws IOException { - assertEquals(new JsonNumber("0.1e9"), parse("0.1e9")); - assertEquals(new JsonNumber("0.1e9"), parse("0.1e9")); - assertEquals(new JsonNumber("0.1E9"), parse("0.1E9")); - assertEquals(new JsonNumber("-0.23e9"), parse("-0.23e9")); - assertEquals(new JsonNumber("0.23e9"), parse("0.23e9")); - assertEquals(new JsonNumber("0.23e+9"), parse("0.23e+9")); - assertEquals(new JsonNumber("0.23e-9"), parse("0.23e-9")); - } - - @Test - public void numberswithInvalidFormat() { - assertParseException(0, "Expected value", "+1"); - assertParseException(0, "Expected value", ".1"); - assertParseException(1, "Unexpected character", "02"); - assertParseException(2, "Unexpected character", "-02"); - assertParseException(1, "Expected digit", "-x"); - assertParseException(2, "Expected digit", "1.x"); - assertParseException(2, "Expected digit", "1ex"); - assertParseException(3, "Unexpected character", "1e1x"); - } - - @Test - public void numbersincomplete() { - assertParseException(1, "Unexpected end of input", "-"); - assertParseException(2, "Unexpected end of input", "1."); - assertParseException(4, "Unexpected end of input", "1.0e"); - assertParseException(5, "Unexpected end of input", "1.0e-"); - } - - @Test - public void nullcomplete() throws IOException { - assertEquals(JsonLiteral.NULL, parse("null")); - } - - @Test - public void nullincomplete() { - assertParseException(1, "Unexpected end of input", "n"); - assertParseException(2, "Unexpected end of input", "nu"); - assertParseException(3, "Unexpected end of input", "nul"); - } - - @Test - public void nullwithIllegalCharacter() { - assertParseException(1, "Expected 'u'", "nx"); - assertParseException(2, "Expected 'l'", "nux"); - assertParseException(3, "Expected 'l'", "nulx"); - assertParseException(4, "Unexpected character", "nullx"); - } - - @Test - public void truecomplete() throws IOException { - assertSame(JsonLiteral.TRUE, parse("true")); - } - - @Test - public void trueincomplete() { - assertParseException(1, "Unexpected end of input", "t"); - assertParseException(2, "Unexpected end of input", "tr"); - assertParseException(3, "Unexpected end of input", "tru"); - } - - @Test - public void truewithIllegalCharacter() { - assertParseException(1, "Expected 'r'", "tx"); - assertParseException(2, "Expected 'u'", "trx"); - assertParseException(3, "Expected 'e'", "trux"); - assertParseException(4, "Unexpected character", "truex"); - } - - @Test - public void falsecomplete() throws IOException { - assertSame(JsonLiteral.FALSE, parse("false")); - } - - @Test - public void falseincomplete() { - assertParseException(1, "Unexpected end of input", "f"); - assertParseException(2, "Unexpected end of input", "fa"); - assertParseException(3, "Unexpected end of input", "fal"); - assertParseException(4, "Unexpected end of input", "fals"); - } - - @Test - public void falsewithIllegalCharacter() { - assertParseException(1, "Expected 'a'", "fx"); - assertParseException(2, "Expected 'l'", "fax"); - assertParseException(3, "Expected 's'", "falx"); - assertParseException(4, "Expected 'e'", "falsx"); - assertParseException(5, "Unexpected character", "falsex"); - } - - private void assertParseException(int offset, String message, final String json) { - TestHandler handler = new TestHandler(); - JsonReader reader = new JsonReader<>(new StringReader(json), handler); - JsonException exception = Assertions.assertThrows(JsonException.class, () -> { - try { - reader.parse(); - } catch (IOException e) { - // ignore - } - }); - assertThat(exception.getMessage(), startsWith(message)); - } - - private static class TestHandler implements JsonHandler { - - StringBuilder log = new StringBuilder(); - int sequence = 0; - - @Override - public void startNull() { - record("startNull"); - } - - @Override - public void endNull() { - record("endNull"); - } - - @Override - public void startBoolean() { - record("startBoolean"); - } - - @Override - public void endBoolean(boolean value) { - record("endBoolean", value); - } - - @Override - public void startString() { - record("startString"); - } - - @Override - public void endString(String string) { - record("endString", string); - } - - @Override - public void startNumber() { - record("startNumber"); - } - - @Override - public void endNumber(String string) { - record("endNumber", string); - } - - @Override - public Object startArray() { - record("startArray"); - return "a" + ++sequence; - } - - @Override - public void endArray(Object array) { - record("endArray", array); - } - - @Override - public void startArrayValue(Object array) { - record("startArrayValue", array); - } - - @Override - public void endArrayValue(Object array) { - record("endArrayValue", array); - } - - @Override - public Object startObject() { - record("startObject"); - return "o" + ++sequence; - } - - @Override - public void endObject(Object object) { - record("endObject", object); - } - - @Override - public void startObjectName(Object object) { - record("startObjectName", object); - } - - @Override - public void endObjectName(Object object, String name) { - record("endObjectName", object, name); - } - - @Override - public void startObjectValue(Object object, String name) { - record("startObjectValue", object, name); - } - - @Override - public void endObjectValue(Object object, String name) { - record("endObjectValue", object, name); - } - - private void record(String event, Object... args) { - log.append(event); - for (Object arg : args) { - log.append(' ').append(arg); - } - log.append(' ').append('\n'); - } - - String getLog() { - return log.toString(); - } - - } -} diff --git a/src/test/java/org/xbib/marc/json/JsonStringTest.java b/src/test/java/org/xbib/marc/json/JsonStringTest.java deleted file mode 100755 index 4f4ec0d..0000000 --- a/src/test/java/org/xbib/marc/json/JsonStringTest.java +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import java.io.IOException; -import java.io.StringWriter; - -public class JsonStringTest { - - private StringWriter stringWriter; - - private JsonWriter jsonWriter; - - @BeforeEach - public void setUp() { - stringWriter = new StringWriter(); - jsonWriter = new JsonWriter(stringWriter); - } - - @Test - public void constructorFailsWithNull() { - Assertions.assertThrows(NullPointerException.class, () -> { - new JsonString(null); - }); - } - - @Test - public void write() throws IOException { - new JsonString("foo").write(jsonWriter); - assertEquals("\"foo\"", stringWriter.toString()); - } - - @Test - public void writeEscapesStrings() throws IOException { - new JsonString("foo\\bar").write(jsonWriter); - assertEquals("\"foo\\\\bar\"", stringWriter.toString()); - } - - @Test - public void isString() { - assertTrue(new JsonString("foo").isString()); - } - - @Test - public void asString() { - assertEquals("foo", new JsonString("foo").asString()); - } - - @Test - public void equalsTrueForSameInstance() { - JsonString string = new JsonString("foo"); - assertEquals(string, string); - } - - @Test - public void equalsTrueForEqualStrings() { - assertEquals(new JsonString("foo"), new JsonString("foo")); - } - - @Test - public void equalsFalseForDifferentStrings() { - assertNotEquals(new JsonString(""), new JsonString("foo")); - assertNotEquals(new JsonString("foo"), new JsonString("bar")); - } - - @Test - public void equalsFalseForNull() { - assertNotEquals(null, new JsonString("foo")); - } - - @Test - public void equalsFalseForSubclass() { - assertNotEquals(new JsonString("foo"), new JsonString("foo") { - }); - } - - @Test - public void hashCodeEqualsForEqualStrings() { - assertEquals(new JsonString("foo").hashCode(), new JsonString("foo").hashCode()); - } - - @Test - public void hashCodeDiffersForDifferentStrings() { - assertNotEquals(new JsonString("").hashCode(), new JsonString("foo").hashCode()); - assertNotEquals(new JsonString("foo").hashCode(), new JsonString("bar").hashCode()); - } -} diff --git a/src/test/java/org/xbib/marc/json/JsonTest.java b/src/test/java/org/xbib/marc/json/JsonTest.java deleted file mode 100755 index ba6c0f9..0000000 --- a/src/test/java/org/xbib/marc/json/JsonTest.java +++ /dev/null @@ -1,219 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; - -public class JsonTest { - - @Test - public void literalConstants() { - assertTrue(JsonLiteral.NULL.isNull()); - assertTrue(JsonLiteral.TRUE.isTrue()); - assertTrue(JsonLiteral.FALSE.isFalse()); - } - - @Test - public void valueInt() { - assertEquals("0", Json.of(0).toString()); - assertEquals("23", Json.of(23).toString()); - assertEquals("-1", Json.of(-1).toString()); - assertEquals("2147483647", Json.of(Integer.MAX_VALUE).toString()); - assertEquals("-2147483648", Json.of(Integer.MIN_VALUE).toString()); - } - - @Test - public void valueLong() { - assertEquals("0", Json.of(0L).toString()); - assertEquals("9223372036854775807", Json.of(Long.MAX_VALUE).toString()); - assertEquals("-9223372036854775808", Json.of(Long.MIN_VALUE).toString()); - } - - @Test - public void valueFloat() { - assertEquals("23.5", Json.of(23.5f).toString()); - assertEquals("-3.1416", Json.of(-3.1416f).toString()); - assertEquals("1.23E-6", Json.of(0.00000123f).toString()); - assertEquals("-1.23E7", Json.of(-12300000f).toString()); - } - - @Test - public void valueFloatCutsOffPointZero() { - assertEquals("0", Json.of(0f).toString()); - assertEquals("-1", Json.of(-1f).toString()); - assertEquals("10", Json.of(10f).toString()); - } - - @Test - public void valueFloatFailsWithInfinity() { - String message = "Infinite and NaN values not permitted in JSON"; - Assertions.assertThrows(IllegalArgumentException.class, - () -> Json.of(Float.POSITIVE_INFINITY), message); - } - - @Test - public void valuefloatfailsWithNaN() { - String message = "Infinite and NaN values not permitted in JSON"; - Assertions.assertThrows(IllegalArgumentException.class, - () -> Json.of(Float.NaN), message); - } - - @Test - public void valueDouble() { - assertEquals("23.5", Json.of(23.5d).toString()); - assertEquals("3.1416", Json.of(3.1416d).toString()); - assertEquals("1.23E-6", Json.of(0.00000123d).toString()); - assertEquals("1.7976931348623157E308", Json.of(1.7976931348623157E308d).toString()); - } - - @Test - public void valueDoublecutsOffPointZero() { - assertEquals("0", Json.of(0d).toString()); - assertEquals("-1", Json.of(-1d).toString()); - assertEquals("10", Json.of(10d).toString()); - } - - @Test - public void valuedoublefailsWithInfinity() { - String message = "Infinite and NaN values not permitted in JSON"; - Assertions.assertThrows(IllegalArgumentException.class, - () -> Json.of(Double.POSITIVE_INFINITY), message); - } - - @Test - public void valuedoublefailsWithNaN() { - String message = "Infinite and NaN values not permitted in JSON"; - Assertions.assertThrows(IllegalArgumentException.class, - () -> Json.of(Double.NaN), message); - } - - @Test - public void valueboolean() { - assertSame(JsonLiteral.TRUE, Json.of(true)); - assertSame(JsonLiteral.FALSE, Json.of(false)); - } - - @Test - public void valuestring() { - assertEquals("", Json.of("").asString()); - assertEquals("Hello", Json.of("Hello").asString()); - assertEquals("\"Hello\"", Json.of("\"Hello\"").asString()); - } - - @Test - public void valuestringtoleratesNull() { - assertSame(JsonLiteral.NULL, Json.of(null)); - } - - @Test - public void array() { - assertEquals(new JsonArray(), Json.array()); - } - - @Test - public void arrayint() { - assertEquals(new JsonArray().add(23), Json.array(23)); - assertEquals(new JsonArray().add(23).add(42), Json.array(23, 42)); - } - - @Test - public void arrayintfailsWithNull() { - Assertions.assertThrows(NullPointerException.class, () -> Json.array((int[]) null)); - } - - @Test - public void arraylong() { - assertEquals(new JsonArray().add(23L), Json.array(23L)); - assertEquals(new JsonArray().add(23L).add(42L), Json.array(23L, 42L)); - } - - @Test - public void arraylongfailsWithNull() { - Assertions.assertThrows(NullPointerException.class, () -> Json.array((long[]) null)); - } - - @Test - public void arrayfloat() { - assertEquals(new JsonArray().add(3.14f), Json.array(3.14f)); - assertEquals(new JsonArray().add(3.14f).add(1.41f), Json.array(3.14f, 1.41f)); - } - - @Test - public void arrayfloatfailsWithNull() { - Assertions.assertThrows(NullPointerException.class, () -> Json.array((float[]) null)); - } - - @Test - public void arraydouble() { - assertEquals(new JsonArray().add(3.14d), Json.array(3.14d)); - assertEquals(new JsonArray().add(3.14d).add(1.41d), Json.array(3.14d, 1.41d)); - } - - @Test - public void arraydoublefailsWithNull() { - Assertions.assertThrows(NullPointerException.class, () -> Json.array((double[]) null)); - } - - @Test - public void arrayboolean() { - assertEquals(new JsonArray().add(true), Json.array(true)); - assertEquals(new JsonArray().add(true).add(false), Json.array(true, false)); - } - - @Test - public void arraybooleanfailsWithNull() { - Assertions.assertThrows(NullPointerException.class, () -> Json.array((boolean[]) null)); - } - - @Test - public void arraystring() { - assertEquals(new JsonArray().add("foo"), Json.array("foo")); - assertEquals(new JsonArray().add("foo").add("bar"), Json.array("foo", "bar")); - } - - @Test - public void arraystringfailsWithNull() { - Assertions.assertThrows(NullPointerException.class, () -> Json.array((String[]) null)); - } - - @Test - public void object() { - assertEquals(new JsonObject(), Json.object()); - } - - @Test - public void parsestring() throws IOException { - assertEquals(Json.of(23), Json.parse("23")); - } - - @Test - public void parsestringfailsWithNull() { - Assertions.assertThrows(NullPointerException.class, () -> Json.parse((String) null)); - } - - @Test - public void parseReader() throws IOException { - Reader reader = new StringReader("23"); - assertEquals(Json.of(23), Json.parse(reader)); - } -} diff --git a/src/test/java/org/xbib/marc/json/MarcJsonWriterTest.java b/src/test/java/org/xbib/marc/json/MarcJsonWriterTest.java index 8993cd7..9c1c247 100644 --- a/src/test/java/org/xbib/marc/json/MarcJsonWriterTest.java +++ b/src/test/java/org/xbib/marc/json/MarcJsonWriterTest.java @@ -22,7 +22,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.xbib.marc.StreamMatcher.assertStream; import java.io.IOException; -import java.io.OutputStream; import java.io.StringWriter; import java.nio.file.Files; import java.nio.file.Path; @@ -43,8 +42,6 @@ import java.util.Comparator; import java.util.EnumSet; import java.util.List; import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; public class MarcJsonWriterTest { diff --git a/src/test/java/org/xbib/marc/json/PrettyPrintTest.java b/src/test/java/org/xbib/marc/json/PrettyPrintTest.java deleted file mode 100755 index 7ad7e0c..0000000 --- a/src/test/java/org/xbib/marc/json/PrettyPrintTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright 2016-2022 Jörg Prante - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * Apache License 2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.xbib.marc.json; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotSame; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.Mockito.mock; -import static org.xbib.marc.json.JsonWriterConfig.prettyPrint; -import org.junit.jupiter.api.Test; -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; -import java.util.Locale; - -public class PrettyPrintTest { - - @Test - public void testIndentWithSpaces_emptyArray() throws IOException { - StringWriter sw = new StringWriter(); - JsonWriter output = JsonWriterConfig.prettyPrint(2).createWriter(sw); - new JsonArray().write(output); - assertEquals("[\n \n]", sw.toString()); - } - - @Test - public void testIndentWithSpaces_emptyObject() throws IOException { - StringWriter sw = new StringWriter(); - JsonWriter output = JsonWriterConfig.prettyPrint(2).createWriter(sw); - new JsonObject().write(output); - assertEquals("{\n \n}", sw.toString()); - } - - @Test - public void testIndentWithSpaces_array() throws IOException { - StringWriter sw = new StringWriter(); - JsonWriter output = JsonWriterConfig.prettyPrint(2).createWriter(sw); - new JsonArray().add(23).add(42).write(output); - assertEquals("[\n 23,\n 42\n]", sw.toString()); - } - - @Test - public void testIndentWithSpaces_nestedArray() throws IOException { - StringWriter sw = new StringWriter(); - JsonWriter output = JsonWriterConfig.prettyPrint(2).createWriter(sw); - new JsonArray().add(23).add(new JsonArray().add(42)).write(output); - assertEquals("[\n 23,\n [\n 42\n ]\n]", sw.toString()); - } - - @Test - public void testIndentWithSpaces_object() throws IOException { - StringWriter sw = new StringWriter(); - JsonWriter output = JsonWriterConfig.prettyPrint(2).createWriter(sw); - new JsonObject().add("a", 23).add("b", 42).write(output); - assertEquals("{\n \"a\": 23,\n \"b\": 42\n}", sw.toString()); - } - - @Test - public void testIndentWithSpaces_nestedObject() throws IOException { - StringWriter sw = new StringWriter(); - JsonWriter output = JsonWriterConfig.prettyPrint(2).createWriter(sw); - new JsonObject().add("a", 23).add("b", new JsonObject().add("c", 42)).write(output); - assertEquals("{\n \"a\": 23,\n \"b\": {\n \"c\": 42\n }\n}", sw.toString()); - } - - @Test - public void testIndentWithSpaces_zero() throws IOException { - StringWriter sw = new StringWriter(); - JsonWriter output = JsonWriterConfig.prettyPrint(0).createWriter(sw); - new JsonArray().add(23).add(42).write(output); - assertEquals("[\n23,\n42\n]", sw.toString()); - } - - @Test - public void testIndentWithSpaces_one() throws IOException { - StringWriter sw = new StringWriter(); - JsonWriter output = JsonWriterConfig.prettyPrint(1).createWriter(sw); - new JsonArray().add(23).add(42).write(output); - assertEquals("[\n 23,\n 42\n]", sw.toString()); - } - - @Test - public void testIndentWithSpaces_failsWithNegativeValues() { - try { - prettyPrint(-1); - fail(); - } catch (IllegalArgumentException ex) { - assertTrue(ex.getMessage().toLowerCase(Locale.US).contains("negative")); - } - } - - @Test - public void testIndentWithSpaces_createsIndependentInstances() { - Writer writer = mock(Writer.class); - JsonWriterConfig config = prettyPrint(1); - Object instance1 = config.createWriter(writer); - Object instance2 = config.createWriter(writer); - assertNotSame(instance1, instance2); - } -} diff --git a/src/test/resources/org/xbib/marc/json/test_ubl.mrc.json b/src/test/resources/org/xbib/marc/json/test_ubl.mrc.json index c13c642..420af74 100644 --- a/src/test/resources/org/xbib/marc/json/test_ubl.mrc.json +++ b/src/test/resources/org/xbib/marc/json/test_ubl.mrc.json @@ -1 +1 @@ -{"_FORMAT":"MarcXchange","_TYPE":"Bibliographic","_LEADER":"04427cam a2201081 c4500","001":["0-730849546"],"003":["DE-627"],"005":["20190704100209.0"],"007":["tu"],"008":["121203s2019 xx ||||| 00| ||ger c"],"015":{" ":[{"a":"12,N48"},{"2":"dnb"}]},"016":{"7 ":[{"a":"1028162669"},{"2":"DE-101"}]},"020":{" ":[{"a":"3579026895"},{"c":"Gb.ca. EUR 168.00 (DE)"},{"9":"3-579-02689-5"}]},"020":{" ":[{"a":"9783579026893"},{"c":"Gb.ca. EUR 168.00 (DE)"},{"9":"978-3-579-02689-3"}]},"024":{"3 ":[{"a":"9783579026893"}]},"035":{" ":[{"a":"(DE-627)730849546"}]},"035":{" ":[{"a":"(DE-576)9730849544"}]},"035":{" ":[{"a":"(DE-599)DNB1028162669"}]},"035":{" ":[{"a":"(DE-101)1028162669"}]},"040":{" ":[{"a":"DE-627"},{"b":"ger"},{"c":"DE-627"},{"e":"rda"}]},"041":{" ":[{"a":"ger"}]},"044":{" ":[{"c":"XA-DE-NW"}]},"082":{"04":[{"a":"290"},{"q":"DNB"}]},"084":{" ":[{"a":"1"},{"2":"ssgn"}]},"100":{"1 ":[{"a":"Buber, Martin"},{"d":"1878-1965"},{"e":"VerfasserIn"},{"0":"(DE-588)118516477"},{"0":"(DE-627)133399109"},{"0":"(DE-576)208877088"},{"4":"aut"}]},"245":{"10":[{"a":"Werkausgabe"},{"n":"13"},{"p":"Schriften zur biblischen Religion / herausgegeben von Christian Wiese unter Mitarbeit von Heike Breitenbach ; eingeleitet von Michael Fishbane ; kommentiert von Christian Wiese und Heike Breitenbach unter Mitarbeit von Andreas Losch"},{"n":"1"},{"p":"Text"},{"c":"Martin Buber ; im Auftrag der Philosophischen Fakult©Þt der Heinrich Heine Universit©Þt D©ơsseldorf und der Israel Acadademy of Sciences and Humanities herausgegeben von Paul Mendes-Flohr und Bernd Witte"}]},"250":{" ":[{"a":"1. Auflage"}]},"264":{" 1":[{"a":"G©ơtersloh"},{"b":"G©ơtersloher Verlagshaus"},{"c":"[2019]"}]},"264":{" 4":[{"c":"℗♭ 2019"}]},"300":{" ":[{"a":"726 Seiten"},{"c":"23 cm"}]},"336":{" ":[{"a":"Text"},{"b":"txt"},{"2":"rdacontent"}]},"337":{" ":[{"a":"ohne Hilfsmittel zu benutzen"},{"b":"n"},{"2":"rdamedia"}]},"338":{" ":[{"a":"Band"},{"b":"nc"},{"2":"rdacarrier"}]},"655":{" 7":[{"a":"Quelle"},{"0":"(DE-588)4135952-5"},{"0":"(DE-627)105661236"},{"0":"(DE-576)209665084"},{"2":"gnd-content"}]},"689":{"00":[{"D":"u"},{"0":"(DE-588)4001515-4"},{"0":"(DE-627)104603666"},{"0":"(DE-576)208843116"},{"a":"Bibel"},{"p":"Altes Testament"},{"2":"gnd"}]},"689":{"01":[{"D":"s"},{"0":"(DE-588)4015950-4"},{"0":"(DE-627)104675314"},{"0":"(DE-576)20891434X"},{"a":"Exegese"},{"2":"gnd"}]},"689":{"02":[{"D":"s"},{"0":"(DE-588)4136677-3"},{"0":"(DE-627)105655740"},{"0":"(DE-576)209671181"},{"a":"J©ơdische Philosophie"},{"2":"gnd"}]},"689":{"0 ":[{"5":"(DE-627)"}]},"689":{"10":[{"D":"u"},{"0":"(DE-588)4001515-4"},{"0":"(DE-627)104603666"},{"0":"(DE-576)208843116"},{"a":"Bibel"},{"p":"Altes Testament"},{"2":"gnd"}]},"689":{"11":[{"D":"s"},{"0":"(DE-588)4059758-1"},{"0":"(DE-627)104131446"},{"0":"(DE-576)209132159"},{"a":"Theologie"},{"2":"gnd"}]},"689":{"1 ":[{"5":"(DE-627)"}]},"700":{"1 ":[{"a":"Wiese, Christian"},{"d":"1961-"},{"e":"HerausgeberIn"},{"0":"(DE-588)121451917"},{"0":"(DE-627)081317069"},{"0":"(DE-576)181294958"},{"4":"edt"}]},"700":{"1 ":[{"a":"Fishbane, Michael A."},{"d":"1943-"},{"e":"VerfasserIn einer Einleitung"},{"0":"(DE-588)131399837"},{"0":"(DE-627)50877294X"},{"0":"(DE-576)178775916"},{"4":"win"}]},"700":{"1 ":[{"a":"Buber, Martin"},{"d":"1878-1965"},{"t":"Schriften zur biblischen Religion"}]},"773":{"18":[{"w":"(DE-627)1165587912"},{"w":"(DE-576)095587918"},{"g":"13,1"},{"q":"13,1"}]},"856":{"42":[{"u":"http://deposit.d-nb.de/cgi-bin/dokserv?id=4186947&prov=M&dok_var=1&dok_ext=htm"},{"n":"Verlag: MVB"},{"q":"text/html"},{"v":"2013-05-01"},{"3":"Inhaltstext"}]},"856":{"42":[{"u":"http://swbplus.bsz-bw.de/bsz730849546inh.htm"},{"m":"V:DE-576;B:DE-21"},{"q":"application/pdf"},{"v":"20190605155100"},{"3":"Inhaltsverzeichnis"}]},"982":{" ":[{"a":"(DE-15)0025681716"},{"x":"1"}]},"969":{" ":[{"b":"FHTheol"},{"x":"1"}]},"983":{" ":[{"a":"(DE-15)3717597"}]},"969":{" ":[{"c":"B"}]},"982":{" ":[{"a":"(DE-14)34144552"},{"x":"1"}]},"984":{" ":[{"b":"FH1"},{"x":"1"}]},"983":{" ":[{"a":"(DE-14)16659087"}]},"984":{" ":[{"c":"B"}]},"900":{" ":[{"a":"PY¿ BYYN, M©ʼk♯Ê¿♯“l"}]},"900":{" ":[{"a":"Fishbane, Michael"}]},"900":{" ":[{"a":"Fi¿Łbeyn, M©ʼı̀æ♯Ê¾♯“l"}]},"900":{" ":[{"a":"B¿±ber, Mordekaj Mar£̀Ưin"}]},"900":{" ":[{"a":"Buber, Martin Mordehai"}]},"900":{" ":[{"a":"Buber, Mar£̀Ưin"}]},"900":{" ":[{"a":"Mpumper, Martin"}]},"900":{" ":[{"a":"B©»ber, Mordekay Mart©ʼn"}]},"900":{" ":[{"a":"B¿±b♯, Marutin"}]},"900":{" ":[{"a":"Buber, Mordehai Martin"}]},"900":{" ":[{"a":"Buber, Mordekhai Mar£̀Ưin"}]},"900":{" ":[{"a":"בוב׷, מ׷טין"}]},"900":{" ":[{"a":"בוב׷, מ׷דכי מ׷טין"}]},"950":{" ":[{"a":"Christliche Theologie"}]},"950":{" ":[{"a":"БоÐđослоÐøÐıÐæ"}]},"950":{" ":[{"a":"Judentum"}]},"950":{" ":[{"a":"Philosophie"}]},"950":{" ":[{"a":"©–stliche Philosophie"}]},"950":{" ":[{"a":"ИуÐþÐæУсÐðÐʻя фÐıлософÐıя"}]},"950":{" ":[{"a":"Alter Bund"}]},"950":{" ":[{"a":"Bibelauslegung"}]},"950":{" ":[{"a":"Bibelexegese"}]},"950":{" ":[{"a":"Biblische Auslegung"}]},"950":{" ":[{"a":"Bibelinterpretation"}]},"950":{" ":[{"a":"Biblische Exegese"}]},"950":{" ":[{"a":"Schriftauslegung"}]},"950":{" ":[{"a":"Bibelwissenschaft"}]},"950":{" ":[{"a":"Exeget"}]},"950":{" ":[{"a":"ÐƯÐðÐʺÐæÐđÐæтÐıÐðÐʻ"}]},"951":{" ":[{"b":"XA-AT"}]},"951":{" ":[{"b":"XA-DE"}]},"951":{" ":[{"b":"XB-IL"}]},"951":{" ":[{"b":"XY"}]},"852":{" ":[{"a":"DE-14"},{"z":"2019-05-06T09:35:35Z"}]},"852":{" ":[{"a":"DE-L325"},{"z":"2019-05-28T10:02:58Z"}]},"852":{" ":[{"a":"DE-16"},{"z":"2019-05-15T10:23:35Z"}]},"852":{" ":[{"a":"DE-15"},{"z":"2019-05-09T09:13:04Z"}]},"980":{" ":[{"a":"730849546"},{"b":"0"},{"k":"730849546"},{"o":"9730849544"}]}} \ No newline at end of file +{"_FORMAT":"MarcXchange","_TYPE":"Bibliographic","_LEADER":"04427cam a2201081 c4500","001":"0-730849546","003":"DE-627","005":"20190704100209.0","007":"tu","008":"121203s2019 xx ||||| 00| ||ger c","015":{" ":[{"a":"12,N48"},{"2":"dnb"}]},"016":{"7 ":[{"a":"1028162669"},{"2":"DE-101"}]},"020":{" ":[{"a":"3579026895"},{"c":"Gb.ca. EUR 168.00 (DE)"},{"9":"3-579-02689-5"}]},"020":{" ":[{"a":"9783579026893"},{"c":"Gb.ca. EUR 168.00 (DE)"},{"9":"978-3-579-02689-3"}]},"024":{"3 ":[{"a":"9783579026893"}]},"035":{" ":[{"a":"(DE-627)730849546"}]},"035":{" ":[{"a":"(DE-576)9730849544"}]},"035":{" ":[{"a":"(DE-599)DNB1028162669"}]},"035":{" ":[{"a":"(DE-101)1028162669"}]},"040":{" ":[{"a":"DE-627"},{"b":"ger"},{"c":"DE-627"},{"e":"rda"}]},"041":{" ":[{"a":"ger"}]},"044":{" ":[{"c":"XA-DE-NW"}]},"082":{"04":[{"a":"290"},{"q":"DNB"}]},"084":{" ":[{"a":"1"},{"2":"ssgn"}]},"100":{"1 ":[{"a":"Buber, Martin"},{"d":"1878-1965"},{"e":"VerfasserIn"},{"0":"(DE-588)118516477"},{"0":"(DE-627)133399109"},{"0":"(DE-576)208877088"},{"4":"aut"}]},"245":{"10":[{"a":"Werkausgabe"},{"n":"13"},{"p":"Schriften zur biblischen Religion / herausgegeben von Christian Wiese unter Mitarbeit von Heike Breitenbach ; eingeleitet von Michael Fishbane ; kommentiert von Christian Wiese und Heike Breitenbach unter Mitarbeit von Andreas Losch"},{"n":"1"},{"p":"Text"},{"c":"Martin Buber ; im Auftrag der Philosophischen Fakult©Þt der Heinrich Heine Universit©Þt D©ơsseldorf und der Israel Acadademy of Sciences and Humanities herausgegeben von Paul Mendes-Flohr und Bernd Witte"}]},"250":{" ":[{"a":"1. Auflage"}]},"264":{" 1":[{"a":"G©ơtersloh"},{"b":"G©ơtersloher Verlagshaus"},{"c":"[2019]"}]},"264":{" 4":[{"c":"℗♭ 2019"}]},"300":{" ":[{"a":"726 Seiten"},{"c":"23 cm"}]},"336":{" ":[{"a":"Text"},{"b":"txt"},{"2":"rdacontent"}]},"337":{" ":[{"a":"ohne Hilfsmittel zu benutzen"},{"b":"n"},{"2":"rdamedia"}]},"338":{" ":[{"a":"Band"},{"b":"nc"},{"2":"rdacarrier"}]},"655":{" 7":[{"a":"Quelle"},{"0":"(DE-588)4135952-5"},{"0":"(DE-627)105661236"},{"0":"(DE-576)209665084"},{"2":"gnd-content"}]},"689":{"00":[{"D":"u"},{"0":"(DE-588)4001515-4"},{"0":"(DE-627)104603666"},{"0":"(DE-576)208843116"},{"a":"Bibel"},{"p":"Altes Testament"},{"2":"gnd"}]},"689":{"01":[{"D":"s"},{"0":"(DE-588)4015950-4"},{"0":"(DE-627)104675314"},{"0":"(DE-576)20891434X"},{"a":"Exegese"},{"2":"gnd"}]},"689":{"02":[{"D":"s"},{"0":"(DE-588)4136677-3"},{"0":"(DE-627)105655740"},{"0":"(DE-576)209671181"},{"a":"J©ơdische Philosophie"},{"2":"gnd"}]},"689":{"0 ":[{"5":"(DE-627)"}]},"689":{"10":[{"D":"u"},{"0":"(DE-588)4001515-4"},{"0":"(DE-627)104603666"},{"0":"(DE-576)208843116"},{"a":"Bibel"},{"p":"Altes Testament"},{"2":"gnd"}]},"689":{"11":[{"D":"s"},{"0":"(DE-588)4059758-1"},{"0":"(DE-627)104131446"},{"0":"(DE-576)209132159"},{"a":"Theologie"},{"2":"gnd"}]},"689":{"1 ":[{"5":"(DE-627)"}]},"700":{"1 ":[{"a":"Wiese, Christian"},{"d":"1961-"},{"e":"HerausgeberIn"},{"0":"(DE-588)121451917"},{"0":"(DE-627)081317069"},{"0":"(DE-576)181294958"},{"4":"edt"}]},"700":{"1 ":[{"a":"Fishbane, Michael A."},{"d":"1943-"},{"e":"VerfasserIn einer Einleitung"},{"0":"(DE-588)131399837"},{"0":"(DE-627)50877294X"},{"0":"(DE-576)178775916"},{"4":"win"}]},"700":{"1 ":[{"a":"Buber, Martin"},{"d":"1878-1965"},{"t":"Schriften zur biblischen Religion"}]},"773":{"18":[{"w":"(DE-627)1165587912"},{"w":"(DE-576)095587918"},{"g":"13,1"},{"q":"13,1"}]},"856":{"42":[{"u":"http://deposit.d-nb.de/cgi-bin/dokserv?id=4186947&prov=M&dok_var=1&dok_ext=htm"},{"n":"Verlag: MVB"},{"q":"text/html"},{"v":"2013-05-01"},{"3":"Inhaltstext"}]},"856":{"42":[{"u":"http://swbplus.bsz-bw.de/bsz730849546inh.htm"},{"m":"V:DE-576;B:DE-21"},{"q":"application/pdf"},{"v":"20190605155100"},{"3":"Inhaltsverzeichnis"}]},"982":{" ":[{"a":"(DE-15)0025681716"},{"x":"1"}]},"969":{" ":[{"b":"FHTheol"},{"x":"1"}]},"983":{" ":[{"a":"(DE-15)3717597"}]},"969":{" ":[{"c":"B"}]},"982":{" ":[{"a":"(DE-14)34144552"},{"x":"1"}]},"984":{" ":[{"b":"FH1"},{"x":"1"}]},"983":{" ":[{"a":"(DE-14)16659087"}]},"984":{" ":[{"c":"B"}]},"900":{" ":[{"a":"PY¿ BYYN, M©ʼk♯Ê¿♯“l"}]},"900":{" ":[{"a":"Fishbane, Michael"}]},"900":{" ":[{"a":"Fi¿Łbeyn, M©ʼı̀æ♯Ê¾♯“l"}]},"900":{" ":[{"a":"B¿±ber, Mordekaj Mar£̀Ưin"}]},"900":{" ":[{"a":"Buber, Martin Mordehai"}]},"900":{" ":[{"a":"Buber, Mar£̀Ưin"}]},"900":{" ":[{"a":"Mpumper, Martin"}]},"900":{" ":[{"a":"B©»ber, Mordekay Mart©ʼn"}]},"900":{" ":[{"a":"B¿±b♯, Marutin"}]},"900":{" ":[{"a":"Buber, Mordehai Martin"}]},"900":{" ":[{"a":"Buber, Mordekhai Mar£̀Ưin"}]},"900":{" ":[{"a":"בוב׷, מ׷טין"}]},"900":{" ":[{"a":"בוב׷, מ׷דכי מ׷טין"}]},"950":{" ":[{"a":"Christliche Theologie"}]},"950":{" ":[{"a":"БоÐđослоÐøÐıÐæ"}]},"950":{" ":[{"a":"Judentum"}]},"950":{" ":[{"a":"Philosophie"}]},"950":{" ":[{"a":"©–stliche Philosophie"}]},"950":{" ":[{"a":"ИуÐþÐæУсÐðÐʻя фÐıлософÐıя"}]},"950":{" ":[{"a":"Alter Bund"}]},"950":{" ":[{"a":"Bibelauslegung"}]},"950":{" ":[{"a":"Bibelexegese"}]},"950":{" ":[{"a":"Biblische Auslegung"}]},"950":{" ":[{"a":"Bibelinterpretation"}]},"950":{" ":[{"a":"Biblische Exegese"}]},"950":{" ":[{"a":"Schriftauslegung"}]},"950":{" ":[{"a":"Bibelwissenschaft"}]},"950":{" ":[{"a":"Exeget"}]},"950":{" ":[{"a":"ÐƯÐðÐʺÐæÐđÐæтÐıÐðÐʻ"}]},"951":{" ":[{"b":"XA-AT"}]},"951":{" ":[{"b":"XA-DE"}]},"951":{" ":[{"b":"XB-IL"}]},"951":{" ":[{"b":"XY"}]},"852":{" ":[{"a":"DE-14"},{"z":"2019-05-06T09:35:35Z"}]},"852":{" ":[{"a":"DE-L325"},{"z":"2019-05-28T10:02:58Z"}]},"852":{" ":[{"a":"DE-16"},{"z":"2019-05-15T10:23:35Z"}]},"852":{" ":[{"a":"DE-15"},{"z":"2019-05-09T09:13:04Z"}]},"980":{" ":[{"a":"730849546"},{"b":"0"},{"k":"730849546"},{"o":"9730849544"}]}} \ No newline at end of file