WIP
[maven-thymeleaf-skin] / src / main / java / de / juplo / jackson / SimpleMapper.java
1 package de.juplo.jackson;
2
3
4 import com.fasterxml.jackson.core.JsonFactory;
5 import com.fasterxml.jackson.core.JsonLocation;
6 import com.fasterxml.jackson.core.JsonParser;
7 import com.fasterxml.jackson.core.JsonToken;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.util.Collections;
11 import java.util.Iterator;
12 import java.util.LinkedHashMap;
13 import java.util.LinkedList;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.NoSuchElementException;
17 import java.util.Spliterator;
18 import static java.util.Spliterator.IMMUTABLE;
19 import java.util.Spliterators;
20 import java.util.function.Consumer;
21 import java.util.stream.Stream;
22 import java.util.stream.StreamSupport;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26
27
28 /**
29  *
30  * @author kai
31  */
32 public class SimpleMapper
33 {
34   private static final Logger LOG =
35       LoggerFactory.getLogger(SimpleMapper.class);
36
37
38   private JsonFactory factory = new JsonFactory();
39
40
41   public static Stream<Object> getArrayStream(final JsonParser parser)
42       throws
43         IOException
44   {
45     return StreamSupport.stream(getArraySpliterator(parser), false);
46   }
47
48   public static Spliterator<Object> getArraySpliterator(final JsonParser parser)
49       throws
50         IOException
51   {
52     JsonToken token = parser.nextToken();
53
54     if (token == null)
55     {
56       LOG.warn("empty input");
57       return Spliterators.emptySpliterator();
58     }
59
60     if (!JsonToken.START_ARRAY.equals(token))
61       fail(parser, "The root-element must be an array!");
62
63     return new Spliterator<Object>()
64     {
65       @Override
66       public boolean tryAdvance(Consumer<? super Object> action)
67       {
68         try
69         {
70           JsonToken token = parser.nextToken();
71           if (token == null)
72             fail(parser, "Unexpected end of data!");
73           if (JsonToken.END_ARRAY.equals(token))
74             return false;
75           action.accept(convert(parser));
76           return true;
77         }
78         catch (IOException e)
79         {
80           throw new IllegalArgumentException(e);
81         }
82       }
83
84       @Override
85       public Spliterator<Object> trySplit()
86       {
87         return null;
88       }
89
90       @Override
91       public long estimateSize()
92       {
93         return Long.MAX_VALUE;
94       }
95
96       @Override
97       public int characteristics()
98       {
99         return IMMUTABLE;
100       }
101     };
102   }
103
104   public static Iterator<Object> getArrayIterator(final JsonParser parser)
105       throws
106         IOException
107   {
108     Spliterator<Object> spliterator = getArraySpliterator(parser);
109     return new Iterator<Object>()
110     {
111       private Object next = null;
112       
113       @Override
114       public boolean hasNext()
115       {
116         if (next != null)
117           return true;
118
119         return spliterator.tryAdvance(new Consumer<Object>()
120         {
121           @Override
122           public void accept(Object o)
123           {
124             next = o;
125           }
126         });
127       }
128
129       @Override
130       public Object next()
131       {
132         if (next == null && !hasNext())
133           throw new NoSuchElementException();
134         Object o = next;
135         next = null;
136         return o;
137       }
138     };
139   }
140
141
142   static Object convert(JsonParser parser) throws IOException
143   {
144     JsonToken token = parser.getCurrentToken();
145     if (token == null)
146       fail(parser, "unexpected EOF");
147
148     switch (token)
149     {
150       case VALUE_STRING:       return parser.getText();
151       case VALUE_NUMBER_INT:   return parser.getIntValue();
152       case VALUE_NUMBER_FLOAT: return parser.getDoubleValue();
153       case START_OBJECT:       return convertObject(parser);
154       case START_ARRAY:        return convertArray(parser);
155       case VALUE_TRUE:         return Boolean.TRUE;
156       case VALUE_FALSE:        return Boolean.FALSE;
157       case VALUE_NULL:         return null;
158     }
159
160     fail(parser, "unexpected token " + token.toString());
161     return null; // << Will never be reached, because fail always throws an exception
162   }
163
164   static Map<String, Object> convertObject(JsonParser parser) throws IOException
165   {
166     JsonToken token = parser.nextToken();
167     if (token == null)
168       fail(parser, "unexpected EOF");
169
170     Map<String, Object> map = new LinkedHashMap<>();
171
172     while (!JsonToken.END_OBJECT.equals(token))
173     {
174       if (!JsonToken.FIELD_NAME.equals(token))
175         fail(parser, "expected a field-name");
176
177       String name = parser.getText();
178       parser.nextToken();
179       Object value = convert(parser);
180       map.put(name, value);
181
182       token = parser.nextToken();
183       if (token == null)
184         fail(parser, "unexpected EOF");
185     }
186
187     return map;
188   }
189
190   static List<Object> convertArray(JsonParser parser) throws IOException
191   {
192     JsonToken token = parser.nextToken();
193     if (token == null)
194       fail(parser, "unexpected EOF");
195
196     List<Object> list = new LinkedList<>();
197
198     while (!JsonToken.END_ARRAY.equals(token))
199     {
200       list.add(convert(parser));
201
202       token = parser.nextToken();
203       if (token == null)
204         fail(parser, "unexpected EOF");
205     }
206
207     return list;
208   }
209
210   static void fail(JsonParser parser, String message)
211   {
212     JsonLocation location = parser.getCurrentLocation();
213     LOG.error(
214         "{} at char-offset {} (line {}, column {})",
215         message,
216         location.getCharOffset(),
217         location.getLineNr(),
218         location.getColumnNr()
219         );
220     throw new IllegalArgumentException("Cannot parse JSON: " + message);
221   }
222 }