Package fr.adrienbrault.idea.symfony2plugin.templating.util

Source Code of fr.adrienbrault.idea.symfony2plugin.templating.util.TwigExtensionParser$TwigFilterVisitor

package fr.adrienbrault.idea.symfony2plugin.templating.util;

import com.intellij.openapi.project.Project;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
import com.jetbrains.php.PhpIcons;
import com.jetbrains.php.PhpIndex;
import com.jetbrains.php.lang.parser.PhpElementTypes;
import com.jetbrains.php.lang.psi.elements.*;
import com.jetbrains.php.lang.psi.elements.impl.NewExpressionImpl;
import com.jetbrains.php.phpunit.PhpUnitUtil;
import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigExtension;
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.util.*;

public class TwigExtensionParser  {

    private Project project;

    private HashMap<String, TwigExtension> functions;
    private HashMap<String, TwigExtension> filters;

    public TwigExtensionParser(Project project) {
        this.project = project;
    }

    public HashMap<String, TwigExtension> getFunctions() {
        if(functions == null) {
            this.parseElementType(TwigElementType.METHOD);
        }
        return functions;
    }

    public HashMap<String, TwigExtension> getFilters() {
        if(filters == null) {
            this.parseElementType(TwigElementType.FILTER);
        }
        return filters;
    }

    public enum TwigElementType {
        FILTER, METHOD
    }

    public enum TwigExtensionType {
        FUNCTION_METHOD, FUNCTION_NODE, SIMPLE_FUNCTION, FILTER
    }

    private void parseElementType(TwigElementType type) {


        Set<String> classNames = new HashSet<String>();

        // only the interface gaves use all elements; container dont hold all
        for(PhpClass phpClass : PhpIndex.getInstance(this.project).getAllSubclasses("\\Twig_ExtensionInterface")) {
            // dont add unit tests classes
            if(!PhpUnitUtil.isPhpUnitTestFile(phpClass.getContainingFile())) {
                String className = phpClass.getPresentableFQN();
                if(className != null) {
                    classNames.add(className);
                }
            }
        }

        if(type.equals(TwigElementType.FILTER)) {
            this.parseFilters(classNames);
        }

        if(type.equals(TwigElementType.METHOD)) {
            this.parseFunctions(classNames);
        }

    }

    private void parseFilters(Collection<String> classNames) {
        this.filters = new HashMap<String, TwigExtension>();
        for(String phpClassName : classNames) {
            Method method = PhpElementsUtil.getClassMethod(this.project, phpClassName, "getFilters");
            if(method != null) {
                parseFilter(method, this.filters);
            }
        }
    }

    private void parseFunctions(Collection<String> classNames) {
        this.functions = new HashMap<String, TwigExtension>();
        for(String phpClassName : classNames) {
            Method method = PhpElementsUtil.getClassMethod(this.project, phpClassName, "getFunctions");
            if(method != null) {
                parseFunctions(method, this.functions);
            }
        }
    }

    protected Map<String, TwigExtension> parseFunctions(final Method method, final HashMap<String, TwigExtension> filters) {

        final PhpClass containingClass = method.getContainingClass();
        if(containingClass == null) {
            return new HashMap<String, TwigExtension>();
        }

        method.acceptChildren(new TwigFunctionVisitor(method, filters, containingClass));

        return filters;

    }

    /**
     *  Get signature for callable like array($this, 'getUrl'), or 'function'
     */
    @Nullable
    private static String getCallableSignature(PsiElement psiElement, Method method) {

        // array($this, 'getUrl')
        if(psiElement instanceof ArrayCreationExpression) {
            List<PsiElement> arrayValues = (List<PsiElement>) PsiElementUtils.getChildrenOfTypeAsList(psiElement, PlatformPatterns.psiElement(PhpElementTypes.ARRAY_VALUE));
            if(arrayValues.size() > 1) {
                PsiElement firstChild = arrayValues.get(0).getFirstChild();
                if(firstChild instanceof Variable && "this".equals(((Variable) firstChild).getName())) {
                    String methodName = PhpElementsUtil.getStringValue(arrayValues.get(1).getFirstChild());
                    if(StringUtils.isNotBlank(methodName)) {
                        PhpClass phpClass = method.getContainingClass();
                        if(phpClass != null) {
                            return String.format("#M#C\\%s.%s", phpClass.getPresentableFQN(), methodName);
                        }
                    }
                }
            }

        } else {
            String funcTargetName = PhpElementsUtil.getStringValue(psiElement);
            if(funcTargetName != null) {
                return "#F" + funcTargetName;
            }
        }

        return null;
    }

