    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}