org.ballerinalang.composer.service.workspace.rest.datamodel.BLangFragmentParser.java Source code

Java tutorial

Introduction

Here is the source code for org.ballerinalang.composer.service.workspace.rest.datamodel.BLangFragmentParser.java

Source

/*
*  Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
*  WSO2 Inc. licenses this file to you under the Apache License,
*  Version 2.0 (the "License"); you may not use this file except
*  in compliance with the License.
*  You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing,
*  software distributed under the License is distributed on an
*  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
*  KIND, either express or implied.  See the License for the
*  specific language governing permissions and limitations
*  under the License.
*/
package org.ballerinalang.composer.service.workspace.rest.datamodel;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.apache.commons.lang3.StringUtils;
import org.ballerinalang.model.BLangPackage;
import org.ballerinalang.model.BallerinaFile;
import org.ballerinalang.model.BallerinaFunction;
import org.ballerinalang.model.GlobalScope;
import org.ballerinalang.model.types.BTypes;
import org.ballerinalang.util.parser.BallerinaLexer;
import org.ballerinalang.util.parser.BallerinaParser;
import org.ballerinalang.util.parser.antlr4.BLangAntlr4Listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * Utility for parsing BLang source fragments
 */
public class BLangFragmentParser {

    private static final Logger logger = LoggerFactory.getLogger(BLangFragmentParser.class);

    public static final String SYNTAX_ERRORS = "syntax_errors";
    public static final String TEMP_UNTITLED = "temp/untitled";
    public static final String ERROR = "error";

    public static String parseFragment(BLangSourceFragment sourceFragment) {
        try {
            String parsableString = getParsableString(sourceFragment);
            JsonObject jsonModel = getJsonModel(parsableString);
            if (jsonModel.getAsJsonArray(SYNTAX_ERRORS) != null) {
                return jsonModel.toString();
            }
            JsonObject jsonNodeForFragment = getJsonNodeForFragment(jsonModel, sourceFragment);
            return jsonNodeForFragment.toString();
        } catch (Exception e) {
            logger.error("Error while parsing BLang fragment.", e);
            JsonObject errObj = new JsonObject();
            errObj.addProperty(ERROR, e.getMessage());
            return errObj.toString();
        }
    }

    protected static JsonObject getJsonNodeForFragment(JsonObject jsonModel, BLangSourceFragment fragment) {
        JsonObject fragmentNode = null;
        JsonArray jsonArray = jsonModel.getAsJsonArray(BLangJSONModelConstants.ROOT);
        JsonObject functionObj = jsonArray.get(1).getAsJsonObject(); // 0 is package def
        switch (fragment.getExpectedNodeType()) {
        case BLangFragmentParserConstants.EXPRESSION:
            // 0 & 1 are function args and return types, 2 is the var def stmt
            JsonObject varDef = functionObj.getAsJsonArray(BLangJSONModelConstants.CHILDREN).get(2)
                    .getAsJsonObject();
            // 0th child is the var ref expression of var def stmt
            fragmentNode = varDef.getAsJsonArray(BLangJSONModelConstants.CHILDREN).get(1).getAsJsonObject();
            break;
        case BLangFragmentParserConstants.VARIABLE_REFERENCE_LIST:
            // 0 & 1 are function args and return types, 2 is the assignment statement
            JsonObject assignmentStmt = functionObj.getAsJsonArray(BLangJSONModelConstants.CHILDREN).get(2)
                    .getAsJsonObject();
            // 0th child is the var ref list expression of assignment stmt
            fragmentNode = assignmentStmt.getAsJsonArray(BLangJSONModelConstants.CHILDREN).get(0).getAsJsonObject();
            break;
        case BLangFragmentParserConstants.STATEMENT:
            // 0 & 1 are function args and return types, 2 is the statement came from source fragment
            fragmentNode = functionObj.getAsJsonArray(BLangJSONModelConstants.CHILDREN).get(2).getAsJsonObject();
            break;
        case BLangFragmentParserConstants.JOIN_CONDITION:
            fragmentNode = functionObj.getAsJsonArray(BLangJSONModelConstants.CHILDREN).get(2).getAsJsonObject()
                    .getAsJsonArray(BLangJSONModelConstants.CHILDREN).get(0).getAsJsonObject();
            fragmentNode.remove(BLangJSONModelConstants.CHILDREN);
            fragmentNode.remove(BLangJSONModelConstants.JOIN_PARAMETER);
            break;
        case BLangFragmentParserConstants.ARGUMENT_PARAMETER:
            fragmentNode = functionObj.getAsJsonArray(BLangJSONModelConstants.CHILDREN).get(0).getAsJsonObject()
                    .getAsJsonArray(BLangJSONModelConstants.CHILDREN).get(0).getAsJsonObject();
            break;
        case BLangFragmentParserConstants.RETURN_PARAMETER:
            fragmentNode = functionObj.getAsJsonArray(BLangJSONModelConstants.CHILDREN).get(1).getAsJsonObject()
                    .getAsJsonArray(BLangJSONModelConstants.CHILDREN).get(0).getAsJsonObject();
            break;
        case BLangFragmentParserConstants.TRANSACTION_FAILED:
            fragmentNode = functionObj.getAsJsonArray(BLangJSONModelConstants.CHILDREN).get(2).getAsJsonObject()
                    .getAsJsonArray(BLangJSONModelConstants.CHILDREN).get(1).getAsJsonObject()
                    .getAsJsonArray(BLangJSONModelConstants.CHILDREN).get(0).getAsJsonObject();
            break;
        default:
            fragmentNode = new JsonObject();
            fragmentNode.addProperty(ERROR, "cannot find node for given fragment");
        }
        return fragmentNode;
    }

