All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ai.vespa.schemals.schemadocument.YQLDocument Maven / Gradle / Ivy

There is a newer version: 8.441.21
Show newest version
package ai.vespa.schemals.schemadocument;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;

import ai.vespa.schemals.SchemaDiagnosticsHandler;
import ai.vespa.schemals.common.ClientLogger;
import ai.vespa.schemals.common.StringUtils;
import ai.vespa.schemals.context.ParseContext;
import ai.vespa.schemals.index.SchemaIndex;
import ai.vespa.schemals.parser.yqlplus.ParseException;
import ai.vespa.schemals.parser.yqlplus.YQLPlusParser;
import ai.vespa.schemals.schemadocument.parser.Identifier;
import ai.vespa.schemals.tree.CSTUtils;
import ai.vespa.schemals.tree.Node;
import ai.vespa.schemals.tree.SchemaNode;
import ai.vespa.schemals.tree.YQLNode;
import ai.vespa.schemals.tree.YQL.YQLUtils;

public class YQLDocument implements DocumentManager {

    public static class ParseResult {
        public List diagnostics;
        public Optional CST;

        ParseResult(List diagnostics, Optional CST) {
            this.diagnostics = diagnostics;
            this.CST = CST;
        }

        public List diagnostics() { return this.diagnostics; }
        public Optional CST() { return this.CST; }
    }

    static class YQLPartParseResult extends ParseResult {
        public int charsRead;

        YQLPartParseResult(List diagnostics, Optional CST, int charsRead) {
            super(diagnostics, CST);
            this.charsRead = charsRead;
        }

        public int charsRead() { return this.charsRead; }
    }

    boolean isOpen = false;
    String fileURI;
    String fileContent = "";

    ClientLogger logger;
    private SchemaDiagnosticsHandler diagnosticsHandler;
    private SchemaIndex schemaIndex;
    private SchemaDocumentScheduler scheduler;

    private YQLNode CST;

    YQLDocument(ClientLogger logger, SchemaDiagnosticsHandler diagnosticsHandler, SchemaIndex schemaIndex, SchemaDocumentScheduler scheduler, String fileURI) {
        this.fileURI = fileURI;
        this.logger = logger;
        this.diagnosticsHandler = diagnosticsHandler;
        this.schemaIndex = schemaIndex;
        this.scheduler = scheduler;
    }

    @Override
    public ParseContext getParseContext() {
        ParseContext context = new ParseContext(fileContent, logger, fileURI, schemaIndex, scheduler);
        context.useVespaGroupingIdentifiers();
        return context;
    }

    @Override
    public void updateFileContent(String content) {
        fileContent = content;

        ParseContext context = getParseContext();

        ParseResult parseResult = parseContent(context);

        diagnosticsHandler.publishDiagnostics(fileURI, parseResult.diagnostics());

        if (parseResult.CST.isPresent()) {
            CST = parseResult.CST.get();
        }
    }

    @Override
    public void updateFileContent(String content, Integer version) {
        updateFileContent(content);
    }

    private static YQLPartParseResult parseYQLPart(CharSequence content, ClientLogger logger, Position offset) {
        // CharSequence charSequence = content.toLowerCase();
        YQLPlusParser parser = new YQLPlusParser(content);

        try {
            parser.statement();
        } catch (ParseException exception) {
            // Ignored, marked by dirty node
        }

        int charsRead = parser.getToken(0).getEndOffset();

        ai.vespa.schemals.parser.yqlplus.Node node = parser.rootNode();
        YQLNode retNode = new YQLNode(node, offset);
        // YQLUtils.printTree(logger, node);

        return new YQLPartParseResult(List.of(), Optional.of(retNode), charsRead);
    }

