+package de.juplo.jackson;
+
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonLocation;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Spliterator;
+import static java.util.Spliterator.IMMUTABLE;
+import java.util.Spliterators;
+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 class SimpleMapper
+{
+ private static final Logger LOG =
+ LoggerFactory.getLogger(SimpleMapper.class);
+
+
+ private JsonFactory factory = new JsonFactory();
+
+
+ public static Stream<Object> getArrayStream(final JsonParser parser)
+ throws
+ IOException
+ {
+ return StreamSupport.stream(getArraySpliterator(parser), false);
+ }
+
+ public static Spliterator<Object> getArraySpliterator(final JsonParser parser)
+ throws
+ IOException
+ {
+ JsonToken token = parser.nextToken();
+
+ if (token == null)
+ {
+ LOG.warn("empty input");
+ return Spliterators.emptySpliterator();
+ }
+
+ if (!JsonToken.START_ARRAY.equals(token))
+ {
+ throw new IllegalArgumentException("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)
+ throw new IllegalArgumentException("Unexpected end of data!");
+ if (JsonToken.END_ARRAY.equals(token))
+ return false;
+ action.accept(convert(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 Iterator<Object> getArrayIterator(final JsonParser parser)
+ throws
+ IOException
+ {
+ JsonToken token = parser.nextToken();
+
+ if (token == null)
+ {
+ LOG.warn("empty input");
+ return Collections.emptyIterator();
+ }
+
+ if (!JsonToken.START_ARRAY.equals(token))
+ {
+ throw new IllegalArgumentException("The root-element must be an array!");
+ }
+
+ return new Iterator<Object>()
+ {
+
+ @Override
+ public boolean tryAdvance(Consumer<? super Object> action)
+ {
+ try
+ {
+ JsonToken token = parser.nextToken();
+ if (token == null)
+ throw new IllegalArgumentException("Unexpected end of data!");
+ if (JsonToken.END_ARRAY.equals(token))
+ throw new NoSuchElementException();
+ action.accept(convert(parser));
+ return true;
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException(e);
+ }
+ }
+ @Override
+ public boolean hasNext()
+ {
+ }
+
+ @Override
+ public Object next()
+ {
+ try
+ {
+ JsonToken token = parser.nextToken();
+ if (token == null)
+ throw new IllegalArgumentException("Unexpected end of data!");
+ if (JsonToken.END_ARRAY.equals(token))
+ return false;
+ action.accept(convert(parser));
+ return true;
+ }
+ catch (IOException e)
+ {
+ throw new IllegalArgumentException(e);
+ }
+ }
+ };
+ }
+
+ public Object processAttribute(InputStream is)
+ {
+ try
+ {
+ /**
+ * Read the JSON and create the variables
+ */
+ JsonParser parser = factory.createParser(is);
+
+ JsonToken token = parser.nextToken();
+
+ if (token == null)
+ {
+ LOG.warn("empty input-stream");
+ return null;
+ }
+
+ Object result = convert(parser);
+
+ if (parser.nextToken() != null)
+ fail(parser, "unexpected data after parsed variables");
+
+ return result;
+ }
+ catch (IOException e)
+ {
+ LOG.error("cannot parse input-stream as JSON: {}", e.getMessage());
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ static Object convert(JsonParser parser) throws IOException
+ {
+ JsonToken token = parser.getCurrentToken();
+ if (token == null)
+ fail(parser, "unexpected EOF");
+
+ 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 convertObject(parser);
+ case START_ARRAY: return convertArray(parser);
+ case VALUE_TRUE: return Boolean.TRUE;
+ case VALUE_FALSE: return Boolean.FALSE;
+ case VALUE_NULL: return null;
+ }
+
+ fail(parser, "unexpected token " + token.toString());
+ return null; // << Will never be reached, because fail always throws an exception
+ }
+
+ static Map<String, Object> convertObject(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 = convert(parser);
+ map.put(name, value);
+
+ token = parser.nextToken();
+ if (token == null)
+ fail(parser, "unexpected EOF");
+ }
+
+ return map;
+ }
+
+ static List<Object> convertArray(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(convert(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 RuntimeException("Cannot parse JSON: " + message);
+ }
+}