package com.nvlad.yii2support.objectfactory;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.jetbrains.php.PhpIndex;
import com.jetbrains.php.lang.psi.elements.*;
import com.nvlad.yii2support.common.ClassUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
/**
* Created by oleg on 14.03.2017.
*/
public class ObjectFactoryUtils {
@Nullable
static public PhpClass findClassByArray(@NotNull ArrayCreationExpression arrayCreationExpression) {
HashMap<String, String> keys = new HashMap<>();
for (ArrayHashElement arrayHashElement : arrayCreationExpression.getHashElements()) {
PhpPsiElement child = arrayHashElement.getKey();
if (child != null && ((child instanceof StringLiteralExpression))) {
String key;
if (child instanceof StringLiteralExpression) {
key = ((StringLiteralExpression) child).getContents();
} else {
key = child.getText();
}
Project project = child.getProject();
if (key.equals("class")) {
String className = "";
PhpPsiElement value = arrayHashElement.getValue();
PhpClass methodRef = ClassUtils.getPhpClassUniversal(project, value);
if (methodRef != null) return methodRef;
}
}
}
return null;
}
@Nullable
static PhpClass getPhpClassByYiiCreateObject(ArrayCreationExpression arrayCreation) {
PhpClass phpClass = null;
PsiElement parent = arrayCreation.getParent().getParent();
if (parent != null && parent instanceof MethodReference) {
MethodReference method = (MethodReference) parent;
if (method.getName() != null && method.getName().equals("createObject")) {
PhpExpression methodClass = method.getClassReference();
if (methodClass != null && methodClass.getName() != null && methodClass.getName().equals("Yii")) {
PsiElement[] pList = method.getParameters();
if (pList.length == 2 && ClassUtils.indexForElementInParameterList(arrayCreation) == 1) { // \Yii::createObject takes 2 paramters
phpClass = ClassUtils.getPhpClassUniversal(method.getProject(), (PhpPsiElement) pList[0]);
}
}
}
}
return phpClass;
}
static PhpClass getPhpClassInConfig(PsiDirectory dir, ArrayCreationExpression arrayCreation) {
PhpClass phpClass = null;
if (dir != null && (dir.getName().equals("config") || dir.getName().equals("src") /* for tests */)) {
PsiElement parent = arrayCreation.getParent().getParent();
if (parent instanceof ArrayHashElement) {
ArrayHashElement hash = (ArrayHashElement) parent;
PsiElement element = hash.getKey();
if (element instanceof StringLiteralExpression) {
StringLiteralExpression literal = (StringLiteralExpression) element;
String key = literal.getContents();
phpClass = getStandardPhpClass(PhpIndex.getInstance(literal.getProject()), key);
}
}
}
return phpClass;
}
static PhpClass getPhpClassInWidget(ArrayCreationExpression arrayCreation) {
PsiElement parent = arrayCreation.getParent().getParent();
if (parent != null && parent instanceof MethodReference) {
MethodReference methodRef = (MethodReference) parent;
if (methodRef.getName() != null && (methodRef.getName().equals("widget") || methodRef.getName().equals("begin"))) {
Method method = (Method) methodRef.resolve();
PhpExpression ref = methodRef.getClassReference();
if (ref != null && ref instanceof ClassReference && ClassUtils.indexForElementInParameterList(arrayCreation) == 0) {
PhpClass callingClass = (PhpClass) ((ClassReference) ref).resolve();
PhpClass superClass = ClassUtils.getClass(PhpIndex.getInstance(methodRef.getProject()), "\\yii\\base\\Widget");
if (ClassUtils.isClassInheritsOrEqual(callingClass, superClass))
return callingClass;
} else if (method != null && ref != null && ref instanceof MethodReference && ClassUtils.indexForElementInParameterList(arrayCreation) == 1) {
// This code process
// $form->field($model, 'username')->widget(\Class::className())
PhpClass callingClass = method.getContainingClass();
PhpClass superClass = ClassUtils.getClass(PhpIndex.getInstance(methodRef.getProject()), "yii\\widgets\\ActiveField");
if (ClassUtils.isClassInheritsOrEqual(callingClass, superClass)
&& method.getParameters().length == 2 &&
method.getParameters()[0].getName().equals("class")) {
PhpPsiElement element = (PhpPsiElement) methodRef.getParameters()[0];
PhpClass widgetClass = ClassUtils.getPhpClassUniversal(methodRef.getProject(), element);
if (widgetClass != null)
return widgetClass;
}
}
}
}
return null;
}
static PhpClass getPhpClassInGridColumns(ArrayCreationExpression arrayCreation) {
PsiElement parent = arrayCreation.getParent().getParent();
if (parent != null && parent instanceof ArrayCreationExpression) {
PsiElement possibleHashElement = arrayCreation.getParent().getParent().getParent().getParent();
if (!(possibleHashElement instanceof ArrayHashElement)) {
return null;
}
PsiElement key = ((ArrayHashElement) possibleHashElement).getKey();
if (key != null &
key.getText() != null &&
key.getText().replace("\"", "").replace("\'", "").equals("columns")) {
PsiElement methodRef = possibleHashElement.getParent().getParent().getParent();
if (methodRef instanceof MethodReference) {
MethodReference method = (MethodReference) methodRef;
if (method.getClassReference() != null) {
PhpExpression methodClass = method.getClassReference();
if (! (methodClass instanceof ClassReference) )
return null;
PhpClass callingClass = (PhpClass) ((ClassReference) methodClass).resolve();
if (callingClass != null && callingClass.getFQN().equals("\\yii\\grid\\GridView")) {
return ClassUtils.getClass(PhpIndex.getInstance(methodClass.getProject()), "\\yii\\grid\\DataColumn");
}
}
}
}
}
return null;
}
@Nullable
static PhpClass findClassByArrayCreation(ArrayCreationExpression arrayCreation, PsiDirectory dir) {
PhpClass phpClass;
phpClass = findClassByArray(arrayCreation);
if (phpClass == null) {
phpClass = getClassByInstatiation(arrayCreation);
}
if (phpClass == null) {
phpClass = getPhpClassByYiiCreateObject(arrayCreation);
}
if (phpClass == null) {
phpClass = getPhpClassInWidget(arrayCreation);
}
if (phpClass == null) {
phpClass = getPhpClassInGridColumns(arrayCreation);
}
if (phpClass == null) {
phpClass = getClassByParameterType(arrayCreation);
}
if (phpClass == null && arrayCreation.getParent().getParent() instanceof ArrayHashElement) {
phpClass = getPhpClassByHash((ArrayHashElement) arrayCreation.getParent().getParent(), dir);
}
if (phpClass == null) {
phpClass = getPhpClassInConfig(dir, arrayCreation);
}
return phpClass;
}
/**
* Find class if array is parameter
* @return Class
*/
@Nullable
private static PhpClass getClassByParameterType(ArrayCreationExpression arrayCreation) {
if (arrayCreation.getParent() instanceof ParameterList) {
int index = ClassUtils.indexForElementInParameterList(arrayCreation);
if (index > -1) {
PsiElement possibleMethodRef = arrayCreation.getParent().getParent();
if (possibleMethodRef instanceof MethodReference) {
Method method = (Method)((MethodReference) possibleMethodRef).resolve();
if (method != null && method.getParameters().length > index) {
Parameter parameter = method.getParameters()[index];
PhpClass resultClass = ClassUtils.getElementType(parameter);
if (resultClass != null) return resultClass;
}
}
}
}
return null;
}
private static PhpClass getPhpClassByHash(ArrayHashElement hashElement, PsiDirectory dir) {
if (hashElement.getParent() instanceof ArrayCreationExpression) {
PhpClass phpClass = findClassByArrayCreation((ArrayCreationExpression) hashElement.getParent(), dir);
if (phpClass == null)
return null;
String fieldName = hashElement.getKey() != null ? hashElement.getKey().getText() : null;
if (fieldName == null)
return null;
PhpClassMember field = ClassUtils.findWritableField(phpClass, fieldName);
if (field == null)
return null;
PhpClass resultClass = ClassUtils.getElementType(field);
if (resultClass != null) return resultClass;
}
return null;
}
static PhpClass getClassByInstatiation(PhpExpression element) {
PsiElement newElement = element.getParent().getParent();
if (newElement != null && newElement instanceof NewExpression) {
ClassReference ref = ((NewExpression) newElement).getClassReference();
if (ref == null)
return null;
PsiElement possiblePhpClass = ref.resolve();
if (!(possiblePhpClass instanceof PhpClass))
return null;
PhpClass phpClass = (PhpClass) possiblePhpClass;
if (phpClass != null) {
Method constructor = phpClass.getConstructor();
PhpClass yiiObjectClass = ClassUtils.getClass(PhpIndex.getInstance(element.getProject()), "\\yii\\base\\Object");
if (!ClassUtils.isClassInheritsOrEqual(phpClass, yiiObjectClass))
return null;
Parameter[] parameterList = constructor.getParameters();
if (parameterList.length > 0 && parameterList[0].getName().equals("config") && ClassUtils.indexForElementInParameterList(element) == 0)
return phpClass;
}
}
return null;
}
static PhpClass getStandardPhpClass(PhpIndex phpIndex, String shortName) {
switch (shortName){
// web/Application
case "request": return ClassUtils.getClass(phpIndex, "\\yii\\web\\Request");
case "response": return ClassUtils.getClass(phpIndex, "\\yii\\web\\Response");
case "session": return ClassUtils.getClass(phpIndex, "\\yii\\web\\Session");
case "user": return ClassUtils.getClass(phpIndex, "\\yii\\web\\User");
case "errorHandler": return ClassUtils.getClass(phpIndex, "\\yii\\web\\ErrorHandler");
// base/Application
case "log": return ClassUtils.getClass(phpIndex, "\\yii\\log\\Dispatcher");
case "view": return ClassUtils.getClass(phpIndex, "\\yii\\web\\View");
case "formatter": return ClassUtils.getClass(phpIndex, "yii\\i18n\\Formatter");
case "i18n": return ClassUtils.getClass(phpIndex, "yii\\i18n\\I18N");
case "mailer": return ClassUtils.getClass(phpIndex, "\\yii\\swiftmailer\\Mailer");
case "urlManager": return ClassUtils.getClass(phpIndex, "\\yii\\web\\UrlManager");
case "assetManager": return ClassUtils.getClass(phpIndex, "\\yii\\web\\AssetManager");
case "security": return ClassUtils.getClass(phpIndex, "\\yii\\base\\Security");
// custom
case "db": return ClassUtils.getClass(phpIndex, "\\yii\\db\\Connection");
}
return null;
}
}