package fr.adrienbrault.idea.symfony2plugin.util.psi.matcher; import com.intellij.openapi.util.Pair; import com.intellij.patterns.PlatformPatterns; import com.intellij.patterns.PsiElementPattern; import com.intellij.psi.PsiElement; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.php.lang.parser.PhpElementTypes; import com.jetbrains.php.lang.patterns.PhpPatterns; import com.jetbrains.php.lang.psi.elements.*; import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil; import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils; import fr.adrienbrault.idea.symfony2plugin.util.psi.PsiElementAssertUtil; import org.apache.commons.lang.ArrayUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; /** * @author Daniel Espendiller <daniel@espendiller.net> */ public class ArrayValueWithKeyAndNewExpressionMatcher { /** * $menu->addChild([ * 'route' => '<caret>', * ]); */ @NotNull public static PsiElementPattern.Capture<PsiElement> pattern() { return PhpPatterns.psiElement().withParent( PlatformPatterns.psiElement(StringLiteralExpression.class).withParent( PlatformPatterns.psiElement(PhpElementTypes.ARRAY_VALUE).inside(ParameterList.class) ) ); } /** * A array value inside array and method reference * * $menu->addChild([ * 'route' => '<caret>', * ]); */ @Nullable public static Result match(@NotNull PsiElement psiElement, @NotNull Matcher matcher) { PsiElement arrayValue = psiElement.getParent(); if(PsiElementAssertUtil.isNotNullAndIsElementType(arrayValue, PhpElementTypes.ARRAY_VALUE)) { PsiElement arrayHashElement = arrayValue.getParent(); if(arrayHashElement instanceof ArrayHashElement) { PhpPsiElement arrayKey = ((ArrayHashElement) arrayHashElement).getKey(); if(arrayKey != null && ArrayUtils.contains(matcher.getArrayKeys(), PhpElementsUtil.getStringValue(arrayKey))) { PsiElement arrayCreationExpression = arrayHashElement.getParent(); if(arrayCreationExpression instanceof ArrayCreationExpression) { Pair<NewExpressionCall, NewExpression> matchPair = matchesMethodCall((ArrayCreationExpression) arrayCreationExpression, matcher.getNewExpressionCalls()); if(matchPair != null) { return new Result(matchPair.getFirst(), matchPair.getSecond(), arrayKey); } } } } } return null; } @Nullable private static Pair<NewExpressionCall, NewExpression> matchesMethodCall(@NotNull ArrayCreationExpression arrayCreationExpression, @NotNull NewExpressionCall[] classes) { // get parameter, we less then 0 we are not inside method reference int parameterIndex = PsiElementUtils.getParameterIndexValue(arrayCreationExpression); if(parameterIndex < 0) { return null; } // filter index List<NewExpressionCall> filter = ContainerUtil.filter(classes, expressionCall -> expressionCall.getIndex() == parameterIndex ); if(filter.size() == 0) { return null; } PsiElement parameterList = arrayCreationExpression.getParent(); if(!(parameterList instanceof ParameterList)) { return null; } PsiElement newExpression = parameterList.getParent(); if (!(newExpression instanceof NewExpression)) { return null; } for (NewExpressionCall aClass : filter) { if(PhpElementsUtil.getNewExpressionPhpClassWithInstance((NewExpression) newExpression, aClass.getClazz()) == null) { continue; } return Pair.create(aClass, (NewExpression) newExpression); } return null; } public static class Matcher { @NotNull private final String[] arrayKeys; @NotNull private final NewExpressionCall[] classes; public Matcher(@NotNull String[] arrayKeys, @NotNull NewExpressionCall... classes) { this.arrayKeys = arrayKeys; this.classes = classes; } public Matcher(@NotNull String arrayKeys, @NotNull NewExpressionCall... classes) { this(new String[] {arrayKeys}, classes); } public Matcher(@NotNull String arrayKeys, @NotNull String clazz) { this(new String[] {arrayKeys}, new NewExpressionCall(clazz, 0)); } @NotNull public String[] getArrayKeys() { return arrayKeys; } @NotNull public NewExpressionCall[] getNewExpressionCalls() { return classes; } } public static class Result { @NotNull private final NewExpression newExpression; @NotNull private final NewExpressionCall expressionCall; @NotNull private final PsiElement arrayKey; public Result(@NotNull NewExpressionCall expressionCall, @NotNull NewExpression newExpression, @NotNull PsiElement arrayKey) { this.expressionCall = expressionCall; this.newExpression = newExpression; this.arrayKey = arrayKey; } @NotNull public NewExpression getNewExpression() { return newExpression; } @NotNull public NewExpressionCall getExpressionCall() { return expressionCall; } @NotNull public PsiElement getArrayKey() { return arrayKey; } } public static class NewExpressionCall { @NotNull private final String clazz; private int index = 0; public NewExpressionCall(@NotNull String clazz) { this(clazz, 0); } public NewExpressionCall(@NotNull String clazz, int index) { this.clazz = clazz; this.index = index; } @NotNull public String getClazz() { return clazz; } public int getIndex() { return index; } } }