Package fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder

Source Code of fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder.QueryBuilderMethodReferenceParser

package fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder;

import com.intellij.openapi.project.Project;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.php.PhpIndex;
import com.jetbrains.php.lang.parser.PhpElementTypes;
import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression;
import com.jetbrains.php.lang.psi.elements.MethodReference;
import com.jetbrains.php.lang.psi.elements.PhpClass;
import fr.adrienbrault.idea.symfony2plugin.Symfony2InterfacesUtil;
import fr.adrienbrault.idea.symfony2plugin.doctrine.EntityHelper;
import fr.adrienbrault.idea.symfony2plugin.doctrine.ObjectRepositoryTypeProvider;
import fr.adrienbrault.idea.symfony2plugin.doctrine.dict.DoctrineModelField;
import fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder.dict.QueryBuilderJoin;
import fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder.dict.QueryBuilderPropertyAlias;
import fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder.dict.QueryBuilderRelation;
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
import fr.adrienbrault.idea.symfony2plugin.util.PhpTypeProviderUtil;
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
import fr.adrienbrault.idea.symfony2plugin.util.dict.DoctrineModel;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class QueryBuilderMethodReferenceParser {

    private boolean collectParameter = true;
    private boolean collectJoins = true;
    private boolean collectProperties = true;

    final private Project project;
    final private Collection<MethodReference> methodReferences;

    public QueryBuilderMethodReferenceParser(Project project, Collection<MethodReference> methodReferences) {
        this.methodReferences = methodReferences;
        this.project = project;
    }

    public QueryBuilderScopeContext collect() {
        QueryBuilderScopeContext qb = new QueryBuilderScopeContext();

        // doctrine needs valid root with an alias, try to find one in method references or scope
        Map<String, String> map = this.findRootDefinition(methodReferences);
        if(map.size() > 0) {
            Map.Entry<String, String> entry = map.entrySet().iterator().next();
            qb.addTable(entry.getKey(), entry.getValue());
        }

        for(MethodReference methodReference: methodReferences) {

            String name = methodReference.getName();
            if(name != null) {
                collectParameter(qb, methodReference, name);
                collectJoins(qb, methodReference, name);
                collectSelects(qb, methodReference, name);
                collectSelectInForm(qb, methodReference, name);
            }

        }

        // first tableMap entry is root, we add several initial data
        if(qb.getTableMap().size() > 0) {
            Map.Entry<String, String> entry = qb.getTableMap().entrySet().iterator().next();
            String className = entry.getKey();
            PhpClass phpClass = PhpElementsUtil.getClassInterface(project, className);

            // add root select fields
            if(phpClass != null) {

                qb.addPropertyAlias(entry.getValue(), new QueryBuilderPropertyAlias(entry.getValue(), null, new DoctrineModelField(entry.getValue()).addTarget(phpClass).setTypeName(phpClass.getPresentableFQN())));

                List<QueryBuilderRelation> relationList = new ArrayList<QueryBuilderRelation>();

                //qb.addRelation(entry.getValue(), attachRelationFields(phpClass));
                for(DoctrineModelField field: EntityHelper.getModelFields(phpClass)) {
                    qb.addPropertyAlias(entry.getValue() + "." + field.getName(), new QueryBuilderPropertyAlias(entry.getValue(), field.getName(), field));
                    if(field.getRelation() != null && field.getRelationType() != null) {
                        relationList.add(new QueryBuilderRelation(field.getName(), field.getRelation()));
                    }
                }

                qb.addRelation(entry.getValue(), relationList);
            }

            QueryBuilderRelationClassResolver resolver = new QueryBuilderRelationClassResolver(project, entry.getValue(), entry.getKey(), qb.getRelationMap(), qb.getJoinMap());
            resolver.collect();

        }

        // we have a querybuilder which complete known elements now
        // se we can builder a property (field) map table from it
        this.buildPropertyMap(qb);

        return qb;

    }

    private void collectSelectInForm(QueryBuilderScopeContext qb, MethodReference methodReference, String name) {

        // $qb->from('foo', 'select')
        if(!"from".equals(name)) {
            return;
        }

        PsiElement psiElement = PsiElementUtils.getMethodParameterPsiElementAt(methodReference, 1);
        String literalValue = PhpElementsUtil.getStringValue(psiElement);
        if(literalValue != null) {
            qb.addSelect(literalValue);
        }

    }

    private void collectSelects(QueryBuilderScopeContext qb, MethodReference methodReference, String name) {

        if(!Arrays.asList("select", "addSelect").contains(name)) {
            return;
        }

        // $qb->select('foo')
        // $qb->select('foo', 'foo1')
        for(PsiElement parameter: methodReference.getParameters()) {
            String literalValue = PhpElementsUtil.getStringValue(parameter);
            if(literalValue != null) {
                qb.addSelect(literalValue);
            }
        }

        // $qb->select(array('foo', 'bar', 'accessoryDetail'))
        PsiElement psiElement = PsiElementUtils.getMethodParameterPsiElementAt(methodReference, 0);
        if(psiElement instanceof ArrayCreationExpression) {
            for(PsiElement arrayValue: PsiElementUtils.getChildrenOfTypeAsList(psiElement, PlatformPatterns.psiElement(PhpElementTypes.ARRAY_VALUE))) {
                if(arrayValue.getChildren().length == 1) {
                    String arrayValueString = PhpElementsUtil.getStringValue(arrayValue.getChildren()[0]);
                    if(arrayValueString != null) {
                        qb.addSelect(arrayValueString);
                    }
                }
            }
        }

    }

    private void collectJoins(QueryBuilderScopeContext qb, MethodReference methodReference, String name) {

        if(!collectJoins || !Arrays.asList("join", "leftJoin", "rightJoin", "innerJoin").contains(name)) {
            return;
        }

        String join = PsiElementUtils.getMethodParameterAt(methodReference, 0);
        String alias = PsiElementUtils.getMethodParameterAt(methodReference, 1);
        if(join != null && alias != null) {
            qb.addJoin(alias, new QueryBuilderJoin(join, alias));
        }

    }

    private void collectParameter(QueryBuilderScopeContext qb, MethodReference methodReference, String name) {

        if(!collectParameter || !Arrays.asList("where", "andWhere").contains(name)) {
            return;
        }

        String value = PsiElementUtils.getMethodParameterAt(methodReference, 0);
        if(value != null) {
            Matcher matcher = Pattern.compile(":(\\w+)", Pattern.MULTILINE).matcher(value);
            while(matcher.find()){
                qb.addParameter(matcher.group(1));
            }
        }

    }


    private Map<String, String> findRootDefinition(Collection<MethodReference> methodReferences) {

        Map<String, String> roots = new HashMap<String, String>();

        if(methodReferences.size() == 0) {
            return roots;
        }

        String rootAlias = null;
        String repository = null;


        for(MethodReference methodReference: methodReferences) {
            String methodReferenceName = methodReference.getName();

            // get alias
            // ->createQueryBuilder('test');
            if("createQueryBuilder".equals(methodReferenceName)) {
                String possibleAlias = PhpElementsUtil.getStringValue(PsiElementUtils.getMethodParameterPsiElementAt(methodReference, 0));
                if(possibleAlias != null) {
                    rootAlias = possibleAlias;
                }
            }

            // find repository class
            // getRepository('Foo')->createQueryBuilder('test');
            if("getRepository".equals(methodReferenceName)) {
                String possibleRepository = PhpElementsUtil.getStringValue(PsiElementUtils.getMethodParameterPsiElementAt(methodReference, 0));
                if(possibleRepository != null) {
                    repository = possibleRepository;
                    PhpClass phpClass = EntityHelper.resolveShortcutName(project, repository);
                    if(phpClass != null) {
                        repository = phpClass.getPresentableFQN();
                    }
                }
            }

            // $qb->from('Foo\Class', 'article')
            if("from".equals(methodReferenceName)) {
                String table = PhpElementsUtil.getStringValue(PsiElementUtils.getMethodParameterPsiElementAt(methodReference, 0));
                String alias = PhpElementsUtil.getStringValue(PsiElementUtils.getMethodParameterPsiElementAt(methodReference, 1));
                if(table != null && alias != null) {
                    PhpClass phpClass = EntityHelper.resolveShortcutName(project, table);
                    if(phpClass != null) {
                        table = phpClass.getPresentableFQN();
                    }

                    roots.put(table, alias);
                }
            }

        }

        // we have a valid root so add it
        if(rootAlias != null && repository != null) {
            roots.put(repository, rootAlias);
        }

        // we found a alias but not a repository name, so try a scope search if we are inside repository class
        // class implements \Doctrine\Common\Persistence\ObjectRepository, so search for model name of "repositoryClass"
        if(rootAlias != null && repository == null) {
            MethodReference methodReference = methodReferences.iterator().next();
            PhpClass phpClass = PsiTreeUtil.getParentOfType(methodReference, PhpClass.class);
            if(new Symfony2InterfacesUtil().isInstanceOf(phpClass, "\\Doctrine\\Common\\Persistence\\ObjectRepository")) {
                for(DoctrineModel model: EntityHelper.getModelClasses(project)) {
                    String className = model.getPhpClass().getPresentableFQN();
                    if(className != null) {
                        PhpClass resolvedRepoName = EntityHelper.getEntityRepositoryClass(project, className);
                        if(PhpElementsUtil.isEqualClassName(resolvedRepoName, phpClass.getPresentableFQN())) {
                            roots.put(className, rootAlias);
                            return roots;
                        }
                    }
                }
            }

        }

        // search on PhpTypeProvider
        // $er->createQueryBuilder()
        if(rootAlias != null && repository == null) {
            for(MethodReference methodReference: methodReferences) {
                if("createQueryBuilder".equals(methodReference.getName())) {
                    String signature = methodReference.getSignature();
                    int endIndex = signature.lastIndexOf(ObjectRepositoryTypeProvider.TRIM_KEY);
                    if(endIndex != -1) {
                        String parameter = signature.substring(endIndex + 1);
                        int point = parameter.indexOf(".");
                        if(point > -1) {
                            parameter = parameter.substring(0, point);
                            parameter = PhpTypeProviderUtil.getResolvedParameter(PhpIndex.getInstance(project), parameter);
                            if(parameter != null) {
                                PhpClass phpClass = EntityHelper.resolveShortcutName(project, parameter);
                                if(phpClass != null && phpClass.getPresentableFQN() != null) {
                                    roots.put(phpClass.getPresentableFQN(), rootAlias);
                                    return roots;
                                }
                            }
                        }
                    }
                }
            }
        }

        return roots;
    }

    private void buildPropertyMap(QueryBuilderScopeContext qb) {

        if(!collectProperties) {
            return;
        }

        for(QueryBuilderJoin join: qb.getJoinMap().values()) {
            String className = join.getResolvedClass();
            if(className != null) {
                PhpClass phpClass = PhpElementsUtil.getClassInterface(project, className);
                if(phpClass != null) {
                    qb.addPropertyAlias(join.getAlias(), new QueryBuilderPropertyAlias(join.getAlias(), null, new DoctrineModelField(join.getAlias()).addTarget(phpClass).setTypeName(phpClass.getPresentableFQN())));

                    // add entity properties
                    for(DoctrineModelField field: EntityHelper.getModelFields(phpClass)) {
                        qb.addPropertyAlias(join.getAlias() + "." + field.getName(), new QueryBuilderPropertyAlias(join.getAlias(), field.getName(), field));
                    }
                }
            }
        }

    }

    public static List<QueryBuilderRelation> attachRelationFields(PhpClass phpClass) {

        List<QueryBuilderRelation> relations = new ArrayList<QueryBuilderRelation>();

        for(DoctrineModelField field: EntityHelper.getModelFields(phpClass)) {
            if(field.getRelation() != null && field.getRelationType() != null) {
                relations.add(new QueryBuilderRelation(field.getName(), field.getRelation()));
            }
        }

        return relations;
    }

}
TOP

Related Classes of fr.adrienbrault.idea.symfony2plugin.doctrine.querybuilder.QueryBuilderMethodReferenceParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.