    private static YQLPartParseResult parseYQLQuery(ParseContext context, String queryString, Position offset) {
        YQLNode ret = new YQLNode(new Range(offset, offset));

        int pipeIndex = queryString.indexOf('|');
        String YQLString = pipeIndex == -1 ? queryString : queryString.substring(0, pipeIndex);
        YQLPartParseResult YQLResult = parseYQLPart(YQLString, context.logger(), offset);

        if (YQLResult.CST.isEmpty()) return YQLResult;

        ret.addChild(YQLResult.CST.get());

        ArrayList diagnostics = new ArrayList<>(YQLResult.diagnostics());

        int charsRead = YQLResult.charsRead();

        if (pipeIndex != -1 && pipeIndex + 1 < queryString.length()) {
            String charsBeforePipe = queryString.substring(charsRead, pipeIndex);
            if (charsBeforePipe.strip().length() == 0) {
                String groupingString = queryString.substring(pipeIndex + 1); // Do not include pipe char
                Position YQLStringPosition = StringUtils.getStringPosition(YQLString);
                Position groupOffsetWithoutPipe = CSTUtils.addPositions(offset, YQLStringPosition);

                Position groupOffset = CSTUtils.addPositions(groupOffsetWithoutPipe, new Position(0, 1)); // Add pipe char

                ret.addChild(new YQLNode(new Range(groupOffsetWithoutPipe, groupOffset), "|"));
    
                YQLPartParseResult groupingResult = VespaGroupingParser.parseVespaGrouping(groupingString, context.logger(), groupOffset);
                if (groupingResult.CST.isPresent()) {
                    ret.addChild(groupingResult.CST.get());
                }
    
                diagnostics.addAll(groupingResult.diagnostics());
                charsRead += 1 + groupingResult.charsRead(); // Add one for the pipe symbol
            }

        }

        Range newRange = StringUtils.getStringRange(queryString.substring(0, charsRead));
        ret.setRange(CSTUtils.addPositionToRange(offset, newRange));

        traverseCST(ret, context, diagnostics);

        return new YQLPartParseResult(diagnostics, Optional.of(ret), charsRead);
    }

    public static ParseResult parseContent(ParseContext context) {
        String content = context.content();
        YQLNode ret = new YQLNode(StringUtils.getStringRange(content));
        ArrayList diagnostics = new ArrayList<>();

        int charsRead = 0;
        int linesRead = 0;

        while (charsRead < content.length()) {

            String toParser = content.substring(charsRead);
            if (toParser.strip().length() == 0) {
                break;
            }

            YQLPartParseResult result = parseYQLQuery(context, toParser, new Position(linesRead, 0));
            diagnostics.addAll(result.diagnostics());
    
            if (result.CST().isPresent()) {
                ret.addChild(result.CST().get());
            }
            
            int newOffset = content.indexOf('\n', charsRead + result.charsRead());
            if (newOffset == -1) {
                newOffset = content.length();
            }
            String substr = content.substring(charsRead, newOffset);
            linesRead += StringUtils.countNewLines(substr);
            charsRead = newOffset;
        }

        // YQLUtils.printTree(context.logger(), ret);

        return new ParseResult(diagnostics, Optional.of(ret));
    }

    private static void traverseCST(YQLNode node, ParseContext context, ArrayList diagnostics) {

        for (Identifier identifier : context.YQLIdentifiers()) {
            diagnostics.addAll(identifier.identify(node));
        }

        for (Node child : node) {
            traverseCST(child.getYQLNode(), context, diagnostics);
        }
    }

    @Override
    public void reparseContent() {
        if (this.fileContent != null) {
            this.updateFileContent(this.fileContent);
        }
    }

    @Override
    public void setIsOpen(boolean isOpen) {
        this.isOpen = isOpen;
    }

    @Override
    public boolean getIsOpen() { return isOpen; }

    @Override
    public SchemaNode getRootNode() {
        return null;
    }

    @Override
    public YQLNode getRootYQLNode() {
        return CST;
    }

    @Override
    public SchemaDocumentLexer lexer() {
        throw new UnsupportedOperationException("Getting the lexer from a YQLDocument is not implemented");
    }

    @Override
    public String getFileURI() {
        return fileURI;
    }

    @Override
    public String getCurrentContent() {
        return fileContent;
    }

    @Override
    public VersionedTextDocumentIdentifier getVersionedTextDocumentIdentifier() {
        return new VersionedTextDocumentIdentifier(fileURI, 0);
    }

    @Override
    public DocumentType getDocumentType() { return DocumentType.YQL; }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy