com.haulmont.cuba.core.sys.jpql.QueryTreeAnalyzer.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.cuba.core.sys.jpql.QueryTreeAnalyzer.java

Source

/*
 * Copyright (c) 2008-2016 Haulmont.
 *
 * 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 com.haulmont.cuba.core.sys.jpql;

import com.google.common.base.Preconditions;
import com.haulmont.cuba.core.sys.jpql.antlr2.JPA2Lexer;
import com.haulmont.cuba.core.sys.jpql.model.JpqlEntityModel;
import com.haulmont.cuba.core.sys.jpql.pointer.EntityPointer;
import com.haulmont.cuba.core.sys.jpql.pointer.Pointer;
import com.haulmont.cuba.core.sys.jpql.transform.NodesFinder;
import com.haulmont.cuba.core.sys.jpql.tree.*;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.Tree;
import org.antlr.runtime.tree.TreeVisitor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class QueryTreeAnalyzer {
    protected DomainModel model;
    protected IdVarSelector idVarSelector;
    protected CommonTree tree;

    public void prepare(DomainModel model, String query) throws RecognitionException {
        prepare(model, query, true);
    }

    public void prepare(DomainModel model, String query, boolean failOnErrors) throws RecognitionException {
        Preconditions.checkNotNull(query, "query is null");
        this.model = model;
        query = query.replace("\n", " ");
        query = query.replace("\r", " ");
        query = query.replace("\t", " ");
        tree = Parser.parse(query, failOnErrors);
        TreeVisitor visitor = new TreeVisitor();
        idVarSelector = new IdVarSelector(model);
        visitor.visit(tree, idVarSelector);
    }

    public QueryVariableContext getRootQueryVariableContext() {
        return idVarSelector.getContextTree();
    }

    public List<ErrorRec> getInvalidIdVarNodes() {
        return idVarSelector.getInvalidIdVarNodes();
    }

    public CommonTree getTree() {
        return tree;
    }

    public String getRootEntityVariableName(String entityName) {
        QueryVariableContext ctx = getRootQueryVariableContext();
        return ctx.getVariableNameByEntity(entityName);
    }

    public PathNode getSelectedPathNode() {
        Tree selectedItems = tree.getFirstChildWithType(JPA2Lexer.T_SELECTED_ITEMS);
        boolean isDistinct = "DISTINCT".equalsIgnoreCase(selectedItems.getChild(0).getText());
        SelectedItemNode selectedItemNode;
        if (isDistinct) {
            if (selectedItems.getChildCount() != 2)
                throw new IllegalStateException("Cannot select path node if multiple fields selected");
            selectedItemNode = (SelectedItemNode) selectedItems.getChild(1);
        } else {
            if (selectedItems.getChildCount() != 1)
                throw new IllegalStateException("Cannot select path node if multiple fields selected");
            selectedItemNode = (SelectedItemNode) selectedItems.getChild(0);
        }

        if (!(selectedItemNode.getChild(0) instanceof PathNode)) {
            throw new IllegalStateException("An entity path is assumed to be selected");
        }
        return (PathNode) selectedItemNode.getChild(0);
    }

    public JpqlEntityModel getSelectedEntity(PathNode path) {
        Pointer pointer = path.resolvePointer(model, getRootQueryVariableContext());
        if (!(pointer instanceof EntityPointer)) {
            throw new IllegalStateException("A path resulting in an entity is assumed to be selected");
        }
        return ((EntityPointer) pointer).getEntity();
    }

    @Nullable
    public IdentificationVariableNode getMainEntityIdentification() {
        List<IdentificationVariableNode> identificationVariables = getIdentificationVariableNodes();

        String returnedVariableName = getFirstReturnedVariableName();
        if (returnedVariableName != null) {
            for (IdentificationVariableNode identificationVariable : identificationVariables) {
                if (identificationVariable.getVariableName().equalsIgnoreCase(returnedVariableName)) {
                    return identificationVariable;
                }
            }
        }

        return identificationVariables.size() > 0 ? identificationVariables.get(0) : null;
    }

    @Nullable
    public String getFirstReturnedVariableName() {
        PathNode returnedPathNode = getFirstReturnedPathNode();
        if (returnedPathNode != null) {
            return returnedPathNode.getEntityVariableName();
        }

        return null;
    }

    @Nullable
    public PathNode getFirstReturnedPathNode() {
        List<PathNode> pathNodes = getReturnedPathNodes();
        if (CollectionUtils.isNotEmpty(pathNodes)) {
            PathNode pathNode = pathNodes.get(0);
            return pathNode;
        }

        return null;
    }

    @Nullable
    public List<PathNode> getReturnedPathNodes() {
        CommonTree selectedItems = (CommonTree) tree.getFirstChildWithType(JPA2Lexer.T_SELECTED_ITEMS);
        if (selectedItems == null) {
            return null;
        }

        return getChildrenByClass(selectedItems, SelectedItemNode.class).stream()
                .flatMap(selectedItemNode -> getChildrenByClass(selectedItemNode, PathNode.class).stream())
                .collect(Collectors.toList());
    }

    public List<IdentificationVariableNode> getIdentificationVariableNodes() {
        CommonTree sourceNode = (CommonTree) tree.getFirstChildWithType(JPA2Lexer.T_SOURCES);
        List<IdentificationVariableNode> identificationVariableNodes = new ArrayList<>();

        List<SelectionSourceNode> selectionSources = getChildrenByClass(sourceNode, SelectionSourceNode.class);
        for (SelectionSourceNode selectionSource : selectionSources) {
            identificationVariableNodes
                    .addAll(getChildrenByClass(selectionSource, IdentificationVariableNode.class));
        }

        return identificationVariableNodes;
    }

    public List<SimpleConditionNode> findAllConditionsForMainEntityAttribute(String attribute) {
        IdentificationVariableNode mainEntityIdentification = getMainEntityIdentification();
        if (mainEntityIdentification != null) {
            return findAllConditions().stream().filter(condition -> {
                List<PathNode> childrenByClass = getChildrenByClass(condition, PathNode.class);
                return childrenByClass.stream().anyMatch(pathNode -> {
                    String pathNodeAttribute = StringUtils.join(pathNode.getChildren(), ".");
                    return pathNode.getEntityVariableName().equals(mainEntityIdentification.getVariableName())
                            && attribute.equals(pathNodeAttribute);
                });
            }).collect(Collectors.toList());
        } else {
            return Collections.emptyList();
        }
    }

    public List<SimpleConditionNode> findConditionsForParameter(String paramName) {
        CommonTree whereTree = (CommonTree) tree.getFirstChildWithType(JPA2Lexer.T_CONDITION);
        List<SimpleConditionNode> conditionNodes = getChildrenByClass(whereTree, SimpleConditionNode.class);
        return conditionNodes.stream().filter((SimpleConditionNode n) -> {
            ParameterNode parameter = (ParameterNode) n.getFirstChildWithType(JPA2Lexer.T_PARAMETER);
            return parameter != null && (parameter.getChild(0).getText().contains(paramName)
                    || parameter.getChildCount() > 1 && paramName.equals(parameter.getChild(1).getText()));
        }).collect(Collectors.toList());
    }

    public List<SimpleConditionNode> findAllConditions() {
        NodesFinder<SimpleConditionNode> nodesFinder = new NodesFinder<>(SimpleConditionNode.class);
        TreeVisitor treeVisitor = new TreeVisitor();
        treeVisitor.visit(tree, nodesFinder);
        return nodesFinder.getFoundNodes();
    }

    public boolean hasJoins() {
        CommonTree sourceNode = (CommonTree) tree.getFirstChildWithType(JPA2Lexer.T_SOURCES);
        List<SelectionSourceNode> selectionSourceNodes = getChildrenByClass(sourceNode, SelectionSourceNode.class);
        if (selectionSourceNodes.size() > 1) {
            return true;
        } else if (selectionSourceNodes.size() == 1) {
            NodesFinder<JoinVariableNode> nodesFinder = new NodesFinder<>(JoinVariableNode.class);
            TreeVisitor treeVisitor = new TreeVisitor();
            treeVisitor.visit(tree, nodesFinder);
            return !nodesFinder.getFoundNodes().isEmpty();
        } else {
            return false;
        }
    }

    protected <T> List<T> getChildrenByClass(CommonTree commonTree, Class<T> clazz) {
        List<Object> childrenByClass = new ArrayList<>();
        for (Object o : commonTree.getChildren()) {
            if (clazz.isAssignableFrom(o.getClass())) {
                childrenByClass.add(o);
            }
        }

        return (List<T>) childrenByClass;
    }
}