org.ballerinalang.langserver.common.utils.CommonUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.ballerinalang.langserver.common.utils.CommonUtil.java

Source

/*
 * Copyright (c) 2018, WSO2 Inc. (http://wso2.com) All Rights Reserved.
 *
 * Licensed 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.langserver.common.utils;

import com.google.common.collect.Lists;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenStream;
import org.ballerinalang.langserver.LSGlobalContextKeys;
import org.ballerinalang.langserver.SnippetBlock;
import org.ballerinalang.langserver.command.testgen.TestGenerator.TestFunctionGenerator;
import org.ballerinalang.langserver.common.UtilSymbolKeys;
import org.ballerinalang.langserver.compiler.DocumentServiceKeys;
import org.ballerinalang.langserver.compiler.LSCompilerUtil;
import org.ballerinalang.langserver.compiler.LSContext;
import org.ballerinalang.langserver.compiler.common.LSDocument;
import org.ballerinalang.langserver.compiler.common.modal.BallerinaPackage;
import org.ballerinalang.langserver.compiler.workspace.WorkspaceDocumentException;
import org.ballerinalang.langserver.compiler.workspace.WorkspaceDocumentManager;
import org.ballerinalang.langserver.completions.CompletionKeys;
import org.ballerinalang.langserver.completions.SymbolInfo;
import org.ballerinalang.langserver.completions.util.ItemResolverConstants;
import org.ballerinalang.langserver.completions.util.Priority;
import org.ballerinalang.langserver.completions.util.Snippet;
import org.ballerinalang.langserver.index.LSIndexException;
import org.ballerinalang.langserver.index.LSIndexImpl;
import org.ballerinalang.langserver.index.dao.BPackageSymbolDAO;
import org.ballerinalang.langserver.index.dao.DAOType;
import org.ballerinalang.langserver.index.dto.BPackageSymbolDTO;
import org.ballerinalang.model.elements.Flag;
import org.ballerinalang.model.elements.PackageID;
import org.ballerinalang.model.symbols.SymbolKind;
import org.ballerinalang.model.tree.TopLevelNode;
import org.ballerinalang.util.BLangConstants;
import org.eclipse.lsp4j.CompletionItem;
import org.eclipse.lsp4j.CompletionItemKind;
import org.eclipse.lsp4j.InsertTextFormat;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.TextEdit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wso2.ballerinalang.compiler.semantics.analyzer.Types;
import org.wso2.ballerinalang.compiler.semantics.model.Scope;
import org.wso2.ballerinalang.compiler.semantics.model.symbols.BAnnotationSymbol;
import org.wso2.ballerinalang.compiler.semantics.model.symbols.BInvokableSymbol;
import org.wso2.ballerinalang.compiler.semantics.model.symbols.BObjectTypeSymbol;
import org.wso2.ballerinalang.compiler.semantics.model.symbols.BOperatorSymbol;
import org.wso2.ballerinalang.compiler.semantics.model.symbols.BServiceSymbol;
import org.wso2.ballerinalang.compiler.semantics.model.symbols.BSymbol;
import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol;
import org.wso2.ballerinalang.compiler.semantics.model.symbols.BVarSymbol;
import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType;
import org.wso2.ballerinalang.compiler.semantics.model.types.BField;
import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType;
import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType;
import org.wso2.ballerinalang.compiler.semantics.model.types.BIntermediateCollectionType;
import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType;
import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType;
import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType;
import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType;
import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType;
import org.wso2.ballerinalang.compiler.semantics.model.types.BStructureType;
import org.wso2.ballerinalang.compiler.semantics.model.types.BTupleType;
import org.wso2.ballerinalang.compiler.semantics.model.types.BType;
import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType;
import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType;
import org.wso2.ballerinalang.compiler.tree.BLangCompilationUnit;
import org.wso2.ballerinalang.compiler.tree.BLangFunction;
import org.wso2.ballerinalang.compiler.tree.BLangImportPackage;
import org.wso2.ballerinalang.compiler.tree.BLangNode;
import org.wso2.ballerinalang.compiler.tree.BLangPackage;
import org.wso2.ballerinalang.compiler.tree.BLangSimpleVariable;
import org.wso2.ballerinalang.compiler.tree.BLangTypeDefinition;
import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression;
import org.wso2.ballerinalang.compiler.tree.expressions.BLangInvocation;
import org.wso2.ballerinalang.compiler.tree.expressions.BLangLambdaFunction;
import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral;
import org.wso2.ballerinalang.compiler.tree.expressions.BLangSimpleVarRef;
import org.wso2.ballerinalang.compiler.tree.statements.BLangAssignment;
import org.wso2.ballerinalang.compiler.tree.statements.BLangBlockStmt;
import org.wso2.ballerinalang.compiler.tree.statements.BLangSimpleVariableDef;
import org.wso2.ballerinalang.compiler.tree.statements.BLangTupleDestructure;
import org.wso2.ballerinalang.compiler.tree.types.BLangFunctionTypeNode;
import org.wso2.ballerinalang.compiler.util.CompilerContext;
import org.wso2.ballerinalang.compiler.util.Name;
import org.wso2.ballerinalang.compiler.util.Names;
import org.wso2.ballerinalang.compiler.util.TypeTags;
import org.wso2.ballerinalang.compiler.util.diagnotic.DiagnosticPos;
import org.wso2.ballerinalang.util.Flags;

import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.annotation.Nullable;

import static org.ballerinalang.langserver.compiler.LSCompilerUtil.getUntitledFilePath;
import static org.ballerinalang.util.BLangConstants.CONSTRUCTOR_FUNCTION_SUFFIX;

/**
 * Common utils to be reuse in language server implementation.
 */
public class CommonUtil {
    private static final Logger logger = LoggerFactory.getLogger(CommonUtil.class);

    public static final String MD_LINE_SEPARATOR = "  " + System.lineSeparator();

    public static final String LINE_SEPARATOR = System.lineSeparator();

    public static final String FILE_SEPARATOR = File.separator;

    public static final String LINE_SEPARATOR_SPLIT = "\\r?\\n";

    public static final boolean LS_DEBUG_ENABLED;

    public static final String BALLERINA_HOME;

    public static final String PLAIN_TEXT_MARKUP_KIND = "plaintext";

    public static final String MARKDOWN_MARKUP_KIND = "markdown";

    static {
        String debugLogStr = System.getProperty("ballerina.debugLog");
        LS_DEBUG_ENABLED = debugLogStr != null && Boolean.parseBoolean(debugLogStr);
        BALLERINA_HOME = System.getProperty("ballerina.home");
    }

    private CommonUtil() {
    }

    /**
     * Get the package URI to the given package name.
     *
     * @param pkgName        Name of the package that need the URI for
     * @param pkgPath String URI of the current package
     * @param currentPkgName Name of the current package
     * @return String URI for the given path.
     */
    public static String getPackageURI(String pkgName, String pkgPath, String currentPkgName) {
        String newPackagePath;
        // If current package path is not null and current package is not default package continue,
        // else new package path is same as the current package path.
        if (pkgPath != null && !currentPkgName.equals(".")) {
            int indexOfCurrentPkgName = pkgPath.lastIndexOf(currentPkgName);
            if (indexOfCurrentPkgName >= 0) {
                newPackagePath = pkgPath.substring(0, indexOfCurrentPkgName);
            } else {
                newPackagePath = pkgPath;
            }

            if (pkgName.equals(".")) {
                newPackagePath = Paths.get(newPackagePath).toString();
            } else {
                newPackagePath = Paths.get(newPackagePath, pkgName).toString();
            }
        } else {
            newPackagePath = pkgPath;
        }
        return newPackagePath;
    }

    /**
     * Calculate the user defined type position.
     *
     * @param position position of the node
     * @param name     name of the user defined type
     * @param pkgAlias package alias name of the user defined type
     */
    public static void calculateEndColumnOfGivenName(DiagnosticPos position, String name, String pkgAlias) {
        position.eCol = position.sCol + name.length() + (!pkgAlias.isEmpty() ? (pkgAlias + ":").length() : 0);
    }

    /**
     * Convert the diagnostic position to a zero based positioning diagnostic position.
     *
     * @param diagnosticPos - diagnostic position to be cloned
     * @return {@link DiagnosticPos} converted diagnostic position
     */
    public static DiagnosticPos toZeroBasedPosition(DiagnosticPos diagnosticPos) {
        int startLine = diagnosticPos.getStartLine() - 1;
        int endLine = diagnosticPos.getEndLine() - 1;
        int startColumn = diagnosticPos.getStartColumn() - 1;
        int endColumn = diagnosticPos.getEndColumn() - 1;
        return new DiagnosticPos(diagnosticPos.getSource(), startLine, endLine, startColumn, endColumn);
    }

