source: josm/trunk/src/org/glassfish/json/JsonParserImpl.java@ 12602

Last change on this file since 12602 was 6756, checked in by Don-vip, 11 years ago

fix #9590 - replace org.json with GPL-compliant jsonp + remove mention of old world image removed in r1680

File size: 11.7 KB
Line 
1/*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
5 *
6 * The contents of this file are subject to the terms of either the GNU
7 * General Public License Version 2 only ("GPL") or the Common Development
8 * and Distribution License("CDDL") (collectively, the "License"). You
9 * may not use this file except in compliance with the License. You can
10 * obtain a copy of the License at
11 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
12 * or packager/legal/LICENSE.txt. See the License for the specific
13 * language governing permissions and limitations under the License.
14 *
15 * When distributing the software, include this License Header Notice in each
16 * file and include the License file at packager/legal/LICENSE.txt.
17 *
18 * GPL Classpath Exception:
19 * Oracle designates this particular file as subject to the "Classpath"
20 * exception as provided by Oracle in the GPL Version 2 section of the License
21 * file that accompanied this code.
22 *
23 * Modifications:
24 * If applicable, add the following below the License Header, with the fields
25 * enclosed by brackets [] replaced by your own identifying information:
26 * "Portions Copyright [year] [name of copyright owner]"
27 *
28 * Contributor(s):
29 * If you wish your version of this file to be governed by only the CDDL or
30 * only the GPL Version 2, indicate your decision by adding "[Contributor]
31 * elects to include this software in this distribution under the [CDDL or GPL
32 * Version 2] license." If you don't indicate a single choice of license, a
33 * recipient has the option to distribute your version of this file under
34 * either the CDDL, the GPL Version 2 or to extend the choice of license to
35 * its licensees as provided above. However, if you add GPL Version 2 code
36 * and therefore, elected the GPL Version 2 license, then the option applies
37 * only if the new code is made subject to such option by the copyright
38 * holder.
39 */
40
41package org.glassfish.json;
42
43import javax.json.*;
44import javax.json.stream.JsonLocation;
45import javax.json.stream.JsonParser;
46import javax.json.stream.JsonParsingException;
47import java.io.*;
48import java.math.BigDecimal;
49import java.nio.charset.Charset;
50import java.util.*;
51
52import org.glassfish.json.JsonTokenizer.JsonToken;
53import org.glassfish.json.api.BufferPool;
54
55/**
56 * JSON parser implementation. NoneContext, ArrayContext, ObjectContext is used
57 * to go to next parser state.
58 *
59 * @author Jitendra Kotamraju
60 */
61public class JsonParserImpl implements JsonParser {
62
63 private Context currentContext = new NoneContext();
64 private Event currentEvent;
65
66 private final Stack stack = new Stack();
67 private final StateIterator stateIterator;
68 private final JsonTokenizer tokenizer;
69
70 public JsonParserImpl(Reader reader, BufferPool bufferPool) {
71 tokenizer = new JsonTokenizer(reader, bufferPool);
72 stateIterator = new StateIterator();
73 }
74
75 public JsonParserImpl(InputStream in, BufferPool bufferPool) {
76 UnicodeDetectingInputStream uin = new UnicodeDetectingInputStream(in);
77 tokenizer = new JsonTokenizer(new InputStreamReader(uin, uin.getCharset()), bufferPool);
78 stateIterator = new StateIterator();
79 }
80
81 public JsonParserImpl(InputStream in, Charset encoding, BufferPool bufferPool) {
82 tokenizer = new JsonTokenizer(new InputStreamReader(in, encoding), bufferPool);
83 stateIterator = new StateIterator();
84 }
85
86 public String getString() {
87 if (currentEvent == Event.KEY_NAME || currentEvent == Event.VALUE_STRING
88 || currentEvent == Event.VALUE_NUMBER) {
89 return tokenizer.getValue();
90 }
91 throw new IllegalStateException(
92 JsonMessages.PARSER_GETSTRING_ERR(currentEvent));
93 }
94
95 @Override
96 public boolean isIntegralNumber() {
97 if (currentEvent != Event.VALUE_NUMBER) {
98 throw new IllegalStateException(
99 JsonMessages.PARSER_ISINTEGRALNUMBER_ERR(currentEvent));
100 }
101 return tokenizer.isIntegral();
102 }
103
104 @Override
105 public int getInt() {
106 if (currentEvent != Event.VALUE_NUMBER) {
107 throw new IllegalStateException(
108 JsonMessages.PARSER_GETINT_ERR(currentEvent));
109 }
110 return tokenizer.getInt();
111 }
112
113 boolean isDefinitelyInt() {
114 return tokenizer.isDefinitelyInt();
115 }
116
117 @Override
118 public long getLong() {
119 if (currentEvent != Event.VALUE_NUMBER) {
120 throw new IllegalStateException(
121 JsonMessages.PARSER_GETLONG_ERR(currentEvent));
122 }
123 return tokenizer.getBigDecimal().longValue();
124 }
125
126 @Override
127 public BigDecimal getBigDecimal() {
128 if (currentEvent != Event.VALUE_NUMBER) {
129 throw new IllegalStateException(
130 JsonMessages.PARSER_GETBIGDECIMAL_ERR(currentEvent));
131 }
132 return tokenizer.getBigDecimal();
133 }
134
135 @Override
136 public JsonLocation getLocation() {
137 return tokenizer.getLocation();
138 }
139
140 public JsonLocation getLastCharLocation() {
141 return tokenizer.getLastCharLocation();
142 }
143
144 public boolean hasNext() {
145 return stateIterator.hasNext();
146 }
147
148 public Event next() {
149 return stateIterator.next();
150 }
151
152 private class StateIterator implements Iterator<JsonParser.Event> {
153
154 @Override
155 public boolean hasNext() {
156 if (stack.isEmpty() && (currentEvent == Event.END_ARRAY || currentEvent == Event.END_OBJECT)) {
157 JsonToken token = tokenizer.nextToken();
158 if (token != JsonToken.EOF) {
159 throw new JsonParsingException(JsonMessages.PARSER_EXPECTED_EOF(token),
160 getLastCharLocation());
161 }
162 return false;
163 }
164 return true;
165 }
166
167 @Override
168 public JsonParser.Event next() {
169 if (!hasNext()) {
170 throw new NoSuchElementException();
171 }
172 return currentEvent = currentContext.getNextEvent();
173 }
174
175 @Override
176 public void remove() {
177 throw new UnsupportedOperationException();
178 }
179 }
180
181 public void close() {
182 try {
183 tokenizer.close();
184 } catch (IOException e) {
185 throw new JsonException(JsonMessages.PARSER_TOKENIZER_CLOSE_IO(), e);
186 }
187 }
188
189 // Using the optimized stack impl as we don't require other things
190 // like iterator etc.
191 private static final class Stack {
192 private Context head;
193
194 private void push(Context context) {
195 context.next = head;
196 head = context;
197 }
198
199 private Context pop() {
200 if (head == null) {
201 throw new NoSuchElementException();
202 }
203 Context temp = head;
204 head = head.next;
205 return temp;
206 }
207
208 private boolean isEmpty() {
209 return head == null;
210 }
211 }
212
213 private abstract class Context {
214 Context next;
215 abstract Event getNextEvent();
216 }
217
218 private final class NoneContext extends Context {
219 @Override
220 public Event getNextEvent() {
221 // Handle 1. { 2. [
222 JsonToken token = tokenizer.nextToken();
223 if (token == JsonToken.CURLYOPEN) {
224 stack.push(currentContext);
225 currentContext = new ObjectContext();
226 return Event.START_OBJECT;
227 } else if (token == JsonToken.SQUAREOPEN) {
228 stack.push(currentContext);
229 currentContext = new ArrayContext();
230 return Event.START_ARRAY;
231 }
232 throw parsingException(token, "[CURLYOPEN, SQUAREOPEN]");
233 }
234 }
235
236 private JsonParsingException parsingException(JsonToken token, String expectedTokens) {
237 JsonLocation location = getLastCharLocation();
238 return new JsonParsingException(
239 JsonMessages.PARSER_INVALID_TOKEN(token, location, expectedTokens), location);
240 }
241
242 private final class ObjectContext extends Context {
243 private boolean firstValue = true;
244
245 /*
246 * Some more things could be optimized. For example, instead
247 * tokenizer.nextToken(), one could use tokenizer.matchColonToken() to
248 * match ':'. That might optimize a bit, but will fragment nextToken().
249 * I think the current one is more readable.
250 *
251 */
252 @Override
253 public Event getNextEvent() {
254 // Handle 1. } 2. name:value 3. ,name:value
255 JsonToken token = tokenizer.nextToken();
256 if (currentEvent == Event.KEY_NAME) {
257 // Handle 1. :value
258 if (token != JsonToken.COLON) {
259 throw parsingException(token, "[COLON]");
260 }
261 token = tokenizer.nextToken();
262 if (token.isValue()) {
263 return token.getEvent();
264 } else if (token == JsonToken.CURLYOPEN) {
265 stack.push(currentContext);
266 currentContext = new ObjectContext();
267 return Event.START_OBJECT;
268 } else if (token == JsonToken.SQUAREOPEN) {
269 stack.push(currentContext);
270 currentContext = new ArrayContext();
271 return Event.START_ARRAY;
272 }
273 throw parsingException(token, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL]");
274 } else {
275 // Handle 1. } 2. name 3. ,name
276 if (token == JsonToken.CURLYCLOSE) {
277 currentContext = stack.pop();
278 return Event.END_OBJECT;
279 }
280 if (firstValue) {
281 firstValue = false;
282 } else {
283 if (token != JsonToken.COMMA) {
284 throw parsingException(token, "[COMMA]");
285 }
286 token = tokenizer.nextToken();
287 }
288 if (token == JsonToken.STRING) {
289 return Event.KEY_NAME;
290 }
291 throw parsingException(token, "[STRING]");
292 }
293 }
294
295 }
296
297 private final class ArrayContext extends Context {
298 private boolean firstValue = true;
299
300 // Handle 1. ] 2. value 3. ,value
301 @Override
302 public Event getNextEvent() {
303 JsonToken token = tokenizer.nextToken();
304 if (token == JsonToken.SQUARECLOSE) {
305 currentContext = stack.pop();
306 return Event.END_ARRAY;
307 }
308 if (firstValue) {
309 firstValue = false;
310 } else {
311 if (token != JsonToken.COMMA) {
312 throw parsingException(token, "[COMMA]");
313 }
314 token = tokenizer.nextToken();
315 }
316 if (token.isValue()) {
317 return token.getEvent();
318 } else if (token == JsonToken.CURLYOPEN) {
319 stack.push(currentContext);
320 currentContext = new ObjectContext();
321 return Event.START_OBJECT;
322 } else if (token == JsonToken.SQUAREOPEN) {
323 stack.push(currentContext);
324 currentContext = new ArrayContext();
325 return Event.START_ARRAY;
326 }
327 throw parsingException(token, "[CURLYOPEN, SQUAREOPEN, STRING, NUMBER, TRUE, FALSE, NULL]");
328 }
329
330 }
331
332}
Note: See TracBrowser for help on using the repository browser.