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.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.dict.PhpMethodReferenceCall;
import fr.adrienbrault.idea.symfony2plugin.util.psi.PsiElementAssertUtil;
import org.apache.commons.lang.ArrayUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class ArrayValueWithKeyAndMethodMatcher {
/**
* $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 instanceof StringLiteralExpression && ArrayUtils.contains(matcher.getArrayKeys(), ((StringLiteralExpression) arrayKey).getContents())) {
PsiElement arrayCreationExpression = arrayHashElement.getParent();
if(arrayCreationExpression instanceof ArrayCreationExpression) {
Pair<PhpMethodReferenceCall, MethodReference> matchPair = matchesMethodCall(arrayCreationExpression, matcher.getMethodCalls());
if(matchPair != null) {
return new Result(matchPair.getFirst(), matchPair.getSecond(), (StringLiteralExpression) arrayKey);
}
}
}
}
}
return null;
}
@Nullable
private static Pair<PhpMethodReferenceCall, MethodReference> matchesMethodCall(@NotNull PsiElement psiFirstMethodReferenceChild, @NotNull PhpMethodReferenceCall[] methodCalls) {
// get parameter, we less then 0 we are not inside method reference
int parameterIndex = PsiElementUtils.getParameterIndexValue(psiFirstMethodReferenceChild);
if(parameterIndex < 0) {
return null;
}
PsiElement parameterList = psiFirstMethodReferenceChild.getParent();
if(!(parameterList instanceof ParameterList)) {
return null;
}
PsiElement methodReference = parameterList.getParent();
if (!(methodReference instanceof MethodReference)) {
return null;
}
for (PhpMethodReferenceCall methodCall : methodCalls) {
if(parameterIndex != methodCall.getIndex() ||
!ArrayUtils.contains(methodCall.getMethods(), ((MethodReference) methodReference).getName()) ||
!PhpElementsUtil.isMethodReferenceInstanceOf((MethodReference) methodReference, methodCall.getClassName())
)
{
continue;
}
return Pair.create(methodCall, (MethodReference) methodReference);
}
return null;
}
public static class Matcher {
private final String[] arrayKey;
@NotNull
private final PhpMethodReferenceCall[] methodCalls;
public Matcher(@NotNull String arrayKeys, @NotNull PhpMethodReferenceCall... methodCalls) {
this(new String[] {arrayKeys}, methodCalls);
}
public Matcher(@NotNull String[] arrayKeys, @NotNull PhpMethodReferenceCall... methodCalls) {
this.arrayKey = arrayKeys;
this.methodCalls = methodCalls;
}
public String[] getArrayKeys() {
return arrayKey;
}
@NotNull
public PhpMethodReferenceCall[] getMethodCalls() {
return methodCalls;
}
}
public static class Result {
@NotNull
private final PhpMethodReferenceCall methodCall;
@NotNull
private final StringLiteralExpression arrayKey;
@NotNull
private final MethodReference methodReference;
public Result(@NotNull PhpMethodReferenceCall methodCall, @NotNull MethodReference methodReference, @NotNull StringLiteralExpression arrayKey) {
this.methodCall = methodCall;
this.methodReference = methodReference;
this.arrayKey = arrayKey;
}
@NotNull
public StringLiteralExpression getArrayKey() {
return arrayKey;
}
@NotNull
public MethodReference getMethodReference() {
return methodReference;
}
@NotNull
public PhpMethodReferenceCall getMethodCall() {
return methodCall;
}
}
}