    protected static JsonObject getJsonModel(String source) throws IOException {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8));
        ANTLRInputStream antlrInputStream = new ANTLRInputStream(inputStream);
        BallerinaLexer ballerinaLexer = new BallerinaLexer(antlrInputStream);
        CommonTokenStream ballerinaToken = new CommonTokenStream(ballerinaLexer);

        BallerinaParser ballerinaParser = new BallerinaParser(ballerinaToken);
        BallerinaComposerErrorStrategy errorStrategy = new BallerinaComposerErrorStrategy();
        ballerinaParser.setErrorHandler(errorStrategy);

        GlobalScope globalScope = GlobalScope.getInstance();
        BTypes.loadBuiltInTypes(globalScope);
        BLangPackage bLangPackage = new BLangPackage(globalScope);
        BLangPackage.PackageBuilder packageBuilder = new BLangPackage.PackageBuilder(bLangPackage);
        BallerinaComposerModelBuilder bLangModelBuilder = new BallerinaComposerModelBuilder(packageBuilder,
                StringUtils.EMPTY);
        BLangAntlr4Listener ballerinaBaseListener = new BLangAntlr4Listener(true, ballerinaToken, bLangModelBuilder,
                new File(TEMP_UNTITLED).toPath());
        ballerinaParser.addParseListener(ballerinaBaseListener);
        ballerinaParser.compilationUnit();
        BallerinaFile bFile = bLangModelBuilder.build();

        JsonObject jsonModelRoot = new JsonObject();
        JsonArray errors = new JsonArray();
        for (SyntaxError error : errorStrategy.getErrorTokens()) {
            // reduce number of lines in wrapper function from row count
            error.setRow(error.getRow() - 1);
            errors.add(error.toJson());
        }

        if (errorStrategy.getErrorTokens().isEmpty()
                /* if parser recovered the error */
                || (bFile.getCompilationUnits().length > 0 && (((BallerinaFunction) bFile.getCompilationUnits()[0])
                        .getCallableUnitBody().getStatements().length == 1)
                /* if the only recovered error is the additional semicolon error*/
                        && errorStrategy.getErrorTokens().size() == 1
                        && errorStrategy.getErrorTokens().get(0).getText().contains("unwanted token ';'"))) {
            BLangJSONModelBuilder jsonModelBuilder = new BLangJSONModelBuilder(jsonModelRoot);
            bFile.accept(jsonModelBuilder);
        } else {
            jsonModelRoot.add(SYNTAX_ERRORS, errors);
        }

        return jsonModelRoot;
    }

    protected static String getParsableString(BLangSourceFragment sourceFragment) {
        String parsableText = null;
        switch (sourceFragment.getExpectedNodeType()) {
        case BLangFragmentParserConstants.EXPRESSION:
            parsableText = getFromTemplate(BLangFragmentParserConstants.VAR_DEF_STMT_EXPR_WRAPPER,
                    sourceFragment.getSource());
            break;
        case BLangFragmentParserConstants.STATEMENT:
            parsableText = getFromTemplate(BLangFragmentParserConstants.FUNCTION_BODY_STMT_WRAPPER,
                    sourceFragment.getSource());
            break;
        case BLangFragmentParserConstants.JOIN_CONDITION:
            parsableText = getFromTemplate(BLangFragmentParserConstants.FORK_JOIN_CONDITION_WRAPPER,
                    sourceFragment.getSource());
            break;
        case BLangFragmentParserConstants.ARGUMENT_PARAMETER:
            parsableText = getFromTemplate(BLangFragmentParserConstants.FUNCTION_SIGNATURE_PARAMETER_WRAPPER,
                    sourceFragment.getSource());
            break;
        case BLangFragmentParserConstants.RETURN_PARAMETER:
            parsableText = getFromTemplate(BLangFragmentParserConstants.FUNCTION_SIGNATURE_RETURN_WRAPPER,
                    sourceFragment.getSource());
            break;
        case BLangFragmentParserConstants.TRANSACTION_FAILED:
            parsableText = getFromTemplate(BLangFragmentParserConstants.TRANSACTION_FAILED_RETRY_WRAPPER,
                    sourceFragment.getSource());
            break;
        case BLangFragmentParserConstants.VARIABLE_REFERENCE_LIST:
            parsableText = getFromTemplate(BLangFragmentParserConstants.VAR_REFERENCE_LIST_WRAPPER,
                    sourceFragment.getSource());
            break;
        default:
            parsableText = "";
        }
        return parsableText;
    }

    protected static String getFromTemplate(String template, String source) {
        return template.replace(BLangFragmentParserConstants.FRAGMENT_PLACE_HOLDER, source);
    }
}