    /**
     * Replace and returns a diagnostic position with a new position.
     *
     * @param oldPos old position
     * @param newPos new position
     */
    public static void replacePosition(DiagnosticPos oldPos, DiagnosticPos newPos) {
        oldPos.sLine = newPos.sLine;
        oldPos.eLine = newPos.eLine;
        oldPos.sCol = newPos.sCol;
        oldPos.eCol = newPos.eCol;
    }

    /**
     * Get the previous default token from the given start index.
     *
     * @param tokenStream Token Stream
     * @param startIndex  Start token index
     * @return {@link Token}    Previous default token
     */
    public static Token getPreviousDefaultToken(TokenStream tokenStream, int startIndex) {
        return getDefaultTokenToLeftOrRight(tokenStream, startIndex, -1);
    }

    /**
     * Get the next default token from the given start index.
     *
     * @param tokenStream Token Stream
     * @param startIndex  Start token index
     * @return {@link Token}    Previous default token
     */
    public static Token getNextDefaultToken(TokenStream tokenStream, int startIndex) {
        return getDefaultTokenToLeftOrRight(tokenStream, startIndex, 1);
    }

    /**
     * Get n number of default tokens from a given start index.
     *
     * @param tokenStream Token Stream
     * @param n           number of tokens to extract
     * @param startIndex  Start token index
     * @return {@link List}     List of tokens extracted
     */
    public static List<Token> getNDefaultTokensToLeft(TokenStream tokenStream, int n, int startIndex) {
        List<Token> tokens = new ArrayList<>();
        Token t;
        while (n > 0) {
            t = getDefaultTokenToLeftOrRight(tokenStream, startIndex, -1);
            if (t == null) {
                return new ArrayList<>();
            }
            tokens.add(t);
            n--;
            startIndex = t.getTokenIndex();
        }

        return Lists.reverse(tokens);
    }

    /**
     * Get the Nth Default token to the left of current token index.
     *
     * @param tokenStream Token Stream to traverse
     * @param startIndex  Start position of the token stream
     * @param offset      Number of tokens to traverse left
     * @return {@link Token}    Nth Token
     */
    public static Token getNthDefaultTokensToLeft(TokenStream tokenStream, int startIndex, int offset) {
        Token token = null;
        int indexCounter = startIndex;
        for (int i = 0; i < offset; i++) {
            token = getPreviousDefaultToken(tokenStream, indexCounter);
            indexCounter = token.getTokenIndex();
        }

        return token;
    }

    /**
     * Get the current token index from the token stream.
     *
     * @param context LSServiceOperationContext
     * @return {@link Integer}      token index
     */
    public static int getCurrentTokenFromTokenStream(LSContext context) {
        TokenStream tokenStream = context.get(CompletionKeys.TOKEN_STREAM_KEY);
        Position position = context.get(DocumentServiceKeys.POSITION_KEY).getPosition();
        Token lastToken = null;
        int line = position.getLine();
        int col = position.getCharacter();
        int tokenLine;
        int tokenCol;
        int index = 0;

        if (tokenStream == null) {
            return -1;
        }

        while (true) {
            Token token = tokenStream.get(index);
            tokenLine = token.getLine() - 1;
            tokenCol = token.getCharPositionInLine();
            if (tokenLine > line || (tokenLine == line && tokenCol >= col)) {
                break;
            }
            index++;
            lastToken = token;
        }

        return lastToken == null ? -1 : lastToken.getTokenIndex();
    }

    /**
     * Pop n number of Elements from the stack and return as a List.
     * <p>
     * Note: If n is greater than stack, then all the elements of list will be returned
     *
     * @param itemList Item Stack to pop elements from
     * @param n         number of elements to pop
     * @param <T>       Type of the Elements
     * @return {@link List}     List of popped Items
     */
    public static <T> List<T> popNFromList(List<T> itemList, int n) {
        List<T> poppedList = new ArrayList<>(itemList);
        if (n > poppedList.size()) {
            return poppedList;
        }

        return itemList.subList(itemList.size() - n, itemList.size());
    }

    private static Token getDefaultTokenToLeftOrRight(TokenStream tokenStream, int startIndex, int direction) {
        Token token = null;
        while (true) {
            startIndex += direction;
            if (startIndex < 0 || startIndex == tokenStream.size()) {
                break;
            }
            token = tokenStream.get(startIndex);
            if (token.getChannel() == Token.DEFAULT_CHANNEL) {
                break;
            }
        }
        return token;
    }

    /**
     * Get the top level node type at the cursor line.
     *
     * @param identifier Document Identifier
     * @param cursorLine Cursor line
     * @param docManager Workspace document manager
     * @return {@link String}   Top level node type
     */
    public static String topLevelNodeInLine(TextDocumentIdentifier identifier, int cursorLine,
            WorkspaceDocumentManager docManager) {
        List<String> topLevelKeywords = Arrays.asList("function", "service", "resource", "endpoint");
        LSDocument document = new LSDocument(identifier.getUri());

        try {
            Path filePath = document.getPath();
            Path compilationPath = getUntitledFilePath(filePath.toString()).orElse(filePath);
            String fileContent = docManager.getFileContent(compilationPath);
            String[] splitedFileContent = fileContent.split(LINE_SEPARATOR_SPLIT);
            if ((splitedFileContent.length - 1) >= cursorLine) {
                String lineContent = splitedFileContent[cursorLine];
                List<String> alphaNumericTokens = new ArrayList<>(Arrays.asList(lineContent.split("[^\\w']+")));

                ListIterator<String> iterator = alphaNumericTokens.listIterator();
                int tokenCounter = 0;
                while (iterator.hasNext()) {
                    String topLevelKeyword = iterator.next();

                    boolean validTypeDef = (topLevelKeyword.equals(UtilSymbolKeys.RECORD_KEYWORD_KEY)
                            || topLevelKeyword.equals(UtilSymbolKeys.OBJECT_KEYWORD_KEY)) && tokenCounter > 1
                            && alphaNumericTokens.get(tokenCounter - 2).equals("type");

                    if (validTypeDef || (topLevelKeywords.contains(topLevelKeyword)
                            && (!iterator.hasNext() || !CONSTRUCTOR_FUNCTION_SUFFIX.equals(iterator.next())))) {
                        return topLevelKeyword;
                    }
                    tokenCounter++;
                }

            }
            return null;
        } catch (WorkspaceDocumentException e) {
            logger.error("Error occurred while reading content of file: " + document.toString());
            return null;
        }
    }

    /**
     * Get current package by given file name.
     *
     * @param packages list of packages to be searched
     * @param fileUri  string file URI
     * @return {@link BLangPackage} current package
     */
    public static BLangPackage getCurrentPackageByFileName(List<BLangPackage> packages, String fileUri) {
        Path filePath = new LSDocument(fileUri).getPath();
        String currentModule = LSCompilerUtil.getCurrentModulePath(filePath).getFileName().toString();
        try {
            for (BLangPackage bLangPackage : packages) {
                if (bLangPackage.packageID.sourceFileName != null
                        && bLangPackage.packageID.sourceFileName.value.equals(filePath.getFileName().toString())) {
                    return bLangPackage;
                } else if (currentModule.equals(bLangPackage.packageID.name.value)) {
                    return bLangPackage;
                }
            }
        } catch (NullPointerException e) {
            return packages.get(0);
        }
        return null;
    }

