--- /dev/null
+package de.juplo.simplemapper;
+
+
+import com.fasterxml.jackson.core.JsonLocation;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Spliterator;
+import static java.util.Spliterator.IMMUTABLE;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+
+/**
+ *
+ * @author kai
+ */
+public abstract class SimpleMapper
+{
+ private static final Logger LOG =
+ LoggerFactory.getLogger(SimpleMapper.class);
+
+
+ public static Spliterator<Object> getArraySpliterator(final JsonParser parser)
+ throws
+ IOException
+ {
+ JsonToken token = parser.nextToken();
+
+ if (token == null)
+ return null;
+
+ if (!JsonToken.START_ARRAY.equals(token))
+ fail(parser, "The root-element must be an array!");
+
+ return new Spliterator<Object>()
+ {
+ @Override
+ public boolean tryAdvance(Consumer<? super Object> action)
+ {
+ try
+ {
+ JsonToken token = parser.nextToken();
+ if (token == null)
+ fail(parser, "Unexpected end of data!");
+ if (JsonToken.END_ARRAY.equals(token))
+ {
+ if (parser.nextToken() != null)
+ fail(parser, "unexpected data after parsed array");
+ return false;
+ }
+ action.accept(convertInternal(parser));
+ return true;
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @Override
+ public Spliterator<Object> trySplit()
+ {
+ return null;
+ }
+
+ @Override
+ public long estimateSize()
+ {
+ return Long.MAX_VALUE;
+ }
+
+ @Override
+ public int characteristics()
+ {
+ return IMMUTABLE;
+ }
+ };
+ }
+
+ public static Stream<Object> getArrayStream(final JsonParser parser)
+ throws
+ IOException
+ {
+ return StreamSupport.stream(getArraySpliterator(parser), false);
+ }
+
+ public static Iterator<Object> getArrayIterator(final JsonParser parser)
+ throws
+ IOException
+ {
+ Spliterator<Object> spliterator = getArraySpliterator(parser);
+ return new Iterator<Object>()
+ {
+ private Object next = null;
+
+
+ @Override
+ public boolean hasNext()
+ {
+ if (next != null)
+ return true;
+
+ return spliterator.tryAdvance((Object o) -> { next = o; });
+ }
+
+ @Override
+ public Object next()
+ {
+ if (next == null && !hasNext())
+ throw new NoSuchElementException();
+ Object o = next;
+ next = null;
+ return o;
+ }
+ };
+ }
+
+
+ public static Spliterator<Entry<String, Object>> getObjectSpliterator(final JsonParser parser)
+ throws
+ IOException
+ {
+ JsonToken token = parser.nextToken();
+
+ if (token == null)
+ return null;
+
+ if (!JsonToken.START_OBJECT.equals(token))
+ fail(parser, "The root-element must be an object!");
+
+ return new Spliterator<Entry<String, Object>>()
+ {
+ @Override
+ public boolean tryAdvance(Consumer<? super Entry<String, Object>> action)
+ {
+ try
+ {
+ JsonToken token = parser.nextToken();
+ if (token == null)
+ fail(parser, "Unexpected end of data!");
+ if (JsonToken.END_OBJECT.equals(token))
+ {
+ if (parser.nextToken() != null)
+ fail(parser, "unexpected data after parsed object");
+ return false;
+ }
+ if (!JsonToken.FIELD_NAME.equals(token))
+ fail(parser, "expected a field-name");
+ final String key = parser.getText();
+ parser.nextToken();
+ final Object value = convertInternal(parser);
+ action.accept(new Entry<String, Object>()
+ {
+ @Override
+ public String getKey()
+ {
+ return key;
+ }
+
+ @Override
+ public Object getValue()
+ {
+ return value;
+ }
+
+ @Override
+ public Object setValue(Object value)
+ {
+ throw new UnsupportedOperationException("Not supported.");
+ }
+ });
+ return true;
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @Override
+ public Spliterator<Entry<String, Object>> trySplit()
+ {
+ return null;
+ }
+
+ @Override
+ public long estimateSize()
+ {
+ return Long.MAX_VALUE;
+ }
+
+ @Override
+ public int characteristics()
+ {
+ return IMMUTABLE;
+ }
+ };
+ }
+
+ public static Stream<Entry<String, Object>> getObjectStream(final JsonParser parser)
+ throws
+ IOException
+ {
+ return StreamSupport.stream(getObjectSpliterator(parser), false);
+ }
+
+ public static Iterator<Entry<String, Object>> getObjectIterator(
+ final JsonParser parser
+ )
+ throws
+ IOException
+ {
+ Spliterator<Entry<String, Object>> spliterator = getObjectSpliterator(parser);
+ return new Iterator<Entry<String, Object>>()
+ {
+ private Entry<String, Object> next = null;
+
+
+ @Override
+ public boolean hasNext()
+ {
+ if (next != null)
+ return true;
+
+ return spliterator.tryAdvance((Entry<String, Object> e) -> { next = e; });
+ }
+
+ @Override
+ public Entry<String, Object> next()
+ {
+ if (next == null && !hasNext())
+ throw new NoSuchElementException();
+ Entry<String, Object> e = next;
+ next = null;
+ return e;
+ }
+ };
+ }
+
+
+ public static List<Object> convertArray(JsonParser parser) throws IOException
+ {
+ JsonToken token = parser.nextToken();
+
+ if (token == null)
+ return null;
+
+ if (!JsonToken.START_ARRAY.equals(token))
+ fail(parser, "The root-element must be an array!");
+
+ List<Object> array = convertArrayInternal(parser);
+
+ if (parser.nextToken() != null)
+ fail(parser, "unexpected data after parsed array");
+
+ return array;
+ }
+
+ public static Map<String, Object> convertObject(JsonParser parser) throws IOException
+ {
+ JsonToken token = parser.nextToken();
+
+ if (token == null)
+ return null;
+
+ if (!JsonToken.START_OBJECT.equals(token))
+ fail(parser, "The root-element must be an object!");
+
+ Map<String, Object> object = convertObjectInternal(parser);
+
+ if (parser.nextToken() != null)
+ fail(parser, "unexpected data after parsed object");
+
+ return object;
+ }
+
+ public static Object convert(JsonParser parser) throws IOException
+ {
+ JsonToken token = parser.nextToken();
+
+ if (token == null)
+ return null;
+
+ switch (token)
+ {
+ case START_ARRAY:
+ case START_OBJECT:
+ break;
+ default:
+ fail(parser, "The root-element must be either an object or an array!");
+ }
+
+ Object object = convertInternal(parser);
+
+ if (parser.nextToken() != null)
+ fail(parser, "unexpected data after parsed object");
+
+ return object;
+ }
+
+
+ static Object convertInternal(JsonParser parser) throws IOException
+ {
+ JsonToken token = parser.getCurrentToken();
+ if (token == null)
+ {
+ fail(parser, "unexpected EOF");
+ return null; // << Will never be reached, because fail always throws an exception
+ }
+
+ switch (token)
+ {
+ case VALUE_STRING: return parser.getText();
+ case VALUE_NUMBER_INT: return parser.getIntValue();
+ case VALUE_NUMBER_FLOAT: return parser.getDoubleValue();
+ case START_OBJECT: return convertObjectInternal(parser);
+ case START_ARRAY: return convertArrayInternal(parser);
+ case VALUE_TRUE: return Boolean.TRUE;
+ case VALUE_FALSE: return Boolean.FALSE;
+ case VALUE_NULL: return null;
+ }
+
+ fail(parser, "unexpected token " + token);
+ return null; // << Will never be reached, because fail always throws an exception
+ }
+
+
+ static Map<String, Object> convertObjectInternal(JsonParser parser)
+ throws
+ IOException
+ {
+ JsonToken token = parser.nextToken();
+ if (token == null)
+ fail(parser, "unexpected EOF");
+
+ Map<String, Object> map = new LinkedHashMap<>();
+
+ while (!JsonToken.END_OBJECT.equals(token))
+ {
+ if (!JsonToken.FIELD_NAME.equals(token))
+ fail(parser, "expected a field-name");
+
+ String name = parser.getText();
+ parser.nextToken();
+ Object value = convertInternal(parser);
+ map.put(name, value);
+
+ token = parser.nextToken();
+ if (token == null)
+ fail(parser, "unexpected EOF");
+ }
+
+ return map;
+ }
+
+ static List<Object> convertArrayInternal(JsonParser parser) throws IOException
+ {
+ JsonToken token = parser.nextToken();
+ if (token == null)
+ fail(parser, "unexpected EOF");
+
+ List<Object> list = new LinkedList<>();
+
+ while (!JsonToken.END_ARRAY.equals(token))
+ {
+ list.add(convertInternal(parser));
+
+ token = parser.nextToken();
+ if (token == null)
+ fail(parser, "unexpected EOF");
+ }
+
+ return list;
+ }
+
+
+ static void fail(JsonParser parser, String message)
+ {
+ JsonLocation location = parser.getCurrentLocation();
+ LOG.error(
+ "{} at char-offset {} (line {}, column {})",
+ message,
+ location.getCharOffset(),
+ location.getLineNr(),
+ location.getColumnNr()
+ );
+ throw new IllegalArgumentException("Cannot parse JSON: " + message);
+ }
+}