com.aptana.ide.editor.json.parsing.JSONParser.java Source code

Java tutorial

Introduction

Here is the source code for com.aptana.ide.editor.json.parsing.JSONParser.java

Source

/**
 * This file Copyright (c) 2005-2008 Aptana, Inc. This program is
 * dual-licensed under both the Aptana Public License and the GNU General
 * Public license. You may elect to use one or the other of these licenses.
 *
 * This program is distributed in the hope that it will be useful, but
 * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
 * NONINFRINGEMENT. Redistribution, except as permitted by whichever of
 * the GPL or APL you select, is prohibited.
 *
 * 1. For the GPL license (GPL), you can redistribute and/or modify this
 * program under the terms of the GNU General Public License,
 * Version 3, as published by the Free Software Foundation.  You should
 * have received a copy of the GNU General Public License, Version 3 along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Aptana provides a special exception to allow redistribution of this file
 * with certain Eclipse Public Licensed code and certain additional terms
 * pursuant to Section 7 of the GPL. You may view the exception and these
 * terms on the web at http://www.aptana.com/legal/gpl/.
 *
 * 2. For the Aptana Public License (APL), this program and the
 * accompanying materials are made available under the terms of the APL
 * v1.0 which accompanies this distribution, and is available at
 * http://www.aptana.com/legal/apl/.
 *
 * You may view the GPL, Aptana's exception and additional terms, and the
 * APL in the file titled license.html at the root of the corresponding
 * plugin containing this source file.
 *
 * Any modifications to this file must keep this entire header intact.
 */
package com.aptana.ide.editor.json.parsing;

import java.text.ParseException;

import org.eclipse.jface.preference.IPreferenceStore;

import com.aptana.ide.editor.json.lexing.JSONTokenTypes;
import com.aptana.ide.editor.json.parsing.nodes.JSONParseNode;
import com.aptana.ide.editor.json.parsing.nodes.JSONParseNodeTypes;
import com.aptana.ide.editors.UnifiedEditorsPlugin;
import com.aptana.ide.editors.unified.folding.FoldingExtensionPointLoader;
import com.aptana.ide.editors.unified.parsing.UnifiedParser;
import com.aptana.ide.lexer.ILexer;
import com.aptana.ide.lexer.Lexeme;
import com.aptana.ide.lexer.LexerException;
import com.aptana.ide.lexer.TokenCategories;
import com.aptana.ide.parsing.IParseState;
import com.aptana.ide.parsing.ParserInitializationException;
import com.aptana.ide.parsing.nodes.IParseNode;
import com.aptana.ide.parsing.nodes.IParseNodeFactory;

/**
 * @author Kevin Lindsey
 */
public class JSONParser extends UnifiedParser {
    /**
     * JSONParser
     * 
     * @throws ParserInitializationException
     */
    public JSONParser() throws ParserInitializationException {
        this(JSONMimeType.MimeType);
    }

    /**
     * JSONParser
     * 
     * @param mimeType
     * @throws ParserInitializationException
     */
    public JSONParser(String mimeType) throws ParserInitializationException {
        super(mimeType);

        IPreferenceStore unifiedStore = UnifiedEditorsPlugin.getDefault().getPreferenceStore();
        unifiedStore.setDefault(FoldingExtensionPointLoader.createEnablePreferenceId(JSONMimeType.MimeType), true);
    }

    /**
     * @see com.aptana.ide.parsing.AbstractParser#advance()
     */
    protected void advance() throws LexerException {
        super.advance();

        while (this.isEOS() == false && this.currentLexeme.getCategoryIndex() == TokenCategories.WHITESPACE) {
            super.advance();
        }
    }

    /**
     * createNode
     * 
     * @param type
     * @param startingLexeme
     * @return JSParseNode
     */
    private JSONParseNode createNode(int type, Lexeme startingLexeme) {
        IParseNodeFactory factory = this.getParseNodeFactory();
        JSONParseNode result = null;

        if (factory != null) {
            result = (JSONParseNode) factory.createParseNode(type, startingLexeme);
        } else {
            // we need to return something to prevent NPE's
            result = new JSONParseNode(type, startingLexeme);
        }

        return result;
    }

    /**
     * @see com.aptana.ide.parsing.AbstractParser#createParseState(com.aptana.ide.parsing.IParseState)
     */
    public IParseState createParseState(IParseState parent) {
        IParseState result;

        if (parent == null) {
            result = new JSONParseState();
        } else {
            result = new JSONParseState(parent);
        }

        return result;
    }

    /**
     * @see com.aptana.ide.parsing.AbstractParser#initializeLexer()
     */
    public void initializeLexer() throws LexerException {
        ILexer lexer = this.getLexer();
        String language = this.getLanguage();

        lexer.setIgnoreSet(language, new int[] { JSONTokenTypes.WHITESPACE });
        lexer.setLanguageAndGroup(language, "default"); //$NON-NLS-1$
    }