    /**
     * Get the Annotation completion Item.
     *
     * @param packageID        Package Id
     * @param annotationSymbol BLang annotation to extract the completion Item
     * @param ctx                       LS Service operation context, in this case completion context
     * @return {@link CompletionItem}   Completion item for the annotation
     */
    public static CompletionItem getAnnotationCompletionItem(PackageID packageID,
            BAnnotationSymbol annotationSymbol, LSContext ctx) {
        String label = getAnnotationLabel(packageID, annotationSymbol);
        String insertText = getAnnotationInsertText(packageID, annotationSymbol);
        CompletionItem annotationItem = new CompletionItem();
        annotationItem.setLabel(label);
        annotationItem.setInsertText(insertText);
        annotationItem.setInsertTextFormat(InsertTextFormat.Snippet);
        annotationItem.setDetail(ItemResolverConstants.ANNOTATION_TYPE);
        annotationItem.setKind(CompletionItemKind.Property);
        String relativePath = ctx.get(DocumentServiceKeys.RELATIVE_FILE_PATH_KEY);
        BLangPackage pkg = ctx.get(DocumentServiceKeys.CURRENT_BLANG_PACKAGE_CONTEXT_KEY);
        BLangPackage srcOwnerPkg = CommonUtil.getSourceOwnerBLangPackage(relativePath, pkg);
        List<BLangImportPackage> imports = CommonUtil.getCurrentFileImports(srcOwnerPkg, ctx);
        Optional currentPkgImport = imports.stream().filter(bLangImportPackage -> {
            String pkgName = bLangImportPackage.orgName + "/"
                    + CommonUtil.getPackageNameComponentsCombined(bLangImportPackage);
            String evalPkgName = packageID.orgName + "/"
                    + packageID.nameComps.stream().map(Name::getValue).collect(Collectors.joining("."));
            return pkgName.equals(evalPkgName);
        }).findAny();
        // if the particular import statement not available we add the additional text edit to auto import
        if (!currentPkgImport.isPresent()) {
            annotationItem.setAdditionalTextEdits(
                    getAutoImportTextEdits(ctx, packageID.orgName.getValue(), packageID.name.getValue()));
        }
        return annotationItem;
    }

    /**
     * Get the text edit for an auto import statement.
     *
     * @param ctx               Service operation context
     * @param orgName           package org name
     * @param pkgName           package name
     * @return {@link List}     List of Text Edits to apply
     */
    public static List<TextEdit> getAutoImportTextEdits(LSContext ctx, String orgName, String pkgName) {
        if (UtilSymbolKeys.BALLERINA_KW.equals(orgName) && UtilSymbolKeys.BUILTIN_KW.equals(pkgName)) {
            return new ArrayList<>();
        }
        String relativePath = ctx.get(DocumentServiceKeys.RELATIVE_FILE_PATH_KEY);
        BLangPackage pkg = ctx.get(DocumentServiceKeys.CURRENT_BLANG_PACKAGE_CONTEXT_KEY);
        if (relativePath == null || pkg == null) {
            return new ArrayList<>();
        }
        BLangPackage srcOwnerPkg = CommonUtil.getSourceOwnerBLangPackage(relativePath, pkg);
        List<BLangImportPackage> imports = CommonUtil.getCurrentFileImports(srcOwnerPkg, ctx);
        for (BLangImportPackage importPackage : imports) {
            if (importPackage.orgName.value.equals(orgName) && importPackage.alias.value.equals(pkgName)) {
                return new ArrayList<>();
            }
        }
        Position start = new Position(0, 0);
        if (!imports.isEmpty()) {
            BLangImportPackage last = CommonUtil.getLastItem(imports);
            int endLine = last.getPosition().getEndLine();
            start = new Position(endLine, 0);
        }

        String importStatement = ItemResolverConstants.IMPORT + " " + orgName + UtilSymbolKeys.SLASH_KEYWORD_KEY
                + pkgName + UtilSymbolKeys.SEMI_COLON_SYMBOL_KEY + CommonUtil.LINE_SEPARATOR;
        return Collections.singletonList(new TextEdit(new Range(start, start), importStatement));
    }

    /**
     * Fill the completion items extracted from LS Index db with the auto import text edits.
     * Here the Completion Items are mapped against the respective package ID.
     * @param completionsMap    Completion Map to evaluate
     * @param ctx               Lang Server Operation Context
     * @return {@link List} List of modified completion items
     */
    public static List<CompletionItem> fillCompletionWithPkgImport(
            HashMap<Integer, ArrayList<CompletionItem>> completionsMap, LSContext ctx) {
        LSIndexImpl lsIndex = ctx.get(LSGlobalContextKeys.LS_INDEX_KEY);
        List<CompletionItem> returnList = new ArrayList<>();
        completionsMap.forEach((integer, completionItems) -> {
            try {
                BPackageSymbolDTO dto = ((BPackageSymbolDAO) lsIndex.getDaoFactory().get(DAOType.PACKAGE_SYMBOL))
                        .get(integer);
                completionItems.forEach(completionItem -> {
                    List<TextEdit> textEdits = CommonUtil.getAutoImportTextEdits(ctx, dto.getOrgName(),
                            dto.getName());
                    completionItem.setAdditionalTextEdits(textEdits);
                    returnList.add(completionItem);
                });
            } catch (LSIndexException e) {
                logger.error("Error While retrieving Package Symbol for text edits");
            }
        });

        return returnList;
    }

    /**
     * Populate the given map with the completion item.
     *
     * @param map               ID to completion item map
     * @param id                pkg id in index
     * @param completionItem    completion item to populate
     */
    public static void populateIdCompletionMap(HashMap<Integer, ArrayList<CompletionItem>> map, int id,
            CompletionItem completionItem) {
        if (map.containsKey(id)) {
            map.get(id).add(completionItem);
        } else {
            map.put(id, new ArrayList<>(Collections.singletonList(completionItem)));
        }
    }

    /**
     * Get the annotation Insert text.
     *
     * @param packageID        Package ID
     * @param annotationSymbol Annotation to get the insert text
     * @return {@link String}   Insert text
     */
    private static String getAnnotationInsertText(PackageID packageID, BAnnotationSymbol annotationSymbol) {
        String pkgAlias = CommonUtil.getLastItem(packageID.getNameComps()).getValue();
        StringBuilder annotationStart = new StringBuilder();
        if (!packageID.getName().getValue().equals(Names.BUILTIN_PACKAGE.getValue())) {
            annotationStart.append(pkgAlias).append(UtilSymbolKeys.PKG_DELIMITER_KEYWORD);
        }
        if (annotationSymbol.attachedType != null) {
            annotationStart.append(annotationSymbol.getName().getValue()).append(" ")
                    .append(UtilSymbolKeys.OPEN_BRACE_KEY).append(LINE_SEPARATOR).append("\t").append("${1}")
                    .append(LINE_SEPARATOR).append(UtilSymbolKeys.CLOSE_BRACE_KEY);
        } else {
            annotationStart.append(annotationSymbol.getName().getValue());
        }

        return annotationStart.toString();
    }

    /**
     * Get the completion Label for the annotation.
     *
     * @param packageID  Package ID
     * @param annotation BLang annotation
     * @return {@link String}          Label string
     */
    private static String getAnnotationLabel(PackageID packageID, BAnnotationSymbol annotation) {
        String pkgComponent = "";
        if (!packageID.getName().getValue().equals(Names.BUILTIN_PACKAGE.getValue())) {
            pkgComponent = CommonUtil.getLastItem(packageID.getNameComps()).getValue()
                    + UtilSymbolKeys.PKG_DELIMITER_KEYWORD;
        }

        return pkgComponent + annotation.getName().getValue();
    }

    /**
     * Get the default value for the given BType.
     *
     * @param bType BType to get the default value
     * @return {@link String}   Default value as a String
     */
    public static String getDefaultValueForType(BType bType) {
        String typeString;
        if (bType == null) {
            return "()";
        }
        switch (bType.getKind()) {
        case INT:
            typeString = Integer.toString(0);
            break;
        case FLOAT:
            typeString = Float.toString(0);
            break;
        case STRING:
            typeString = "\"\"";
            break;
        case BOOLEAN:
            typeString = Boolean.toString(false);
            break;
        case ARRAY:
        case BLOB:
            typeString = "[]";
            break;
        case RECORD:
        case MAP:
            typeString = "{}";
            break;
        case FINITE:
            List<BLangExpression> valueSpace = new ArrayList<>(((BFiniteType) bType).valueSpace);
            String value = valueSpace.get(0).toString();
            BType type = valueSpace.get(0).type;
            typeString = value;
            if (type.toString().equals("string")) {
                typeString = "\"" + typeString + "\"";
            }
            break;
        case UNION:
            List<BType> memberTypes = new ArrayList<>(((BUnionType) bType).getMemberTypes());
            typeString = getDefaultValueForType(memberTypes.get(0));
            break;
        case STREAM:
        default:
            typeString = "()";
            break;
        }
        return typeString;
    }

    /**
     * Check whether a given symbol is client object or not.
     *
     * @param bSymbol BSymbol to evaluate
     * @return {@link Boolean}  Symbol evaluation status
     */
    public static boolean isClientObject(BSymbol bSymbol) {
        return bSymbol.type != null && bSymbol.type.tsymbol != null
                && SymbolKind.OBJECT.equals(bSymbol.type.tsymbol.kind)
                && (bSymbol.type.tsymbol.flags & Flags.CLIENT) == Flags.CLIENT;
    }

    /**
     * Check whether the symbol is a listener object.
     *
     * @param bSymbol           Symbol to evaluate
     * @return {@link Boolean}  whether listener or not
     */
    public static boolean isListenerObject(BSymbol bSymbol) {
        if (!(bSymbol instanceof BObjectTypeSymbol)) {
            return false;
        }
        List<String> attachedFunctions = ((BObjectTypeSymbol) bSymbol).attachedFuncs.stream()
                .map(function -> function.funcName.getValue()).collect(Collectors.toList());
        return attachedFunctions.contains("__start") && attachedFunctions.contains("__stop")
                && attachedFunctions.contains("__attach");
    }

    /**
     * Given an Object type, extract the non-remote functions.
     *
     * @param objectTypeSymbol  Object Symbol
     * @return {@link Map}      Map of filtered scope entries
     */
    public static Map<Name, Scope.ScopeEntry> getObjectFunctions(BObjectTypeSymbol objectTypeSymbol) {
        return objectTypeSymbol.methodScope.entries.entrySet().stream()
                .filter(entry -> (entry.getValue().symbol.flags & Flags.REMOTE) != Flags.REMOTE)
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    }

    /**
     * Check whether the packages list contains a given package.
     *
     * @param pkg     Package to check
     * @param pkgList List of packages to check against
     * @return {@link Boolean}  Check status of the package
     */
    public static boolean listContainsPackage(String pkg, List<BallerinaPackage> pkgList) {
        return pkgList.stream()
                .anyMatch(ballerinaPackage -> ballerinaPackage.getFullPackageNameAlias().equals(pkg));
    }

    /**
     * Get completion items list for struct fields.
     *
     * @param structFields List of struct fields
     * @return {@link List}     List of completion items for the struct fields
     */
    public static List<CompletionItem> getStructFieldCompletionItems(List<BField> structFields) {
        List<CompletionItem> completionItems = new ArrayList<>();
        structFields.forEach(bStructField -> {
            StringBuilder insertText = new StringBuilder(bStructField.getName().getValue() + ": ");
            if (bStructField.getType() instanceof BStructureType) {
                insertText.append("{").append(LINE_SEPARATOR).append("\t${1}").append(LINE_SEPARATOR).append("}");
            } else {
                insertText.append("${1:").append(getDefaultValueForType(bStructField.getType())).append("}");
                if (bStructField.getType() instanceof BFiniteType || bStructField.getType() instanceof BUnionType) {
                    insertText.append(getFiniteAndUnionTypesComment(bStructField.getType()));
                }
            }
            CompletionItem fieldItem = new CompletionItem();
            fieldItem.setInsertText(insertText.toString());
            fieldItem.setInsertTextFormat(InsertTextFormat.Snippet);
            fieldItem.setLabel(bStructField.getName().getValue());
            fieldItem.setDetail(ItemResolverConstants.FIELD_TYPE);
            fieldItem.setKind(CompletionItemKind.Field);
            fieldItem.setSortText(Priority.PRIORITY120.toString());
            completionItems.add(fieldItem);
        });

        return completionItems;
    }

    /**
     * Get the completion item to fill all the struct fields.
     *
     * @param fields List of struct fields
     * @return {@link CompletionItem}   Completion Item to fill all the options
     */
    public static CompletionItem getFillAllStructFieldsItem(List<BField> fields) {
        List<String> fieldEntries = new ArrayList<>();

        for (int i = 0; i < fields.size(); i++) {
            BField bStructField = fields.get(i);
            String defaultFieldEntry = bStructField.getName().getValue() + UtilSymbolKeys.PKG_DELIMITER_KEYWORD
                    + " " + getDefaultValueForType(bStructField.getType());
            if (bStructField.getType() instanceof BFiniteType || bStructField.getType() instanceof BUnionType) {
                defaultFieldEntry += (i < fields.size() - 1 ? "," : "")
                        + getFiniteAndUnionTypesComment(bStructField.type);
            }
            fieldEntries.add(defaultFieldEntry);
        }

        String insertText = String.join(("," + LINE_SEPARATOR), fieldEntries);
        String label = "Add All Attributes";

        CompletionItem completionItem = new CompletionItem();
        completionItem.setLabel(label);
        completionItem.setInsertText(insertText);
        completionItem.setDetail(ItemResolverConstants.NONE);
        completionItem.setKind(CompletionItemKind.Property);
        completionItem.setSortText(Priority.PRIORITY110.toString());

        return completionItem;
    }

    /**
     * Get the BType name as string.
     *
     * @param bType BType to get the name
     * @param ctx   LS Operation Context
     * @return {@link String}   BType Name as String
     */
    public static String getBTypeName(BType bType, LSContext ctx) {
        PackageID pkgId = bType.tsymbol.pkgID;
        PackageID currentPkgId = ctx.get(DocumentServiceKeys.CURRENT_BLANG_PACKAGE_CONTEXT_KEY).packageID;
        String[] nameComponents = bType.toString().split(":");
        if (pkgId.toString().equals(currentPkgId.toString()) || pkgId.getName().getValue().equals("builtin")) {
            return nameComponents[nameComponents.length - 1];
        } else {
            return pkgId.getName().getValue() + UtilSymbolKeys.PKG_DELIMITER_KEYWORD
                    + nameComponents[nameComponents.length - 1];
        }
    }

    /**
     * Get the last item of the List.
     *
     * @param list  List to get the Last Item
     * @param <T>   List content Type
     * @return      Extracted last Item
     */
    public static <T> T getLastItem(List<T> list) {
        return list.get(list.size() - 1);
    }

    /**
     * Check whether the source is a test source.
     *
     * @param relativeFilePath  source path relative to the package
     * @return {@link Boolean}  Whether a test source or not
     */
    public static boolean isTestSource(String relativeFilePath) {
        return relativeFilePath.startsWith("tests" + FILE_SEPARATOR);
    }

    /**
     * Get the Source's owner BLang package, this can be either the parent package or the testable BLang package.
     *
     * @param relativePath Relative source path
     * @param parentPkg    parent package
     * @return {@link BLangPackage} Resolved BLangPackage
     */
    public static BLangPackage getSourceOwnerBLangPackage(String relativePath, BLangPackage parentPkg) {
        return isTestSource(relativePath) ? parentPkg.getTestablePkg() : parentPkg;
    }

    /**
     * Get the string values list of forced consumed tokens, from the LSContext.
     *
     * @param ctx               Language Server context
     * @return {@link List}     Token string list
     */
    public static List<String> getPoppedTokenStrings(LSContext ctx) {
        return ctx.get(CompletionKeys.FORCE_CONSUMED_TOKENS_KEY).stream().map(Token::getText)
                .collect(Collectors.toList());
    }