    protected HashMap<String, TwigExtension> parseFilter(Method method, HashMap<String, TwigExtension> filters) {


        final PhpClass containingClass = method.getContainingClass();
        if(containingClass == null) {
            return new HashMap<String, TwigExtension>();
        }

        method.acceptChildren(new TwigFilterVisitor(method, filters, containingClass));

        return filters;

    }

    public static Icon getIcon(TwigExtensionType twigExtensionType) {

        if(twigExtensionType == TwigExtensionType.FUNCTION_NODE) {
            return PhpIcons.CLASS_INITIALIZER;
        }

        if(twigExtensionType == TwigExtensionType.SIMPLE_FUNCTION) {
            return PhpIcons.FUNCTION;
        }

        if(twigExtensionType == TwigExtensionType.FUNCTION_METHOD) {
            return PhpIcons.METHOD_ICON;
        }

        if(twigExtensionType == TwigExtensionType.FILTER) {
            return PhpIcons.STATIC_FIELD;
        }

        return PhpIcons.WEB_ICON;
    }
    private static class TwigFilterVisitor extends PsiRecursiveElementWalkingVisitor {
        private final Method method;
        private final HashMap<String, TwigExtension> filters;
        private final PhpClass containingClass;

        public TwigFilterVisitor(Method method, HashMap<String, TwigExtension> filters, PhpClass containingClass) {
            this.method = method;
            this.filters = filters;
            this.containingClass = containingClass;
        }

        @Override
        public void visitElement(PsiElement element) {
            if(element instanceof NewExpressionImpl) {
                this.visitNewExpression((NewExpressionImpl) element);
            }
            super.visitElement(element);
        }

        private void visitNewExpression(NewExpressionImpl element) {

            ClassReference classReference = element.getClassReference();
            if(classReference == null) {
                return;
            }

            String expressionName = classReference.getName();

            // new \Twig_SimpleFunction('url', array($this, 'getUrl'), array('is_safe_callback' => array($this, 'isUrlGenerationSafe'))),
            if("Twig_SimpleFilter".equals(expressionName)) {
                PsiElement[] psiElement = element.getParameters();
                if(psiElement.length > 0) {
                    String funcName = PhpElementsUtil.getStringValue(psiElement[0]);
                    if(funcName != null && !funcName.contains("*")) {

                        String signature = null;
                        if(psiElement.length > 1) {
                            signature = getCallableSignature(psiElement[1], method);
                        }

                        filters.put(funcName, new TwigExtension(TwigExtensionType.FILTER, signature));
                    }

                }

                return;
            }

            // array('shuffle' => new Twig_Filter_Function('twig_shuffle_filter'),)
            if("Twig_Filter_Function".equals(expressionName)) {
                PsiElement arrayValue = element.getParent();
                if(arrayValue != null && arrayValue.getNode().getElementType() == PhpElementTypes.ARRAY_VALUE) {
                    PsiElement arrayHash = arrayValue.getParent();
                    if(arrayHash instanceof ArrayHashElement) {
                        PsiElement arrayKey = ((ArrayHashElement) arrayHash).getKey();
                        String funcName = PhpElementsUtil.getStringValue(arrayKey);
                        if(funcName != null && !funcName.contains("*")) {

                            PsiElement[] parameters = element.getParameters();
                            String signature = null;
                            if(parameters.length > 0) {
                                String funcTargetName = PhpElementsUtil.getStringValue(parameters[0]);
                                if(funcTargetName != null) {
                                    signature = "#F" + funcTargetName;
                                }
                            }

                            filters.put(funcName, new TwigExtension(TwigExtensionType.FILTER, signature));
                        }
                    }
                }

                return;
            }

            // return array('serialize'  => new \Twig_Filter_Method($this, 'serialize'), );
            if("Twig_Filter_Method".equals(expressionName)) {
                PsiElement arrayValue = element.getParent();
                if(arrayValue != null && arrayValue.getNode().getElementType() == PhpElementTypes.ARRAY_VALUE) {
                    PsiElement arrayHash = arrayValue.getParent();
                    if(arrayHash instanceof ArrayHashElement) {
                        PsiElement arrayKey = ((ArrayHashElement) arrayHash).getKey();
                        String funcName = PhpElementsUtil.getStringValue(arrayKey);
                        if(funcName != null && funcName.matches("\\w+")) {

                            PsiElement[] parameters = element.getParameters();
                            String signature = null;
                            if(parameters.length > 1) {
                                if(parameters[0] instanceof Variable && "this".equals(((Variable) parameters[0]).getName())) {
                                    String methodName = PhpElementsUtil.getStringValue(parameters[1]);
                                    if(methodName != null) {
                                        String presentableFQN = containingClass.getPresentableFQN();
                                        if(presentableFQN != null) {
                                            signature = String.format("#M#C\\%s.%s", presentableFQN, methodName);
                                        }
                                    }

                                }
                            }

                            filters.put(funcName, new TwigExtension(TwigExtensionType.FILTER, signature));
                        }
                    }
                }

            }

        }
    }

