    001/* ============
    002 * Orson Charts
    003 * ============
    004 *
    005 * (C)opyright 2013, 2014, 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 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    public Object parse(Reader in, ContainerFactory containerFactory)
    131            throws IOException, ParseException{
    132        reset(in);
    133        LinkedList statusStack = new LinkedList();
    134        LinkedList valueStack = new LinkedList();
    135
    136        try {
    137            do {
    138                nextToken();
    139                switch(status) {
    140                case S_INIT:
    141                    switch(token.type) {
    142                    case Yytoken.TYPE_VALUE:
    143                        status=S_IN_FINISHED_VALUE;
    144                        statusStack.addFirst(new Integer(status));
    145                        valueStack.addFirst(token.value);
    146                        break;
    147                    case Yytoken.TYPE_LEFT_BRACE:
    148                        status=S_IN_OBJECT;
    149                        statusStack.addFirst(new Integer(status));
    150                        valueStack.addFirst(createObjectContainer(containerFactory));
    151                        break;
    152                    case Yytoken.TYPE_LEFT_SQUARE:
    153                        status=S_IN_ARRAY;
    154                        statusStack.addFirst(new Integer(status));
    155                        valueStack.addFirst(createArrayContainer(containerFactory));
    156                        break;
    157                    default:
    158                        status=S_IN_ERROR;
    159                    }//inner switch
    160                    break;
    161
    162                case S_IN_FINISHED_VALUE:
    163                    if (token.type == Yytoken.TYPE_EOF) {
    164                        return valueStack.removeFirst();
    165                    } else {
    166                    throw new ParseException(getPosition(),
    167                            ParseException.ERROR_UNEXPECTED_TOKEN, token);
    168                }
    169
    170                case S_IN_OBJECT:
    171                    switch(token.type) {
    172                    case Yytoken.TYPE_COMMA:
    173                        break;
    174                    case Yytoken.TYPE_VALUE:
    175                        if (token.value instanceof String) {
    176                            String key = (String) token.value;
    177                            valueStack.addFirst(key);
    178                            status = S_PASSED_PAIR_KEY;
    179                            statusStack.addFirst(new Integer(status));
    180                        }
    181                        else{
    182                            status = S_IN_ERROR;
    183                        }
    184                        break;
    185                    case Yytoken.TYPE_RIGHT_BRACE:
    186                        if (valueStack.size() > 1) {
    187                            statusStack.removeFirst();
    188                            valueStack.removeFirst();
    189                            status = peekStatus(statusStack);
    190                        }
    191                        else{
    192                            status = S_IN_FINISHED_VALUE;
    193                        }
    194                        break;
    195                    default:
    196                        status = S_IN_ERROR;
    197                        break;
    198                    }//inner switch
    199                    break;
    200
    201                case S_PASSED_PAIR_KEY:
    202                    switch(token.type) {
    203                    case Yytoken.TYPE_COLON:
    204                        break;
    205                    case Yytoken.TYPE_VALUE:
    206                        statusStack.removeFirst();
    207                        String key = (String) valueStack.removeFirst();
    208                        Map parent = (Map) valueStack.getFirst();
    209                        parent.put(key,token.value);
    210                        status = peekStatus(statusStack);
    211                        break;
    212                    case Yytoken.TYPE_LEFT_SQUARE:
    213                        statusStack.removeFirst();
    214                        key = (String) valueStack.removeFirst();
    215                        parent = (Map) valueStack.getFirst();
    216                        List newArray = createArrayContainer(containerFactory);
    217                        parent.put(key,newArray);
    218                        status = S_IN_ARRAY;
    219                        statusStack.addFirst(new Integer(status));
    220                        valueStack.addFirst(newArray);
    221                        break;
    222                    case Yytoken.TYPE_LEFT_BRACE:
    223                        statusStack.removeFirst();
    224                        key = (String) valueStack.removeFirst();
    225                        parent = (Map) valueStack.getFirst();
    226                        Map newObject = createObjectContainer(containerFactory);
    227                        parent.put(key, newObject);
    228                        status = S_IN_OBJECT;
    229                        statusStack.addFirst(new Integer(status));
    230                        valueStack.addFirst(newObject);
    231                        break;
    232                    default:
    233                        status = S_IN_ERROR;
    234                    }
    235                    break;
    236
    237                case S_IN_ARRAY:
    238                    switch(token.type) {
    239                    case Yytoken.TYPE_COMMA:
    240                        break;
    241                    case Yytoken.TYPE_VALUE:
    242                        List val = (List) valueStack.getFirst();
    243                        val.add(token.value);
    244                        break;
    245                    case Yytoken.TYPE_RIGHT_SQUARE:
    246                        if (valueStack.size() > 1) {
    247                            statusStack.removeFirst();
    248                            valueStack.removeFirst();
    249                            status = peekStatus(statusStack);
    250                        }
    251                        else {
    252                            status = S_IN_FINISHED_VALUE;
    253                        }
    254                        break;
    255                    case Yytoken.TYPE_LEFT_BRACE:
    256                        val = (List) valueStack.getFirst();
    257                        Map newObject = createObjectContainer(containerFactory);
    258                        val.add(newObject);
    259                        status = S_IN_OBJECT;
    260                        statusStack.addFirst(new Integer(status));
    261                        valueStack.addFirst(newObject);
    262                        break;
    263                    case Yytoken.TYPE_LEFT_SQUARE:
    264                        val = (List) valueStack.getFirst();
    265                        List newArray = createArrayContainer(containerFactory);
    266                        val.add(newArray);
    267                        status = S_IN_ARRAY;
    268                        statusStack.addFirst(new Integer(status));
    269                        valueStack.addFirst(newArray);
    270                        break;
    271                    default:
    272                        status = S_IN_ERROR;
    273                    }//inner switch
    274                    break;
    275                case S_IN_ERROR:
    276                    throw new ParseException(getPosition(),
    277                            ParseException.ERROR_UNEXPECTED_TOKEN, token);
    278                }//switch
    279                if (status == S_IN_ERROR) {
    280                    throw new ParseException(getPosition(),
    281                            ParseException.ERROR_UNEXPECTED_TOKEN, token);
    282                }
    283            } while (token.type != Yytoken.TYPE_EOF);
    284        }
    285        catch (IOException ie) {
    286            throw ie;
    287        }
    288
    289        throw new ParseException(getPosition(),
    290                ParseException.ERROR_UNEXPECTED_TOKEN, token);
    291    }
    292
    293    private void nextToken() throws ParseException, IOException {
    294        token = lexer.yylex();
    295        if (token == null) {
    296            token = new Yytoken(Yytoken.TYPE_EOF, null);
    297        }
    298    }
    299
    300    private Map createObjectContainer(ContainerFactory containerFactory) {
    301        if (containerFactory == null) {
    302            return new JSONObject();
    303        }
    304        Map m = containerFactory.createObjectContainer();
    305        if (m == null) {
    306            return new JSONObject();
    307        }
    308        return m;
    309    }
    310
    311    private List createArrayContainer(ContainerFactory containerFactory) {
    312        if (containerFactory == null) {
    313            return new JSONArray();
    314        }
    315        List l = containerFactory.creatArrayContainer();
    316        if (l == null) {
    317            return new JSONArray();
    318        }
    319        return l;
    320    }
    321
    322    public void parse(String s, ContentHandler contentHandler)
    323            throws ParseException {
    324        parse(s, contentHandler, false);
    325    }
    326
    327    public void parse(String s, ContentHandler contentHandler, boolean isResume)
    328            throws ParseException{
    329        StringReader in = new StringReader(s);
    330        try{
    331            parse(in, contentHandler, isResume);
    332        }
    333        catch(IOException ie) {
    334            /*
    335             * Actually it will never happen.
    336             */
    337            throw new ParseException(-1,
    338                    ParseException.ERROR_UNEXPECTED_EXCEPTION, ie);
    339        }
    340    }
    341
    342    public void parse(Reader in, ContentHandler contentHandler)
    343            throws IOException, ParseException {
    344        parse(in, contentHandler, false);
    345    }
    346
    347    /**
    348     * Stream processing of JSON text.
    349     *
    350     * @see ContentHandler
    351     *
    352     * @param in  the input.
    353     * @param contentHandler  the content handler.
    354     * @param isResume  indicates if it continues previous parsing operation.
    355     *     If set to true, resume parsing the old stream, and parameter 'in'
    356     *     will be ignored.  If this method is called for the first time in
    357     *     this instance, isResume will be ignored.
    358     *
    359     * @throws IOException if there is an I/O problem.
    360     * @throws ParseException if there is a parsing problem.
    361     */
    362    public void parse(Reader in, ContentHandler contentHandler,
    363            boolean isResume) throws IOException, ParseException {
    364        if (!isResume) {
    365            reset(in);
    366            handlerStatusStack = new LinkedList();
    367        }
    368        else{
    369            if (handlerStatusStack == null) {
    370                isResume = false;
    371                reset(in);
    372                handlerStatusStack = new LinkedList();
    373            }
    374        }
    375
    376        LinkedList statusStack = handlerStatusStack;
    377
    378        try {
    379            do {
    380                switch(status) {
    381                case S_INIT:
    382                    contentHandler.startJSON();
    383                    nextToken();
    384                    switch(token.type) {
    385                    case Yytoken.TYPE_VALUE:
    386                        status=S_IN_FINISHED_VALUE;
    387                        statusStack.addFirst(new Integer(status));
    388                        if (!contentHandler.primitive(token.value)) {
    389                            return;
    390                        }
    391                        break;
    392                    case Yytoken.TYPE_LEFT_BRACE:
    393                        status = S_IN_OBJECT;
    394                        statusStack.addFirst(new Integer(status));
    395                        if (!contentHandler.startObject()) {
    396                            return;
    397                        }
    398                        break;
    399                    case Yytoken.TYPE_LEFT_SQUARE:
    400                        status = S_IN_ARRAY;
    401                        statusStack.addFirst(new Integer(status));
    402                        if (!contentHandler.startArray()) {
    403                            return;
    404                        }
    405                        break;
    406                    default:
    407                        status = S_IN_ERROR;
    408                    }//inner switch
    409                    break;
    410
    411                case S_IN_FINISHED_VALUE:
    412                    nextToken();
    413                    if (token.type == Yytoken.TYPE_EOF) {
    414                        contentHandler.endJSON();
    415                        status = S_END;
    416                        return;
    417                    }
    418                    else {
    419                        status = S_IN_ERROR;
    420                        throw new ParseException(getPosition(),
    421                                ParseException.ERROR_UNEXPECTED_TOKEN, token);
    422                    }
    423
    424                case S_IN_OBJECT:
    425                    nextToken();
    426                    switch(token.type) {
    427                    case Yytoken.TYPE_COMMA:
    428                        break;
    429                    case Yytoken.TYPE_VALUE:
    430                        if (token.value instanceof String) {
    431                            String key = (String) token.value;
    432                            status = S_PASSED_PAIR_KEY;
    433                            statusStack.addFirst(new Integer(status));
    434                            if (!contentHandler.startObjectEntry(key)) {
    435                                return;
    436                            }
    437                        }
    438                        else {
    439                            status = S_IN_ERROR;
    440                        }
    441                        break;
    442                    case Yytoken.TYPE_RIGHT_BRACE:
    443                        if (statusStack.size() > 1) {
    444                            statusStack.removeFirst();
    445                            status = peekStatus(statusStack);
    446                        }
    447                        else{
    448                            status = S_IN_FINISHED_VALUE;
    449                        }
    450                        if (!contentHandler.endObject()) {
    451                            return;
    452                        }
    453                        break;
    454                    default:
    455                        status = S_IN_ERROR;
    456                        break;
    457                    }//inner switch
    458                    break;
    459
    460                case S_PASSED_PAIR_KEY:
    461                    nextToken();
    462                    switch(token.type) {
    463                    case Yytoken.TYPE_COLON:
    464                        break;
    465                    case Yytoken.TYPE_VALUE:
    466                        statusStack.removeFirst();
    467                        status = peekStatus(statusStack);
    468                        if (!contentHandler.primitive(token.value)) {
    469                            return;
    470                        }
    471                        if (!contentHandler.endObjectEntry()) {
    472                            return;
    473                        }
    474                        break;
    475                    case Yytoken.TYPE_LEFT_SQUARE:
    476                        statusStack.removeFirst();
    477                        statusStack.addFirst(new Integer(S_IN_PAIR_VALUE));
    478                        status = S_IN_ARRAY;
    479                        statusStack.addFirst(new Integer(status));
    480                        if (!contentHandler.startArray()) {
    481                            return;
    482                        }
    483                        break;
    484                    case Yytoken.TYPE_LEFT_BRACE:
    485                        statusStack.removeFirst();
    486                        statusStack.addFirst(new Integer(S_IN_PAIR_VALUE));
    487                        status = S_IN_OBJECT;
    488                        statusStack.addFirst(new Integer(status));
    489                        if (!contentHandler.startObject()) {
    490                            return;
    491                        }
    492                        break;
    493                    default:
    494                        status = S_IN_ERROR;
    495                    }
    496                    break;
    497
    498                case S_IN_PAIR_VALUE:
    499                    /*
    500                     * S_IN_PAIR_VALUE is just a marker to indicate the end of
    501                     * an object entry, it doesn't proccess any token,
    502                     * therefore delay consuming token until next round.
    503                     */
    504                    statusStack.removeFirst();
    505                    status = peekStatus(statusStack);
    506                    if (!contentHandler.endObjectEntry()) {
    507                        return;
    508                    }
    509                    break;
    510
    511                case S_IN_ARRAY:
    512                    nextToken();
    513                    switch(token.type){
    514                    case Yytoken.TYPE_COMMA:
    515                        break;
    516                    case Yytoken.TYPE_VALUE:
    517                        if (!contentHandler.primitive(token.value)) {
    518                            return;
    519                        }
    520                        break;
    521                    case Yytoken.TYPE_RIGHT_SQUARE:
    522                        if (statusStack.size() > 1) {
    523                            statusStack.removeFirst();
    524                            status = peekStatus(statusStack);
    525                        }
    526                        else{
    527                            status = S_IN_FINISHED_VALUE;
    528                        }
    529                        if (!contentHandler.endArray()) {
    530                            return;
    531                        }
    532                        break;
    533                    case Yytoken.TYPE_LEFT_BRACE:
    534                        status = S_IN_OBJECT;
    535                        statusStack.addFirst(new Integer(status));
    536                        if (!contentHandler.startObject()) {
    537                            return;
    538                        }
    539                        break;
    540                    case Yytoken.TYPE_LEFT_SQUARE:
    541                        status = S_IN_ARRAY;
    542                        statusStack.addFirst(new Integer(status));
    543                        if (!contentHandler.startArray()) {
    544                            return;
    545                        }
    546                        break;
    547                    default:
    548                        status = S_IN_ERROR;
    549                    }//inner switch
    550                    break;
    551
    552                case S_END:
    553                    return;
    554
    555                case S_IN_ERROR:
    556                    throw new ParseException(getPosition(),
    557                            ParseException.ERROR_UNEXPECTED_TOKEN, token);
    558                }//switch
    559                if (status == S_IN_ERROR) {
    560                    throw new ParseException(getPosition(),
    561                            ParseException.ERROR_UNEXPECTED_TOKEN, token);
    562                }
    563            } while(token.type != Yytoken.TYPE_EOF);
    564        }
    565        catch (IOException ie) {
    566            status = S_IN_ERROR;
    567            throw ie;
    568        }
    569        catch (ParseException pe) {
    570            status = S_IN_ERROR;
    571            throw pe;
    572        }
    573        catch (RuntimeException re) {
    574            status = S_IN_ERROR;
    575            throw re;
    576        }
    577        catch (Error e) {
    578            status = S_IN_ERROR;
    579            throw e;
    580        }
    581
    582        status = S_IN_ERROR;
    583        throw new ParseException(getPosition(),
    584                ParseException.ERROR_UNEXPECTED_TOKEN, token);
    585    }
    586}