    static void populateIterableAndBuiltinFunctions(SymbolInfo variable, List<SymbolInfo> symbolInfoList,
            LSContext context) {
        BType bType = variable.getScopeEntry().symbol.getType();
        if (iterableType(bType)) {
            SymbolInfo itrForEach = getIterableOpSymbolInfo(Snippet.ITR_FOREACH.get(), bType,
                    ItemResolverConstants.ITR_FOREACH_LABEL, context);
            SymbolInfo itrMap = getIterableOpSymbolInfo(Snippet.ITR_MAP.get(), bType,
                    ItemResolverConstants.ITR_MAP_LABEL, context);
            SymbolInfo itrFilter = getIterableOpSymbolInfo(Snippet.ITR_FILTER.get(), bType,
                    ItemResolverConstants.ITR_FILTER_LABEL, context);
            SymbolInfo itrCount = getIterableOpSymbolInfo(Snippet.ITR_COUNT.get(), bType,
                    ItemResolverConstants.ITR_COUNT_LABEL, context);
            symbolInfoList.addAll(Arrays.asList(itrForEach, itrMap, itrFilter, itrCount));

            if (bType.tag == TypeTags.TABLE) {
                SymbolInfo itrSelect = getIterableOpSymbolInfo(Snippet.ITR_SELECT.get(), bType,
                        ItemResolverConstants.ITR_SELECT_LABEL, context);
                symbolInfoList.add(itrSelect);
            }

            if (aggregateFunctionsAllowed(bType)) {
                SymbolInfo itrMin = getIterableOpSymbolInfo(Snippet.ITR_MIN.get(), bType,
                        ItemResolverConstants.ITR_MIN_LABEL, context);
                SymbolInfo itrMax = getIterableOpSymbolInfo(Snippet.ITR_MAX.get(), bType,
                        ItemResolverConstants.ITR_MAX_LABEL, context);
                SymbolInfo itrAvg = getIterableOpSymbolInfo(Snippet.ITR_AVERAGE.get(), bType,
                        ItemResolverConstants.ITR_AVERAGE_LABEL, context);
                SymbolInfo itrSum = getIterableOpSymbolInfo(Snippet.ITR_SUM.get(), bType,
                        ItemResolverConstants.ITR_SUM_LABEL, context);
                symbolInfoList.addAll(Arrays.asList(itrMin, itrMax, itrAvg, itrSum));
            }

            // TODO: Add support for Table and Tuple collection
        }

        if (builtinLengthFunctionAllowed(bType)) {
            // For the iterable types, add the length builtin function
            SymbolInfo lengthSymbolInfo = getIterableOpSymbolInfo(Snippet.BUILTIN_LENGTH.get(), bType,
                    ItemResolverConstants.BUILTIN_LENGTH_LABEL, context);
            symbolInfoList.add(lengthSymbolInfo);
        }

        if (builtinFreezeFunctionAllowed(context, bType)) {
            // For the any data value type, add the freeze, isFrozen builtin function
            SymbolInfo freeze = getIterableOpSymbolInfo(Snippet.BUILTIN_FREEZE.get(), bType,
                    ItemResolverConstants.BUILTIN_FREEZE_LABEL, context);
            SymbolInfo isFrozen = getIterableOpSymbolInfo(Snippet.BUILTIN_IS_FROZEN.get(), bType,
                    ItemResolverConstants.BUILTIN_IS_FROZEN_LABEL, context);
            symbolInfoList.addAll(Arrays.asList(freeze, isFrozen));
        }

        if (isAnyData(context, bType)) {
            // For the any data value type, add the stamp,clone,create builtin functions
            SymbolInfo stamp = getIterableOpSymbolInfo(Snippet.BUILTIN_STAMP.get(), bType,
                    ItemResolverConstants.BUILTIN_STAMP_LABEL, context);
            SymbolInfo clone = getIterableOpSymbolInfo(Snippet.BUILTIN_CLONE.get(), bType,
                    ItemResolverConstants.BUILTIN_CLONE_LABEL, context);
            SymbolInfo convert = getIterableOpSymbolInfo(Snippet.BUILTIN_CONVERT.get(), bType,
                    ItemResolverConstants.BUILTIN_CONVERT_LABEL, context);
            symbolInfoList.addAll(Arrays.asList(stamp, clone, convert));
        }

        // Populate the Builtin Functions
        if (bType.tag == TypeTags.FLOAT) {
            SymbolInfo isNaN = getIterableOpSymbolInfo(Snippet.BUILTIN_IS_NAN.get(), bType,
                    ItemResolverConstants.BUILTIN_IS_NAN_LABEL, context);
            SymbolInfo isFinite = getIterableOpSymbolInfo(Snippet.BUILTIN_IS_FINITE.get(), bType,
                    ItemResolverConstants.BUILTIN_IS_FINITE_LABEL, context);
            SymbolInfo isInfinite = getIterableOpSymbolInfo(Snippet.BUILTIN_IS_INFINITE.get(), bType,
                    ItemResolverConstants.BUILTIN_IS_INFINITE_LABEL, context);
            symbolInfoList.addAll(Arrays.asList(isNaN, isFinite, isInfinite));
        }

        if (bType.tag == TypeTags.ERROR) {
            SymbolInfo detail = getIterableOpSymbolInfo(Snippet.BUILTIN_DETAIL.get(), bType,
                    ItemResolverConstants.BUILTIN_DETAIL_LABEL, context);
            SymbolInfo reason = getIterableOpSymbolInfo(Snippet.BUILTIN_REASON.get(), bType,
                    ItemResolverConstants.BUILTIN_REASON_LABEL, context);
            symbolInfoList.addAll(Arrays.asList(detail, reason));
        }
    }

    /**
     * Check whether the symbol is a valid invokable symbol.
     *
     * @param symbol Symbol to be evaluated
     * @return {@link Boolean}  valid status
     */
    public static boolean isValidInvokableSymbol(BSymbol symbol) {
        if (!(symbol instanceof BInvokableSymbol)) {
            return false;
        }

        BInvokableSymbol bInvokableSymbol = (BInvokableSymbol) symbol;
        return ((bInvokableSymbol.kind == null && (SymbolKind.RECORD.equals(bInvokableSymbol.owner.kind)
                || SymbolKind.FUNCTION.equals(bInvokableSymbol.owner.kind)))
                || SymbolKind.FUNCTION.equals(bInvokableSymbol.kind))
                && (!(bInvokableSymbol.name.value.endsWith(BLangConstants.INIT_FUNCTION_SUFFIX)
                        || bInvokableSymbol.name.value.endsWith(BLangConstants.START_FUNCTION_SUFFIX)
                        || bInvokableSymbol.name.value.endsWith(BLangConstants.STOP_FUNCTION_SUFFIX)));
    }

    /**
     * Get the current file's imports.
     *
     * @param pkg               BLangPackage to extract content from
     * @param ctx               LS Operation Context
     * @return {@link List}     List of imports in the current file
     */
    public static List<BLangImportPackage> getCurrentFileImports(BLangPackage pkg, LSContext ctx) {
        String currentFile = ctx.get(DocumentServiceKeys.RELATIVE_FILE_PATH_KEY);
        return getCurrentFileTopLevelNodes(pkg, ctx).stream()
                .filter(topLevelNode -> topLevelNode instanceof BLangImportPackage)
                .map(topLevelNode -> (BLangImportPackage) topLevelNode)
                .filter(bLangImportPackage -> bLangImportPackage.pos.getSource().cUnitName
                        .replace("/", FILE_SEPARATOR).equals(currentFile)
                        && !(bLangImportPackage.getOrgName().getValue().equals("ballerina")
                                && getPackageNameComponentsCombined(bLangImportPackage).equals("transaction")))
                .collect(Collectors.toList());
    }

    public static boolean isInvalidSymbol(BSymbol symbol) {
        return ("_".equals(symbol.name.getValue()) || "runtime".equals(symbol.getName().getValue())
                || "transactions".equals(symbol.getName().getValue()) || symbol instanceof BAnnotationSymbol
                || symbol instanceof BServiceSymbol || symbol instanceof BOperatorSymbol
                || symbolContainsInvalidChars(symbol));
    }

    /**
     * Check whether the given node is a worker derivative node.
     *
     * @param node              Node to be evaluated
     * @return {@link Boolean}  whether a worker derivative
     */
    public static boolean isWorkerDereivative(BLangNode node) {
        return (node instanceof BLangSimpleVariableDef) && ((BLangSimpleVariableDef) node).var.expr != null
                && ((BLangSimpleVariableDef) node).var.expr.type instanceof BFutureType
                && ((BFutureType) ((BLangSimpleVariableDef) node).var.expr.type).workerDerivative;
    }

    public static boolean isWithinWorkerDeclaration(BLangNode bLangNode) {
        return bLangNode instanceof BLangBlockStmt && bLangNode.parent instanceof BLangLambdaFunction
                && ((BLangLambdaFunction) bLangNode.parent).function.flagSet.contains(Flag.WORKER);
    }

    /**
     * Get the TopLevel nodes of the current file.
     *
     * @param pkgNode           Current Package node
     * @param ctx               Service Operation context
     * @return {@link List}     List of Top Level Nodes
     */
    public static List<TopLevelNode> getCurrentFileTopLevelNodes(BLangPackage pkgNode, LSContext ctx) {
        String relativeFilePath = ctx.get(DocumentServiceKeys.RELATIVE_FILE_PATH_KEY);
        BLangCompilationUnit filteredCUnit = pkgNode.compUnits.stream()
                .filter(cUnit -> cUnit.getPosition().getSource().cUnitName.replace("/", FILE_SEPARATOR)
                        .equals(relativeFilePath))
                .findAny().orElse(null);
        List<TopLevelNode> topLevelNodes = filteredCUnit == null ? new ArrayList<>()
                : new ArrayList<>(filteredCUnit.getTopLevelNodes());

        // Filter out the lambda functions from the top level nodes
        return topLevelNodes.stream()
                .filter(topLevelNode -> !(topLevelNode instanceof BLangFunction
                        && ((BLangFunction) topLevelNode).flagSet.contains(Flag.LAMBDA))
                        && !(topLevelNode instanceof BLangSimpleVariable
                                && ((BLangSimpleVariable) topLevelNode).flagSet.contains(Flag.SERVICE)))
                .collect(Collectors.toList());
    }

    /**
     * Get the package name components combined.
     * 
     * @param importPackage     BLangImportPackage node
     * @return {@link String}   Combined package name
     */
    public static String getPackageNameComponentsCombined(BLangImportPackage importPackage) {
        return String.join(".",
                importPackage.pkgNameComps.stream().map(id -> id.value).collect(Collectors.toList()));
    }

    private static SymbolInfo getIterableOpSymbolInfo(SnippetBlock operation, @Nullable BType bType, String label,
            LSContext context) {
        boolean isSnippet = context.get(CompletionKeys.CLIENT_CAPABILITIES_KEY).getCompletionItem()
                .getSnippetSupport();
        String signature = "";
        SymbolInfo.CustomOperationSignature customOpSignature;
        SymbolInfo iterableOperation = new SymbolInfo();
        switch (operation.getLabel()) {
        case ItemResolverConstants.ITR_FOREACH_LABEL: {
            String params = getIterableOpLambdaParam(bType, context);
            signature = operation.getString(isSnippet).replace(UtilSymbolKeys.ITR_OP_LAMBDA_PARAM_REPLACE_TOKEN,
                    params);
            break;
        }
        case ItemResolverConstants.ITR_MAP_LABEL: {
            String params = getIterableOpLambdaParam(bType, context);
            signature = operation.getString(isSnippet).replace(UtilSymbolKeys.ITR_OP_LAMBDA_PARAM_REPLACE_TOKEN,
                    params);
            break;
        }
        case ItemResolverConstants.ITR_FILTER_LABEL: {
            String params = getIterableOpLambdaParam(bType, context);
            signature = operation.getString(isSnippet).replace(UtilSymbolKeys.ITR_OP_LAMBDA_PARAM_REPLACE_TOKEN,
                    params);
            break;
        }
        default: {
            signature = operation.getString(isSnippet);
            break;
        }
        }

        customOpSignature = new SymbolInfo.CustomOperationSignature(label, signature);
        iterableOperation.setCustomOperation(true);
        iterableOperation.setCustomOperationSignature(customOpSignature);
        return iterableOperation;
    }

    private static String getIterableOpLambdaParam(BType bType, LSContext context) {
        String params = "";
        boolean isSnippet = context.get(CompletionKeys.CLIENT_CAPABILITIES_KEY).getCompletionItem()
                .getSnippetSupport();
        PackageID currentPkgId = context.get(DocumentServiceKeys.CURRENT_PACKAGE_ID_KEY);
        if (bType instanceof BMapType) {
            BMapType bMapType = (BMapType) bType;
            String valueType = FunctionGenerator.generateTypeDefinition(null, currentPkgId, bMapType.constraint);
            params = Snippet.ITR_ON_MAP_PARAMS.get().getString(isSnippet)
                    .replace(UtilSymbolKeys.ITR_OP_LAMBDA_KEY_REPLACE_TOKEN, "string")
                    .replace(UtilSymbolKeys.ITR_OP_LAMBDA_VALUE_REPLACE_TOKEN, valueType);
        } else if (bType instanceof BArrayType) {
            BArrayType bArrayType = (BArrayType) bType;
            String valueType = FunctionGenerator.generateTypeDefinition(null, currentPkgId, bArrayType.eType);
            params = valueType + " value";
        } else if (bType instanceof BJSONType) {
            params = Snippet.ITR_ON_JSON_PARAMS.get().getString(isSnippet);
        } else if (bType instanceof BXMLType) {
            params = Snippet.ITR_ON_XML_PARAMS.get().getString(isSnippet);
        }

        return params;
    }

    private static boolean iterableType(BType bType) {
        switch (bType.tag) {
        case TypeTags.ARRAY:
        case TypeTags.MAP:
        case TypeTags.JSON:
        case TypeTags.XML:
        case TypeTags.TABLE:
        case TypeTags.INTERMEDIATE_COLLECTION:
            return true;
        }
        return false;
    }

    private static boolean aggregateFunctionsAllowed(BType bType) {
        return bType instanceof BArrayType && (((BArrayType) bType).eType.toString().equals("int")
                || ((BArrayType) bType).eType.toString().equals("float"));
    }

    private static boolean symbolContainsInvalidChars(BSymbol bSymbol) {
        return bSymbol.getName().getValue().contains(UtilSymbolKeys.LT_SYMBOL_KEY)
                || bSymbol.getName().getValue().contains(UtilSymbolKeys.GT_SYMBOL_KEY)
                || bSymbol.getName().getValue().contains(UtilSymbolKeys.DOLLAR_SYMBOL_KEY)
                || bSymbol.getName().getValue().equals("main") || bSymbol.getName().getValue().endsWith(".new");
    }

    private static boolean builtinLengthFunctionAllowed(BType bType) {
        switch (bType.tag) {
        case TypeTags.ARRAY:
        case TypeTags.MAP:
        case TypeTags.JSON:
        case TypeTags.XML:
        case TypeTags.TABLE:
        case TypeTags.TUPLE:
        case TypeTags.RECORD:
            return true;
        }
        return false;
    }

    private static boolean builtinFreezeFunctionAllowed(LSContext context, BType bType) {
        CompilerContext compilerContext = context.get(DocumentServiceKeys.COMPILER_CONTEXT_KEY);
        if (compilerContext != null) {
            Types types = Types.getInstance(compilerContext);
            return types.isLikeAnydataOrNotNil(bType);
        }
        return false;
    }

    private static boolean isAnyData(LSContext context, BType bType) {
        CompilerContext compilerContext = context.get(DocumentServiceKeys.COMPILER_CONTEXT_KEY);
        if (compilerContext != null) {
            Types types = Types.getInstance(compilerContext);
            return types.isAnydata(bType);
        }
        return false;
    }

    ///////////////////////////////
    /////      Predicates     /////
    ///////////////////////////////

    /**
     * Predicate to check for the invalid symbols.
     *
     * @return {@link Predicate}    Predicate for the check
     */
    public static Predicate<SymbolInfo> invalidSymbolsPredicate() {
        return symbolInfo -> !symbolInfo.isCustomOperation() && symbolInfo.getScopeEntry() != null
                && isInvalidSymbol(symbolInfo.getScopeEntry().symbol);
    }

    /**
     * Predicate to check for the invalid type definitions.
     *
     * @return {@link Predicate}    Predicate for the check
     */
    public static Predicate<TopLevelNode> checkInvalidTypesDefs() {
        return topLevelNode -> {
            if (topLevelNode instanceof BLangTypeDefinition) {
                BLangTypeDefinition typeDefinition = (BLangTypeDefinition) topLevelNode;
                return !(typeDefinition.flagSet.contains(Flag.SERVICE)
                        || typeDefinition.flagSet.contains(Flag.RESOURCE));
            }
            return true;
        };
    }

    /**
     * Generate variable code.
     *
     * @param variableName variable name
     * @param variableType variable type
     * @return {@link String}       generated function signature
     */
    public static String createVariableDeclaration(String variableName, String variableType) {
        return variableType + " " + variableName + " = ";
    }

    /**
     * Generates a random name.
     *
     * @param value    index of the argument
     * @param argNames argument set
     * @return random argument name
     */
    public static String generateName(int value, Set<String> argNames) {
        StringBuilder result = new StringBuilder();
        int index = value;
        while (--index >= 0) {
            result.insert(0, (char) ('a' + index % 26));
            index /= 26;
        }
        while (argNames.contains(result.toString())) {
            result = new StringBuilder(generateName(++value, argNames));
        }
        return result.toString();
    }

    public static BLangPackage getPackageNode(BLangNode bLangNode) {
        BLangNode parent = bLangNode.parent;
        if (parent != null) {
            return (parent instanceof BLangPackage) ? (BLangPackage) parent : getPackageNode(parent);
        }
        return null;
    }

    /**
     * Inner class for generating function code.
     */
    public static class FunctionGenerator {

        /**
         * Generate function code.
         *
         * @param name               function name
         * @param args               Function arguments
         * @param returnType         return type
         * @param returnDefaultValue default return value
         * @return {@link String}       generated function signature
         */
        public static String createFunction(String name, String args, String returnType,
                String returnDefaultValue) {
            String funcBody = CommonUtil.LINE_SEPARATOR;
            String funcReturnSignature = "";
            if (returnType != null) {
                funcBody = returnDefaultValue + funcBody;
                funcReturnSignature = " returns " + returnType + " ";
            }
            return CommonUtil.LINE_SEPARATOR + CommonUtil.LINE_SEPARATOR + "function " + name + "(" + args + ")"
                    + funcReturnSignature + "{" + CommonUtil.LINE_SEPARATOR + funcBody + "}"
                    + CommonUtil.LINE_SEPARATOR;
        }

