/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF 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.cyclop.service.completion.intern.parser; import java.util.List; import java.util.Optional; import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.inject.Named; import org.cyclop.model.ContextCqlCompletion; import org.cyclop.model.CqlCompletion; import org.cyclop.model.CqlQuery; import org.cyclop.model.CqlQueryType; import org.cyclop.model.exception.ServiceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * LL(1) like cql parser * * @author Maciej Miklas */ @Named public class CqlParser { private final static Logger LOG = LoggerFactory.getLogger(CqlParser.class); @Inject private List<DecisionListSupport> decisionListSupportDef; private CqlCompletion initialCqlCompletion = null; @PostConstruct public void init() { CqlCompletion.Builder cb = CqlCompletion.Builder.naturalOrder(); decisionListSupportDef.forEach(cf -> cb.all(cf.beginnsWith())); initialCqlCompletion = cb.build(); LOG.debug("Initial completion {}", initialCqlCompletion); } private Optional<DecisionListSupport> findCompletionDecision(CqlQuery query) { Optional<DecisionListSupport> found = decisionListSupportDef.stream().filter(d -> d.supports(query)) .findFirst(); LOG.debug("Found Decision List for query {} -> {}", query, found); return found; } public CqlCompletion findInitialCompletion() { return initialCqlCompletion; } public Optional<ContextCqlCompletion> findCompletion(CqlQuery cqlQuery, int cursorPosition) { LOG.debug("Find completion for {} on {}", cqlQuery, cursorPosition); if (cursorPosition == -1) { cursorPosition = cqlQuery.part.length() - 1; } cursorPosition++; Optional<DecisionListSupport> dlsOp = findCompletionDecision(cqlQuery); if (!dlsOp.isPresent()) { // user started typing, has first world and there is no decision // list for it ContextCqlCompletion initial = null; if (!cqlQuery.partLc.isEmpty() && !cqlQuery.partLc.contains(" ")) { initial = new ContextCqlCompletion(CqlQueryType.UNKNOWN, initialCqlCompletion); } return Optional.ofNullable(initial); } if (cursorPosition < 0) { cursorPosition = 0; } DecisionListSupport dls = dlsOp.get(); CqlPartCompletion[][] decisionList = dls.getDecisionList(); if (decisionList.length == 0) { return Optional.empty(); } int cqLength = cqlQuery.partLc.length(); if (cursorPosition > cqLength) { cursorPosition = cqLength; } String cqlLc = cqlQuery.partLc.substring(0, cursorPosition); int offset = 0; CqlPartCompletion lastMatchingCompletion = null; // go over all parsing decisions, until you find one that cannot be // applied - this means that previous one // is the right chose for completion for (CqlPartCompletion[] partCompletionList : decisionList) { LOG.debug("Next completion"); boolean found = false; for (CqlPartCompletion partCompletion : partCompletionList) { LOG.debug("Checking: {}", partCompletion); int completionStartMarker = -1; if (partCompletion instanceof MarkerBasedCompletion) { MarkerBasedCompletion partStatic = (MarkerBasedCompletion) partCompletion; String startMarker = partStatic.startMarker().partLc; completionStartMarker = cqlLc.indexOf(startMarker, offset); } else if (partCompletion instanceof OffsetBasedCompletion) { OffsetBasedCompletion partDynamic = (OffsetBasedCompletion) partCompletion; completionStartMarker = partDynamic.canApply(cqlQuery, offset); } else { throw new ServiceException("Unsupported CqlPartCompletion: " + partCompletion.getClass()); } LOG.debug("completionStartMarker: {}", completionStartMarker); if (completionStartMarker == -1) { // this decision cannot be applied - try next one continue; } found = true; // current decision can be applied - try next one offset = completionStartMarker + 1; lastMatchingCompletion = partCompletion; } if (!found) { break; } } ContextCqlCompletion cqc = null; if (lastMatchingCompletion != null) { CqlCompletion cqlCompletion = lastMatchingCompletion.getCompletion(cqlQuery); cqc = new ContextCqlCompletion(dls.queryName(), cqlCompletion); LOG.debug("Completion found: {}", cqc); } else { LOG.debug("Completion not found"); } return Optional.ofNullable(cqc); } }