001/* ============
002 * Orson Charts
003 * ============
004 * 
005 * (C)opyright 2013-2016, by Object Refinery Limited.
006 * 
007 * http://www.object-refinery.com/orsoncharts/index.html
008 * 
009 * JSON.simple
010 * -----------
011 * The code in this file originates from the JSON.simple project by 
012 * FangYidong<fangyidong@yahoo.com.cn>:
013 * 
014 *     https://code.google.com/p/json-simple/
015 *  
016 * which is licensed under the Apache Software License version 2.0.  
017 * 
018 * It has been modified locally and repackaged under 
019 * com.orsoncharts.util.json.* to avoid conflicts with any other version that
020 * may be present on the classpath. 
021 * 
022 */
023
024package com.orsoncharts.util.json.parser;
025
026import java.io.IOException;
027import java.io.Reader;
028import java.io.StringReader;
029import java.util.LinkedList;
030import java.util.List;
031import java.util.Map;
032import com.orsoncharts.util.json.JSONArray;
033import com.orsoncharts.util.json.JSONObject;
034
035
036/**
037 * Parser for JSON text. Please note that JSONParser is NOT thread-safe.
038 */
039public class JSONParser {
040    
041    public static final int S_INIT = 0;
042    public static final int S_IN_FINISHED_VALUE = 1;//string,number,boolean,null,object,array
043    public static final int S_IN_OBJECT = 2;
044    public static final int S_IN_ARRAY = 3;
045    public static final int S_PASSED_PAIR_KEY = 4;
046    public static final int S_IN_PAIR_VALUE = 5;
047    public static final int S_END = 6;
048    public static final int S_IN_ERROR = -1;
049    
050    private LinkedList<Integer> handlerStatusStack;
051    private Yylex lexer = new Yylex((Reader)null);
052    private Yytoken token = null;
053    private int status = S_INIT;
054    
055    private int peekStatus(LinkedList statusStack){
056        if (statusStack.size() == 0) {
057            return -1;
058        }
059        Integer result = (Integer) statusStack.getFirst();
060        return result.intValue();
061    }
062    
063    /**
064     * Reset the parser to the initial state without resetting the underlying 
065     * reader.
066     */
067    public void reset(){
068        token = null;
069        status = S_INIT;
070        handlerStatusStack = null;
071    }
072    
073    /**
074     * Reset the parser to the initial state with a new character reader.
075     * 
076     * @param in  the new character reader.
077     */
078    public void reset(Reader in){
079        lexer.yyreset(in);
080        reset();
081    }
082    
083    /**
084     * @return The position of the beginning of the current token.
085     */
086    public int getPosition(){
087        return lexer.getPosition();
088    }
089    
090    public Object parse(String s) throws ParseException {
091        return parse(s, (ContainerFactory) null);
092    }
093    
094    public Object parse(String s, ContainerFactory containerFactory) 
095            throws ParseException {
096        StringReader in = new StringReader(s);
097        try {
098            return parse(in, containerFactory);
099        }
100        catch(IOException ie){
101            /*
102             * Actually it will never happen.
103             */
104            throw new ParseException(-1, 
105                    ParseException.ERROR_UNEXPECTED_EXCEPTION, ie);
106        }
107    }
108    
109    public Object parse(Reader in) throws IOException, ParseException {
110        return parse(in, (ContainerFactory) null);
111    }
112    
113    /**
114     * Parse JSON text into java object from the input source.
115     *     
116     * @param in  the input source.
117     * @param containerFactory  use this factory to create your own JSON 
118     *     object and JSON array containers.
119     * @return Instance of the following:
120     *     com.orsoncharts.util.json.simple.JSONObject,
121     *     com.orsoncharts.util.json.simple.JSONArray,
122     *     java.lang.String,
123     *     java.lang.Number,
124     *     java.lang.Boolean,
125     *     null
126     * 
127     * @throws IOException if there is an I/O problem.
128     * @throws ParseException if there is a parsing problem.
129     */
130    @SuppressWarnings("unchecked")
131    public Object parse(Reader in, ContainerFactory containerFactory) 
132            throws IOException, ParseException{
133        reset(in);
134        LinkedList<Integer> statusStack = new LinkedList<Integer>();
135        LinkedList<Object> valueStack = new LinkedList<Object>();
136        
137        try {
138            do {
139                nextToken();
140                switch(status) {
141                case S_INIT:
142                    switch(token.type) {
143                    case Yytoken.TYPE_VALUE:
144                        status=S_IN_FINISHED_VALUE;
145                        statusStack.addFirst(new Integer(status));
146                        valueStack.addFirst(token.value);
147                        break;
148                    case Yytoken.TYPE_LEFT_BRACE:
149                        status=S_IN_OBJECT;
150                        statusStack.addFirst(new Integer(status));
151                        valueStack.addFirst(createObjectContainer(containerFactory));
152                        break;
153                    case Yytoken.TYPE_LEFT_SQUARE:
154                        status=S_IN_ARRAY;
155                        statusStack.addFirst(new Integer(status));
156                        valueStack.addFirst(createArrayContainer(containerFactory));
157                        break;
158                    default:
159                        status=S_IN_ERROR;
160                    }//inner switch
161                    break;
162                    
163                case S_IN_FINISHED_VALUE:
164                    if (token.type == Yytoken.TYPE_EOF) {
165                        return valueStack.removeFirst();
166                    } else {
167                    throw new ParseException(getPosition(), 
168                            ParseException.ERROR_UNEXPECTED_TOKEN, token);
169                }
170                    
171                case S_IN_OBJECT:
172                    switch(token.type) {
173                    case Yytoken.TYPE_COMMA:
174                        break;
175                    case Yytoken.TYPE_VALUE:
176                        if (token.value instanceof String) {
177                            String key = (String) token.value;
178                            valueStack.addFirst(key);
179                            status = S_PASSED_PAIR_KEY;
180                            statusStack.addFirst(new Integer(status));
181                        }
182                        else{
183                            status = S_IN_ERROR;
184                        }
185                        break;
186                    case Yytoken.TYPE_RIGHT_BRACE:
187                        if (valueStack.size() > 1) {
188                            statusStack.removeFirst();
189                            valueStack.removeFirst();
190                            status = peekStatus(statusStack);
191                        }
192                        else{
193                            status = S_IN_FINISHED_VALUE;
194                        }
195                        break;
196                    default:
197                        status = S_IN_ERROR;
198                        break;
199                    }//inner switch
200                    break;
201                    
202                case S_PASSED_PAIR_KEY:
203                    switch(token.type) {
204                    case Yytoken.TYPE_COLON:
205                        break;
206                    case Yytoken.TYPE_VALUE:
207                        statusStack.removeFirst();
208                        String key = (String) valueStack.removeFirst();
209                        Map parent = (Map) valueStack.getFirst();
210                        parent.put(key,token.value);
211                        status = peekStatus(statusStack);
212                        break;
213                    case Yytoken.TYPE_LEFT_SQUARE:
214                        statusStack.removeFirst();
215                        key = (String) valueStack.removeFirst();
216                        parent = (Map) valueStack.getFirst();
217                        List newArray = createArrayContainer(containerFactory);
218                        parent.put(key,newArray);
219                        status = S_IN_ARRAY;
220                        statusStack.addFirst(new Integer(status));
221                        valueStack.addFirst(newArray);
222                        break;
223                    case Yytoken.TYPE_LEFT_BRACE:
224                        statusStack.removeFirst();
225                        key = (String) valueStack.removeFirst();
226                        parent = (Map) valueStack.getFirst();
227                        Map newObject = createObjectContainer(containerFactory);
228                        parent.put(key, newObject);
229                        status = S_IN_OBJECT;
230                        statusStack.addFirst(new Integer(status));
231                        valueStack.addFirst(newObject);
232                        break;
233                    default:
234                        status = S_IN_ERROR;
235                    }
236                    break;
237                    
238                case S_IN_ARRAY:
239                    switch(token.type) {
240                    case Yytoken.TYPE_COMMA:
241                        break;
242                    case Yytoken.TYPE_VALUE:
243                        List val = (List) valueStack.getFirst();
244                        val.add(token.value);
245                        break;
246                    case Yytoken.TYPE_RIGHT_SQUARE:
247                        if (valueStack.size() > 1) {
248                            statusStack.removeFirst();
249                            valueStack.removeFirst();
250                            status = peekStatus(statusStack);
251                        }
252                        else {
253                            status = S_IN_FINISHED_VALUE;
254                        }
255                        break;
256                    case Yytoken.TYPE_LEFT_BRACE:
257                        val = (List) valueStack.getFirst();
258                        Map newObject = createObjectContainer(containerFactory);
259                        val.add(newObject);
260                        status = S_IN_OBJECT;
261                        statusStack.addFirst(new Integer(status));
262                        valueStack.addFirst(newObject);
263                        break;
264                    case Yytoken.TYPE_LEFT_SQUARE:
265                        val = (List) valueStack.getFirst();
266                        List newArray = createArrayContainer(containerFactory);
267                        val.add(newArray);
268                        status = S_IN_ARRAY;
269                        statusStack.addFirst(new Integer(status));
270                        valueStack.addFirst(newArray);
271                        break;
272                    default:
273                        status = S_IN_ERROR;
274                    }//inner switch
275                    break;
276                case S_IN_ERROR:
277                    throw new ParseException(getPosition(), 
278                            ParseException.ERROR_UNEXPECTED_TOKEN, token);
279                }//switch
280                if (status == S_IN_ERROR) {
281                    throw new ParseException(getPosition(), 
282                            ParseException.ERROR_UNEXPECTED_TOKEN, token);
283                }
284            } while (token.type != Yytoken.TYPE_EOF);
285        }
286        catch (IOException ie) {
287            throw ie;
288        }
289        
290        throw new ParseException(getPosition(), 
291                ParseException.ERROR_UNEXPECTED_TOKEN, token);
292    }
293    
294    private void nextToken() throws ParseException, IOException {
295        token = lexer.yylex();
296        if (token == null) {
297            token = new Yytoken(Yytoken.TYPE_EOF, null);
298        }
299    }
300    
301    private Map createObjectContainer(ContainerFactory containerFactory) {
302        if (containerFactory == null) {
303            return new JSONObject();
304        }
305        Map m = containerFactory.createObjectContainer();
306        if (m == null) {
307            return new JSONObject();
308        }
309        return m;
310    }
311    
312    private List createArrayContainer(ContainerFactory containerFactory) {
313        if (containerFactory == null) {
314            return new JSONArray();
315        }
316        List l = containerFactory.creatArrayContainer();
317        if (l == null) {
318            return new JSONArray();
319        }
320        return l;
321    }
322    
323    public void parse(String s, ContentHandler contentHandler) 
324            throws ParseException {
325        parse(s, contentHandler, false);
326    }
327    
328    public void parse(String s, ContentHandler contentHandler, boolean isResume)
329            throws ParseException{
330        StringReader in = new StringReader(s);
331        try{
332            parse(in, contentHandler, isResume);
333        }
334        catch(IOException ie) {
335            /*
336             * Actually it will never happen.
337             */
338            throw new ParseException(-1, 
339                    ParseException.ERROR_UNEXPECTED_EXCEPTION, ie);
340        }
341    }
342    
343    public void parse(Reader in, ContentHandler contentHandler) 
344            throws IOException, ParseException {
345        parse(in, contentHandler, false);
346    }
347    
348    /**
349     * Stream processing of JSON text.
350     * 
351     * @see ContentHandler
352     * 
353     * @param in  the input.
354     * @param contentHandler  the content handler.
355     * @param isResume  indicates if it continues previous parsing operation.
356     *     If set to true, resume parsing the old stream, and parameter 'in' 
357     *     will be ignored.  If this method is called for the first time in 
358     *     this instance, isResume will be ignored.
359     * 
360     * @throws IOException if there is an I/O problem.
361     * @throws ParseException if there is a parsing problem.
362     */
363    public void parse(Reader in, ContentHandler contentHandler, 
364            boolean isResume) throws IOException, ParseException {
365        if (!isResume) {
366            reset(in);
367            handlerStatusStack = new LinkedList<Integer>();
368        }
369        else{
370            if (handlerStatusStack == null) {
371                isResume = false;
372                reset(in);
373                handlerStatusStack = new LinkedList<Integer>();
374            }
375        }
376        
377        LinkedList<Integer> statusStack = handlerStatusStack;    
378        
379        try {
380            do {
381                switch(status) {
382                case S_INIT:
383                    contentHandler.startJSON();
384                    nextToken();
385                    switch(token.type) {
386                    case Yytoken.TYPE_VALUE:
387                        status=S_IN_FINISHED_VALUE;
388                        statusStack.addFirst(new Integer(status));
389                        if (!contentHandler.primitive(token.value)) {
390                            return;
391                        }
392                        break;
393                    case Yytoken.TYPE_LEFT_BRACE:
394                        status = S_IN_OBJECT;
395                        statusStack.addFirst(new Integer(status));
396                        if (!contentHandler.startObject()) {
397                            return;
398                        }
399                        break;
400                    case Yytoken.TYPE_LEFT_SQUARE:
401                        status = S_IN_ARRAY;
402                        statusStack.addFirst(new Integer(status));
403                        if (!contentHandler.startArray()) {
404                            return;
405                        }
406                        break;
407                    default:
408                        status = S_IN_ERROR;
409                    }//inner switch
410                    break;
411                    
412                case S_IN_FINISHED_VALUE:
413                    nextToken();
414                    if (token.type == Yytoken.TYPE_EOF) {
415                        contentHandler.endJSON();
416                        status = S_END;
417                        return;
418                    }
419                    else {
420                        status = S_IN_ERROR;
421                        throw new ParseException(getPosition(), 
422                                ParseException.ERROR_UNEXPECTED_TOKEN, token);
423                    }
424            
425                case S_IN_OBJECT:
426                    nextToken();
427                    switch(token.type) {
428                    case Yytoken.TYPE_COMMA:
429                        break;
430                    case Yytoken.TYPE_VALUE:
431                        if (token.value instanceof String) {
432                            String key = (String) token.value;
433                            status = S_PASSED_PAIR_KEY;
434                            statusStack.addFirst(new Integer(status));
435                            if (!contentHandler.startObjectEntry(key)) {
436                                return;
437                            }
438                        }
439                        else {
440                            status = S_IN_ERROR;
441                        }
442                        break;
443                    case Yytoken.TYPE_RIGHT_BRACE:
444                        if (statusStack.size() > 1) {
445                            statusStack.removeFirst();
446                            status = peekStatus(statusStack);
447                        }
448                        else{
449                            status = S_IN_FINISHED_VALUE;
450                        }
451                        if (!contentHandler.endObject()) {
452                            return;
453                        }
454                        break;
455                    default:
456                        status = S_IN_ERROR;
457                        break;
458                    }//inner switch
459                    break;
460                    
461                case S_PASSED_PAIR_KEY:
462                    nextToken();
463                    switch(token.type) {
464                    case Yytoken.TYPE_COLON:
465                        break;
466                    case Yytoken.TYPE_VALUE:
467                        statusStack.removeFirst();
468                        status = peekStatus(statusStack);
469                        if (!contentHandler.primitive(token.value)) {
470                            return;
471                        }
472                        if (!contentHandler.endObjectEntry()) {
473                            return;
474                        }
475                        break;
476                    case Yytoken.TYPE_LEFT_SQUARE:
477                        statusStack.removeFirst();
478                        statusStack.addFirst(new Integer(S_IN_PAIR_VALUE));
479                        status = S_IN_ARRAY;
480                        statusStack.addFirst(new Integer(status));
481                        if (!contentHandler.startArray()) {
482                            return;
483                        }
484                        break;
485                    case Yytoken.TYPE_LEFT_BRACE:
486                        statusStack.removeFirst();
487                        statusStack.addFirst(new Integer(S_IN_PAIR_VALUE));
488                        status = S_IN_OBJECT;
489                        statusStack.addFirst(new Integer(status));
490                        if (!contentHandler.startObject()) {
491                            return;
492                        }
493                        break;
494                    default:
495                        status = S_IN_ERROR;
496                    }
497                    break;
498                
499                case S_IN_PAIR_VALUE:
500                    /*
501                     * S_IN_PAIR_VALUE is just a marker to indicate the end of 
502                     * an object entry, it doesn't proccess any token,
503                     * therefore delay consuming token until next round.
504                     */
505                    statusStack.removeFirst();
506                    status = peekStatus(statusStack);
507                    if (!contentHandler.endObjectEntry()) {
508                        return;
509                    }
510                    break;
511                    
512                case S_IN_ARRAY:
513                    nextToken();
514                    switch(token.type){
515                    case Yytoken.TYPE_COMMA:
516                        break;
517                    case Yytoken.TYPE_VALUE:
518                        if (!contentHandler.primitive(token.value)) {
519                            return;
520                        }
521                        break;
522                    case Yytoken.TYPE_RIGHT_SQUARE:
523                        if (statusStack.size() > 1) {
524                            statusStack.removeFirst();
525                            status = peekStatus(statusStack);
526                        }
527                        else{
528                            status = S_IN_FINISHED_VALUE;
529                        }
530                        if (!contentHandler.endArray()) {
531                            return;
532                        }
533                        break;
534                    case Yytoken.TYPE_LEFT_BRACE:
535                        status = S_IN_OBJECT;
536                        statusStack.addFirst(new Integer(status));
537                        if (!contentHandler.startObject()) {
538                            return;
539                        }
540                        break;
541                    case Yytoken.TYPE_LEFT_SQUARE:
542                        status = S_IN_ARRAY;
543                        statusStack.addFirst(new Integer(status));
544                        if (!contentHandler.startArray()) {
545                            return;
546                        }
547                        break;
548                    default:
549                        status = S_IN_ERROR;
550                    }//inner switch
551                    break;
552                    
553                case S_END:
554                    return;
555                    
556                case S_IN_ERROR:
557                    throw new ParseException(getPosition(), 
558                            ParseException.ERROR_UNEXPECTED_TOKEN, token);
559                }//switch
560                if (status == S_IN_ERROR) {
561                    throw new ParseException(getPosition(), 
562                            ParseException.ERROR_UNEXPECTED_TOKEN, token);
563                }
564            } while(token.type != Yytoken.TYPE_EOF);
565        }
566        catch (IOException ie) {
567            status = S_IN_ERROR;
568            throw ie;
569        }
570        catch (ParseException pe) {
571            status = S_IN_ERROR;
572            throw pe;
573        }
574        catch (RuntimeException re) {
575            status = S_IN_ERROR;
576            throw re;
577        }
578        catch (Error e) {
579            status = S_IN_ERROR;
580            throw e;
581        }
582        
583        status = S_IN_ERROR;
584        throw new ParseException(getPosition(), 
585                ParseException.ERROR_UNEXPECTED_TOKEN, token);
586    }
587}