        /**
         * Generate function call.
         *
         * @param name               function name
         * @param args               Function arguments
         * @param returnType         return type
         * @param returnDefaultValue default return value
         * @return {@link String}       generated function signature
         */
        public static String createFunctionCall(String name, String args, String returnType,
                String returnDefaultValue) {
            String funcBody = CommonUtil.LINE_SEPARATOR;
            String funcReturnSignature = "";
            if (returnType != null) {
                funcBody = returnDefaultValue + funcBody;
                funcReturnSignature = " returns " + returnType + " ";
            }
            return CommonUtil.LINE_SEPARATOR + CommonUtil.LINE_SEPARATOR + "function " + name + "(" + args + ")"
                    + funcReturnSignature + "{" + CommonUtil.LINE_SEPARATOR + funcBody + "}"
                    + CommonUtil.LINE_SEPARATOR;
        }

        /**
         * Get the default function return statement.
         *
         * @param importsAcceptor imports acceptor
         * @param currentPkgId    current package id
         * @param bLangNode       BLangNode to evaluate
         * @param template        return statement to modify
         * @return {@link String}   Default return statement
         */
        public static String generateReturnValue(BiConsumer<String, String> importsAcceptor, PackageID currentPkgId,
                BLangNode bLangNode, String template) {
            if (bLangNode.type == null && bLangNode instanceof BLangTupleDestructure) {
                // Check for tuple assignment eg. (int, int)
                List<String> list = new ArrayList<>();
                for (BLangExpression bLangExpression : ((BLangTupleDestructure) bLangNode).varRef.expressions) {
                    if (bLangExpression.type != null) {
                        list.add(generateReturnValue(importsAcceptor, currentPkgId, bLangExpression.type, "{%1}"));
                    }
                }
                return template.replace("{%1}", "(" + String.join(", ", list) + ")");
            } else if (bLangNode instanceof BLangLiteral) {
                return template.replace("{%1}", ((BLangLiteral) bLangNode).getValue().toString());
            } else if (bLangNode instanceof BLangAssignment) {
                return template.replace("{%1}", "0");
            }
            return (bLangNode.type != null)
                    ? generateReturnValue(importsAcceptor, currentPkgId, bLangNode.type, template)
                    : null;
        }

        private static String generateReturnValue(BiConsumer<String, String> importsAcceptor,
                PackageID currentPkgId, BType bType, String template) {
            if (bType.tsymbol == null && bType instanceof BArrayType) {
                return template.replace("{%1}",
                        "[" + generateReturnValue(((BArrayType) bType).eType.tsymbol, "") + "]");
            } else if (bType instanceof BFiniteType) {
                // Check for finite set assignment
                BFiniteType bFiniteType = (BFiniteType) bType;
                Set<BLangExpression> valueSpace = bFiniteType.valueSpace;
                if (!valueSpace.isEmpty()) {
                    return generateReturnValue(importsAcceptor, currentPkgId, valueSpace.stream().findFirst().get(),
                            template);
                }
            } else if (bType instanceof BMapType && ((BMapType) bType).constraint != null) {
                // Check for constrained map assignment eg. map<Student>
                BType constraintType = ((BMapType) bType).constraint;
                String mapDef = "{key: "
                        + generateReturnValue(importsAcceptor, currentPkgId, constraintType, "{%1}") + "}";
                return template.replace("{%1}", mapDef);
            } else if (bType instanceof BUnionType) {
                BUnionType bUnionType = (BUnionType) bType;
                Set<BType> memberTypes = bUnionType.getMemberTypes();
                if (memberTypes.size() == 2
                        && memberTypes.stream().anyMatch(bType1 -> bType1 instanceof BNilType)) {
                    Optional<BType> type = memberTypes.stream().filter(bType1 -> !(bType1 instanceof BNilType))
                            .findFirst();
                    if (type.isPresent()) {
                        return generateReturnValue(importsAcceptor, currentPkgId, type.get(), "{%1}?");
                    }
                }
                if (!memberTypes.isEmpty()) {
                    BType firstBType = memberTypes.stream().findFirst().get();
                    return generateReturnValue(importsAcceptor, currentPkgId, firstBType, template);
                }
            } else if (bType instanceof BTupleType) {
                BTupleType bTupleType = (BTupleType) bType;
                List<BType> tupleTypes = bTupleType.tupleTypes;
                List<String> list = new ArrayList<>();
                for (BType type : tupleTypes) {
                    list.add(generateReturnValue(importsAcceptor, currentPkgId, type, "{%1}"));
                }
                return template.replace("{%1}", "(" + String.join(", ", list) + ")");
            } else if (bType instanceof BObjectType && ((BObjectType) bType).tsymbol instanceof BObjectTypeSymbol) {
                BObjectTypeSymbol bStruct = (BObjectTypeSymbol) ((BObjectType) bType).tsymbol;
                List<String> list = new ArrayList<>();
                for (BVarSymbol param : bStruct.initializerFunc.symbol.params) {
                    list.add(generateReturnValue(param.type.tsymbol, "{%1}"));
                }
                String pkgPrefix = getPackagePrefix(importsAcceptor, currentPkgId, bStruct.pkgID);
                String paramsStr = String.join(", ", list);
                String newObjStr = "new " + pkgPrefix + bStruct.name.getValue() + "(" + paramsStr + ")";
                return template.replace("{%1}", newObjStr);
            }
            return (bType.tsymbol != null) ? generateReturnValue(bType.tsymbol, template)
                    : template.replace("{%1}", "()");
        }

        private static String generateReturnValue(BTypeSymbol tSymbol, String template) {
            String result;
            switch (tSymbol.name.getValue()) {
            case "int":
            case "any":
                result = "0";
                break;
            case "string":
                result = "\"\"";
                break;
            case "float":
                result = "0.0";
                break;
            case "json":
                result = "{}";
                break;
            case "map":
                result = "<map>{}";
                break;
            case "boolean":
                result = "false";
                break;
            case "xml":
                result = "xml ` `";
                break;
            case "byte":
                result = "0";
                break;
            default:
                result = "()";
                break;
            }
            return template.replace("{%1}", result);
        }

        /**
         * Returns signature of the return type.
         *
         * @param importsAcceptor imports acceptor
         * @param currentPkgId    current package id
         * @param bLangNode       {@link BLangNode}
         * @return return type signature
         */
        public static String generateTypeDefinition(BiConsumer<String, String> importsAcceptor,
                PackageID currentPkgId, BLangNode bLangNode) {
            if (bLangNode.type == null && bLangNode instanceof BLangTupleDestructure) {
                // Check for tuple assignment eg. (int, int)
                List<String> list = new ArrayList<>();
                for (BLangExpression bLangExpression : ((BLangTupleDestructure) bLangNode).varRef.expressions) {
                    if (bLangExpression.type != null) {
                        list.add(generateTypeDefinition(importsAcceptor, currentPkgId, bLangExpression.type));
                    }
                }
                return "(" + String.join(", ", list) + ")";
            } else if (bLangNode instanceof BLangAssignment) {
                if (((BLangAssignment) bLangNode).declaredWithVar) {
                    return "any";
                }
            } else if (bLangNode instanceof BLangFunctionTypeNode) {
                BLangFunctionTypeNode funcType = (BLangFunctionTypeNode) bLangNode;
                TestFunctionGenerator generator = new TestFunctionGenerator(importsAcceptor, currentPkgId,
                        funcType);
                String[] typeSpace = generator.getTypeSpace();
                String[] nameSpace = generator.getNamesSpace();
                StringJoiner params = new StringJoiner(", ");
                IntStream.range(0, typeSpace.length - 1).forEach(index -> {
                    String type = typeSpace[index];
                    String name = nameSpace[index];
                    params.add(type + " " + name);
                });
                return "function (" + params.toString() + ") returns (" + typeSpace[typeSpace.length - 1] + ")";
            }
            return (bLangNode.type != null) ? generateTypeDefinition(importsAcceptor, currentPkgId, bLangNode.type)
                    : null;
        }

