From f218529cd556dae38ba6cfdad91032dd8c386341 Mon Sep 17 00:00:00 2001 From: Falkreon Date: Fri, 24 Feb 2023 15:40:44 -0600 Subject: [PATCH] Rip and tear - remove 1.2.x API --- .../blue/endless/jankson/api/Jankson.java | 391 ---------- .../blue/endless/jankson/api/Marshaller.java | 10 +- .../jankson/api/element/JsonArray.java | 539 -------------- .../jankson/api/element/JsonElement.java | 60 -- .../endless/jankson/api/element/JsonNull.java | 66 -- .../jankson/api/element/JsonObject.java | 672 ------------------ .../jankson/api/element/JsonPrimitive.java | 290 -------- .../jankson/impl/AnnotatedElement.java | 51 -- .../endless/jankson/impl/MarshallerImpl.java | 663 +++++++++-------- .../jankson/impl/POJODeserializer.java | 498 +++++++------ .../jankson/impl/context/ElementContext.java | 52 -- .../jankson/impl/context/ParserContext.java | 52 -- .../jankson/impl/context/ParserFrame.java | 48 -- .../impl/context/json/ArrayParserContext.java | 83 --- .../context/json/CommentParserContext.java | 124 ---- .../context/json/ElementParserContext.java | 156 ---- .../context/json/NumberParserContext.java | 121 ---- .../context/json/ObjectElementContext.java | 120 ---- .../context/json/ObjectParserContext.java | 168 ----- .../context/json/StringParserContext.java | 184 ----- .../impl/context/json/TokenParserContext.java | 83 --- .../serializer/DeserializerFunctionPool.java | 11 +- .../java/blue/endless/jankson/BasicTests.java | 202 +++--- .../blue/endless/jankson/TestCosmetic.java | 202 +++--- .../endless/jankson/TestDeserializer.java | 406 ++++++----- .../blue/endless/jankson/TestSerializer.java | 164 +++-- 26 files changed, 1061 insertions(+), 4355 deletions(-) delete mode 100644 src/main/java/blue/endless/jankson/api/element/JsonArray.java delete mode 100644 src/main/java/blue/endless/jankson/api/element/JsonElement.java delete mode 100644 src/main/java/blue/endless/jankson/api/element/JsonNull.java delete mode 100644 src/main/java/blue/endless/jankson/api/element/JsonObject.java delete mode 100644 src/main/java/blue/endless/jankson/api/element/JsonPrimitive.java delete mode 100644 src/main/java/blue/endless/jankson/impl/AnnotatedElement.java delete mode 100644 src/main/java/blue/endless/jankson/impl/context/ElementContext.java delete mode 100644 src/main/java/blue/endless/jankson/impl/context/ParserContext.java delete mode 100644 src/main/java/blue/endless/jankson/impl/context/ParserFrame.java delete mode 100644 src/main/java/blue/endless/jankson/impl/context/json/ArrayParserContext.java delete mode 100644 src/main/java/blue/endless/jankson/impl/context/json/CommentParserContext.java delete mode 100644 src/main/java/blue/endless/jankson/impl/context/json/ElementParserContext.java delete mode 100644 src/main/java/blue/endless/jankson/impl/context/json/NumberParserContext.java delete mode 100644 src/main/java/blue/endless/jankson/impl/context/json/ObjectElementContext.java delete mode 100644 src/main/java/blue/endless/jankson/impl/context/json/ObjectParserContext.java delete mode 100644 src/main/java/blue/endless/jankson/impl/context/json/StringParserContext.java delete mode 100644 src/main/java/blue/endless/jankson/impl/context/json/TokenParserContext.java diff --git a/src/main/java/blue/endless/jankson/api/Jankson.java b/src/main/java/blue/endless/jankson/api/Jankson.java index baa75a1..fd08bcc 100644 --- a/src/main/java/blue/endless/jankson/api/Jankson.java +++ b/src/main/java/blue/endless/jankson/api/Jankson.java @@ -24,411 +24,20 @@ package blue.endless.jankson.api; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.OutputStream; import java.io.Reader; import java.io.StringReader; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import javax.annotation.Nonnull; import blue.endless.jankson.api.document.DocumentBuilder; import blue.endless.jankson.api.document.ValueElement; -import blue.endless.jankson.api.element.JsonElement; -import blue.endless.jankson.api.element.JsonNull; -import blue.endless.jankson.api.element.JsonObject; -import blue.endless.jankson.api.io.JsonIOException; import blue.endless.jankson.api.io.JsonReader; import blue.endless.jankson.api.io.JsonReaderOptions; -import blue.endless.jankson.impl.AnnotatedElement; -import blue.endless.jankson.impl.context.ParserContext; -import blue.endless.jankson.impl.context.json.ElementParserContext; -import blue.endless.jankson.impl.context.json.ObjectParserContext; public class Jankson { - private Deque> contextStack = new ArrayDeque<>(); - private JsonObject root; - private int line = 0; - private int column = 0; - private int withheldCodePoint = -1; - @SuppressWarnings("deprecation") - private Marshaller marshaller = blue.endless.jankson.impl.MarshallerImpl.getFallback(); - private boolean allowBareRootObject = false; - - private int retries = 0; - private SyntaxError delayedError = null; - - private Jankson(Builder builder) {} - - @Nonnull - public JsonObject load(String s) throws SyntaxError { - ByteArrayInputStream in = new ByteArrayInputStream(s.getBytes(Charset.forName("UTF-8"))); - try { - return load(in); - } catch (IOException ex) { - throw new RuntimeException(ex); //ByteArrayInputStream never throws - } - } - - @Nonnull - public JsonObject load(File f) throws IOException, SyntaxError { - try(InputStream in = new FileInputStream(f)) { - return load(in); - } - } - - @Nonnull - public JsonObject load(InputStream in) throws IOException, SyntaxError { - InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8); - - withheldCodePoint = -1; - root = null; - - push(new ObjectParserContext(allowBareRootObject), (it)->{ - root = it; - }); - - //int codePoint = 0; - while (root==null) { - if (delayedError!=null) { - throw delayedError; - } - - if (withheldCodePoint!=-1) { - retries++; - if (retries>25) throw new IOException("Parser got stuck near line "+line+" column "+column); - processCodePoint(withheldCodePoint); - } else { - //int inByte = getCodePoint(in); - int inByte = reader.read(); - if (inByte==-1) { - //Walk up the stack sending EOF to things until either an error occurs or the stack completes - while(!contextStack.isEmpty()) { - ParserFrame frame = contextStack.pop(); - try { - frame.context.eof(); - if (frame.context.isComplete()) { - frame.supply(); - } - } catch (SyntaxError error) { - error.setStartParsing(frame.startLine, frame.startCol); - error.setEndParsing(line, column); - throw error; - } - } - if (root==null) { - root = new JsonObject(); - root.setMarshaller(marshaller); - } - return root; - } - processCodePoint(inByte); - } - } - - return root; - } - - /** Experimental: Parses the supplied String as a JsonElement, which may or may not be an object at the root level */ - @Nonnull - public JsonElement loadElement(String s) throws SyntaxError { - ByteArrayInputStream in = new ByteArrayInputStream(s.getBytes(Charset.forName("UTF-8"))); - try { - return loadElement(in); - } catch (IOException ex) { - throw new RuntimeException(ex); //ByteArrayInputStream never throws - } - } - - /** Experimental: Parses the supplied File as a JsonElement, which may or may not be an object at the root level */ - @Nonnull - public JsonElement loadElement(File f) throws IOException, SyntaxError { - try(InputStream in = new FileInputStream(f)) { - return loadElement(in); - } - } - - private AnnotatedElement rootElement; - /** Experimental: Parses the supplied InputStream as a JsonElement, which may or may not be an object at the root level */ - @Nonnull - public JsonElement loadElement(InputStream in) throws IOException, SyntaxError { - InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8); - - withheldCodePoint = -1; - rootElement = null; - - push(new ElementParserContext(), (it)->{ - rootElement = it; - }); - - //int codePoint = 0; - while (rootElement==null) { - if (delayedError!=null) { - throw delayedError; - } - - if (withheldCodePoint!=-1) { - retries++; - if (retries>25) throw new IOException("Parser got stuck near line "+line+" column "+column); - processCodePoint(withheldCodePoint); - } else { - //int inByte = getCodePoint(in); - int inByte = reader.read(); - if (inByte==-1) { - //Walk up the stack sending EOF to things until either an error occurs or the stack completes - while(!contextStack.isEmpty()) { - ParserFrame frame = contextStack.pop(); - try { - frame.context.eof(); - if (frame.context.isComplete()) { - frame.supply(); - } - } catch (SyntaxError error) { - error.setStartParsing(frame.startLine, frame.startCol); - error.setEndParsing(line, column); - throw error; - } - } - if (rootElement==null) { - return JsonNull.INSTANCE; - } else { - return rootElement.getElement(); - } - } - processCodePoint(inByte); - } - } - - return rootElement.getElement(); - } - - public T fromJson(JsonObject obj, Class clazz) { - return marshaller.marshall(clazz, obj); - } - - public T fromJson(String json, Class clazz) throws SyntaxError { - JsonObject obj = load(json); - return fromJson(obj, clazz); - } - - /** - * Converts a String of json into an object of the specified class in fail-fast mode, throwing an exception - * proactively if problems arise. - * @param json A string containing json data to be unpacked - * @param clazz The class to convert the data into - * @return An object representing the data in json - * @throws SyntaxError If the json cannot be parsed - * @throws JsonIOException If the conversion into an instance of the specified type fails - */ - public T fromJsonCarefully(String json, Class clazz) throws SyntaxError, JsonIOException { - JsonObject obj = load(json); - return fromJsonCarefully(obj, clazz); - } - - /** - * Converts a JsonObject into an object of the specified class, in fail-fast mode, throwing an exception - * proactively if problems arise - * @param obj A JsonObject holding the data to be unpacked - * @param clazz The class to convert the data into - * @return An object of the specified class, holding the data from json - * @throws JsonIOException If the conversion into an instance of the specified type fails - */ - public T fromJsonCarefully(JsonObject obj, Class clazz) throws JsonIOException { - return marshaller.marshallCarefully(clazz, obj); - } - - public JsonElement toJson(T t) { - return marshaller.serialize(t); - } - - public JsonElement toJson(T t, blue.endless.jankson.api.Marshaller alternateMarshaller) { - return alternateMarshaller.serialize(t); - } - - private void processCodePoint(int codePoint) throws SyntaxError { - ParserFrame frame = contextStack.peek(); - if (frame==null) throw new IllegalStateException("Parser problem! The ParserContext stack underflowed! (line "+line+", col "+column+")"); - - //Do a limited amount of tail call recursion - try { - if (frame.context().isComplete()) { - contextStack.pop(); - frame.supply(); - frame = contextStack.peek(); - } - } catch (SyntaxError error) { - error.setStartParsing(frame.startLine, frame.startCol); - error.setEndParsing(line, column); - throw error; - } - - try { - boolean consumed = frame.context.consume(codePoint, this); - if (frame.context.isComplete()) { - contextStack.pop(); - frame.supply(); - } - if (consumed) { - withheldCodePoint = -1; - retries=0; - } else { - withheldCodePoint = codePoint; - } - - } catch (SyntaxError error) { - error.setStartParsing(frame.startLine, frame.startCol); - error.setEndParsing(line, column); - throw error; - } - - column++; - if (codePoint=='\n') { - line++; - column = 0; - } - } - - - /** Pushes a context onto the stack. MAY ONLY BE CALLED BY THE ACTIVE CONTEXT */ - public void push(ParserContext t, Consumer consumer) { - ParserFrame frame = new ParserFrame(t, consumer); - frame.startLine = line; - frame.startCol = column; - contextStack.push(frame); - } - - public blue.endless.jankson.api.Marshaller getMarshaller() { - return marshaller; - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - @SuppressWarnings("deprecation") - blue.endless.jankson.impl.MarshallerImpl marshaller = new blue.endless.jankson.impl.MarshallerImpl(); - boolean allowBareRootObject = false; - - /** - * Registers a deserializer that can transform a JsonObject into an instance of the specified class. Please note - * that these type adapters are unsuitable for generic types, as these types are erased during jvm execution. - * @param clazz The class to register deserialization for - * @param adapter A function which takes a JsonObject and converts it into an equivalent object of the class `clazz` - * @return This Builder for further modification. - * @deprecated please use {@link #registerDeserializer(Class, Class, DeserializerFunction)} instead. - */ - @Deprecated - public Builder registerTypeAdapter(Class clazz, Function adapter) { - marshaller.registerTypeAdapter(clazz, adapter); - return this; - } - - /** - * Registers a marshaller for primitive types. Most built-in json and java types are already supported, but this - * allows one to change the deserialization behavior of Json primitives. Please note that these adapters are not - * suitable for generic types, as these types are erased during jvm execution. - * @param clazz The class to register a type adapter for - * @param adapter A function which takes a plain java object and converts it into the class `clazz` - * @return This Builder for further modification. - * @deprecated please use {@link #registerDeserializer(Class, Class, DeserializerFunction)} instead. - */ - @Deprecated - public Builder registerPrimitiveTypeAdapter(Class clazz, Function adapter) { - marshaller.register(clazz, adapter); - return this; - } - - /** - * Registers a function to serialize an object into json. This can be useful if a class's serialized form is not - * meant to resemble its live-memory form. - * @param clazz The class to register a serializer for - * @param serializer A function which takes the object and a Marshaller, and produces a serialized JsonElement - * @return This Builder for further modificaton. - */ - @SuppressWarnings("deprecation") - public Builder registerSerializer(Class clazz, BiFunction serializer) { - marshaller.registerSerializer(clazz, serializer); - return this; - } - - @SuppressWarnings("deprecation") - public Builder registerDeserializer(Class sourceClass, Class targetClass, DeserializerFunction function) { - marshaller.registerDeserializer(sourceClass, targetClass, function); - return this; - } - - /** - * Registers a factory that can generate empty objects of the specified type. Sometimes it's not practical - * to have a no-arg constructor available on an object, so the function to create blanks can be specified - * here. - * @param clazz The class to use an alternate factory for - * @param factory A Supplier which can create blank objects of class `clazz` for deserialization - * @return This Builder for further modification. - */ - @SuppressWarnings("deprecation") - public Builder registerTypeFactory(Class clazz, Supplier factory) { - marshaller.registerTypeFactory(clazz, factory); - return this; - } - - /** - * Allows loading JSON files that do not contain root braces, as generated with - * {@link JsonGrammar.Builder#bareRootObject(boolean) bareRootObject}. - * @return This Builder for further modification. - */ - public Builder allowBareRootObject() { - allowBareRootObject = true; - return this; - } - - public Jankson build() { - Jankson result = new Jankson(this); - result.marshaller = marshaller; - result.allowBareRootObject = allowBareRootObject; - return result; - } - } - - private static class ParserFrame { - private ParserContext context; - private Consumer consumer; - private int startLine = 0; - private int startCol = 0; - - public ParserFrame(ParserContext context, Consumer consumer) { - this.context = context; - this.consumer = consumer; - } - - public ParserContext context() { return context; } - //public Consumer consumer() { return consumer; } - - /** Feed the result directly from the context at this entry to its corresponding consumer */ - public void supply() throws SyntaxError { - consumer.accept(context.getResult()); - } - } - - public void throwDelayed(SyntaxError syntaxError) { - syntaxError.setEndParsing(line, column); - delayedError = syntaxError; - } - - /* Jankson 2.0 API */ /** * Reads in json data from a String using the settings provided. diff --git a/src/main/java/blue/endless/jankson/api/Marshaller.java b/src/main/java/blue/endless/jankson/api/Marshaller.java index c06cdb1..868fa1c 100644 --- a/src/main/java/blue/endless/jankson/api/Marshaller.java +++ b/src/main/java/blue/endless/jankson/api/Marshaller.java @@ -25,13 +25,11 @@ package blue.endless.jankson.api; import java.lang.reflect.Type; - -import blue.endless.jankson.api.element.JsonElement; import blue.endless.jankson.api.io.JsonIOException; public interface Marshaller { /** Turns a java object into its json intermediate representation. */ - JsonElement serialize(Object obj); + //JsonElement serialize(Object obj); /** * Unpacks the provided JsonElement into a new object of type {@code clazz}, making a best @@ -46,7 +44,7 @@ public interface Marshaller { * @param The type of the object to create and deserialize * @return A new object of the provided class that represents the data in the json provided. */ - E marshall(Class clazz, JsonElement elem); + // E marshall(Class clazz, JsonElement elem); /** * Unpacks the provided JsonElement into an object of the provided Type, and force-casts it to @@ -56,7 +54,7 @@ public interface Marshaller { * @param The type to force-cast to at the end * @return A new object of the provided Type that represents the data in the json provided. */ - E marshall(Type type, JsonElement elem); + // E marshall(Type type, JsonElement elem); /** * Unpacks the provided JsonElement in fail-fast mode. A detailed exception is thrown for any @@ -67,5 +65,5 @@ public interface Marshaller { * @return A new object of the provided class that represents the data in the json provided. * @throws JsonIOException if any problems are encountered unpacking the data. */ - E marshallCarefully(Class clazz, JsonElement elem) throws JsonIOException; + // E marshallCarefully(Class clazz, JsonElement elem) throws JsonIOException; } diff --git a/src/main/java/blue/endless/jankson/api/element/JsonArray.java b/src/main/java/blue/endless/jankson/api/element/JsonArray.java deleted file mode 100644 index a402c53..0000000 --- a/src/main/java/blue/endless/jankson/api/element/JsonArray.java +++ /dev/null @@ -1,539 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2023 Falkreon (Isaac Ellingson) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package blue.endless.jankson.api.element; - -import java.io.IOException; -import java.io.Writer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.Objects; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import blue.endless.jankson.api.JsonGrammar; -import blue.endless.jankson.api.Marshaller; -import blue.endless.jankson.impl.serializer.CommentSerializer; - -@SuppressWarnings("deprecation") -@Deprecated -public class JsonArray extends JsonElement implements List, Iterable { - private List entries = new ArrayList<>(); - protected Marshaller marshaller = blue.endless.jankson.impl.MarshallerImpl.getFallback(); - - public JsonArray() {} - - public JsonArray(T[] ts, Marshaller marshaller) { - this.marshaller = marshaller; - for(T t : ts) { - this.add(marshaller.serialize(t)); - } - } - - public JsonArray(Collection ts, Marshaller marshaller) { - this.marshaller = marshaller; - for(Object t : ts) { - this.add(marshaller.serialize(t)); - } - } - - public JsonElement get(int i) { - return entries.get(i).value; - } - - //Convenience getters - - public String getString(int index, String defaultValue) { - JsonElement elem = get(index); - if (elem != null && elem instanceof JsonPrimitive) { - return ((JsonPrimitive)elem).asString(); - } - return defaultValue; - } - - public boolean getBoolean(int index, boolean defaultValue) { - JsonElement elem = get(index); - if (elem != null && elem instanceof JsonPrimitive) { - return ((JsonPrimitive)elem).asBoolean(defaultValue); - } - return defaultValue; - } - - public byte getByte(int index, byte defaultValue) { - JsonElement elem = get(index); - if (elem != null && elem instanceof JsonPrimitive) { - return ((JsonPrimitive)elem).asByte(defaultValue); - } - return defaultValue; - } - - public char getChar(int index, char defaultValue) { - JsonElement elem = get(index); - if (elem != null && elem instanceof JsonPrimitive) { - return ((JsonPrimitive)elem).asChar(defaultValue); - } - return defaultValue; - } - - public short getShort(int index, short defaultValue) { - JsonElement elem = get(index); - if (elem != null && elem instanceof JsonPrimitive) { - return ((JsonPrimitive)elem).asShort(defaultValue); - } - return defaultValue; - } - - public int getInt(int index, int defaultValue) { - JsonElement elem = get(index); - if (elem != null && elem instanceof JsonPrimitive) { - return ((JsonPrimitive)elem).asInt(defaultValue); - } - return defaultValue; - } - - public long getLong(int index, long defaultValue) { - JsonElement elem = get(index); - if (elem != null && elem instanceof JsonPrimitive) { - return ((JsonPrimitive)elem).asLong(defaultValue); - } - return defaultValue; - } - - public float getFloat(int index, float defaultValue) { - JsonElement elem = get(index); - if (elem != null && elem instanceof JsonPrimitive) { - return ((JsonPrimitive)elem).asFloat(defaultValue); - } - return defaultValue; - } - - public double getDouble(int index, double defaultValue) { - JsonElement elem = get(index); - if (elem != null && elem instanceof JsonPrimitive) { - return ((JsonPrimitive)elem).asDouble(defaultValue); - } - return defaultValue; - } - - public String getComment(int i) { - return entries.get(i).getComment(); - } - - public void setComment(int i, String comment) { - entries.get(i).setComment(comment); - } - - @Override - public String toJson(boolean comments, boolean newlines, int depth) { - JsonGrammar grammar = JsonGrammar.builder().withComments(comments).printWhitespace(newlines).build(); - return toJson(grammar, depth); - } - - @Override - public void toJson(Writer writer, JsonGrammar grammar, int depth) throws IOException { - int effectiveDepth = (grammar.isBareRootObject()) ? depth-1 : depth; - //int nextDepth = (grammar.bareRootObject) ? depth-1 : depth; - - writer.append("["); - - if (entries.size()>0) { - if (grammar.shouldOutputWhitespace()) { - writer.append('\n'); - } else { - writer.append(' '); - } - } - - for(int i=0; i0) { - if (grammar.shouldOutputWhitespace() && depth>0) { - for(int j=0; j0) { - if (!grammar.shouldOutputWhitespace()) writer.append(' '); - } - - writer.append(']'); - } - - public String toString() { - return toJson(true, false, 0); - } - - public boolean add(@Nonnull JsonElement e, String comment) { - //if (contains(e)) return false; - - Entry entry = new Entry(); - entry.value = e; - entry.setComment(comment); - entries.add(entry); - return true; - } - - @Override - public boolean equals(Object other) { - if (other==null || !(other instanceof JsonArray)) return false; - - List a = this.entries; - List b = ((JsonArray)other).entries; - if (a.size()!=b.size()) return false; - - for(int i=0; i E get(@Nonnull Class clazz, int index) { - JsonElement elem = get(index); - return marshaller.marshall(clazz, elem); - } - - - - public void setMarshaller(Marshaller marshaller) { - this.marshaller = marshaller; - } - - public Marshaller getMarshaller() { - return this.marshaller; - } - - //IMPLEMENTATION for Cloneable - @Override - public JsonArray clone() { - JsonArray result = new JsonArray(); - result.marshaller = marshaller; - for(Entry entry : entries) { - result.add(entry.value.clone(), entry.getComment()); - } - return result; - } - - //implements List { - - @Override - public int size() { - return entries.size(); - } - - @Override - public boolean add(@Nonnull JsonElement e) { - Entry entry = new Entry(); - entry.value = e; - entries.add(entry); - return true; - } - - @Override - public boolean addAll(Collection c) { - boolean result = false; - for(JsonElement elem : c) result |= add(elem); - - return result; - } - - @Override - public void clear() { - entries.clear(); - } - - @Override - public boolean contains(Object o) { - if (o==null || !(o instanceof JsonElement)) return false; - - for(Entry entry : entries) { - if (entry.value.equals(o)) return true; - } - return false; - } - - @Override - public boolean containsAll(Collection c) { - for(Object o : c) { - if (!contains(o)) return false; - } - - return true; - } - - @Override - public boolean isEmpty() { - return entries.isEmpty(); - } - - @Override - public boolean remove(Object o) { - for(int i=0; i c) { - throw new UnsupportedOperationException("removeAll not supported"); - } - - @Override - public boolean retainAll(Collection c) { - throw new UnsupportedOperationException("retainAll not supported"); - } - - @Override - public JsonElement[] toArray() { - JsonElement[] result = new JsonElement[entries.size()]; - for(int i=0; i T[] toArray(T[] a) { - if (a.lengthentries.size()) { - a[entries.size()] = null; //Little-known and basically unused quirk of the toArray contract - } - return a; - } - - @Override - public Iterator iterator() { - return new EntryIterator(entries); - } - - @Override - public void add(int index, JsonElement element) { - entries.add(index, new Entry(element)); - } - - @Override - public boolean addAll(int index, Collection elements) { - if (elements.isEmpty()) return false; - int i = index; - for (JsonElement element : elements) { - entries.add(i, new Entry(element)); - i++; - } - return true; - } - - @Override - public int indexOf(Object obj) { - if (obj==null) return -1; - for(int i=0; i=0; i--) { - JsonElement val = entries.get(i).value; - if (val!=null && val.equals(obj)) return i; - } - return -1; - } - - @Override - public ListIterator listIterator() { - return new EntryIterator(entries); - } - - @Override - public ListIterator listIterator(int index) { - return new EntryIterator(entries, index); - } - - @Override - public JsonElement remove(int index) { - return entries.remove(index).value; - } - - @Override - public JsonElement set(int index, JsonElement element) { - Entry cur = new Entry(element); - Entry old = entries.get(index); - if (old!=null) cur.setComment(old.getComment()); - entries.set(index, cur); - - return (old==null) ? null : old.value; - } - - @Override - public List subList(int arg0, int arg1) { - throw new UnsupportedOperationException(); //TODO: Implement - } - - //} - - - //MISC CLASSES - - private static class EntryIterator implements ListIterator { - private final ListIterator delegate; - - public EntryIterator(List list) { - delegate = list.listIterator(); - } - - public EntryIterator(List list, int index) { - delegate = list.listIterator(index); - } - - @Override - public boolean hasNext() { - return delegate.hasNext(); - } - - @Override - public JsonElement next() { - return delegate.next().value; - } - - @Override - public void remove() { - delegate.remove(); - } - - @Override - public void add(JsonElement elem) { - delegate.add(new Entry(elem)); - } - - @Override - public boolean hasPrevious() { - return delegate.hasPrevious(); - } - - @Override - public int nextIndex() { - return delegate.nextIndex(); - } - - @Override - public JsonElement previous() { - return delegate.previous().value; - } - - @Override - public int previousIndex() { - return delegate.previousIndex(); - } - - @Override - public void set(JsonElement obj) { - delegate.set(new Entry(obj)); - } - } - - private static class Entry { - String comment; - JsonElement value; - - public Entry() {} - public Entry(JsonElement value) { this.value = value; } - - @Override - public boolean equals(Object other) { - if (!(other instanceof Entry)) return false; - Entry o = (Entry)other; - return Objects.equals(comment, o.comment) && - Objects.equals(value, o.value); - } - - public String getComment() { - return comment; - } - - public void setComment(String comment) { - if (comment!=null && !comment.trim().isEmpty()) { - this.comment = comment; - } else { - this.comment = null; - } - } - - @Override - public int hashCode() { - return Objects.hash(comment, value); - } - } -} diff --git a/src/main/java/blue/endless/jankson/api/element/JsonElement.java b/src/main/java/blue/endless/jankson/api/element/JsonElement.java deleted file mode 100644 index 51b701e..0000000 --- a/src/main/java/blue/endless/jankson/api/element/JsonElement.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2023 Falkreon (Isaac Ellingson) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package blue.endless.jankson.api.element; - -import java.io.IOException; -import java.io.StringWriter; -import java.io.Writer; - -import blue.endless.jankson.api.JsonGrammar; - -/** Tagging class for Json objects, arrays, and primitives */ -@Deprecated -public abstract class JsonElement implements Cloneable { - public abstract JsonElement clone(); - public String toJson() { - return toJson(false, false, 0); - } - public String toJson(boolean comments, boolean newlines) { - return toJson(comments, newlines, 0); - } - @Deprecated - public abstract String toJson(boolean comments, boolean newlines, int depth); - public String toJson(JsonGrammar grammar, int depth) { - StringWriter w = new StringWriter(); - try { - toJson(w, grammar, depth); - w.flush(); - return w.toString(); - } catch (IOException ex) { - throw new RuntimeException(ex); - } - } - public String toJson(JsonGrammar grammar) { - return toJson(grammar, 0); - } - - public abstract void toJson(Writer writer, JsonGrammar grammar, int depth) throws IOException; -} diff --git a/src/main/java/blue/endless/jankson/api/element/JsonNull.java b/src/main/java/blue/endless/jankson/api/element/JsonNull.java deleted file mode 100644 index 13b7505..0000000 --- a/src/main/java/blue/endless/jankson/api/element/JsonNull.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2023 Falkreon (Isaac Ellingson) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package blue.endless.jankson.api.element; - -import java.io.IOException; -import java.io.Writer; - -import blue.endless.jankson.api.JsonGrammar; - -@Deprecated -public class JsonNull extends JsonElement { - public static final JsonNull INSTANCE = new JsonNull(); - private JsonNull() {} - - public String toString() { - return "null"; - } - - @Override - public boolean equals(Object other) { - return other==JsonNull.INSTANCE; - } - - @Override - public int hashCode() { - return 0; - } - - @Override - public String toJson(boolean comments, boolean newlines, int depth) { - return "null"; - } - - @Override - public void toJson(Writer writer, JsonGrammar grammar, int depth) throws IOException { - writer.write("null"); - } - - //IMPLEMENTATION for Cloneable - @Override - public JsonNull clone() { - return this; //Technically violates the contract for Cloneable, but this is a singleton - } -} diff --git a/src/main/java/blue/endless/jankson/api/element/JsonObject.java b/src/main/java/blue/endless/jankson/api/element/JsonObject.java deleted file mode 100644 index bca68d4..0000000 --- a/src/main/java/blue/endless/jankson/api/element/JsonObject.java +++ /dev/null @@ -1,672 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2023 Falkreon (Isaac Ellingson) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package blue.endless.jankson.api.element; - -import java.io.IOException; -import java.io.Writer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.function.Predicate; -import java.util.regex.Pattern; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import blue.endless.jankson.api.JsonGrammar; -import blue.endless.jankson.api.Marshaller; -import blue.endless.jankson.impl.serializer.CommentSerializer; - -@Deprecated -public class JsonObject extends JsonElement implements Map { - /** This pattern matches JsonObject keys that are permitted to appear unquoted */ - private static final Predicate CAN_BE_UNQUOTED = Pattern.compile("^[a-zA-Z0-9]+$").asPredicate(); - @SuppressWarnings("deprecation") - protected Marshaller marshaller = blue.endless.jankson.impl.MarshallerImpl.getFallback(); - private List entries = new ArrayList<>(); - - /** - * If there is an entry at this key, and that entry is a json object, return it. Otherwise returns null. - */ - @Nullable - public JsonObject getObject(@Nonnull String name) { - for(Entry entry : entries) { - if (entry.key.equalsIgnoreCase(name)) { - if (entry.value instanceof JsonObject) { - return (JsonObject)entry.value; - } else { - return null; - } - } - } - - return null; - } - - /** - * Replaces a key-value mapping in this object if it exists, or adds the mapping to the end of the object if it - * doesn't. Returns the old value mapped to this key if there was one. - */ - public JsonElement put(@Nonnull String key, @Nonnull JsonElement elem, @Nullable String comment) { - for(Entry entry : entries) { - if (entry.key.equalsIgnoreCase(key)) { - JsonElement result = entry.value; - entry.value = elem; - entry.setComment(comment); - return result; - } - } - - //If we reached here, there's no existing mapping, so make one. - Entry entry = new Entry(); - if (elem instanceof JsonObject) ((JsonObject)elem).marshaller = marshaller; - if (elem instanceof JsonArray) ((JsonArray)elem).marshaller = marshaller; - entry.key = key; - entry.value = elem; - entry.setComment(comment); - entries.add(entry); - return null; - } - - @Nonnull - public JsonElement putDefault(@Nonnull String key, @Nonnull JsonElement elem, @Nullable String comment) { - for(Entry entry : entries) { - if (entry.key.equalsIgnoreCase(key)) { - return entry.value; - } - } - - //If we reached here, there's no existing mapping, so make one. - Entry entry = new Entry(); - entry.key = key; - entry.value = elem; - entry.setComment(comment); - entries.add(entry); - return elem; - } - - /** May return null if the existing object can't be marshalled to elem's class */ - @SuppressWarnings("unchecked") - @Nullable - public T putDefault(@Nonnull String key, @Nonnull T elem, @Nullable String comment) { - return (T) putDefault(key, elem, elem.getClass(), comment); - } - - /** May return null if the existing object can't be marshalled to the target class */ - @Nullable - public T putDefault(@Nonnull String key, @Nonnull T elem, Class clazz, @Nullable String comment) { - for(Entry entry : entries) { - if (entry.key.equalsIgnoreCase(key)) { - return (T) marshaller.marshall(clazz, entry.value); - } - } - - //If we reached here, there's no existing mapping, so make one. - Entry entry = new Entry(); - entry.key = key; - entry.value = marshaller.serialize(elem); - if (entry.value==null) entry.value = JsonNull.INSTANCE; - entry.setComment(comment); - entries.add(entry); - return elem; - } - - /** - * Gets a minimal set of key-value-comment settings which, if added to the supplied JsonObject, would produce this - * JsonObject. See BasicTests::testDiffAgainstDefaults() for more details on this comparison. - * - *
    - *
  • If a key is present in the default and not in the object, it's skipped - *
  • If a key is an object, a deep (recursive) comparison occurs. Comments are ignored in this comparison. - *
  • All other types, including lists, receive a shallow comparison of its value. The comment is ignored in this comparison. - *
  • Whether deep or shallow, if the key is found to be identical in value to its default, it is skipped. - *
  • If the key is found to be different than its default, the key, value, and comment are represented in the - * output. - *
- */ - @Nonnull - public JsonObject getDelta(@Nonnull JsonObject defaults) { - JsonObject result = new JsonObject(); - for(Entry entry : entries) { - String key = entry.key; - JsonElement defaultValue = defaults.get(key); - if (defaultValue==null) { - result.put(entry.key, entry.value, entry.getComment()); - continue; - } - - if (entry.value instanceof JsonObject) { - if (defaultValue instanceof JsonObject) { - JsonObject subDelta = ((JsonObject)entry.value).getDelta((JsonObject)defaultValue); - if (subDelta.isEmpty()) { - continue; - } else { - result.put(entry.key, subDelta, entry.getComment()); - continue; - } - } - } - - if (entry.value.equals(defaultValue)) continue; - - result.put(entry.key, entry.value, entry.getComment()); - } - - return result; - } - - /** - * Returns the comment "attached to" a given key-value mapping, which is to say, the comment appearing immediately - * before it or the single-line comment to the right of it. - */ - @Nullable - public String getComment(@Nonnull String name) { - for(Entry entry : entries) { - if (entry.key.equalsIgnoreCase(name)) { - return entry.getComment(); - } - } - - return null; - } - - public void setComment(@Nonnull String name, @Nullable String comment) { - for(Entry entry : entries) { - if (entry.key.equalsIgnoreCase(name)) { - entry.setComment(comment); - return; - } - } - } - - @Override - public String toJson(boolean comments, boolean newlines, int depth) { - JsonGrammar grammar = JsonGrammar.builder().withComments(comments).printWhitespace(newlines).build(); - return toJson(grammar, depth); - } - - @Override - public void toJson(Writer w, JsonGrammar grammar, int depth) throws IOException { - //StringBuilder builder = new StringBuilder(); - boolean skipBraces = depth==0 && grammar.isBareRootObject(); - int effectiveDepth = (grammar.isBareRootObject()) ? depth-1 : depth; - int nextDepth = (grammar.isBareRootObject()) ? depth : depth+1; - - if (!skipBraces) { - w.append("{"); - - if (grammar.shouldOutputWhitespace() && entries.size()>0) { - w.append('\n'); - } else { - w.append(' '); - } - } - - for(int i=0; i0) { - if (grammar.shouldOutputWhitespace()) { - for(int j=0; j E get(@Nonnull Class clazz, @Nonnull String key) { - if (key.isEmpty()) throw new IllegalArgumentException("Cannot get from empty key"); - - JsonElement elem = get(key); - return marshaller.marshall(clazz, elem); - } - - //Convenience getters - - public boolean getBoolean(@Nonnull String key, boolean defaultValue) { - JsonElement elem = get(key); - if (elem != null && elem instanceof JsonPrimitive) { - return ((JsonPrimitive)elem).asBoolean(defaultValue); - } - return defaultValue; - } - - public byte getByte(@Nonnull String key, byte defaultValue) { - JsonElement elem = get(key); - if (elem != null && elem instanceof JsonPrimitive) { - return ((JsonPrimitive)elem).asByte(defaultValue); - } - return defaultValue; - } - - public char getChar(@Nonnull String key, char defaultValue) { - JsonElement elem = get(key); - if (elem != null && elem instanceof JsonPrimitive) { - return ((JsonPrimitive)elem).asChar(defaultValue); - } - return defaultValue; - } - - public short getShort(@Nonnull String key, short defaultValue) { - JsonElement elem = get(key); - if (elem != null && elem instanceof JsonPrimitive) { - return ((JsonPrimitive)elem).asShort(defaultValue); - } - return defaultValue; - } - - public int getInt(@Nonnull String key, int defaultValue) { - JsonElement elem = get(key); - if (elem != null && elem instanceof JsonPrimitive) { - return ((JsonPrimitive)elem).asInt(defaultValue); - } - return defaultValue; - } - - public long getLong(@Nonnull String key, long defaultValue) { - JsonElement elem = get(key); - if (elem != null && elem instanceof JsonPrimitive) { - return ((JsonPrimitive)elem).asLong(defaultValue); - } - return defaultValue; - } - - public float getFloat(@Nonnull String key, float defaultValue) { - JsonElement elem = get(key); - if (elem != null && elem instanceof JsonPrimitive) { - return ((JsonPrimitive)elem).asFloat(defaultValue); - } - return defaultValue; - } - - public double getDouble(@Nonnull String key, double defaultValue) { - JsonElement elem = get(key); - if (elem != null && elem instanceof JsonPrimitive) { - return ((JsonPrimitive)elem).asDouble(defaultValue); - } - return defaultValue; - } - - /** - * Gets a (potentially nested) element from this object if it exists. - * @param clazz The expected class of the element - * @param key The keys of the nested elements, separated by periods, such as "foo.bar.baz" - * @return The element at that location, if it exists and is of the proper type, otherwise null. - */ - @Nullable - public E recursiveGet(@Nonnull Class clazz, @Nonnull String key) { - if (key.isEmpty()) throw new IllegalArgumentException("Cannot get from empty key"); - String[] parts = key.split("\\."); - JsonObject cur = this; - for(int i=0; i E recursiveGetOrCreate(@Nonnull Class clazz, @Nonnull String key, @Nonnull E fallback, @Nullable String comment) { - if (key.isEmpty()) throw new IllegalArgumentException("Cannot get from empty key"); - String[] parts = key.split("\\."); - JsonObject cur = this; - for(int i=0; i { - - /** - * Replaces a key-value mapping in this object if it exists, or adds the mapping to the end of the object if it - * doesn't. Returns the old value mapped to this key if there was one. - */ - @Override - @Nullable - public JsonElement put(@Nonnull String key, @Nonnull JsonElement elem) { - for(Entry entry : entries) { - if (entry.key.equalsIgnoreCase(key)) { - JsonElement result = entry.value; - entry.value = elem; - return result; - } - } - - //If we reached here, there's no existing mapping, so make one. - Entry entry = new Entry(); - entry.key = key; - entry.value = elem; - entries.add(entry); - return null; - } - - @Override - public void clear() { - entries.clear(); - } - - @Override - public boolean containsKey(@Nullable Object key) { - if (key==null) return false; - if (!(key instanceof String)) return false; - - for(Entry entry : entries) { - if (entry.key.equalsIgnoreCase((String)key)) { - return true; - } - } - - return false; - } - - @Override - public boolean containsValue(@Nullable Object val) { - if (val==null) return false; - if (!(val instanceof JsonElement)) return false; - - for(Entry entry : entries) { - if (entry.value.equals(val)) return true; - } - - return false; - } - - /** - * Creates a semi-live shallow copy instead of a live view - */ - @Override - public Set> entrySet() { - Set> result = new LinkedHashSet<>(); - for(Entry entry : entries) { - result.add(new Map.Entry(){ - @Override - public String getKey() { - return entry.key; - } - - @Override - public JsonElement getValue() { - return entry.value; - } - - @Override - public JsonElement setValue(JsonElement value) { - JsonElement oldValue = entry.value; - entry.value = value; - return oldValue; - } - - }); - } - - return result; - } - - @Override - @Nullable - public JsonElement get(@Nullable Object key) { - if (key==null || !(key instanceof String)) return null; - - for(Entry entry : entries) { - if (entry.key.equalsIgnoreCase((String)key)) { - return entry.value; - } - } - return null; - } - - @Override - public boolean isEmpty() { - return entries.isEmpty(); - } - - /** Returns a defensive copy instead of a live view */ - @Override - @Nonnull - public Set keySet() { - Set keys = new HashSet<>(); - for(Entry entry : entries) { - keys.add(entry.key); - } - return keys; - } - - @Override - public void putAll(Map map) { - for(Map.Entry entry : map.entrySet()) { - put(entry.getKey(), entry.getValue()); - } - } - - @Override - @Nullable - public JsonElement remove(@Nullable Object key) { - if (key==null || !(key instanceof String)) return null; - - for(int i=0; i values() { - List values = new ArrayList<>(); - for(Entry entry : entries) { - values.add(entry.value); - } - return values; - } - //} -} diff --git a/src/main/java/blue/endless/jankson/api/element/JsonPrimitive.java b/src/main/java/blue/endless/jankson/api/element/JsonPrimitive.java deleted file mode 100644 index 774ac01..0000000 --- a/src/main/java/blue/endless/jankson/api/element/JsonPrimitive.java +++ /dev/null @@ -1,290 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2023 Falkreon (Isaac Ellingson) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package blue.endless.jankson.api.element; - -import java.io.IOException; -import java.io.Writer; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Objects; - -import javax.annotation.Nonnull; - -import blue.endless.jankson.api.Escaper; -import blue.endless.jankson.api.JsonGrammar; - -@Deprecated -public class JsonPrimitive extends JsonElement { - /** Convenience instance of json "true". Don't use identity comparison (==) on these! Use equals instead. */ - public static JsonPrimitive TRUE = new JsonPrimitive(Boolean.TRUE); - /** Convenience instance of json "false". Don't use identity comparison (==) on these! Use equals instead. */ - public static JsonPrimitive FALSE = new JsonPrimitive(Boolean.FALSE); - - @Nonnull - private Object value; - - private JsonPrimitive() {} - - /** - * Creates a new JsonPrimitive node representing the passed-in value. - * - *

Note: This constructor may do expensive type inspection to verify that the passed-in object - * is well-formed. Please use one of the JsonPrimitive.of(x) static factory variants if possible, - * because using function polymorphism often winds up validating the results "for free". - * @param value - */ - public JsonPrimitive(@Nonnull Object value) { - if (value instanceof Character) { - this.value = ""+(Character)value; - } else if (value instanceof Long) { - this.value = value; - } else if (value instanceof Double) { - this.value = value; - } else if (value instanceof BigInteger) { - this.value = ((BigInteger)value).toString(16); - } else if (value instanceof Float) { - this.value = Double.valueOf((Float)value); - } else if (value instanceof Number) { - this.value = ((Number)value).longValue(); - } else if (value instanceof CharSequence) { - this.value = value.toString(); - } else if (value instanceof Boolean) { - this.value = value; - } else { - throw new IllegalArgumentException("Object of type '"+value.getClass().getCanonicalName()+"' not allowed as a JsonPrimitive"); - } - } - - @Nonnull - public String asString() { - if (value==null) return "null"; - return value.toString(); - } - - public boolean asBoolean(boolean defaultValue) { - if (value instanceof Boolean) { - return ((Boolean) value).booleanValue(); - } else { - return defaultValue; - } - } - - public byte asByte(byte defaultValue) { - if (value instanceof Number) { - return ((Number)value).byteValue(); - } else { - return defaultValue; - } - } - - public char asChar(char defaultValue) { - if (value instanceof Number) { - return (char)((Number)value).intValue(); - } else if (value instanceof Character) { - return ((Character) value).charValue(); - } else if (value instanceof String) { - if (((String)value).length()==1) { - return ((String) value).charAt(0); - } else { - return defaultValue; - } - } else { - return defaultValue; - } - } - - public short asShort(short defaultValue) { - if (value instanceof Number) { - return ((Number)value).shortValue(); - } else { - return defaultValue; - } - } - - public int asInt(int defaultValue) { - if (value instanceof Number) { - return ((Number)value).intValue(); - } else { - return defaultValue; - } - } - - public long asLong(long defaultValue) { - if (value instanceof Number) { - return ((Number)value).longValue(); - } else { - return defaultValue; - } - } - - public float asFloat(float defaultValue) { - if (value instanceof Number) { - return ((Number)value).floatValue(); - } else { - return defaultValue; - } - } - - public double asDouble(double defaultValue) { - if (value instanceof Number) { - return ((Number)value).doubleValue(); - } else { - return defaultValue; - } - } - - public BigInteger asBigInteger(BigInteger defaultValue) { - if (value instanceof Number) { - return BigInteger.valueOf(((Number)value).longValue()); - } else if (value instanceof String) { - return new BigInteger((String)value, 16); - } else { - return defaultValue; - } - } - - public BigDecimal asBigDecimal(BigDecimal defaultValue) { - if (value instanceof Number) { - return BigDecimal.valueOf(((Number) value).doubleValue()); - } else if (value instanceof String) { - return new BigDecimal((String)value); - } else { - return defaultValue; - } - } - - @Nonnull - public String toString() { - return toJson(); - } - - @Nonnull - public Object getValue() { - return value; - } - - @Override - public boolean equals(Object other) { - if (other==null) return false; - if (other instanceof JsonPrimitive) { - return Objects.equals(value, ((JsonPrimitive)other).value); - } else { - return false; - } - } - - @Override - public int hashCode() { - return value.hashCode(); - } - - @Override - public String toJson(boolean comments, boolean newlines, int depth) { - return toJson(JsonGrammar.builder().withComments(comments).printWhitespace(newlines).build(), depth); - } - - @Override - public void toJson(Writer writer, JsonGrammar grammar, int depth) throws IOException { - - if (value==null) { - writer.write("null"); - return; - } - - if (value instanceof Double && grammar.isBareSpecialNumerics()) { - double d = ((Double)value).doubleValue(); - if (Double.isNaN(d)) { - writer.write("NaN"); - return; - } - if (Double.isInfinite(d)) { - if (d<0) { - writer.write("-Infinity"); - return; - } else { - writer.write("Infinity"); - return; - } - } - writer.write(value.toString()); - return; - } else if (value instanceof Number) { - writer.write(value.toString()); - return; - } - if (value instanceof Boolean) { - writer.write(value.toString()); - return; - } - - writer.write('\"'); - writer.write(Escaper.escapeString(value.toString())); //TODO: Configurable unicode blocks to escape? - writer.write('\"'); - } - - //IMPLEMENTATION for Cloneable - @Override - public JsonPrimitive clone() { - JsonPrimitive result = new JsonPrimitive(); - result.value = this.value; - return result; - } - - public static JsonPrimitive of(@Nonnull String s) { - JsonPrimitive result = new JsonPrimitive(); - result.value = s; - return result; - } - - public static JsonPrimitive of(@Nonnull BigInteger n) { - JsonPrimitive result = new JsonPrimitive(); - result.value = ((BigInteger)n).toString(16); - return result; - } - - public static JsonPrimitive of(@Nonnull BigDecimal n) { - JsonPrimitive result = new JsonPrimitive(); - result.value = n.toString(); //Appropriate for `new BigDecimal(s)` - return result; - } - - public static JsonPrimitive of(@Nonnull Double d) { - JsonPrimitive result = new JsonPrimitive(); - result.value = d; - return result; - } - - public static JsonPrimitive of(@Nonnull Long l) { - JsonPrimitive result = new JsonPrimitive(); - result.value = l; - return result; - } - - public static JsonPrimitive of(@Nonnull Boolean b) { - JsonPrimitive result = new JsonPrimitive(); - result.value = b; - return result; - } -} diff --git a/src/main/java/blue/endless/jankson/impl/AnnotatedElement.java b/src/main/java/blue/endless/jankson/impl/AnnotatedElement.java deleted file mode 100644 index e6b404c..0000000 --- a/src/main/java/blue/endless/jankson/impl/AnnotatedElement.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2023 Falkreon (Isaac Ellingson) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package blue.endless.jankson.impl; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import blue.endless.jankson.api.element.JsonElement; - -/** Holds both a JsonElement and its associated comment, and any other relevant data */ -public class AnnotatedElement { - protected String comment; - protected JsonElement elem; - - public AnnotatedElement(@Nonnull JsonElement elem, @Nullable String comment) { - this.comment = comment; - this.elem = elem; - } - - @Nullable - public String getComment() { - return comment; - } - - @Nonnull - public JsonElement getElement() { - return elem; - } -} diff --git a/src/main/java/blue/endless/jankson/impl/MarshallerImpl.java b/src/main/java/blue/endless/jankson/impl/MarshallerImpl.java index f49d8f5..ccff403 100644 --- a/src/main/java/blue/endless/jankson/impl/MarshallerImpl.java +++ b/src/main/java/blue/endless/jankson/impl/MarshallerImpl.java @@ -46,11 +46,6 @@ import blue.endless.jankson.api.DeserializerFunction; import blue.endless.jankson.api.Marshaller; import blue.endless.jankson.api.annotation.Comment; -import blue.endless.jankson.api.element.JsonArray; -import blue.endless.jankson.api.element.JsonElement; -import blue.endless.jankson.api.element.JsonNull; -import blue.endless.jankson.api.element.JsonObject; -import blue.endless.jankson.api.element.JsonPrimitive; import blue.endless.jankson.api.io.JsonIOException; import blue.endless.jankson.impl.serializer.DeserializerFunctionPool; import blue.endless.jankson.impl.serializer.DeserializerFunctionPool.FunctionMatchFailedException; @@ -65,9 +60,9 @@ public class MarshallerImpl implements blue.endless.jankson.api.Marshaller { public static Marshaller getFallback() { return INSTANCE; } private Map, Function> primitiveMarshallers = new HashMap<>(); - Map, Function> typeAdapters = new HashMap<>(); + //Map, Function> typeAdapters = new HashMap<>(); - private Map, BiFunction> serializers = new HashMap<>(); + //private Map, BiFunction> serializers = new HashMap<>(); private Map, DeserializerFunctionPool> deserializers = new HashMap<>(); private Map, Supplier> typeFactories = new HashMap<>(); @@ -75,19 +70,19 @@ public void register(Class clazz, Function marshaller) { primitiveMarshallers.put(clazz, marshaller); } - public void registerTypeAdapter(Class clazz, Function adapter) { - typeAdapters.put(clazz, adapter); - } + //public void registerTypeAdapter(Class clazz, Function adapter) { + // typeAdapters.put(clazz, adapter); + //} - @SuppressWarnings("unchecked") - public void registerSerializer(Class clazz, Function serializer) { - serializers.put(clazz, (it, marshaller)->serializer.apply((T) it)); - } + //@SuppressWarnings("unchecked") + //public void registerSerializer(Class clazz, Function serializer) { + // serializers.put(clazz, (it, marshaller)->serializer.apply((T) it)); + //} - @SuppressWarnings("unchecked") - public void registerSerializer(Class clazz, BiFunction serializer) { - serializers.put(clazz, (BiFunction) serializer); - } + //@SuppressWarnings("unchecked") + //public void registerSerializer(Class clazz, BiFunction serializer) { + // serializers.put(clazz, (BiFunction) serializer); + //} public void registerTypeFactory(Class clazz, Supplier supplier) { typeFactories.put(clazz, supplier); @@ -128,326 +123,326 @@ public MarshallerImpl() { register(Boolean.TYPE, (it)->(it instanceof Boolean) ? (Boolean)it : null); - registerSerializer(Void.class, (it)->JsonNull.INSTANCE); - registerSerializer(Character.class, (it)->new JsonPrimitive(""+it)); - registerSerializer(String.class, JsonPrimitive::new); - registerSerializer(Byte.class, (it)->new JsonPrimitive(Long.valueOf(it))); - registerSerializer(Short.class, (it)->new JsonPrimitive(Long.valueOf(it))); - registerSerializer(Integer.class, (it)->new JsonPrimitive(Long.valueOf(it))); - registerSerializer(Long.class, JsonPrimitive::new); - registerSerializer(Float.class, (it)->new JsonPrimitive(Double.valueOf(it))); - registerSerializer(Double.class, JsonPrimitive::new); - registerSerializer(Boolean.class, JsonPrimitive::new); + //registerSerializer(Void.class, (it)->JsonNull.INSTANCE); +// registerSerializer(Character.class, (it)->new JsonPrimitive(""+it)); +// registerSerializer(String.class, JsonPrimitive::new); +// registerSerializer(Byte.class, (it)->new JsonPrimitive(Long.valueOf(it))); +// registerSerializer(Short.class, (it)->new JsonPrimitive(Long.valueOf(it))); +// registerSerializer(Integer.class, (it)->new JsonPrimitive(Long.valueOf(it))); +// registerSerializer(Long.class, JsonPrimitive::new); +// registerSerializer(Float.class, (it)->new JsonPrimitive(Double.valueOf(it))); +// registerSerializer(Double.class, JsonPrimitive::new); +// registerSerializer(Boolean.class, JsonPrimitive::new); - registerSerializer(Void.TYPE, (it)->JsonNull.INSTANCE); - registerSerializer(Character.TYPE, (it)->new JsonPrimitive(""+it)); - registerSerializer(Byte.TYPE, (it)->new JsonPrimitive(Long.valueOf(it))); - registerSerializer(Short.TYPE, (it)->new JsonPrimitive(Long.valueOf(it))); - registerSerializer(Integer.TYPE, (it)->new JsonPrimitive(Long.valueOf(it))); - registerSerializer(Long.TYPE, JsonPrimitive::new); - registerSerializer(Float.TYPE, (it)->new JsonPrimitive(Double.valueOf(it))); - registerSerializer(Double.TYPE, JsonPrimitive::new); - registerSerializer(Boolean.TYPE, JsonPrimitive::new); + //registerSerializer(Void.TYPE, (it)->JsonNull.INSTANCE); +// registerSerializer(Character.TYPE, (it)->new JsonPrimitive(""+it)); +// registerSerializer(Byte.TYPE, (it)->new JsonPrimitive(Long.valueOf(it))); +// registerSerializer(Short.TYPE, (it)->new JsonPrimitive(Long.valueOf(it))); +// registerSerializer(Integer.TYPE, (it)->new JsonPrimitive(Long.valueOf(it))); +// registerSerializer(Long.TYPE, JsonPrimitive::new); +// registerSerializer(Float.TYPE, (it)->new JsonPrimitive(Double.valueOf(it))); +// registerSerializer(Double.TYPE, JsonPrimitive::new); +// registerSerializer(Boolean.TYPE, JsonPrimitive::new); } /** EXPERIMENTAL. Marshalls elem into a very specific parameterized type, honoring generic type arguments. */ - @SuppressWarnings("unchecked") - @Nullable - public T marshall(Type type, JsonElement elem) { - if (elem==null) return null; - if (elem==JsonNull.INSTANCE) return null; - - if (type instanceof Class) { - try { - return marshall((Class)type, elem); - } catch (ClassCastException t) { - return null; - } - } - - if (type instanceof ParameterizedType) { - try { - Class clazz = (Class) TypeMagic.classForType(type); - - return marshall(clazz, elem); - } catch (ClassCastException t) { - return null; - } - } - - return null; - } +// @SuppressWarnings("unchecked") +// @Nullable +// public T marshall(Type type, JsonElement elem) { +// if (elem==null) return null; +// //if (elem==JsonNull.INSTANCE) return null; +// +// if (type instanceof Class) { +// try { +// return marshall((Class)type, elem); +// } catch (ClassCastException t) { +// return null; +// } +// } +// +// if (type instanceof ParameterizedType) { +// try { +// Class clazz = (Class) TypeMagic.classForType(type); +// +// return marshall(clazz, elem); +// } catch (ClassCastException t) { +// return null; +// } +// } +// +// return null; +// } +// +// public T marshall(Class clazz, JsonElement elem) { +// try { +// return marshall(clazz, elem, false); +// } catch (Throwable t) { +// return null; +// } +// } - public T marshall(Class clazz, JsonElement elem) { - try { - return marshall(clazz, elem, false); - } catch (Throwable t) { - return null; - } - } +// public T marshallCarefully(Class clazz, JsonElement elem) throws JsonIOException { +// return marshall(clazz, elem, true); +// } +// +// @SuppressWarnings("unchecked") +// @Nullable +// public T marshall(Class clazz, JsonElement elem, boolean failFast) throws JsonIOException { +// if (elem==null) return null; +// //if (elem==JsonNull.INSTANCE) return null; +// if (clazz.isAssignableFrom(elem.getClass())) return (T)elem; //Already the correct type +// +// //Externally registered deserializers +// DeserializerFunctionPool pool = (DeserializerFunctionPool)deserializers.get(clazz); +// if (pool!=null) { +// try { +// return pool.apply(elem, this); +// } catch (FunctionMatchFailedException e) { +// //Don't return the result, but continue +// } +// } +// +// //Internally annotated deserializers +// pool = POJODeserializer.deserializersFor(clazz); +// T poolResult; +// try { +// poolResult = pool.apply(elem, this); +// return poolResult; +// } catch (FunctionMatchFailedException e) { +// //Don't return the result, but continue +// } +// +// +// if (Enum.class.isAssignableFrom(clazz)) { +// if (!(elem instanceof JsonPrimitive)) return null; +// String name = ((JsonPrimitive)elem).getValue().toString(); +// +// T[] constants = clazz.getEnumConstants(); +// if (constants==null) return null; +// for(T t : constants) { +// if (((Enum)t).name().equals(name)) return t; +// } +// } +// +// if (clazz.equals(String.class)) { +// //Almost everything has a String representation +// if (elem instanceof JsonObject) return (T)((JsonObject)elem).toJson(false, false); +// //if (elem instanceof JsonArray) return (T)((JsonArray)elem).toJson(false, false); +// if (elem instanceof JsonPrimitive) { +// ((JsonPrimitive)elem).getValue(); +// return (T)((JsonPrimitive)elem).asString(); +// } +// //if (elem instanceof JsonNull) return (T)"null"; +// +// if (failFast) throw new JsonIOException("Encountered unexpected JsonElement type while deserializing to string: "+elem.getClass().getCanonicalName()); +// return null; +// } +// +// if (elem instanceof JsonPrimitive) { +// Function func = primitiveMarshallers.get(clazz); +// if (func!=null) { +// return (T)func.apply(((JsonPrimitive)elem).getValue()); +// } else { +// if (failFast) throw new JsonIOException("Don't know how to unpack value '"+elem.toString()+"' into target type '"+clazz.getCanonicalName()+"'"); +// return null; +// } +// } else if (elem instanceof JsonObject) { +// +// +// if (clazz.isPrimitive()) throw new JsonIOException("Can't marshall json object into primitive type "+clazz.getCanonicalName()); +// if (JsonPrimitive.class.isAssignableFrom(clazz)) { +// if (failFast) throw new JsonIOException("Can't marshall json object into a json primitive"); +// return null; +// } +// +// JsonObject obj = (JsonObject) elem; +// obj.setMarshaller(this); +// +// if (typeAdapters.containsKey(clazz)) { +// return (T) typeAdapters.get(clazz).apply((JsonObject) elem); +// } +// +// if (typeFactories.containsKey(clazz)) { +// T result = (T)typeFactories.get(clazz).get(); +// try { +// POJODeserializer.unpackObject(result, obj, failFast); +// return result; +// } catch (Throwable t) { +// if (failFast) throw t; +// return null; +// } +// } else { +// +// try { +// T result = TypeMagic.createAndCast(clazz, failFast); +// POJODeserializer.unpackObject(result, obj, failFast); +// return result; +// } catch (Throwable t) { +// if (failFast) throw t; +// return null; +// } +// } +// +//// } else if (elem instanceof JsonArray) { +//// if (clazz.isPrimitive()) return null; +//// if (clazz.isArray()) { +//// Class componentType = clazz.getComponentType(); +//// JsonArray array = (JsonArray)elem; +//// +//// T result = (T) Array.newInstance(componentType, array.size()); +//// for(int i=0; i T marshallCarefully(Class clazz, JsonElement elem) throws JsonIOException { - return marshall(clazz, elem, true); - } - - @SuppressWarnings("unchecked") - @Nullable - public T marshall(Class clazz, JsonElement elem, boolean failFast) throws JsonIOException { - if (elem==null) return null; - if (elem==JsonNull.INSTANCE) return null; - if (clazz.isAssignableFrom(elem.getClass())) return (T)elem; //Already the correct type - - //Externally registered deserializers - DeserializerFunctionPool pool = (DeserializerFunctionPool)deserializers.get(clazz); - if (pool!=null) { - try { - return pool.apply(elem, this); - } catch (FunctionMatchFailedException e) { - //Don't return the result, but continue - } - } - - //Internally annotated deserializers - pool = POJODeserializer.deserializersFor(clazz); - T poolResult; - try { - poolResult = pool.apply(elem, this); - return poolResult; - } catch (FunctionMatchFailedException e) { - //Don't return the result, but continue - } - - - if (Enum.class.isAssignableFrom(clazz)) { - if (!(elem instanceof JsonPrimitive)) return null; - String name = ((JsonPrimitive)elem).getValue().toString(); - - T[] constants = clazz.getEnumConstants(); - if (constants==null) return null; - for(T t : constants) { - if (((Enum)t).name().equals(name)) return t; - } - } - - if (clazz.equals(String.class)) { - //Almost everything has a String representation - if (elem instanceof JsonObject) return (T)((JsonObject)elem).toJson(false, false); - if (elem instanceof JsonArray) return (T)((JsonArray)elem).toJson(false, false); - if (elem instanceof JsonPrimitive) { - ((JsonPrimitive)elem).getValue(); - return (T)((JsonPrimitive)elem).asString(); - } - if (elem instanceof JsonNull) return (T)"null"; - - if (failFast) throw new JsonIOException("Encountered unexpected JsonElement type while deserializing to string: "+elem.getClass().getCanonicalName()); - return null; - } - - if (elem instanceof JsonPrimitive) { - Function func = primitiveMarshallers.get(clazz); - if (func!=null) { - return (T)func.apply(((JsonPrimitive)elem).getValue()); - } else { - if (failFast) throw new JsonIOException("Don't know how to unpack value '"+elem.toString()+"' into target type '"+clazz.getCanonicalName()+"'"); - return null; - } - } else if (elem instanceof JsonObject) { - - - if (clazz.isPrimitive()) throw new JsonIOException("Can't marshall json object into primitive type "+clazz.getCanonicalName()); - if (JsonPrimitive.class.isAssignableFrom(clazz)) { - if (failFast) throw new JsonIOException("Can't marshall json object into a json primitive"); - return null; - } - - JsonObject obj = (JsonObject) elem; - obj.setMarshaller(this); - - if (typeAdapters.containsKey(clazz)) { - return (T) typeAdapters.get(clazz).apply((JsonObject) elem); - } - - if (typeFactories.containsKey(clazz)) { - T result = (T)typeFactories.get(clazz).get(); - try { - POJODeserializer.unpackObject(result, obj, failFast); - return result; - } catch (Throwable t) { - if (failFast) throw t; - return null; - } - } else { - - try { - T result = TypeMagic.createAndCast(clazz, failFast); - POJODeserializer.unpackObject(result, obj, failFast); - return result; - } catch (Throwable t) { - if (failFast) throw t; - return null; - } - } - - } else if (elem instanceof JsonArray) { - if (clazz.isPrimitive()) return null; - if (clazz.isArray()) { - Class componentType = clazz.getComponentType(); - JsonArray array = (JsonArray)elem; - - T result = (T) Array.newInstance(componentType, array.size()); - for(int i=0; i serializer = serializers.get(obj.getClass()); - if (serializer!=null) { - JsonElement result = serializer.apply(obj, this); - if (result instanceof JsonObject) ((JsonObject)result).setMarshaller(this); - if (result instanceof JsonArray) ((JsonArray)result).setMarshaller(this); - return result; - } else { - //Detailed match - for(Map.Entry, BiFunction> entry : serializers.entrySet()) { - if (entry.getKey().isAssignableFrom(obj.getClass())) { - JsonElement result = entry.getValue().apply(obj, this); - if (result instanceof JsonObject) ((JsonObject)result).setMarshaller(this); - if (result instanceof JsonArray) ((JsonArray)result).setMarshaller(this); - return result; - } - } - } - - //Check for annotations - for(Method m : obj.getClass().getDeclaredMethods()) { - if (m.isAnnotationPresent(Serializer.class) && !Modifier.isStatic(m.getModifiers())) { - Class clazz = m.getReturnType(); - if (JsonElement.class.isAssignableFrom(clazz)) { - //This is probably the method we're looking for! Let's figure out its method signature! - Parameter[] params = m.getParameters(); - if (params.length==0) { - try { - boolean access = m.isAccessible(); - if (!access) m.setAccessible(true); - JsonElement result = (JsonElement) m.invoke(obj); - if (!access) m.setAccessible(false); - if (result instanceof JsonObject) ((JsonObject)result).setMarshaller(this); - if (result instanceof JsonArray) ((JsonArray)result).setMarshaller(this); - return result; - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - return JsonNull.INSTANCE; //TODO: This is a very real and important error case. We need a SerializationException and a way to expose exceptions proactively. - } - } else if (params.length==1) { - if (Marshaller.class.isAssignableFrom(params[0].getType())) { - try { - boolean access = m.isAccessible(); - if (!access) m.setAccessible(true); - JsonElement result = (JsonElement) m.invoke(obj, this); - if (!access) m.setAccessible(false); - if (result instanceof JsonObject) ((JsonObject)result).setMarshaller(this); - if (result instanceof JsonArray) ((JsonArray)result).setMarshaller(this); - return result; - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - return JsonNull.INSTANCE; //TODO: Same failure case that needs expressing - } - } - } - } - } - } - - if (obj instanceof Enum) { - return new JsonPrimitive(((Enum)obj).name()); - } - - if (obj.getClass().isArray()) { - - JsonArray array = new JsonArray(); - array.setMarshaller(this); - //Class component = obj.getClass().getComponentType(); - for(int i=0; i)obj) { - JsonElement parsed = serialize(elem); - array.add(parsed); - } - return array; - } - - if (obj instanceof Map) { - JsonObject result = new JsonObject(); - for(Map.Entry entry : ((Map)obj).entrySet()) { - String k = entry.getKey().toString(); - Object v = entry.getValue(); - result.put(k, serialize(v)); - } - return result; - } - - JsonObject result = new JsonObject(); - //Pull in public fields first - for(Field f : obj.getClass().getFields()) { - if ( - Modifier.isStatic(f.getModifiers()) || // Not part of the object - Modifier.isTransient(f.getModifiers())) continue; //Never serialize - f.setAccessible(true); - - try { - Object child = f.get(obj); - String name = f.getName(); - SerializedName nameAnnotation = f.getAnnotation(SerializedName.class); - if (nameAnnotation!=null) name = nameAnnotation.value(); - - Comment comment = f.getAnnotation(Comment.class); - if (comment==null) { - result.put(name, serialize(child)); - } else { - result.put(name, serialize(child), comment.value()); - } - } catch (IllegalArgumentException | IllegalAccessException e) {} - } - - //Add in what private fields we can reach - for (Field f : obj.getClass().getDeclaredFields()) { - if ( - Modifier.isPublic(f.getModifiers()) || // Already serialized - Modifier.isStatic(f.getModifiers()) || // Not part of the object - Modifier.isTransient(f.getModifiers())) continue; //Never serialize - f.setAccessible(true); - - try { - Object child = f.get(obj); - String name = f.getName(); - SerializedName nameAnnotation = f.getAnnotation(SerializedName.class); - if (nameAnnotation!=null) name = nameAnnotation.value(); - - Comment comment = f.getAnnotation(Comment.class); - if (comment==null) { - result.put(name, serialize(child)); - } else { - result.put(name, serialize(child), comment.value()); - } - } catch (IllegalArgumentException | IllegalAccessException e) {} - } - - return result; - } +// public JsonElement serialize(Object obj) { +// //if (obj==null) return JsonNull.INSTANCE; +// +// //Prefer exact match +// BiFunction serializer = serializers.get(obj.getClass()); +// if (serializer!=null) { +// JsonElement result = serializer.apply(obj, this); +// if (result instanceof JsonObject) ((JsonObject)result).setMarshaller(this); +// //if (result instanceof JsonArray) ((JsonArray)result).setMarshaller(this); +// return result; +// } else { +// //Detailed match +// for(Map.Entry, BiFunction> entry : serializers.entrySet()) { +// if (entry.getKey().isAssignableFrom(obj.getClass())) { +// JsonElement result = entry.getValue().apply(obj, this); +// if (result instanceof JsonObject) ((JsonObject)result).setMarshaller(this); +// //if (result instanceof JsonArray) ((JsonArray)result).setMarshaller(this); +// return result; +// } +// } +// } +// +// //Check for annotations +// for(Method m : obj.getClass().getDeclaredMethods()) { +// if (m.isAnnotationPresent(Serializer.class) && !Modifier.isStatic(m.getModifiers())) { +// Class clazz = m.getReturnType(); +// if (JsonElement.class.isAssignableFrom(clazz)) { +// //This is probably the method we're looking for! Let's figure out its method signature! +// Parameter[] params = m.getParameters(); +// if (params.length==0) { +// try { +// boolean access = m.isAccessible(); +// if (!access) m.setAccessible(true); +// JsonElement result = (JsonElement) m.invoke(obj); +// if (!access) m.setAccessible(false); +// if (result instanceof JsonObject) ((JsonObject)result).setMarshaller(this); +// //if (result instanceof JsonArray) ((JsonArray)result).setMarshaller(this); +// return result; +// } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { +// //return JsonNull.INSTANCE; //TODO: This is a very real and important error case. We need a SerializationException and a way to expose exceptions proactively. +// } +// } else if (params.length==1) { +// if (Marshaller.class.isAssignableFrom(params[0].getType())) { +// try { +// boolean access = m.isAccessible(); +// if (!access) m.setAccessible(true); +// JsonElement result = (JsonElement) m.invoke(obj, this); +// if (!access) m.setAccessible(false); +// if (result instanceof JsonObject) ((JsonObject)result).setMarshaller(this); +// //if (result instanceof JsonArray) ((JsonArray)result).setMarshaller(this); +// return result; +// } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { +// //return JsonNull.INSTANCE; //TODO: Same failure case that needs expressing +// } +// } +// } +// } +// } +// } +// +// if (obj instanceof Enum) { +// return new JsonPrimitive(((Enum)obj).name()); +// } +// /* +// if (obj.getClass().isArray()) { +// +// JsonArray array = new JsonArray(); +// array.setMarshaller(this); +// //Class component = obj.getClass().getComponentType(); +// for(int i=0; i)obj) { +// JsonElement parsed = serialize(elem); +// array.add(parsed); +// } +// return array; +// }*/ +// +// if (obj instanceof Map) { +// JsonObject result = new JsonObject(); +// for(Map.Entry entry : ((Map)obj).entrySet()) { +// String k = entry.getKey().toString(); +// Object v = entry.getValue(); +// result.put(k, serialize(v)); +// } +// return result; +// } +// +// JsonObject result = new JsonObject(); +// //Pull in public fields first +// for(Field f : obj.getClass().getFields()) { +// if ( +// Modifier.isStatic(f.getModifiers()) || // Not part of the object +// Modifier.isTransient(f.getModifiers())) continue; //Never serialize +// f.setAccessible(true); +// +// try { +// Object child = f.get(obj); +// String name = f.getName(); +// SerializedName nameAnnotation = f.getAnnotation(SerializedName.class); +// if (nameAnnotation!=null) name = nameAnnotation.value(); +// +// Comment comment = f.getAnnotation(Comment.class); +// if (comment==null) { +// result.put(name, serialize(child)); +// } else { +// result.put(name, serialize(child), comment.value()); +// } +// } catch (IllegalArgumentException | IllegalAccessException e) {} +// } +// +// //Add in what private fields we can reach +// for (Field f : obj.getClass().getDeclaredFields()) { +// if ( +// Modifier.isPublic(f.getModifiers()) || // Already serialized +// Modifier.isStatic(f.getModifiers()) || // Not part of the object +// Modifier.isTransient(f.getModifiers())) continue; //Never serialize +// f.setAccessible(true); +// +// try { +// Object child = f.get(obj); +// String name = f.getName(); +// SerializedName nameAnnotation = f.getAnnotation(SerializedName.class); +// if (nameAnnotation!=null) name = nameAnnotation.value(); +// +// Comment comment = f.getAnnotation(Comment.class); +// if (comment==null) { +// result.put(name, serialize(child)); +// } else { +// result.put(name, serialize(child), comment.value()); +// } +// } catch (IllegalArgumentException | IllegalAccessException e) {} +// } +// +// return result; +// } } diff --git a/src/main/java/blue/endless/jankson/impl/POJODeserializer.java b/src/main/java/blue/endless/jankson/impl/POJODeserializer.java index 79d7a8e..085719a 100644 --- a/src/main/java/blue/endless/jankson/impl/POJODeserializer.java +++ b/src/main/java/blue/endless/jankson/impl/POJODeserializer.java @@ -40,261 +40,257 @@ import blue.endless.jankson.api.annotation.Deserializer; import blue.endless.jankson.api.annotation.SerializedName; import blue.endless.jankson.api.JsonGrammar; -import blue.endless.jankson.api.element.JsonArray; -import blue.endless.jankson.api.element.JsonElement; -import blue.endless.jankson.api.element.JsonNull; -import blue.endless.jankson.api.element.JsonObject; -import blue.endless.jankson.api.element.JsonPrimitive; import blue.endless.jankson.api.io.JsonIOException; import blue.endless.jankson.impl.serializer.InternalDeserializerFunction; import blue.endless.jankson.impl.serializer.DeserializerFunctionPool; public class POJODeserializer { - - public static void unpackObject(Object target, JsonObject source) { - try { - unpackObject(target, source, false); - } catch (Throwable t) { - } - } - - public static void unpackObject(Object target, JsonObject source, boolean failFast) throws JsonIOException { - //if (o.getClass().getTypeParameters().length>0) throw new DeserializationException("Can't safely deserialize generic types!"); - //well, let's try anyway and see if we run into problems. - - //Create a copy we can redact keys from - JsonObject work = source.clone(); - - //Fill public and private fields declared in the target object's immediate class - for(Field f : target.getClass().getDeclaredFields()) { - int modifiers = f.getModifiers(); - if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; - unpackField(target, f, work, failFast); - } - - //Attempt to fill public, accessible fields declared in the target object's superclass. - for(Field f : target.getClass().getFields()) { - int modifiers = f.getModifiers(); - if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; - unpackField(target, f, work, failFast); - } - - if (!work.isEmpty() && failFast) { - throw new JsonIOException("There was data that couldn't be applied to the destination object: "+work.toJson(JsonGrammar.STRICT)); - } - } - - public static void unpackField(Object parent, Field f, JsonObject source, boolean failFast) throws JsonIOException { - String fieldName = f.getName(); - SerializedName nameAnnotation = f.getAnnotation(SerializedName.class); - if (nameAnnotation!=null) fieldName = nameAnnotation.value(); - - if (source.containsKey(fieldName)) { - JsonElement elem = source.get(fieldName); - source.remove(fieldName); //Prevent it from getting re-unpacked - if (elem==null || elem==JsonNull.INSTANCE) { - boolean accessible = f.isAccessible(); - if (!accessible) f.setAccessible(true); - try { - f.set(parent, null); - if (!accessible) f.setAccessible(false); - } catch (IllegalArgumentException | IllegalAccessException ex) { - if (failFast) throw new JsonIOException("Couldn't set field \""+f.getName()+"\" of class \""+parent.getClass().getCanonicalName()+"\"", ex); - } - } else { - try { - unpackFieldData(parent, f, elem, source.getMarshaller()); - } catch (Throwable t) { - if (failFast) throw new JsonIOException("There was a problem unpacking field "+f.getName()+" of class "+parent.getClass().getCanonicalName(), t); - } - } - } - } - - - /** NOT WORKING YET, HIGHLY EXPERIMENTAL */ - @Nullable - public static Object unpack(Type t, JsonElement elem, blue.endless.jankson.api.Marshaller marshaller) { - Class rawClass = TypeMagic.classForType(t); - if (rawClass.isPrimitive()) return null; //We can't unpack a primitive into an object of primitive type. Maybe in the future we can return a boxed type? - - if (elem==null) return null; - /* - if (type instanceof Class) { - try { - return marshaller.marshall((Class) type, elem); - } catch (ClassCastException t) { - return null; - } - } - - if (type instanceof ParameterizedType) { - try { - Class clazz = (Class) TypeMagic.classForType(type); - - if (List.class.isAssignableFrom(clazz)) { - Object result = TypeMagic.createAndCast(type); - - try { - unpackList((List) result, type, elem, marshaller); - return result; - } catch (DeserializationException e) { - e.printStackTrace(); - return result; - } - } - - return null; - } catch (ClassCastException t) { - return null; - } - }*/ - - return null; - } - - @SuppressWarnings("unchecked") - public static boolean unpackFieldData(Object parent, Field field, JsonElement elem, blue.endless.jankson.api.Marshaller marshaller) throws Throwable { - - if (elem==null) return true; - try { - field.setAccessible(true); - } catch (Throwable t) { - return false; //skip this field probably. - } - - if (elem==JsonNull.INSTANCE) { - field.set(parent, null); - return true; - } - - Class fieldClass = field.getType(); - - if (!isCollections(fieldClass)) { - //Try to directly marshall - Object result = marshaller.marshallCarefully(fieldClass, elem); - field.set(parent, result); - return true; - } - - - if (field.get(parent)==null) { - Object fieldValue = TypeMagic.createAndCast(field.getGenericType()); - - if (fieldValue==null) { - return false; //Can't deserialize this somehow - } else { - field.set(parent, fieldValue); - } - } - - if (Map.class.isAssignableFrom(fieldClass)) { - Type[] parameters = ((ParameterizedType)field.getGenericType()).getActualTypeArguments(); - - unpackMap((Map) field.get(parent), parameters[0], parameters[1], elem, marshaller); - - return true; - } - - if (Collection.class.isAssignableFrom(fieldClass)) { - Type elementType = ((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]; - - unpackCollection((Collection)field.get(parent), elementType, elem, marshaller); - - return true; - } - - return false; - } - - private static boolean isCollections(Class clazz) { - return - Map.class.isAssignableFrom(clazz) || - Collection.class.isAssignableFrom(clazz); - } - - public static void unpackMap(Map map, Type keyType, Type valueType, JsonElement elem, blue.endless.jankson.api.Marshaller marshaller) throws JsonIOException { - if (!(elem instanceof JsonObject)) throw new JsonIOException("Cannot deserialize a "+elem.getClass().getSimpleName()+" into a Map - expected a JsonObject!"); - - //Class keyClass = TypeMagic.classForType(keyType); - //Class valueClass = TypeMagic.classForType(valueType); - JsonObject object = (JsonObject)elem; - for(Map.Entry entry : object.entrySet()) { - try { - Object k = marshaller.marshall(keyType, new JsonPrimitive(entry.getKey())); - Object v = marshaller.marshall(valueType, entry.getValue()); - if (k!=null && v!=null) map.put(k, v); - } catch (Throwable t) {} - } - } - - public static void unpackCollection(Collection collection, Type elementType, JsonElement elem, blue.endless.jankson.api.Marshaller marshaller) throws JsonIOException { - if (!(elem instanceof JsonArray)) throw new JsonIOException("Cannot deserialize a "+elem.getClass().getSimpleName()+" into a Set - expected a JsonArray!"); - - JsonArray array = (JsonArray)elem; - for(JsonElement arrayElem : array) { - - Object o = marshaller.marshall(elementType, arrayElem); - if (o!=null) collection.add(o); - } - } - - protected static DeserializerFunctionPool deserializersFor(Class targetClass) { - DeserializerFunctionPool pool = new DeserializerFunctionPool<>(targetClass); - for(Method m: targetClass.getDeclaredMethods()) { - //System.out.println("Examining "+m.getName()); - if (m.getAnnotation(Deserializer.class)==null) continue; //Must be annotated - - if (!Modifier.isStatic(m.getModifiers())) continue; //Must be static - if (!m.getReturnType().equals(targetClass)) continue; //Must return an instance of the class - //System.out.println(" Cleared first screening"); - Parameter[] params = m.getParameters(); - if (params.length>=1) { - Class sourceClass = params[0].getType(); - InternalDeserializerFunction deserializer = makeDeserializer(m, sourceClass, targetClass); - if (deserializer==null) continue; - pool.registerUnsafe(sourceClass, deserializer); - //System.out.println(" Registered deserializer"); - } - } - return pool; - } - - /** Assuming the method is a valid deserializer, and matches the type signature required, produces a DeserializerFunction which delegates to the method provided. - * If the method is not a valid deserializer of this type, returns null instead. - */ - @SuppressWarnings("unchecked") - @Nullable - private static InternalDeserializerFunction makeDeserializer(@Nonnull Method m, @Nonnull Class sourceClass, @Nonnull Class targetClass) { - if (!m.getReturnType().equals(targetClass)) return null; - Parameter[] params = m.getParameters(); - if (params.length==1) { - //if (params[0].getClass().isAssignableFrom(sourceClass)) { - return (Object o, blue.endless.jankson.api.Marshaller marshaller)->{ - try { - return (B)m.invoke(null, o); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { - throw new JsonIOException(ex); - } - }; - //} - //return null; - } else if (params.length==2) { - //if (params[0].getClass().isAssignableFrom(sourceClass)) { - if (params[1].getType().equals(blue.endless.jankson.api.Marshaller.class)) { - return (Object o, blue.endless.jankson.api.Marshaller marshaller)->{ - try { - return (B)m.invoke(null, o, marshaller); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { - throw new JsonIOException(ex); - } - }; - } - //} - return null; - } else { - return null; - } - } +// +// public static void unpackObject(Object target, JsonObject source) { +// try { +// unpackObject(target, source, false); +// } catch (Throwable t) { +// } +// } +// +// public static void unpackObject(Object target, JsonObject source, boolean failFast) throws JsonIOException { +// //if (o.getClass().getTypeParameters().length>0) throw new DeserializationException("Can't safely deserialize generic types!"); +// //well, let's try anyway and see if we run into problems. +// +// //Create a copy we can redact keys from +// JsonObject work = source.clone(); +// +// //Fill public and private fields declared in the target object's immediate class +// for(Field f : target.getClass().getDeclaredFields()) { +// int modifiers = f.getModifiers(); +// if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; +// unpackField(target, f, work, failFast); +// } +// +// //Attempt to fill public, accessible fields declared in the target object's superclass. +// for(Field f : target.getClass().getFields()) { +// int modifiers = f.getModifiers(); +// if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) continue; +// unpackField(target, f, work, failFast); +// } +// +// if (!work.isEmpty() && failFast) { +// throw new JsonIOException("There was data that couldn't be applied to the destination object: "+work.toJson(JsonGrammar.STRICT)); +// } +// } +// +// public static void unpackField(Object parent, Field f, JsonObject source, boolean failFast) throws JsonIOException { +// String fieldName = f.getName(); +// SerializedName nameAnnotation = f.getAnnotation(SerializedName.class); +// if (nameAnnotation!=null) fieldName = nameAnnotation.value(); +// +// if (source.containsKey(fieldName)) { +// JsonElement elem = source.get(fieldName); +// source.remove(fieldName); //Prevent it from getting re-unpacked +//// if (elem==null || elem==JsonNull.INSTANCE) { +//// boolean accessible = f.isAccessible(); +//// if (!accessible) f.setAccessible(true); +//// try { +//// f.set(parent, null); +//// if (!accessible) f.setAccessible(false); +//// } catch (IllegalArgumentException | IllegalAccessException ex) { +//// if (failFast) throw new JsonIOException("Couldn't set field \""+f.getName()+"\" of class \""+parent.getClass().getCanonicalName()+"\"", ex); +//// } +//// } else { +// try { +// unpackFieldData(parent, f, elem, source.getMarshaller()); +// } catch (Throwable t) { +// if (failFast) throw new JsonIOException("There was a problem unpacking field "+f.getName()+" of class "+parent.getClass().getCanonicalName(), t); +// } +//// } +// } +// } +// +// +// /** NOT WORKING YET, HIGHLY EXPERIMENTAL */ +// @Nullable +// public static Object unpack(Type t, JsonElement elem, blue.endless.jankson.api.Marshaller marshaller) { +// Class rawClass = TypeMagic.classForType(t); +// if (rawClass.isPrimitive()) return null; //We can't unpack a primitive into an object of primitive type. Maybe in the future we can return a boxed type? +// +// if (elem==null) return null; +// /* +// if (type instanceof Class) { +// try { +// return marshaller.marshall((Class) type, elem); +// } catch (ClassCastException t) { +// return null; +// } +// } +// +// if (type instanceof ParameterizedType) { +// try { +// Class clazz = (Class) TypeMagic.classForType(type); +// +// if (List.class.isAssignableFrom(clazz)) { +// Object result = TypeMagic.createAndCast(type); +// +// try { +// unpackList((List) result, type, elem, marshaller); +// return result; +// } catch (DeserializationException e) { +// e.printStackTrace(); +// return result; +// } +// } +// +// return null; +// } catch (ClassCastException t) { +// return null; +// } +// }*/ +// +// return null; +// } +// +// @SuppressWarnings("unchecked") +// public static boolean unpackFieldData(Object parent, Field field, JsonElement elem, blue.endless.jankson.api.Marshaller marshaller) throws Throwable { +// +// if (elem==null) return true; +// try { +// field.setAccessible(true); +// } catch (Throwable t) { +// return false; //skip this field probably. +// } +// +// //if (elem==JsonNull.INSTANCE) { +// // field.set(parent, null); +// // return true; +// //} +// +// Class fieldClass = field.getType(); +// +// if (!isCollections(fieldClass)) { +// //Try to directly marshall +// Object result = marshaller.marshallCarefully(fieldClass, elem); +// field.set(parent, result); +// return true; +// } +// +// +// if (field.get(parent)==null) { +// Object fieldValue = TypeMagic.createAndCast(field.getGenericType()); +// +// if (fieldValue==null) { +// return false; //Can't deserialize this somehow +// } else { +// field.set(parent, fieldValue); +// } +// } +// +// if (Map.class.isAssignableFrom(fieldClass)) { +// Type[] parameters = ((ParameterizedType)field.getGenericType()).getActualTypeArguments(); +// +// unpackMap((Map) field.get(parent), parameters[0], parameters[1], elem, marshaller); +// +// return true; +// } +// +// if (Collection.class.isAssignableFrom(fieldClass)) { +// Type elementType = ((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]; +// +// unpackCollection((Collection)field.get(parent), elementType, elem, marshaller); +// +// return true; +// } +// +// return false; +// } +// +// private static boolean isCollections(Class clazz) { +// return +// Map.class.isAssignableFrom(clazz) || +// Collection.class.isAssignableFrom(clazz); +// } +// +// public static void unpackMap(Map map, Type keyType, Type valueType, JsonElement elem, blue.endless.jankson.api.Marshaller marshaller) throws JsonIOException { +// if (!(elem instanceof JsonObject)) throw new JsonIOException("Cannot deserialize a "+elem.getClass().getSimpleName()+" into a Map - expected a JsonObject!"); +// +// //Class keyClass = TypeMagic.classForType(keyType); +// //Class valueClass = TypeMagic.classForType(valueType); +// JsonObject object = (JsonObject)elem; +// for(Map.Entry entry : object.entrySet()) { +// try { +// Object k = marshaller.marshall(keyType, new JsonPrimitive(entry.getKey())); +// Object v = marshaller.marshall(valueType, entry.getValue()); +// if (k!=null && v!=null) map.put(k, v); +// } catch (Throwable t) {} +// } +// } +// +// public static void unpackCollection(Collection collection, Type elementType, JsonElement elem, blue.endless.jankson.api.Marshaller marshaller) throws JsonIOException { +// /* +// if (!(elem instanceof JsonArray)) throw new JsonIOException("Cannot deserialize a "+elem.getClass().getSimpleName()+" into a Set - expected a JsonArray!"); +// +// JsonArray array = (JsonArray)elem; +// for(JsonElement arrayElem : array) { +// +// Object o = marshaller.marshall(elementType, arrayElem); +// if (o!=null) collection.add(o); +// }*/ +// } +// +// protected static DeserializerFunctionPool deserializersFor(Class targetClass) { +// DeserializerFunctionPool pool = new DeserializerFunctionPool<>(targetClass); +// for(Method m: targetClass.getDeclaredMethods()) { +// //System.out.println("Examining "+m.getName()); +// if (m.getAnnotation(Deserializer.class)==null) continue; //Must be annotated +// +// if (!Modifier.isStatic(m.getModifiers())) continue; //Must be static +// if (!m.getReturnType().equals(targetClass)) continue; //Must return an instance of the class +// //System.out.println(" Cleared first screening"); +// Parameter[] params = m.getParameters(); +// if (params.length>=1) { +// Class sourceClass = params[0].getType(); +// InternalDeserializerFunction deserializer = makeDeserializer(m, sourceClass, targetClass); +// if (deserializer==null) continue; +// pool.registerUnsafe(sourceClass, deserializer); +// //System.out.println(" Registered deserializer"); +// } +// } +// return pool; +// } +// +// /** Assuming the method is a valid deserializer, and matches the type signature required, produces a DeserializerFunction which delegates to the method provided. +// * If the method is not a valid deserializer of this type, returns null instead. +// */ +// @SuppressWarnings("unchecked") +// @Nullable +// private static InternalDeserializerFunction makeDeserializer(@Nonnull Method m, @Nonnull Class sourceClass, @Nonnull Class targetClass) { +// if (!m.getReturnType().equals(targetClass)) return null; +// Parameter[] params = m.getParameters(); +// if (params.length==1) { +// //if (params[0].getClass().isAssignableFrom(sourceClass)) { +// return (Object o, blue.endless.jankson.api.Marshaller marshaller)->{ +// try { +// return (B)m.invoke(null, o); +// } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { +// throw new JsonIOException(ex); +// } +// }; +// //} +// //return null; +// } else if (params.length==2) { +// //if (params[0].getClass().isAssignableFrom(sourceClass)) { +// if (params[1].getType().equals(blue.endless.jankson.api.Marshaller.class)) { +// return (Object o, blue.endless.jankson.api.Marshaller marshaller)->{ +// try { +// return (B)m.invoke(null, o, marshaller); +// } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { +// throw new JsonIOException(ex); +// } +// }; +// } +// //} +// return null; +// } else { +// return null; +// } +// } } diff --git a/src/main/java/blue/endless/jankson/impl/context/ElementContext.java b/src/main/java/blue/endless/jankson/impl/context/ElementContext.java deleted file mode 100644 index ab889a8..0000000 --- a/src/main/java/blue/endless/jankson/impl/context/ElementContext.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2023 Falkreon (Isaac Ellingson) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package blue.endless.jankson.impl.context; - -import blue.endless.jankson.api.SyntaxError; -import blue.endless.jankson.api.io.JsonReaderOptions; - -@Deprecated(forRemoval=true) -public interface ElementContext { - /** Consume one codepoint from the stream, and either use it to continue composing the result or to discover that - * the result is complete and processing should stop. Throws a SyntaxError if unexpected or nonsense characters are - * encountered. - */ - public boolean consume(char character, int lineNum, int charNum, JsonReaderOptions options) throws SyntaxError; - - /** Notifies this context that the file ended while in this context and before isComplete returned true. In - * some contexts, like a single-line comment, this is fine. In most contexts, this should throw a descriptive error. - */ - public void eof() throws SyntaxError; - - /** Returns true if the parser has assembled a complete result. After true is returned, no more code points will be - * offered to consume, and getResult will soon be called to retrieve the result. - */ - public boolean isComplete(); - - /** Gets the result of parsing. Will be called only after isComplete reports true and processing of input has - * ceased. - */ - public T getResult() throws SyntaxError; -} diff --git a/src/main/java/blue/endless/jankson/impl/context/ParserContext.java b/src/main/java/blue/endless/jankson/impl/context/ParserContext.java deleted file mode 100644 index ccf141c..0000000 --- a/src/main/java/blue/endless/jankson/impl/context/ParserContext.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2023 Falkreon (Isaac Ellingson) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package blue.endless.jankson.impl.context; - -import blue.endless.jankson.api.Jankson; -import blue.endless.jankson.api.SyntaxError; - -@Deprecated(forRemoval=true) -public interface ParserContext { - /** Consume one codepoint from the stream, and either use it to continue composing the result or to discover that - * the result is complete and processing should stop. Throws a SyntaxError if unexpected or nonsense characters are - * encountered. - */ - - public boolean consume(int codePoint, Jankson loader) throws SyntaxError; - /** Notifies this context that the file ended abruptly while in this context and before isComplete returned true. In - * some contexts, like a single-line comment, this is fine. In most contexts, this should throw a descriptive error. - */ - public void eof() throws SyntaxError; - - /** Returns true if the parser has assembled a complete result. After true is returned, no more code points will be - * offered to consume, and getResult will soon be called to retrieve the result. - */ - public boolean isComplete(); - - /** Gets the result of parsing. Will be called only after isComplete reports true and processing of input has - * ceased. - */ - public T getResult() throws SyntaxError; -} diff --git a/src/main/java/blue/endless/jankson/impl/context/ParserFrame.java b/src/main/java/blue/endless/jankson/impl/context/ParserFrame.java deleted file mode 100644 index cd641de..0000000 --- a/src/main/java/blue/endless/jankson/impl/context/ParserFrame.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2023 Falkreon (Isaac Ellingson) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package blue.endless.jankson.impl.context; - -import java.util.function.Consumer; - -import blue.endless.jankson.api.SyntaxError; - -@Deprecated(forRemoval=true) -public class ParserFrame { - private ElementContext context; - private Consumer consumer; - - public ParserFrame(ElementContext context, Consumer consumer) { - this.context = context; - this.consumer = consumer; - } - - public ElementContext context() { return context; } - public Consumer consumer() { return consumer; } - - /** Feed the result directly from the context at this entry to its corresponding consumer */ - public void supply() throws SyntaxError { - consumer.accept(context.getResult()); - } -} diff --git a/src/main/java/blue/endless/jankson/impl/context/json/ArrayParserContext.java b/src/main/java/blue/endless/jankson/impl/context/json/ArrayParserContext.java deleted file mode 100644 index 686db24..0000000 --- a/src/main/java/blue/endless/jankson/impl/context/json/ArrayParserContext.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2023 Falkreon (Isaac Ellingson) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package blue.endless.jankson.impl.context.json; - -import blue.endless.jankson.api.Jankson; -import blue.endless.jankson.api.SyntaxError; -import blue.endless.jankson.api.element.JsonArray; -import blue.endless.jankson.impl.context.ParserContext; - -@Deprecated(forRemoval=true) -public class ArrayParserContext implements ParserContext { - private JsonArray result = new JsonArray(); - private boolean foundClosingBrace = false; - //private String comment = null; - - /** Assumes the opening brace has already been consumed! */ - public ArrayParserContext() { - - } - - @Override - public boolean consume(int codePoint, Jankson loader) throws SyntaxError { - result.setMarshaller(loader.getMarshaller()); - if (foundClosingBrace) return false; - if (Character.isWhitespace(codePoint) || codePoint==',') return true; - - if (codePoint==']') { - foundClosingBrace = true; - return true; - } - - loader.push(new ElementParserContext(), (it)->{ - if (it.getElement()!=null) { - result.add(it.getElement(), it.getComment()); - } else { - String existing = result.getComment(result.size()-1); - if (existing==null) existing=""; - String combined = existing + "\n" + it.getComment(); - result.setComment(result.size()-1, combined); - } - }); - return false; - } - - @Override - public void eof() throws SyntaxError { - if (foundClosingBrace) return; - throw new SyntaxError("Unexpected end-of-file in the middle of a list! Are you missing a ']'?"); - } - - @Override - public boolean isComplete() { - return foundClosingBrace; - } - - @Override - public JsonArray getResult() throws SyntaxError { - return result; - } - -} diff --git a/src/main/java/blue/endless/jankson/impl/context/json/CommentParserContext.java b/src/main/java/blue/endless/jankson/impl/context/json/CommentParserContext.java deleted file mode 100644 index bd9766f..0000000 --- a/src/main/java/blue/endless/jankson/impl/context/json/CommentParserContext.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2023 Falkreon (Isaac Ellingson) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package blue.endless.jankson.impl.context.json; - -import blue.endless.jankson.api.Jankson; -import blue.endless.jankson.api.SyntaxError; -import blue.endless.jankson.impl.context.ParserContext; - -@Deprecated(forRemoval=true) -public class CommentParserContext implements ParserContext { - int firstChar = -1; - int secondChar = -1; - - StringBuilder result = new StringBuilder(); - - int prevChar = -1; - - boolean startOfLine = true; - boolean multiLine = false; - boolean done = false; - - public CommentParserContext(int codePoint) { - firstChar = codePoint; - } - - @Override - public boolean consume(int codePoint, Jankson loader) throws SyntaxError { - if (done) return false; - - if (firstChar==-1) { - if (codePoint!='/' && codePoint!='#') { - throw new SyntaxError("Was expecting the start of a comment, but found '"+(char)codePoint+"' instead."); - } - firstChar = codePoint; - if (firstChar=='#') multiLine = false; - return true; - } - - if (secondChar==-1 && firstChar!='#') { - secondChar = codePoint; - if (codePoint=='*') { - multiLine = true; - return true; - } else if (codePoint=='/') { - multiLine = false; - return true; - } else { - if (Character.isWhitespace(codePoint)) { - throw new SyntaxError("Was expecting the start of a comment, but found whitespace instead."); - } else { - throw new SyntaxError("Was expecting the start of a comment, but found '"+(char)codePoint+"' instead."); - } - } - } - - //We're past the initiating character(s) - if (multiLine) { - if (codePoint!='\n' && Character.isWhitespace(codePoint)) { - if (startOfLine) return true; - } else if (codePoint=='\n') { - startOfLine = true; - } else { - if (startOfLine) startOfLine = false; - } - - if (codePoint=='/' && prevChar=='*') { - result.deleteCharAt(result.length()-1); //Get rid of the * - done = true; - return true; - } else { - prevChar = codePoint; - result.append((char)codePoint); - return true; - } - } else { - if (codePoint=='\n') { - done = true; - return true; - } else { - prevChar = codePoint; //Not really necessary but whatever. For consistency! :) - result.append((char)codePoint); - return true; - } - } - } - - @Override - public void eof() throws SyntaxError { - if (multiLine) throw new SyntaxError("Unexpected end-of-file while reading a multiline comment."); - } - - @Override - public boolean isComplete() { - return done; - } - - @Override - public String getResult() throws SyntaxError { - return result.toString().trim(); - } - -} diff --git a/src/main/java/blue/endless/jankson/impl/context/json/ElementParserContext.java b/src/main/java/blue/endless/jankson/impl/context/json/ElementParserContext.java deleted file mode 100644 index 16b3f0a..0000000 --- a/src/main/java/blue/endless/jankson/impl/context/json/ElementParserContext.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2023 Falkreon (Isaac Ellingson) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package blue.endless.jankson.impl.context.json; - -import java.util.Locale; - -import blue.endless.jankson.api.Jankson; -import blue.endless.jankson.api.SyntaxError; -import blue.endless.jankson.api.element.JsonElement; -import blue.endless.jankson.api.element.JsonNull; -import blue.endless.jankson.api.element.JsonPrimitive; -import blue.endless.jankson.impl.AnnotatedElement; -import blue.endless.jankson.impl.context.ParserContext; - -@Deprecated(forRemoval=true) -public class ElementParserContext implements ParserContext { - String comment = null; - AnnotatedElement result = null; - boolean childActive = false; - - @Override - public boolean consume(int codePoint, Jankson loader) throws SyntaxError { - //Figure out element type and dispatch down to - - if (Character.isWhitespace(codePoint)) return true; //Whitespace - switch(codePoint) { - case '/': - case '#': //Comment - - loader.push(new CommentParserContext(codePoint), (it)->comment=it); - return true; - case '\'': - case '"': //String - loader.push(new StringParserContext(codePoint), this::setResult); - childActive = true; - return true; - case '{': //Object - loader.push(new ObjectParserContext(false), this::setResult); - childActive = true; - return false; //Give the opening brace to the object context - case '[': //Array - loader.push(new ArrayParserContext(), this::setResult); - childActive = true; - return true; - - case '}': - loader.throwDelayed(new SyntaxError("Found '"+((char)codePoint)+"' while parsing an element - this shouldn't happen!")); - return false; - case ']': - result = new AnnotatedElement(null, comment); - /* - if (this.result==null) { - System.out.println("Comment parser didn't release its context in time?"); - loader.throwDelayed(new SyntaxError("Found '"+((char)codePoint)+"' while parsing an element - this shouldn't happen!")); - } else { - System.out.println("We're being called after we're done"); - }*/ - return false; - default: - if (Character.isDigit(codePoint) || codePoint=='-' || codePoint=='+' || codePoint=='.') { - loader.push(new NumberParserContext(codePoint), this::setResult); - childActive = true; - return true; - } - - loader.push(new TokenParserContext(codePoint), (it)->{ - String token = it.asString().toLowerCase(Locale.ROOT); - - switch(token) { - case "null": - setResult(JsonNull.INSTANCE); - break; - case "true": - setResult(JsonPrimitive.TRUE); - break; - case "false": - setResult(JsonPrimitive.FALSE); - break; - case "infinity": //handled by this token context - case "+infinity": //handled by number context. here for completeness - setResult(new JsonPrimitive(Double.POSITIVE_INFINITY)); - break; - case "-infinity": //number context - setResult(new JsonPrimitive(Double.NEGATIVE_INFINITY)); - break; - case "nan": //token context - setResult(new JsonPrimitive(Double.NaN)); - break; - default: - setResult(it); - //loader.throwDelayed(new SyntaxError("Found unrecognized token '"+it.asString()+"' while looking for a json element of any type.")); - break; - } - /* - if (it.asString().toLowerCase(Locale.ROOT).equals("null")) { - setResult(JsonNull.INSTANCE); - } else if (it.asString().toLowerCase(Locale.ROOT).equals("true")) { - setResult(JsonPrimitive.TRUE); - } else if (it.asString().toLowerCase(Locale.ROOT).equals("false")) { - setResult(JsonPrimitive.FALSE); - } else if (it.asString().toLowerCase(Locale.ROOT).equals("infinity")) { - setResult(new JsonPrimitive(Double.POSITIVE_INFINITY)); - } else { - loader.throwDelayed(new SyntaxError("Found unrecognized token '"+it.asString()+"' while looking for a json element of any type.")); - }*/ - }); - childActive = true; - - return true; - } - - } - - public void setResult(JsonElement elem) { - result = new AnnotatedElement(elem, comment); - } - - @Override - public void eof() throws SyntaxError { - //We should be fine as long as any child parser has been initiated. - if (!childActive) throw new SyntaxError("Unexpected end-of-file while looking for a json element!"); - } - - @Override - public boolean isComplete() { - return result!=null; - } - - @Override - public AnnotatedElement getResult() throws SyntaxError { - return result; - } - -} diff --git a/src/main/java/blue/endless/jankson/impl/context/json/NumberParserContext.java b/src/main/java/blue/endless/jankson/impl/context/json/NumberParserContext.java deleted file mode 100644 index 2de214b..0000000 --- a/src/main/java/blue/endless/jankson/impl/context/json/NumberParserContext.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2023 Falkreon (Isaac Ellingson) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package blue.endless.jankson.impl.context.json; - -import java.util.Locale; - -import blue.endless.jankson.api.Jankson; -import blue.endless.jankson.api.SyntaxError; -import blue.endless.jankson.api.element.JsonPrimitive; -import blue.endless.jankson.impl.context.ParserContext; - -@Deprecated(forRemoval=true) -public class NumberParserContext implements ParserContext { - private String numberString = ""; - private boolean complete = false; - private String acceptedChars = "0123456789.+-eExabcdefInityNn"; - - public NumberParserContext(int firstCodePoint) { - numberString += (char)firstCodePoint; - } - - @Override - public boolean consume(int codePoint, Jankson loader) throws SyntaxError { - if (complete) return false; - - if (acceptedChars.indexOf(codePoint)!=-1) { - numberString+=(char)codePoint; - return true; - } else { - complete = true; - return false; - } - } - - @Override - public void eof() throws SyntaxError { - complete = true; - } - - @Override - public boolean isComplete() { - return complete; - } - - @Override - public JsonPrimitive getResult() throws SyntaxError { - //parse special values - String lc = numberString.toLowerCase(Locale.ROOT); - if (lc.equals("infinity") || lc.equals("+infinity")) { - return JsonPrimitive.of(Double.POSITIVE_INFINITY); - } else if (lc.equals("-infinity")) { - return JsonPrimitive.of(Double.NEGATIVE_INFINITY); - } else if (lc.equals("nan")) { - return JsonPrimitive.of(Double.NaN); - } - - //Fallback to the number parsers - if (numberString.startsWith(".")) numberString = '0'+numberString; - if (numberString.endsWith(".")) numberString = numberString+'0'; - if (numberString.startsWith("0x")) { - numberString = numberString.substring(2); - try { - Long l = Long.parseUnsignedLong(numberString, 16); - return JsonPrimitive.of(l); - } catch(NumberFormatException nfe) { - throw new SyntaxError("Tried to parse '"+numberString+"' as a hexadecimal number, but it appears to be invalid."); - } - } - if (numberString.startsWith("-0x")) { - numberString = numberString.substring(3); - try { - Long l = -Long.parseUnsignedLong(numberString, 16); - return JsonPrimitive.of(l); - } catch(NumberFormatException nfe) { - throw new SyntaxError("Tried to parse '"+numberString+"' as a hexadecimal number, but it appears to be invalid."); - } - } - - - if (numberString.indexOf('.')!=-1) { - //Return as a Double - try { - Double d = Double.valueOf(numberString); - return JsonPrimitive.of(d); - } catch (NumberFormatException ex) { - throw new SyntaxError("Tried to parse '"+numberString+"' as a floating-point number, but it appears to be invalid."); - } - } else { - //Return as a Long - try { - Long l = Long.valueOf(numberString); - return JsonPrimitive.of(l); - } catch (NumberFormatException ex) { - throw new SyntaxError("Tried to parse '"+numberString+"' as an integer, but it appears to be invalid."); - } - } - } - -} diff --git a/src/main/java/blue/endless/jankson/impl/context/json/ObjectElementContext.java b/src/main/java/blue/endless/jankson/impl/context/json/ObjectElementContext.java deleted file mode 100644 index 73550dc..0000000 --- a/src/main/java/blue/endless/jankson/impl/context/json/ObjectElementContext.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2023 Falkreon (Isaac Ellingson) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package blue.endless.jankson.impl.context.json; - -import java.util.Arrays; - -import blue.endless.jankson.api.SyntaxError; -import blue.endless.jankson.api.document.ObjectElement; -import blue.endless.jankson.api.io.JsonReaderOptions; -import blue.endless.jankson.impl.context.ElementContext; - -@Deprecated(forRemoval=true) -public class ObjectElementContext implements ElementContext { - /** - * These characters are pretty much always treated as whitespace. If we're skipping all whitespace, we're skipping these. - */ - private static final char[] CHARS_WHITESPACE = { - 0x09, //tab - 0x0A, 0x0B, 0x0C, 0x0D, // LF, VT, FF, CR - 0x20, //space - 0x85, //NEL - 0xA0, //NBSP - 0x1680, //the space from Ogham script - 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, //bunch of en and em spaces, figure space, etc. - 0x2028, 0x2029, //line-sep, para-sep - 0x202F, 0x205F, //narrow-nbsp, medium-math-space - 0x3000, //ideographic space - }; - - /** - * In certain contexts, especially HOCON, these additional chars are either blocking or skipped depending on where you are in parsing. - */ - private static final char[] CHARS_JOINER = { - 0x180E, 0x200B, 0x200C, 0x200D, 0x2060, 0xFEFF, - }; - - private ObjectElement result = new ObjectElement(); - private boolean openBraced; - private boolean closeBraced; - - public ObjectElementContext() { - //openBraced = options.hasHint(DeserializerOptions.Hint.ALLOW_BARE_ROOTS); - } - - @Override - public boolean consume(char codePoint, int line, int column, JsonReaderOptions options) throws SyntaxError { - if (!openBraced & !options.hasHint(JsonReaderOptions.Hint.ALLOW_BARE_ROOT_OBJECT)) { - //We can only accept whitespace, comments, and open braces. - if (codePoint=='{') { - openBraced = true; - return true; - //} else if (codePoint=='/') { - //TODO: Launch into comment context - } else { - //skip over whitespace - if (Arrays.binarySearch(CHARS_WHITESPACE, codePoint)>=0) { - return true; - } - //Skip over not-strictly-whitespace joiners, breaks, and BOMs - if (Arrays.binarySearch(CHARS_JOINER, codePoint)>=0) { - return true; - } - } - } else if (!closeBraced) { - //We're inside the object body. we can accept anything that could start a key (and then kick off the keyValue context) - if (!openBraced && codePoint=='=') { - throw new SyntaxError("Found close brace for a bare object.", line, column); - } - - } else { - //We're after the object body. We can't continue past a newline, including an end-of-line comment, - //and we can't consume anything but line-end comments or whitespace. - - } - - - - // TODO Auto-generated method stub - return false; - } - - @Override - public void eof() throws SyntaxError { - // TODO Auto-generated method stub - - } - - @Override - public boolean isComplete() { - return false; - } - - @Override - public ObjectElement getResult() throws SyntaxError { - return result; - } - -} diff --git a/src/main/java/blue/endless/jankson/impl/context/json/ObjectParserContext.java b/src/main/java/blue/endless/jankson/impl/context/json/ObjectParserContext.java deleted file mode 100644 index cad4d48..0000000 --- a/src/main/java/blue/endless/jankson/impl/context/json/ObjectParserContext.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2023 Falkreon (Isaac Ellingson) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package blue.endless.jankson.impl.context.json; - -import blue.endless.jankson.api.Jankson; -import blue.endless.jankson.api.SyntaxError; -import blue.endless.jankson.api.element.JsonObject; -import blue.endless.jankson.impl.context.ParserContext; - -@Deprecated(forRemoval=true) -public class ObjectParserContext implements ParserContext { - private JsonObject result = new JsonObject(); - - private final boolean assumeOpen; - - private String comment; - private boolean openBraceFound = false; - private boolean noOpenBrace = false; - private String key; - private boolean colonFound = false; - private boolean closeBraceFound = false; - private boolean eof = false; - - public ObjectParserContext(boolean assumeOpen) { - this.assumeOpen = assumeOpen; - } - - - @Override - public boolean consume(int codePoint, Jankson loader) throws SyntaxError { - result.setMarshaller(loader.getMarshaller()); - if (!openBraceFound) { - if (Character.isWhitespace(codePoint)) return true; //We're fine, this is just whitespace - if (codePoint=='/' || codePoint=='#') { - loader.push(new CommentParserContext(codePoint), (it)->{ - if (comment==null) { - comment = it; - } else { - comment = comment + "\n" + it; - } - }); - return true; - } - - if (codePoint=='{') { //We're good. We can start parsing the object now - openBraceFound = true; - return true; - } - - if (assumeOpen) { - // We're a root parser; maybe this is a file written with omitRootBraces - openBraceFound = true; - noOpenBrace = true; - } else { - throw new SyntaxError("Found character '"+(char)codePoint+"' instead of '{' while looking for the start of an object"); - } - } - if (closeBraceFound) return false; //Shouldn't happen! - - if (key==null) { - if (Character.isWhitespace(codePoint) || codePoint==',') return true; - - //Expecting: Key or End - switch(codePoint) { - case '}': - if (noOpenBrace) throw new SyntaxError("Found spurious '}' while parsing an object with no open brace."); - closeBraceFound = true; - return true; - case ',': - return true; //commas are basically whitespace to us - case '\'': - case '"': - loader.push(new StringParserContext(codePoint), (it)->key=it.asString()); - return true; - case '/': - case '#': - loader.push(new CommentParserContext(codePoint), (it)->{ - if (comment==null) { - comment = it; - } else { - comment = comment + "\n" + it; - } - }); - return true; - - //Let's capture some error cases! - case '{': - loader.throwDelayed(new SyntaxError("Found spurious '{' while parsing an object.")); - return true; - - default: - loader.push(new TokenParserContext(codePoint), (it)->key=it.asString()); - return true; - } - - } else if (colonFound) { - final String elemKey = key; - loader.push(new ElementParserContext(), (it)->{ - - //Combine the two possible comment locations into a canonical form - String resolvedComment = ""; - if (comment!=null) resolvedComment+=comment; - if (comment!=null && it.getComment()!=null) resolvedComment += '\n'; - if (it.getComment()!=null) resolvedComment += it.getComment(); - - //if (key==null) System.out.println("KEY WAS NULL! "+it.getElement()+" using saved key '"+elemKey+"'"); - result.put(elemKey, it.getElement(), resolvedComment); - key = null; - colonFound = false; - comment = null; - - }); - return false; //give the first non-colon character to the resulting element. - - } else { - if (Character.isWhitespace(codePoint)) return true; //Don't care about whitespace - if (codePoint==':') { - colonFound = true; - return true; - } - - throw new SyntaxError("Found unexpected character '"+(char)codePoint+"' while looking for the colon (':') between a key and a value in an object"); - } - } - - @Override - public boolean isComplete() { - return closeBraceFound || (assumeOpen && noOpenBrace && eof); - } - - @Override - public JsonObject getResult() { - return result; - } - - - @Override - public void eof() throws SyntaxError { - eof = true; - if (assumeOpen && noOpenBrace) return; - if (closeBraceFound) return; - throw new SyntaxError("Expected to find '}' to end an object, found EOF instead."); - } - - -} diff --git a/src/main/java/blue/endless/jankson/impl/context/json/StringParserContext.java b/src/main/java/blue/endless/jankson/impl/context/json/StringParserContext.java deleted file mode 100644 index f1638f5..0000000 --- a/src/main/java/blue/endless/jankson/impl/context/json/StringParserContext.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2018-2023 Falkreon (Isaac Ellingson) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package blue.endless.jankson.impl.context.json; - -import java.util.Locale; - -import blue.endless.jankson.api.Jankson; -import blue.endless.jankson.api.SyntaxError; -import blue.endless.jankson.api.element.JsonPrimitive; -import blue.endless.jankson.impl.context.ParserContext; - -@Deprecated(forRemoval=true) -public class StringParserContext implements ParserContext { - private static final String HEX_DIGITS = "0123456789abcdefABCDEF"; - private int quote; - private boolean escape = false; - private int unicodeUs = 0; - private StringBuilder builder = new StringBuilder(); - private boolean complete = false; - private String unicodeSequence = ""; - - public StringParserContext(int quote) { - this.quote = quote; - } - - @Override - public boolean consume(int codePoint, Jankson loader) { - //if (codePoint=='\n') { //At any point, if we've reached the end of the line without an end-quote, terminate to limit the damage. - // complete = true; - // return false; - //} - - if (escape) { - if (unicodeUs>0) { - if (codePoint=='u'||codePoint=='U') { - unicodeUs++; - return true; - } else { - if (HEX_DIGITS.indexOf(codePoint)!=-1) { - //consume the char and emit the sequence if needed - unicodeSequence+=(char)codePoint; - if (unicodeSequence.length()==4) { - emitUnicodeSequence(loader); - escape = false; - } - return true; - } else { - //don't consume the char, but emit the sequence immediately and take us out of escape mode - emitUnicodeSequence(loader); - escape = false; - return false; - } - } - } - - escape = false; - switch(codePoint) { - - - case 'b': - builder.append('\b'); - return true; - case 'f': - builder.append('\f'); - return true; - case 'n': // regular \n - builder.append('\n'); - return true; - case '\n': // JSON5 multiline string - return true; - case 'r': - builder.append('\r'); - return true; - case 't': - builder.append('\t'); - return true; - case '"': - builder.append('"'); - return true; - case '\'': - builder.append('\''); - return true; - case '\\': - builder.append('\\'); - return true; - case 'u': - case 'U': - escape = true; - unicodeUs = 1; - return true; - default: - builder.append((char)codePoint); - return true; - } - } else { - if (codePoint==quote) { - complete = true; - return true; - } - - if (codePoint=='\\') { - escape=true; - return true; - } - - if (codePoint=='\n') { //non-escaped CR. Terminate the string to limit the damage. - complete = true; - return false; - } - - if (codePoint<0xFFFF) { - builder.append((char)codePoint); - return true; - } else { - //Construct a high and low surrogate pair for this code point - - int temp = codePoint - 0x10000; - int highSurrogate = (temp >>> 10) + 0xD800; - int lowSurrogate = (temp & 0b11_1111_1111) + 0xDC00; - - builder.append((char)highSurrogate); - builder.append((char)lowSurrogate); - - return true; - } - } - } - - private void emitUnicodeSequence(Jankson loader) { - if (unicodeUs>1) { - unicodeUs--; - builder.append("\\"); - for(int i=0; i { - private String token = ""; - private boolean complete = false; - - public TokenParserContext(int firstCodePoint) { - token += (char)firstCodePoint; - } - - @Override - public boolean consume(int codePoint, Jankson loader) throws SyntaxError { - if (complete) return false; - - if (codePoint=='~' || Character.isUnicodeIdentifierPart(codePoint)) { - - if (codePoint<0xFFFF) { - token+=((char)codePoint); - return true; - } else { - //Construct a high and low surrogate pair for this code point - //TODO: Finish implementing - int temp = codePoint - 0x10000; - int highSurrogate = (temp >>> 10) + 0xD800; - int lowSurrogate = (temp & 0b11_1111_1111) + 0xDC00; - - token += (char)highSurrogate; - token += (char)lowSurrogate; - - return true; - } - - } else { - complete = true; - return false; - } - } - - @Override - public void eof() throws SyntaxError { - complete = true; - } - - @Override - public boolean isComplete() { - return complete; - } - - @Override - public JsonPrimitive getResult() throws SyntaxError { - return JsonPrimitive.of(token); - } -} diff --git a/src/main/java/blue/endless/jankson/impl/serializer/DeserializerFunctionPool.java b/src/main/java/blue/endless/jankson/impl/serializer/DeserializerFunctionPool.java index 9fad15d..f36104f 100644 --- a/src/main/java/blue/endless/jankson/impl/serializer/DeserializerFunctionPool.java +++ b/src/main/java/blue/endless/jankson/impl/serializer/DeserializerFunctionPool.java @@ -28,10 +28,6 @@ import blue.endless.jankson.api.JsonGrammar; import blue.endless.jankson.api.Marshaller; -import blue.endless.jankson.api.element.JsonArray; -import blue.endless.jankson.api.element.JsonElement; -import blue.endless.jankson.api.element.JsonObject; -import blue.endless.jankson.api.element.JsonPrimitive; import blue.endless.jankson.api.io.JsonIOException; /** @@ -53,7 +49,9 @@ public InternalDeserializerFunction getFunction(Class sourceClass) { return (InternalDeserializerFunction)values.get(sourceClass); } - public B apply(JsonElement elem, Marshaller marshaller) throws JsonIOException, FunctionMatchFailedException { + //public B apply(JsonElement elem, Marshaller marshaller) throws JsonIOException, FunctionMatchFailedException { + // return null; + /* InternalDeserializerFunction selected = null; //This whole block is pretty ugly but there's a very particular selection order @@ -82,7 +80,8 @@ public B apply(JsonElement elem, Marshaller marshaller) throws JsonIOException, //We can't just return null, *because null might be the intended output of one of the functions above!* throw new FunctionMatchFailedException("Couldn't find a deserializer in class '"+targetClass.getCanonicalName()+"' to unpack element '"+elem.toJson(JsonGrammar.JSON5)+"'."); - } + */ + //} public static class FunctionMatchFailedException extends Exception { private static final long serialVersionUID = -7909332778483440658L; diff --git a/src/test/java/blue/endless/jankson/BasicTests.java b/src/test/java/blue/endless/jankson/BasicTests.java index da01f10..97ac47b 100644 --- a/src/test/java/blue/endless/jankson/BasicTests.java +++ b/src/test/java/blue/endless/jankson/BasicTests.java @@ -49,22 +49,11 @@ import blue.endless.jankson.api.JsonGrammar; import blue.endless.jankson.api.SyntaxError; import blue.endless.jankson.api.annotation.Comment; -import blue.endless.jankson.api.element.JsonArray; -import blue.endless.jankson.api.element.JsonElement; -import blue.endless.jankson.api.element.JsonNull; -import blue.endless.jankson.api.element.JsonObject; -import blue.endless.jankson.api.element.JsonPrimitive; import blue.endless.jankson.impl.MarshallerImpl; import blue.endless.jankson.impl.TypeMagic; @SuppressWarnings("deprecation") public class BasicTests { - Jankson jankson; - - @BeforeEach - public void setup() { - jankson = Jankson.builder().build(); - } @Test public void testPrimitiveEquality() { @@ -149,29 +138,32 @@ public void testArrayContentCategories() throws IOException, SyntaxError { /* Unported 1.2.x tests */ - @Test - public void testCommentAttribution() { - try { - String before = "{ /* Hello World */ 'foo': true }"; - JsonObject after = jankson.load(before); - - Assertions.assertEquals("Hello World", after.getComment("foo"), "Comment should be parsed and attributed to child 'foo'"); - - before = "{ /*Hello World */ 'foo': true }"; - after = jankson.load(before); - - Assertions.assertEquals("Hello World", after.getComment("foo"), "Comment should still be parsed and attributed to child 'foo'"); - - before = "{ //\tHello World \n 'foo': true }"; - after = jankson.load(before); - - Assertions.assertEquals("Hello World", after.getComment("foo"), "Single-line comment should be parsed and attributed to child 'foo'"); - - } catch (SyntaxError ex) { - Assertions.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); - } - } + + //@Test + //public void testCommentAttribution() { + // try { + // String before = "{ /* Hello World */ 'foo': true }"; + // JsonObject after = jankson.load(before); + // + // Assertions.assertEquals("Hello World", after.getComment("foo"), "Comment should be parsed and attributed to child 'foo'"); + // + // before = "{ /*Hello World */ 'foo': true }"; + // after = jankson.load(before); + // + // Assertions.assertEquals("Hello World", after.getComment("foo"), "Comment should still be parsed and attributed to child 'foo'"); + // + // before = "{ //\tHello World \n 'foo': true }"; + // after = jankson.load(before); + // + // Assertions.assertEquals("Hello World", after.getComment("foo"), "Single-line comment should be parsed and attributed to child 'foo'"); + // + // } catch (SyntaxError ex) { + // Assertions.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); + // } + //} + + /* @Test public void testDeepNesting() { String subject = "{ a: { a: { a: { a: { a: { a: { a: { a: 'Hello' } } } } } } } }"; @@ -291,14 +283,14 @@ private static class CommentedClass { @Comment("This is a comment.") private String foo = "what?"; } - - @Test - public void testSerializedComments() { - CommentedClass commented = new CommentedClass(); - String serialized = MarshallerImpl.getFallback().serialize(commented).toJson(true, false, 0); - Assertions.assertEquals("{ /* This is a comment. */ \"foo\": \"what?\" }", serialized); - } - + */ + //@Test + //public void testSerializedComments() { + // CommentedClass commented = new CommentedClass(); + // String serialized = MarshallerImpl.getFallback().serialize(commented).toJson(true, false, 0); + // Assertions.assertEquals("{ /* This is a comment. */ \"foo\": \"what?\" }", serialized); + //} + /* private enum ExampleEnum { ANT, BOX, @@ -326,10 +318,10 @@ public void testDeserializeEnums() { Assertions.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); } } - - @Test - public void testDiffAgainstDefaults() { - try { + */ + //@Test + //public void testDiffAgainstDefaults() { + // try { /* * A number of specific behaviors are verified here: * - 'a' is present as a default but not present in the base object. This key is ignored. @@ -345,18 +337,18 @@ public void testDiffAgainstDefaults() { * - 'i' is an object, so it receives a deep comparison. However, it is found to be identical, and so its key is ignored. */ - JsonObject defaultObj = jankson.load("{ a: 'a', b: 'b', c: 'c', d: { e: 'e', f: 'f' }, g: [1, 2], h: [1, 2], i: { j: 'j' } }"); - JsonObject baseObj = jankson.load("{ b: 'b', c: 'test', d: { e: 'e', f: 'test' }, g: [1, 2], h: [2, 3], i: { j: 'j' } }"); - String expected = "{ \"c\": \"test\", \"d\": { \"f\": \"test\" }, \"h\": [ 2, 3 ] }"; + // JsonObject defaultObj = jankson.load("{ a: 'a', b: 'b', c: 'c', d: { e: 'e', f: 'f' }, g: [1, 2], h: [1, 2], i: { j: 'j' } }"); + // JsonObject baseObj = jankson.load("{ b: 'b', c: 'test', d: { e: 'e', f: 'test' }, g: [1, 2], h: [2, 3], i: { j: 'j' } }"); + // String expected = "{ \"c\": \"test\", \"d\": { \"f\": \"test\" }, \"h\": [ 2, 3 ] }"; - String actual = baseObj.getDelta(defaultObj).toJson(); - Assertions.assertEquals(expected, actual); + // String actual = baseObj.getDelta(defaultObj).toJson(); + // Assertions.assertEquals(expected, actual); - } catch (SyntaxError ex) { - Assertions.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); - } - } - + // } catch (SyntaxError ex) { + // Assertions.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); + // } + //} + /* @Test public void testArrayGet() { try { @@ -467,7 +459,7 @@ public void testNestedDeserialization() { Assert.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); } }*/ - + /* @Test public void testNegativeNumbers() { String subject = "{ 'foo': -1, 'bar': [ -1, -3 ] }"; @@ -534,44 +526,44 @@ public void ensureEmptyCommentsAreOmitted() { Assertions.assertEquals(expected, actual); } + */ + //@Test + //public void ensureMultilineCommentsAreIndented() { + // JsonObject subject = new JsonObject(); + // subject.put("foo", new JsonPrimitive("bar"), "This is a line\nAnd this is another line."); + // String actual = subject.toJson(JsonGrammar.JSON5); + // String expected = + // "{\n" + + // " /* This is a line\n" + + // " And this is another line.\n" + //Three spaces precede every subsequent line to line comments up + // " */\n" + //The end-comment is on its own line + // " \"foo\": \"bar\",\n" + //Again, trailing comma per JSON5 + // "}"; + // + // Assertions.assertEquals(expected, actual); + //} - @Test - public void ensureMultilineCommentsAreIndented() { - JsonObject subject = new JsonObject(); - subject.put("foo", new JsonPrimitive("bar"), "This is a line\nAnd this is another line."); - String actual = subject.toJson(JsonGrammar.JSON5); - String expected = - "{\n" + - " /* This is a line\n" + - " And this is another line.\n" + //Three spaces precede every subsequent line to line comments up - " */\n" + //The end-comment is on its own line - " \"foo\": \"bar\",\n" + //Again, trailing comma per JSON5 - "}"; - - Assertions.assertEquals(expected, actual); - } - - @Test - public void ensureMultilineArrayCommentsAreIndented() { - JsonArray subject = new JsonArray(); - subject.add(new JsonPrimitive("foo"), "This is a line\nAnd this is another line."); - String actual = subject.toJson(JsonGrammar.JSON5); - String expected = - "[\n" + - " /* This is a line\n" + - " And this is another line.\n" + //Three spaces precede every subsequent line to line comments up - " */\n" + //The end-comment is on its own line - " \"foo\",\n" + //Trailing comma per JSON5 - "]"; - - Assertions.assertEquals(expected, actual); - } - + //@Test + //public void ensureMultilineArrayCommentsAreIndented() { + // JsonArray subject = new JsonArray(); + // subject.add(new JsonPrimitive("foo"), "This is a line\nAnd this is another line."); + // String actual = subject.toJson(JsonGrammar.JSON5); + // String expected = + // "[\n" + + // " /* This is a line\n" + + // " And this is another line.\n" + //Three spaces precede every subsequent line to line comments up + // " */\n" + //The end-comment is on its own line + // " \"foo\",\n" + //Trailing comma per JSON5 + // "]"; + // + // Assertions.assertEquals(expected, actual); + //} + /* private static class TestClass { private ArrayList strings; private Map scripts = new HashMap<>(); private Queue queue = new ArrayDeque<>(); - } + }*/ /** * This stresses several parts of the POJODeserializer logic. Among other things: @@ -586,7 +578,7 @@ private static class TestClass { * *
  • When re-serializing, Maps MUST be serialized to JsonObject. Collections MUST be serialized to JsonArray. */ - @Test + /*@Test public void testDeserializeGenerics() { try { String serialized = "{ \"strings\": [ \"a\", \"b\", \"c\" ], \"scripts\": { \"arabic\": \"ARABIC\" }, \"queue\": [ \"FUN\" ] }"; @@ -596,7 +588,7 @@ public void testDeserializeGenerics() { } catch (SyntaxError ex) { Assertions.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); } - } + }*/ //private static class RecursiveGenericsTestClass { // ArrayList> lists; @@ -621,12 +613,12 @@ public void testRecursiveGenerics() { Assert.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); } }*/ - + /* private static class GenericArrayContainer { public T[] ts; public int[] ints; public U[] u() { return null; }; - } + }*/ /** * Stresses TypeMagic's array comprehension, and clarifies certain deserializer behaviors and limitations. @@ -636,6 +628,7 @@ private static class GenericArrayContainer { *
  • Wildcard types are tricky. In the tested case, " U[]", it doesn't matter that "U" has type bounds - it's a type variable. * The variable will be treated as Object for the purposes of deserialization, and an Object[] will be created. */ + /* @Test public void testGenericArrayComprehension() { GenericArrayContainer container = new GenericArrayContainer<>(); @@ -659,8 +652,9 @@ private static class NullContainer { public String nonnull = ""; } - + */ /** This test will fail as soon as a key is added for 'nonnull'. 1.2 should fix this. */ + /* @Test public void testDeserializeNulls() { String serialized = "{ \"nullable\": null }"; @@ -688,12 +682,13 @@ public void testJson5EscapedReturn() { } catch (SyntaxError ex) { Assertions.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); } - } + }*/ /** * While this isn't really a *normative* example, it's a pretty good example of all the JSON5 quirks in one tidy * package. Jankson is and should remain fully compatible with JSON5 quirks. */ + /* @Test public void parseJson5InformativeExample() { String serialized = "{\n" + @@ -850,9 +845,10 @@ public void testUnifontCommentTestCase() { private static class NameTest { @SerializedName("foo_bar") public int fooBar = 0; - } + }*/ /** SerializedName should be preferred over the field's name in both POJO serialization and deserialization */ + /* @Test public void testSerializedName() { String subject = @@ -868,9 +864,10 @@ public void testSerializedName() { } catch (SyntaxError ex) { Assertions.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); } - } + }*/ /** This makes sure tilde on its own gets processed as a String by the token parser */ + /* @Test public void testUnquotedStrings() { String subject = @@ -888,12 +885,13 @@ public void testUnquotedStrings() { } catch (SyntaxError ex) { Assertions.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); } - } + }*/ /** * Issue: #21 * Special numerics serializing into quoted String values, so that a save-and-load no longer considers the values numeric. */ + /* @Test public void testSerializeSpecialNumerics() { JsonObject obj = new JsonObject(); @@ -911,12 +909,13 @@ public void testSerializeSpecialNumerics() { String actual = obj.toJson(JsonGrammar.JSON5); Assertions.assertEquals(expected, actual); - } + }*/ /** * Issue: #22 * Offer a grammar option for outputting root objects without braces ( "{}" ) while inner objects retain their delimiters. */ + /* @Test public void testBareObject() { JsonGrammar BARE = JsonGrammar.builder().bareRootObject(true).build(); @@ -940,12 +939,13 @@ public void testBareObject() { String actual = obj.toJson(BARE); Assertions.assertEquals(expected, actual); - } + }*/ /** * Issue: #23 * Grammar option to print unquoted keys */ + /* @Test public void testUnquotedKeys() { JsonGrammar UNQUOTED = JsonGrammar.builder().printUnquotedKeys(true).build(); @@ -970,5 +970,5 @@ public void testUnquotedKeys() { "}"; String actual = obj.toJson(UNQUOTED); Assertions.assertEquals(expected, actual); - } + }*/ } diff --git a/src/test/java/blue/endless/jankson/TestCosmetic.java b/src/test/java/blue/endless/jankson/TestCosmetic.java index d5dce22..e43429a 100644 --- a/src/test/java/blue/endless/jankson/TestCosmetic.java +++ b/src/test/java/blue/endless/jankson/TestCosmetic.java @@ -31,22 +31,14 @@ import blue.endless.jankson.api.Jankson; import blue.endless.jankson.api.JsonGrammar; import blue.endless.jankson.api.SyntaxError; -import blue.endless.jankson.api.element.JsonArray; -import blue.endless.jankson.api.element.JsonObject; -import blue.endless.jankson.api.element.JsonPrimitive; public class TestCosmetic { - Jankson jankson; - - @BeforeEach - public void setup() { - jankson = Jankson.builder().build(); - } - + /** * Issue: #16 * Closing array brace needs to be indented. Empty array should be kept on the same line. */ + /* @Test public void testArrayIndents() { @@ -90,100 +82,100 @@ public void testArrayIndents() { Assertions.assertEquals(expectedJson5, json5); Assertions.assertEquals(expectedStrict, strict); Assertions.assertEquals(expectedRootArray, rootArray); - } - - @Test - public void testBareRootObjectIndentWithComment() { - String expectedJkson = - "/* This is a multiline\n" + - " comment.\n" + - "*/\n" + - "object: {\n" + - "\tfoo: true\n" + - "}\n"; - - JsonObject subject = new JsonObject(); - JsonObject sub = new JsonObject(); - - sub.put("foo", JsonPrimitive.TRUE); - - subject.put("object", sub, "This is a multiline\ncomment.\n"); - - String result = subject.toJson(JsonGrammar.builder() - .bareRootObject(true) - .printCommas(false) - .withComments(true) - .printWhitespace(true) - .printUnquotedKeys(true) - .build()); - - Assertions.assertEquals(expectedJkson, result); - } + }*/ - /** - * Issue #44: Adding comments with 'bareRootObject(true)' messes up indentation - * - * Makes sure that indentation is correct after a single-line comment under "bare root" output - */ - @Test - public void testBareRootObjectIndentWithSingleLineComment() { - JsonGrammar grammar = JsonGrammar.builder().bareRootObject(true).build(); - JsonObject object = new JsonObject(); - object.put("first_thing", new JsonPrimitive("First Thing")); - object.put("second_thing", new JsonPrimitive("Second Thing"), "This has a comment!"); - object.put("third_thing", new JsonPrimitive("Third Thing")); - - String expectedJkson = - "\"first_thing\": \"First Thing\",\n" - + "// This has a comment!\n" - + "\"second_thing\": \"Second Thing\",\n" - + "\"third_thing\": \"Third Thing\"\n"; - - Assertions.assertEquals(expectedJkson, object.toJson(grammar)); - } - - /** - * Issue #36: Multiline comments sometimes lost in serialization/deserialization cycles - */ - @Test - public void testMultilineCommentsRoundTrip() { - JsonObject obj = new JsonObject(); - JsonPrimitive p = new JsonPrimitive(42); - obj.put("thing", p, "this is a multiline\ncomment"); - - String serialized = obj.toJson(JsonGrammar.JSON5); - - String expectedJkson = - "{\n" - + " /* this is a multiline\n" - + " comment\n" - + " */\n" - + " \"thing\": 42,\n" - + "}"; - - Assertions.assertEquals(expectedJkson, serialized); - } - - /** - * Further testing for #36 - * @throws SyntaxError - */ - @Test - public void testMultiCommentDeserialize() throws SyntaxError { - String source = "{ /* this is a multiline */ /* comment */ \"thing\": 42 }"; - - JsonObject object = Jankson.builder().build().load(source); - - String reserialized = object.toJson(JsonGrammar.JSON5); - - String expectedJkson = - "{\n" - + " /* this is a multiline\n" - + " comment\n" - + " */\n" - + " \"thing\": 42,\n" - + "}"; - - Assertions.assertEquals(expectedJkson, reserialized); - } +// @Test +// public void testBareRootObjectIndentWithComment() { +// String expectedJkson = +// "/* This is a multiline\n" + +// " comment.\n" + +// "*/\n" + +// "object: {\n" + +// "\tfoo: true\n" + +// "}\n"; +// +// JsonObject subject = new JsonObject(); +// JsonObject sub = new JsonObject(); +// +// sub.put("foo", JsonPrimitive.TRUE); +// +// subject.put("object", sub, "This is a multiline\ncomment.\n"); +// +// String result = subject.toJson(JsonGrammar.builder() +// .bareRootObject(true) +// .printCommas(false) +// .withComments(true) +// .printWhitespace(true) +// .printUnquotedKeys(true) +// .build()); +// +// Assertions.assertEquals(expectedJkson, result); +// } +// +// /** +// * Issue #44: Adding comments with 'bareRootObject(true)' messes up indentation +// * +// * Makes sure that indentation is correct after a single-line comment under "bare root" output +// */ +// @Test +// public void testBareRootObjectIndentWithSingleLineComment() { +// JsonGrammar grammar = JsonGrammar.builder().bareRootObject(true).build(); +// JsonObject object = new JsonObject(); +// object.put("first_thing", new JsonPrimitive("First Thing")); +// object.put("second_thing", new JsonPrimitive("Second Thing"), "This has a comment!"); +// object.put("third_thing", new JsonPrimitive("Third Thing")); +// +// String expectedJkson = +// "\"first_thing\": \"First Thing\",\n" +// + "// This has a comment!\n" +// + "\"second_thing\": \"Second Thing\",\n" +// + "\"third_thing\": \"Third Thing\"\n"; +// +// Assertions.assertEquals(expectedJkson, object.toJson(grammar)); +// } +// +// /** +// * Issue #36: Multiline comments sometimes lost in serialization/deserialization cycles +// */ +// @Test +// public void testMultilineCommentsRoundTrip() { +// JsonObject obj = new JsonObject(); +// JsonPrimitive p = new JsonPrimitive(42); +// obj.put("thing", p, "this is a multiline\ncomment"); +// +// String serialized = obj.toJson(JsonGrammar.JSON5); +// +// String expectedJkson = +// "{\n" +// + " /* this is a multiline\n" +// + " comment\n" +// + " */\n" +// + " \"thing\": 42,\n" +// + "}"; +// +// Assertions.assertEquals(expectedJkson, serialized); +// } +// +// /** +// * Further testing for #36 +// * @throws SyntaxError +// */ +// @Test +// public void testMultiCommentDeserialize() throws SyntaxError { +// String source = "{ /* this is a multiline */ /* comment */ \"thing\": 42 }"; +// +// JsonObject object = Jankson.builder().build().load(source); +// +// String reserialized = object.toJson(JsonGrammar.JSON5); +// +// String expectedJkson = +// "{\n" +// + " /* this is a multiline\n" +// + " comment\n" +// + " */\n" +// + " \"thing\": 42,\n" +// + "}"; +// +// Assertions.assertEquals(expectedJkson, reserialized); +// } } diff --git a/src/test/java/blue/endless/jankson/TestDeserializer.java b/src/test/java/blue/endless/jankson/TestDeserializer.java index 00a337c..4f72618 100644 --- a/src/test/java/blue/endless/jankson/TestDeserializer.java +++ b/src/test/java/blue/endless/jankson/TestDeserializer.java @@ -34,216 +34,206 @@ import blue.endless.jankson.api.JsonGrammar; import blue.endless.jankson.api.Marshaller; import blue.endless.jankson.api.SyntaxError; -import blue.endless.jankson.api.element.JsonArray; -import blue.endless.jankson.api.element.JsonElement; -import blue.endless.jankson.api.element.JsonObject; -import blue.endless.jankson.api.element.JsonPrimitive; import blue.endless.jankson.api.io.JsonIOException; public class TestDeserializer { - Jankson jankson; - @BeforeEach - public void setup() { - jankson = Jankson.builder().build(); - } - - public static class Foo { - private String value = ""; - private String opt = ""; - - @Deserializer - public static Foo deserialize(String s) { - Foo result = new Foo(); - result.value = s; - return result; - } - - // the unused Marshaller parameter is for testing that it gets recognised properly - @Deserializer - public static Foo deserialize(JsonArray arr, Marshaller marshaller) throws JsonIOException { - if (arr.size()<1 || arr.size()>2) throw new JsonIOException("Array can have either 1 or 2 elements. Found: "+arr.size()); - Foo result = new Foo(); - result.value = arr.getString(0, "OOPS"); - if (arr.size()>1) result.opt = arr.getString(1, "OOPS"); - return result; - } - } - - public static class Bar { - public Foo stringDeserializer; - public Foo arrayDeserializer; - public Foo unmappedDeserializer = new Foo(); - public Foo defaultDeserializer; - } - - /** - * Make sure that various kinds of self-described deserializers are working alongside each other - */ - @Test - public void testBasicFeatures() throws JsonIOException { - String subject = - "{\n" + - " \"stringDeserializer\": \"test\",\n" + - " \"arrayDeserializer\": [ \"someValue\", \"someOptValue\" ],\n" + - " \"unmappedDeserializer\": false,\n" + - " \"defaultDeserializer\": {\n" + - " \"value\": \"someValue\",\n" + - " \"opt\": \"someOptValue\"\n" + - " }\n" + - "\n" + - "}"; - - try { - Bar obj = jankson.fromJson(subject, Bar.class); - - Assertions.assertNotNull(obj.stringDeserializer); - Assertions.assertNotNull(obj.arrayDeserializer); - Assertions.assertNotNull(obj.unmappedDeserializer); - Assertions.assertNotNull(obj.defaultDeserializer); - - Assertions.assertEquals("test", obj.stringDeserializer.value); - Assertions.assertEquals("someValue", obj.arrayDeserializer.value); - Assertions.assertEquals("someOptValue", obj.arrayDeserializer.opt); - Assertions.assertEquals("", obj.unmappedDeserializer.value); - Assertions.assertEquals("someValue", obj.defaultDeserializer.value); - Assertions.assertEquals("someOptValue", obj.defaultDeserializer.opt); - } catch (SyntaxError ex) { - Assertions.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); - } - } - - /** - * Just to be clear: If we can't unpack a key for any reason, Carefully should throw an error. - */ - @Test - public void ensureErrorOnMissingDeserializer() throws SyntaxError, JsonIOException { - String subject = - "{\n" + - " \"stringDeserializer\": \"test\",\n" + - " \"arrayDeserializer\": [ \"someValue\", \"someOptValue\" ],\n" + - " \"unmappedDeserializer\": false,\n" + - " \"defaultDeserializer\": {\n" + - " \"value\": \"someValue\",\n" + - " \"opt\": \"someOptValue\"\n" + - " }\n" + - "\n" + - "}"; - - Assertions.assertThrows( - JsonIOException.class, - ()->jankson.fromJsonCarefully(subject, Bar.class) - ); - } - - public static class Baz { - public Foo foo; - } - - - - - - /** This makes sure that values like Infinity, NaN, and -Infinity are parsed properly */ - @Test - public void testSpecialNumericValues() { - String subject = - "{\n" + - " \"a\": Infinity,\n" + - " \"b\": -Infinity,\n" + - " \"c\": NaN,\n" + - "}"; - try { - JsonObject result = jankson.load(subject); - JsonElement a = result.get("a"); - JsonElement b = result.get("b"); - JsonElement c = result.get("c"); - Assertions.assertTrue(a instanceof JsonPrimitive, "'Infinity' parses to a JsonPrimitive"); - Assertions.assertTrue(b instanceof JsonPrimitive, "'-Infinity' parses to a JsonPrimitive"); - Assertions.assertTrue(c instanceof JsonPrimitive, "'NaN' parses to a JsonPrimitive"); - - Assertions.assertEquals(Double.POSITIVE_INFINITY, ((JsonPrimitive)a).asDouble(-1), 1); - Assertions.assertEquals(Double.NEGATIVE_INFINITY, ((JsonPrimitive)b).asDouble(-1), 1); - Assertions.assertEquals(Double.NaN, ((JsonPrimitive)c).asDouble(-1), 1); - } catch (SyntaxError ex) { - Assertions.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); - } - } - - /** - * Problems reported: loadElement returns jsonNull for an array or a bare numeric element. - */ - @Test - public void testLoadElementNonnull() throws SyntaxError { - Object x; - x = jankson.loadElement("1"); - Assertions.assertTrue(x instanceof JsonPrimitive, "'1' must be a JsonPrimitive"); - x = jankson.loadElement("[1]"); - Assertions.assertTrue(x instanceof JsonArray, "'[1]' must be a jsonArray"); - } - - /** - * Issue #31: Unicode escape sequences aren't deserialized correctly - * - *

    StringParserContext was fitted with a new unicode escape parser to cover this case. - */ - @Test - public void testUnicodeEscapes() throws SyntaxError { - String example = "{ \"test\": \"\\u003C0\" }"; - Assertions.assertEquals("\"<0\"", jankson.load(example).get("test").toJson(), "Unicode escapes must be parsed correctly."); - - String slightlyUnescaped = ((JsonPrimitive)jankson.load("{'test': \"\\uu003C\" }").get("test")).asString(); - Assertions.assertEquals("\\u003c", slightlyUnescaped, "Unicode escapes which are themselves escaped must be unescaped exactly one level."); //implied here is that hex digit case MUST be lost - } - - /** - * Issue #35: Serialize/deserialize cycles of multiline comments cause whitespace of lines after the first to creep larger and larger - * - *

    Now leading whitespace is stripped from each line of multiline comments. - */ - @Test - public void testMultilineCommentReads() throws SyntaxError { - JsonObject obj = new JsonObject(); - JsonPrimitive p = new JsonPrimitive(42); - obj.put("thing", p, "this is a multiline\ncomment"); - - JsonObject recyc = jankson.load(obj.toJson(JsonGrammar.JSON5)); - - Assertions.assertEquals(obj.toJson(JsonGrammar.JSON5), recyc.toJson(JsonGrammar.JSON5)); - } - - @Test - public void testOmitRootBraces() throws JsonIOException { - String subject = - "\"stringDeserializer\": \"test\",\n" + - "\"arrayDeserializer\": [ \"someValue\", \"someOptValue\" ],\n" + - "\"unmappedDeserializer\": false,\n" + - "\"defaultDeserializer\": {\n" + - " \"value\": \"someValue\",\n" + - " \"opt\": \"someOptValue\"\n" + - "}"; - - try { - Bar obj = Jankson.builder().allowBareRootObject().build().fromJson(subject, Bar.class); - - Assertions.assertNotNull(obj.stringDeserializer); - Assertions.assertNotNull(obj.arrayDeserializer); - Assertions.assertNotNull(obj.unmappedDeserializer); - Assertions.assertNotNull(obj.defaultDeserializer); - - Assertions.assertEquals("test", obj.stringDeserializer.value); - Assertions.assertEquals("someValue", obj.arrayDeserializer.value); - Assertions.assertEquals("someOptValue", obj.arrayDeserializer.opt); - Assertions.assertEquals("", obj.unmappedDeserializer.value); - Assertions.assertEquals("someValue", obj.defaultDeserializer.value); - Assertions.assertEquals("someOptValue", obj.defaultDeserializer.opt); - } catch (SyntaxError ex) { - Assertions.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); - } - try { - Jankson.builder().build().fromJson(subject, Bar.class); - Assertions.fail("Should not successfully load bare root object without enabling option"); - } catch (SyntaxError ex) { - } - } +// public static class Foo { +// private String value = ""; +// private String opt = ""; +// +// @Deserializer +// public static Foo deserialize(String s) { +// Foo result = new Foo(); +// result.value = s; +// return result; +// } +// +// // the unused Marshaller parameter is for testing that it gets recognised properly +// @Deserializer +// public static Foo deserialize(JsonArray arr, Marshaller marshaller) throws JsonIOException { +// if (arr.size()<1 || arr.size()>2) throw new JsonIOException("Array can have either 1 or 2 elements. Found: "+arr.size()); +// Foo result = new Foo(); +// result.value = arr.getString(0, "OOPS"); +// if (arr.size()>1) result.opt = arr.getString(1, "OOPS"); +// return result; +// } +// } +// +// public static class Bar { +// public Foo stringDeserializer; +// public Foo arrayDeserializer; +// public Foo unmappedDeserializer = new Foo(); +// public Foo defaultDeserializer; +// } +// +// /** +// * Make sure that various kinds of self-described deserializers are working alongside each other +// */ +// @Test +// public void testBasicFeatures() throws JsonIOException { +// String subject = +// "{\n" + +// " \"stringDeserializer\": \"test\",\n" + +// " \"arrayDeserializer\": [ \"someValue\", \"someOptValue\" ],\n" + +// " \"unmappedDeserializer\": false,\n" + +// " \"defaultDeserializer\": {\n" + +// " \"value\": \"someValue\",\n" + +// " \"opt\": \"someOptValue\"\n" + +// " }\n" + +// "\n" + +// "}"; +// +// try { +// Bar obj = jankson.fromJson(subject, Bar.class); +// +// Assertions.assertNotNull(obj.stringDeserializer); +// Assertions.assertNotNull(obj.arrayDeserializer); +// Assertions.assertNotNull(obj.unmappedDeserializer); +// Assertions.assertNotNull(obj.defaultDeserializer); +// +// Assertions.assertEquals("test", obj.stringDeserializer.value); +// Assertions.assertEquals("someValue", obj.arrayDeserializer.value); +// Assertions.assertEquals("someOptValue", obj.arrayDeserializer.opt); +// Assertions.assertEquals("", obj.unmappedDeserializer.value); +// Assertions.assertEquals("someValue", obj.defaultDeserializer.value); +// Assertions.assertEquals("someOptValue", obj.defaultDeserializer.opt); +// } catch (SyntaxError ex) { +// Assertions.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); +// } +// } +// +// /** +// * Just to be clear: If we can't unpack a key for any reason, Carefully should throw an error. +// */ +// @Test +// public void ensureErrorOnMissingDeserializer() throws SyntaxError, JsonIOException { +// String subject = +// "{\n" + +// " \"stringDeserializer\": \"test\",\n" + +// " \"arrayDeserializer\": [ \"someValue\", \"someOptValue\" ],\n" + +// " \"unmappedDeserializer\": false,\n" + +// " \"defaultDeserializer\": {\n" + +// " \"value\": \"someValue\",\n" + +// " \"opt\": \"someOptValue\"\n" + +// " }\n" + +// "\n" + +// "}"; +// +// Assertions.assertThrows( +// JsonIOException.class, +// ()->jankson.fromJsonCarefully(subject, Bar.class) +// ); +// } +// +// public static class Baz { +// public Foo foo; +// } +// +// +// +// +// +// /** This makes sure that values like Infinity, NaN, and -Infinity are parsed properly */ +// @Test +// public void testSpecialNumericValues() { +// String subject = +// "{\n" + +// " \"a\": Infinity,\n" + +// " \"b\": -Infinity,\n" + +// " \"c\": NaN,\n" + +// "}"; +// try { +// JsonObject result = jankson.load(subject); +// JsonElement a = result.get("a"); +// JsonElement b = result.get("b"); +// JsonElement c = result.get("c"); +// Assertions.assertTrue(a instanceof JsonPrimitive, "'Infinity' parses to a JsonPrimitive"); +// Assertions.assertTrue(b instanceof JsonPrimitive, "'-Infinity' parses to a JsonPrimitive"); +// Assertions.assertTrue(c instanceof JsonPrimitive, "'NaN' parses to a JsonPrimitive"); +// +// Assertions.assertEquals(Double.POSITIVE_INFINITY, ((JsonPrimitive)a).asDouble(-1), 1); +// Assertions.assertEquals(Double.NEGATIVE_INFINITY, ((JsonPrimitive)b).asDouble(-1), 1); +// Assertions.assertEquals(Double.NaN, ((JsonPrimitive)c).asDouble(-1), 1); +// } catch (SyntaxError ex) { +// Assertions.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); +// } +// } +// +// /** +// * Problems reported: loadElement returns jsonNull for an array or a bare numeric element. +// */ +// @Test +// public void testLoadElementNonnull() throws SyntaxError { +// Object x; +// x = jankson.loadElement("1"); +// Assertions.assertTrue(x instanceof JsonPrimitive, "'1' must be a JsonPrimitive"); +// x = jankson.loadElement("[1]"); +// Assertions.assertTrue(x instanceof JsonArray, "'[1]' must be a jsonArray"); +// } +// +// /** +// * Issue #31: Unicode escape sequences aren't deserialized correctly +// * +// *

    StringParserContext was fitted with a new unicode escape parser to cover this case. +// */ +// @Test +// public void testUnicodeEscapes() throws SyntaxError { +// String example = "{ \"test\": \"\\u003C0\" }"; +// Assertions.assertEquals("\"<0\"", jankson.load(example).get("test").toJson(), "Unicode escapes must be parsed correctly."); +// +// String slightlyUnescaped = ((JsonPrimitive)jankson.load("{'test': \"\\uu003C\" }").get("test")).asString(); +// Assertions.assertEquals("\\u003c", slightlyUnescaped, "Unicode escapes which are themselves escaped must be unescaped exactly one level."); //implied here is that hex digit case MUST be lost +// } +// +// /** +// * Issue #35: Serialize/deserialize cycles of multiline comments cause whitespace of lines after the first to creep larger and larger +// * +// *

    Now leading whitespace is stripped from each line of multiline comments. +// */ +// @Test +// public void testMultilineCommentReads() throws SyntaxError { +// JsonObject obj = new JsonObject(); +// JsonPrimitive p = new JsonPrimitive(42); +// obj.put("thing", p, "this is a multiline\ncomment"); +// +// JsonObject recyc = jankson.load(obj.toJson(JsonGrammar.JSON5)); +// +// Assertions.assertEquals(obj.toJson(JsonGrammar.JSON5), recyc.toJson(JsonGrammar.JSON5)); +// } +// +// @Test +// public void testOmitRootBraces() throws JsonIOException { +// String subject = +// "\"stringDeserializer\": \"test\",\n" + +// "\"arrayDeserializer\": [ \"someValue\", \"someOptValue\" ],\n" + +// "\"unmappedDeserializer\": false,\n" + +// "\"defaultDeserializer\": {\n" + +// " \"value\": \"someValue\",\n" + +// " \"opt\": \"someOptValue\"\n" + +// "}"; +// +// try { +// Bar obj = Jankson.builder().allowBareRootObject().build().fromJson(subject, Bar.class); +// +// Assertions.assertNotNull(obj.stringDeserializer); +// Assertions.assertNotNull(obj.arrayDeserializer); +// Assertions.assertNotNull(obj.unmappedDeserializer); +// Assertions.assertNotNull(obj.defaultDeserializer); +// +// Assertions.assertEquals("test", obj.stringDeserializer.value); +// Assertions.assertEquals("someValue", obj.arrayDeserializer.value); +// Assertions.assertEquals("someOptValue", obj.arrayDeserializer.opt); +// Assertions.assertEquals("", obj.unmappedDeserializer.value); +// Assertions.assertEquals("someValue", obj.defaultDeserializer.value); +// Assertions.assertEquals("someOptValue", obj.defaultDeserializer.opt); +// } catch (SyntaxError ex) { +// Assertions.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); +// } +// try { +// Jankson.builder().build().fromJson(subject, Bar.class); +// Assertions.fail("Should not successfully load bare root object without enabling option"); +// } catch (SyntaxError ex) { +// } +// } } diff --git a/src/test/java/blue/endless/jankson/TestSerializer.java b/src/test/java/blue/endless/jankson/TestSerializer.java index 3a0ab33..cb92076 100644 --- a/src/test/java/blue/endless/jankson/TestSerializer.java +++ b/src/test/java/blue/endless/jankson/TestSerializer.java @@ -33,90 +33,86 @@ import blue.endless.jankson.api.Jankson; import blue.endless.jankson.api.JsonGrammar; import blue.endless.jankson.api.SyntaxError; -import blue.endless.jankson.api.element.JsonElement; -import blue.endless.jankson.api.element.JsonNull; -import blue.endless.jankson.api.element.JsonObject; -import blue.endless.jankson.api.element.JsonPrimitive; public class TestSerializer { - Jankson jankson; - - @BeforeEach - public void setup() { - jankson = Jankson.builder().build(); - } - - /** - * Make sure that characters which lie outside the BMP and/or have complex encodings wind up - * decomposed and escaped properly - */ - @Test - public void testUnicodeEscapes() { - String smileyFace = String.valueOf(Character.toChars(0x1F600)); - String result = new JsonPrimitive(smileyFace).toString(); - Assertions.assertEquals("\"\\ud83d\\ude00\"", result); - } - - - private static class DeclaredSerializerTest { - @SuppressWarnings("unused") - private String foo = "bar"; - - @Serializer - public JsonPrimitive serialize() { - return JsonPrimitive.of(42L); - } - } - - @Test - public void testInternalSerializer() { - JsonElement elem = jankson.toJson(new DeclaredSerializerTest()); - Assertions.assertEquals("42", elem.toJson()); - } - - /** - * Issue #34 - switching to Writer caused a small bug where doubles were double-printed - */ - @Test - public void testBareSpecialNumericsDuplication() { - JsonObject subject = new JsonObject(); - subject.put("foo", JsonPrimitive.of(42.0)); - - JsonGrammar grammar = JsonGrammar.builder().bareSpecialNumerics(true).printWhitespace(false).build(); - - Assertions.assertEquals("{ \"foo\": 42.0 }", subject.toJson(grammar)); - } - - /** Issues #26 and #38 - ':' isn't force-quoted in object keys */ - @Test - public void testIssue26() { - JsonObject obj = new JsonObject(); - obj.put("test:key", JsonNull.INSTANCE); - - String result = obj.toJson(JsonGrammar.builder().printWhitespace(false).printUnquotedKeys(true).build()); - Assertions.assertEquals("{ \"test:key\": null }", result); - } - - /** - * Issue #42: String with russian characters is incorrectly parsed. - * - *

    Moved off of a hand-decoding of UTF-8 surrogates onto Reader, which offers a much more robust and future-proof assembly of UTF-8 surrogates into code points. - */ - - @Test - public void testCyrillic() { - String input = - "{\n" + - " en: \"Play with sound?\",\n" + - " ru: \"Играть с музыкой?\"\n" + - "}"; - - try { - JsonObject obj = Jankson.builder().build().load(input); - - Assertions.assertEquals("Играть с музыкой?", obj.get(String.class, "ru")); - } catch (SyntaxError ex) { - Assertions.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); - } - } +// Jankson jankson; +// +// @BeforeEach +// public void setup() { +// jankson = Jankson.builder().build(); +// } +// +// /** +// * Make sure that characters which lie outside the BMP and/or have complex encodings wind up +// * decomposed and escaped properly +// */ +// @Test +// public void testUnicodeEscapes() { +// String smileyFace = String.valueOf(Character.toChars(0x1F600)); +// String result = new JsonPrimitive(smileyFace).toString(); +// Assertions.assertEquals("\"\\ud83d\\ude00\"", result); +// } +// +// +// private static class DeclaredSerializerTest { +// @SuppressWarnings("unused") +// private String foo = "bar"; +// +// @Serializer +// public JsonPrimitive serialize() { +// return JsonPrimitive.of(42L); +// } +// } +// +// @Test +// public void testInternalSerializer() { +// JsonElement elem = jankson.toJson(new DeclaredSerializerTest()); +// Assertions.assertEquals("42", elem.toJson()); +// } +// +// /** +// * Issue #34 - switching to Writer caused a small bug where doubles were double-printed +// */ +// @Test +// public void testBareSpecialNumericsDuplication() { +// JsonObject subject = new JsonObject(); +// subject.put("foo", JsonPrimitive.of(42.0)); +// +// JsonGrammar grammar = JsonGrammar.builder().bareSpecialNumerics(true).printWhitespace(false).build(); +// +// Assertions.assertEquals("{ \"foo\": 42.0 }", subject.toJson(grammar)); +// } +// +// /** Issues #26 and #38 - ':' isn't force-quoted in object keys */ +// @Test +// public void testIssue26() { +// JsonObject obj = new JsonObject(); +// obj.put("test:key", JsonNull.INSTANCE); +// +// String result = obj.toJson(JsonGrammar.builder().printWhitespace(false).printUnquotedKeys(true).build()); +// Assertions.assertEquals("{ \"test:key\": null }", result); +// } +// +// /** +// * Issue #42: String with russian characters is incorrectly parsed. +// * +// *

    Moved off of a hand-decoding of UTF-8 surrogates onto Reader, which offers a much more robust and future-proof assembly of UTF-8 surrogates into code points. +// */ +// +// @Test +// public void testCyrillic() { +// String input = +// "{\n" + +// " en: \"Play with sound?\",\n" + +// " ru: \"Играть с музыкой?\"\n" + +// "}"; +// +// try { +// JsonObject obj = Jankson.builder().build().load(input); +// +// Assertions.assertEquals("Играть с музыкой?", obj.get(String.class, "ru")); +// } catch (SyntaxError ex) { +// Assertions.fail("Should not get a syntax error for a well-formed object: "+ex.getCompleteMessage()); +// } +// } }