    private static class TwigFunctionVisitor extends PsiRecursiveElementWalkingVisitor {
        private final Method method;
        private final HashMap<String, TwigExtension> filters;
        private final PhpClass containingClass;

        public TwigFunctionVisitor(Method method, HashMap<String, TwigExtension> filters, PhpClass containingClass) {
            this.method = method;
            this.filters = filters;
            this.containingClass = containingClass;
        }

        @Override
        public void visitElement(PsiElement element) {
            if(element instanceof NewExpressionImpl) {
                this.visitNewExpression((NewExpressionImpl) element);
            }
            super.visitElement(element);
        }

        private void visitNewExpression(NewExpressionImpl element) {

            ClassReference classReference = element.getClassReference();
            if(classReference == null) {
                return;
            }

            String expressionName = classReference.getName();

            // new \Twig_SimpleFunction('url', array($this, 'getUrl'), array('is_safe_callback' => array($this, 'isUrlGenerationSafe'))),
            if("Twig_SimpleFunction".equals(expressionName)) {
                PsiElement[] psiElement = element.getParameters();
                if(psiElement.length > 0) {
                    String funcName = PhpElementsUtil.getStringValue(psiElement[0]);
                    if(funcName != null && !funcName.contains("*")) {

                        String signature = null;
                        if(psiElement.length > 1) {
                            signature = getCallableSignature(psiElement[1], method);
                        }

                        filters.put(funcName,  new TwigExtension(TwigExtensionType.SIMPLE_FUNCTION, signature));

                    }

                }

                return;
            }

            //array('form_javascript' => new \Twig_Function_Method($this, 'renderJavascript', array('is_safe' => array('html'))),);
            if("Twig_Function_Method".equals(expressionName)) {
                PsiElement arrayValue = element.getParent();
                if(arrayValue != null && arrayValue.getNode().getElementType() == PhpElementTypes.ARRAY_VALUE) {
                    PsiElement arrayHash = arrayValue.getParent();
                    if(arrayHash instanceof ArrayHashElement) {
                        PsiElement arrayKey = ((ArrayHashElement) arrayHash).getKey();
                        String funcName = PhpElementsUtil.getStringValue(arrayKey);
                        if(funcName != null && !funcName.contains("*")) {

                            PsiElement[] parameters = element.getParameters();
                            String signature = null;
                            if(parameters.length > 1) {
                                if(parameters[0] instanceof Variable && "this".equals(((Variable) parameters[0]).getName())) {
                                    String methodName = PhpElementsUtil.getStringValue(parameters[1]);
                                    if(methodName != null) {
                                        String presentableFQN = containingClass.getPresentableFQN();
                                        if(presentableFQN != null) {
                                            signature = String.format("#M#C\\%s.%s", presentableFQN, methodName);
                                        }
                                    }

                                }
                            }

                            filters.put(funcName, new TwigExtension(TwigExtensionType.FUNCTION_METHOD, signature));
                        }
                    }
                }

                return;
            }

            // array('form_help' => new \Twig_Function_Node('Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', array('is_safe' => array('html'))),)
            if("Twig_Function_Node".equals(expressionName)) {
                PsiElement arrayValue = element.getParent();
                if(arrayValue != null && arrayValue.getNode().getElementType() == PhpElementTypes.ARRAY_VALUE) {
                    PsiElement arrayHash = arrayValue.getParent();
                    if(arrayHash instanceof ArrayHashElement) {
                        PsiElement arrayKey = ((ArrayHashElement) arrayHash).getKey();
                        String funcName = PhpElementsUtil.getStringValue(arrayKey);
                        if(funcName != null && !funcName.contains("*")) {

                            PsiElement[] parameters = element.getParameters();
                            String signature = null;
                            if(parameters.length > 0) {
                                String className = PhpElementsUtil.getStringValue(parameters[0]);
                                if(className != null) {

                                    if(className.startsWith("\\")) {
                                        className = className.substring(1);
                                    }

                                    signature = String.format("#M#C\\%s.%s", className, "compile");
                                }

                            }

                            filters.put(funcName, new TwigExtension(TwigExtensionType.FUNCTION_NODE, signature));
                        }
                    }
                }

            }

        }
    }
}
TOP

Related Classes of fr.adrienbrault.idea.symfony2plugin.templating.util.TwigExtensionParser$TwigFilterVisitor

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.