        /**
         * Returns signature of the return type.
         *
         * @param importsAcceptor imports acceptor
         * @param currentPkgId    current package id
         * @param bType           {@link BType}
         * @return return type signature
         */
        public static String generateTypeDefinition(BiConsumer<String, String> importsAcceptor,
                PackageID currentPkgId, BType bType) {
            if ((bType.tsymbol == null || bType.tsymbol.name.value.isEmpty()) && bType instanceof BArrayType) {
                // Check for array assignment eg.  int[]
                return generateTypeDefinition(importsAcceptor, currentPkgId, ((BArrayType) bType).eType.tsymbol)
                        + "[]";
            } else if (bType instanceof BMapType && ((BMapType) bType).constraint != null) {
                // Check for constrained map assignment eg. map<Student>
                BTypeSymbol tSymbol = ((BMapType) bType).constraint.tsymbol;
                if (tSymbol != null) {
                    String constraint = generateTypeDefinition(importsAcceptor, currentPkgId, tSymbol);
                    return ("any".equals(constraint)) ? "map" : "map<" + constraint + ">";
                }
            } else if (bType instanceof BUnionType) {
                // Check for union type assignment eg. int | string
                List<String> list = new ArrayList<>();
                Set<BType> memberTypes = ((BUnionType) bType).getMemberTypes();
                if (memberTypes.size() == 2
                        && memberTypes.stream().anyMatch(bType1 -> bType1 instanceof BNilType)) {
                    Optional<BType> type = memberTypes.stream().filter(bType1 -> !(bType1 instanceof BNilType))
                            .findFirst();
                    if (type.isPresent()) {
                        return generateTypeDefinition(importsAcceptor, currentPkgId, type.get()) + "?";
                    }
                }
                for (BType memberType : memberTypes) {
                    list.add(generateTypeDefinition(importsAcceptor, currentPkgId, memberType));
                }
                return "(" + String.join("|", list) + ")";
            } else if (bType instanceof BTupleType) {
                // Check for tuple type assignment eg. int, string
                List<String> list = new ArrayList<>();
                for (BType memberType : ((BTupleType) bType).tupleTypes) {
                    list.add(generateTypeDefinition(importsAcceptor, currentPkgId, memberType));
                }
                return "(" + String.join(", ", list) + ")";
            } else if (bType instanceof BNilType) {
                return "()";
            } else if (bType instanceof BIntermediateCollectionType) {
                // TODO: 29/11/2018 fix this. A hack to infer type definition
                // We assume;
                // 1. Tuple of <key(string), value(string)> as a map(though it can be a record as well)
                // 2. Tuple of <index(int), value(string)> as an array
                BIntermediateCollectionType collectionType = (BIntermediateCollectionType) bType;
                List<String> list = new ArrayList<>();
                List<BType> tupleTypes = collectionType.tupleType.tupleTypes;
                if (tupleTypes.size() == 2) {
                    BType leftType = tupleTypes.get(0);
                    BType rightType = tupleTypes.get(1);
                    switch (leftType.tsymbol.name.value) {
                    case "int":
                        return generateTypeDefinition(importsAcceptor, currentPkgId, rightType) + "[]";
                    case "string":
                    default:
                        return "map<" + generateTypeDefinition(importsAcceptor, currentPkgId, rightType) + ">";
                    }
                }
                for (BType memberType : tupleTypes) {
                    list.add(generateTypeDefinition(importsAcceptor, currentPkgId, memberType));
                }
                return "(" + String.join(", ", list) + ")[]";
            }
            return (bType.tsymbol != null) ? generateTypeDefinition(importsAcceptor, currentPkgId, bType.tsymbol)
                    : "any";
        }

        private static String generateTypeDefinition(BiConsumer<String, String> importsAcceptor,
                PackageID currentPkgId, BTypeSymbol tSymbol) {
            if (tSymbol != null) {
                String pkgPrefix = getPackagePrefix(importsAcceptor, currentPkgId, tSymbol.pkgID);
                return pkgPrefix + tSymbol.name.getValue();
            }
            return "any";
        }

        public static List<String> getFuncArguments(BiConsumer<String, String> importsAcceptor,
                PackageID currentPkgId, BLangNode parent) {
            List<String> list = new ArrayList<>();
            if (parent instanceof BLangInvocation) {
                BLangInvocation bLangInvocation = (BLangInvocation) parent;
                if (bLangInvocation.argExprs.isEmpty()) {
                    return null;
                }
                int argCounter = 1;
                Set<String> argNames = new HashSet<>();
                for (BLangExpression bLangExpression : bLangInvocation.argExprs) {
                    if (bLangExpression instanceof BLangSimpleVarRef) {
                        BLangSimpleVarRef simpleVarRef = (BLangSimpleVarRef) bLangExpression;
                        String varName = simpleVarRef.variableName.value;
                        String argType = lookupVariableReturnType(importsAcceptor, currentPkgId, varName, parent);
                        list.add(argType + " " + varName);
                        argNames.add(varName);
                    } else if (bLangExpression instanceof BLangInvocation) {
                        BLangInvocation invocation = (BLangInvocation) bLangExpression;
                        String functionName = invocation.name.value;
                        String argType = lookupFunctionReturnType(functionName, parent);
                        String argName = generateName(argCounter++, argNames);
                        list.add(argType + " " + argName);
                        argNames.add(argName);
                    } else {
                        String argName = generateName(argCounter++, argNames);
                        list.add("any " + argName);
                        argNames.add(argName);
                    }
                }
            }
            return (!list.isEmpty()) ? list : null;
        }

        public static List<String> getFuncArguments(BInvokableSymbol bInvokableSymbol) {
            List<String> list = new ArrayList<>();
            if (bInvokableSymbol.type instanceof BInvokableType) {
                BInvokableType bInvokableType = (BInvokableType) bInvokableSymbol.type;
                if (bInvokableType.paramTypes.isEmpty()) {
                    return list;
                }
                int argCounter = 1;
                Set<String> argNames = new HashSet<>();
                for (BType bType : bInvokableType.getParameterTypes()) {
                    String argName = generateName(argCounter++, argNames);
                    String argType = generateTypeDefinition(null, bInvokableSymbol.pkgID, bType);
                    list.add(argType + " " + argName);
                    argNames.add(argName);
                }
            }
            return (!list.isEmpty()) ? list : new ArrayList<>();
        }

        private static String lookupVariableReturnType(BiConsumer<String, String> importsAcceptor,
                PackageID currentPkgId, String variableName, BLangNode parent) {
            if (parent instanceof BLangBlockStmt) {
                BLangBlockStmt blockStmt = (BLangBlockStmt) parent;
                Scope scope = blockStmt.scope;
                if (scope != null) {
                    for (Map.Entry<Name, Scope.ScopeEntry> entry : scope.entries.entrySet()) {
                        String key = entry.getKey().getValue();
                        BSymbol symbol = entry.getValue().symbol;
                        if (variableName.equals(key) && symbol instanceof BVarSymbol) {
                            return generateTypeDefinition(importsAcceptor, currentPkgId, symbol.type);
                        }
                    }
                }
            }
            return (parent != null && parent.parent != null)
                    ? lookupVariableReturnType(importsAcceptor, currentPkgId, variableName, parent.parent)
                    : "any";
        }

        private static String lookupFunctionReturnType(String functionName, BLangNode parent) {
            if (parent instanceof BLangPackage) {
                BLangPackage blockStmt = (BLangPackage) parent;
                List<BLangFunction> functions = blockStmt.functions;
                for (BLangFunction function : functions) {
                    if (functionName.equals(function.name.getValue())) {
                        return generateTypeDefinition(null, ((BLangPackage) parent).packageID,
                                function.returnTypeNode);
                    }
                }
            }
            return (parent != null && parent.parent != null) ? lookupFunctionReturnType(functionName, parent.parent)
                    : "any";
        }
    }

    public static String getPackagePrefix(BiConsumer<String, String> importsAcceptor, PackageID currentPkgId,
            PackageID typePkgId) {
        String pkgPrefix = "";
        if (!typePkgId.equals(currentPkgId)
                && !(typePkgId.orgName.value.equals("ballerina") && typePkgId.name.value.equals("builtin"))) {
            pkgPrefix = typePkgId.name.value + ":";
            if (importsAcceptor != null) {
                importsAcceptor.accept(typePkgId.orgName.value, typePkgId.name.value);
            }
        }
        return pkgPrefix;
    }

    private static String getFiniteAndUnionTypesComment(BType bType) {
        if (bType instanceof BFiniteType) {
            List<BLangExpression> valueSpace = new ArrayList<>(((BFiniteType) bType).valueSpace);
            return " // Values allowed: "
                    + valueSpace.stream().map(Object::toString).collect(Collectors.joining("|"));
        } else if (bType instanceof BUnionType) {
            List<BType> memberTypes = new ArrayList<>(((BUnionType) bType).getMemberTypes());
            return " // Values allowed: "
                    + memberTypes.stream().map(BType::toString).collect(Collectors.joining("|"));
        }

        return "";
    }

    /**
     * Node comparator to compare the nodes by position.                            
     */
    public static class BLangNodeComparator implements Comparator<BLangNode> {
        /**
         * {@inheritDoc}
         */
        @Override
        public int compare(BLangNode node1, BLangNode node2) {
            return node1.getPosition().getStartLine() - node2.getPosition().getStartLine();
        }
    }
}