package fr.adrienbrault.idea.symfony2plugin.doctrine; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.patterns.PlatformPatterns; import com.intellij.psi.PsiElement; import com.jetbrains.php.PhpIndex; import com.jetbrains.php.lang.parser.PhpElementTypes; import com.jetbrains.php.lang.psi.elements.Method; import com.jetbrains.php.lang.psi.elements.MethodReference; import com.jetbrains.php.lang.psi.elements.PhpClass; import com.jetbrains.php.lang.psi.elements.PhpNamedElement; import com.jetbrains.php.lang.psi.resolve.types.PhpType; import com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider3; import fr.adrienbrault.idea.symfony2plugin.Settings; import fr.adrienbrault.idea.symfony2plugin.Symfony2InterfacesUtil; import fr.adrienbrault.idea.symfony2plugin.util.PhpTypeProviderUtil; import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Set; /** * @author Daniel Espendiller <daniel@espendiller.net> */ public class ObjectRepositoryResultTypeProvider implements PhpTypeProvider3 { final static char TRIM_KEY = '\u0184'; @Override public char getKey() { return '\u0152'; } @Nullable @Override public PhpType getType(PsiElement e) { if (!Settings.getInstance(e.getProject()).pluginEnabled || !Settings.getInstance(e.getProject()).objectRepositoryResultTypeProvider) { return null; } // filter out method calls without parameter // $this->get('service_name') if(!PlatformPatterns .psiElement(PhpElementTypes.METHOD_REFERENCE) .withChild(PlatformPatterns .psiElement(PhpElementTypes.PARAMETER_LIST) ).accepts(e)) { return null; } MethodReference methodRef = (MethodReference) e; String refSignature = ((MethodReference)e).getSignature(); if(StringUtil.isEmpty(refSignature)) { return null; } String methodRefName = methodRef.getName(); if(null == methodRefName || !Arrays.asList(new String[] {"find", "findOneBy", "findAll", "findBy"}).contains(methodRefName)) { return null; } // at least one parameter is necessary on some finds PsiElement[] parameters = methodRef.getParameters(); if(!methodRefName.equals("findAll")) { if(parameters.length == 0) { return null; } } else if(parameters.length != 0) { return null; } // we can get the repository name from the signature calls // #M#?#M#?#M#C\Foo\Bar\Controller\BarController.get?doctrine.getRepository?EntityBundle:User.find String repositorySignature = methodRef.getSignature(); int lastRepositoryName = repositorySignature.lastIndexOf(ObjectRepositoryTypeProvider.TRIM_KEY); if(lastRepositoryName == -1) { return null; } repositorySignature = repositorySignature.substring(lastRepositoryName); int nextMethodCall = repositorySignature.indexOf('.' + methodRefName); if(nextMethodCall == -1) { return null; } repositorySignature = repositorySignature.substring(1, nextMethodCall); return new PhpType().add("#" + this.getKey() + refSignature + TRIM_KEY + repositorySignature); } @Override public Collection<? extends PhpNamedElement> getBySignature(String expression, Set<String> visited, int depth, Project project) { // get back our original call int endIndex = expression.lastIndexOf(TRIM_KEY); if(endIndex == -1) { return Collections.emptySet(); } String originalSignature = expression.substring(0, endIndex); String parameter = expression.substring(endIndex + 1); // search for called method PhpIndex phpIndex = PhpIndex.getInstance(project); Collection<? extends PhpNamedElement> phpNamedElementCollections = PhpTypeProviderUtil.getTypeSignature(phpIndex, originalSignature); if(phpNamedElementCollections.size() == 0) { return Collections.emptySet(); } Method method = getObjectRepositoryCall(phpNamedElementCollections); if(method == null) { return Collections.emptySet(); } // we can also pipe php references signatures and resolve them here // overwrite parameter to get string value parameter = PhpTypeProviderUtil.getResolvedParameter(phpIndex, parameter); if(parameter == null) { return Collections.emptySet(); } PhpClass phpClass = EntityHelper.resolveShortcutName(project, parameter); if(phpClass == null) { return Collections.emptySet(); } String name = method.getName(); if(name.equals("findAll") || name.equals("findBy")) { method.getType().add(phpClass.getFQN() + "[]"); return phpNamedElementCollections; } return PhpTypeProviderUtil.mergeSignatureResults(phpNamedElementCollections, phpClass); } private Method getObjectRepositoryCall(Collection<? extends PhpNamedElement> phpNamedElements) { for (PhpNamedElement phpNamedElement: phpNamedElements) { if(phpNamedElement instanceof Method && new Symfony2InterfacesUtil().isObjectRepositoryCall((Method) phpNamedElement)) { return (Method) phpNamedElement; } } return null; } }