    /**
     * @see com.aptana.ide.parsing.AbstractParser#parseAll(com.aptana.ide.parsing.nodes.IParseNode)
     */
    public void parseAll(IParseNode parentNode) throws ParseException, LexerException {
        // set node to be used as the root node for the results of this parse
        IParseNode rootNode;

        if (parentNode == null) {
            IParseNodeFactory nodeFactory = this.getParseNodeFactory();

            if (nodeFactory != null) {
                rootNode = nodeFactory.createRootNode();

                if (parentNode != null) {
                    parentNode.appendChild(rootNode);
                }
            } else {
                rootNode = null;
            }
        } else {
            rootNode = parentNode;
        }

        ILexer lexer = this.getLexer();
        lexer.setLanguageAndGroup(this.getLanguage(), "default"); //$NON-NLS-1$

        this.advance();

        while (this.isEOS() == false) {
            IParseNode result = this.parseStatement();

            if (rootNode != null && result != null) {
                rootNode.appendChild(result);
            }
        }
    }

    /**
     * parseStatement
     * 
     * @return IParseNode
     * @throws LexerException
     */
    private IParseNode parseStatement() throws LexerException {
        IParseNode result = null;

        switch (this.currentLexeme.typeIndex) {
        case JSONTokenTypes.LBRACKET:
            result = this.parseArray();
            break;

        case JSONTokenTypes.LCURLY:
            result = this.parseObject();
            break;

        case JSONTokenTypes.FALSE:
        case JSONTokenTypes.TRUE:
        case JSONTokenTypes.NUMBER:
        case JSONTokenTypes.STRING:
        case JSONTokenTypes.NULL:
        case JSONTokenTypes.REFERENCE:
            result = this.createNode(JSONParseNodeTypes.SCALAR, this.currentLexeme);
            this.advance();
            break;

        default:
            advance();
        }

        return result;
    }

    /**
     * parseArray
     * 
     * @return parse node
     * @throws LexerException
     */
    private IParseNode parseArray() throws LexerException {
        IParseNode result = this.createNode(JSONParseNodeTypes.ARRAY, this.currentLexeme);

        // assuming '[' and advancing over (not asserting to make things a bit easier)
        this.advance();

        while (this.isEOS() == false && this.currentLexeme.typeIndex != JSONTokenTypes.RBRACKET) {
            IParseNode child = this.parseStatement();

            if (child != null) {
                result.appendChild(child);
            } else {
                this.recover(JSONTokenTypes.RBRACKET);
            }

            if (this.currentLexeme.typeIndex == JSONTokenTypes.COMMA) {
                this.advance();
            }
        }

        if (this.currentLexeme.typeIndex == JSONTokenTypes.RBRACKET) {
            result.setEndingLexeme(this.currentLexeme);
            this.advance();
        }

        return result;
    }

    /**
     * parseObject
     * 
     * @return parse node
     * @throws LexerException
     */
    private IParseNode parseObject() throws LexerException {
        IParseNode result = this.createNode(JSONParseNodeTypes.OBJECT, this.currentLexeme);

        // assuming '{' and advancing over (not asserting to make things a bit easier)
        this.advance();

        while (this.isEOS() == false && this.currentLexeme.typeIndex != JSONTokenTypes.RCURLY) {
            IParseNode child = null;

            try {
                child = this.parseNameValuePair();
            } catch (ParseException pe) {
            }

            if (child != null) {
                result.appendChild(child);
            } else {
                this.recover(JSONTokenTypes.RCURLY);
            }

            if (this.currentLexeme.typeIndex == JSONTokenTypes.COMMA) {
                this.advance();
            }
        }

        if (this.currentLexeme.typeIndex == JSONTokenTypes.RCURLY) {
            result.setEndingLexeme(this.currentLexeme);
            this.advance();
        }

        return result;
    }

    /**
     * parseNameValuePair
     * 
     * @return parse node
     * @throws LexerException
     * @throws ParseException
     */
    private IParseNode parseNameValuePair() throws LexerException, ParseException {
        IParseNode result = this.createNode(JSONParseNodeTypes.NAME_VALUE_PAIR, this.currentLexeme);

        switch (this.currentLexeme.typeIndex) {
        case JSONTokenTypes.STRING:
        case JSONTokenTypes.PROPERTY:
            IParseNode name = this.createNode(JSONParseNodeTypes.SCALAR, this.currentLexeme);

            result.appendChild(name);

            this.advance();

            this.assertAndAdvance(JSONTokenTypes.COLON, Messages.getString("JSONParser.Missing_Colon")); //$NON-NLS-1$

            IParseNode value = this.parseStatement();

            if (value != null) {
                result.appendChild(value);
            } else {
                // report failure
                result = null;
            }
            break;

        default:
            // report failure
            result = null;
        }

        return result;
    }

    /**
     * recover
     * 
     * @param closeType
     */
    private void recover(int closeType) throws LexerException {
        Lexeme startLexeme = this.currentLexeme;

        while (this.isEOS() == false) {
            int typeIndex = this.currentLexeme.typeIndex;

            if (typeIndex == closeType) {
                break;
            } else if (typeIndex == JSONTokenTypes.LBRACKET || typeIndex == JSONTokenTypes.LCURLY
                    || typeIndex == JSONTokenTypes.COMMA) {
                if (this.currentLexeme != startLexeme) {
                    break;
                } else {
                    this.advance();
                }
            } else {
                this.advance();
            }
        }
    }
}