private void collectSelects(
      QueryBuilderScopeContext qb, MethodReference methodReference, String name) {

    if (!Arrays.asList("select", "addSelect").contains(name)) {
      return;
    }

    // $qb->select('foo')
    PsiElement psiElement = PsiElementUtils.getMethodParameterPsiElementAt(methodReference, 0);
    String literalValue = PhpElementsUtil.getStringValue(psiElement);
    if (literalValue != null) {
      qb.addSelect(literalValue);
      return;
    }

    // $qb->select(array('foo', 'bar', 'accessoryDetail'))
    if (psiElement instanceof ArrayCreationExpression) {
      for (PsiElement arrayValue :
          PsiElementUtils.getChildrenOfTypeAsList(
              psiElement, PlatformPatterns.psiElement(PhpElementTypes.ARRAY_VALUE))) {
        if (arrayValue.getChildren().length == 1) {
          String arrayValueString = PhpElementsUtil.getStringValue(arrayValue.getChildren()[0]);
          if (arrayValueString != null) {
            qb.addSelect(arrayValueString);
          }
        }
      }
    }
  }
  protected Collection<PsiElement> classGoToDeclaration(PsiElement psiElement, String className) {

    Collection<PsiElement> psiElements = new HashSet<PsiElement>();

    // Class::method
    // Class::FooAction
    // Class:Foo
    if (className.contains(":")) {
      String[] split = className.replaceAll("(:)\\1", "$1").split(":");
      if (split.length == 2) {
        for (String append : new String[] {"", "Action"}) {
          Method classMethod =
              PhpElementsUtil.getClassMethod(psiElement.getProject(), split[0], split[1] + append);
          if (classMethod != null) {
            psiElements.add(classMethod);
          }
        }
      }

      return psiElements;
    }

    // ClassName
    psiElements.addAll(PhpElementsUtil.getClassesInterface(psiElement.getProject(), className));
    return psiElements;
  }
  public static PsiElement findArrayKeyValueInsideReference(
      PsiElement psiElement, String methodReferenceName, String keyName) {

    if (psiElement == null) {
      return null;
    }

    Collection<MethodReference> tests =
        PsiTreeUtil.findChildrenOfType(psiElement, MethodReference.class);
    for (MethodReference methodReference : tests) {

      // instance check
      // methodReference.getSignature().equals("#M#C\\Symfony\\Component\\OptionsResolver\\OptionsResolverInterface.setDefaults")
      if (PhpElementsUtil.isEqualMethodReferenceName(methodReference, methodReferenceName)) {
        PsiElement[] parameters = methodReference.getParameters();
        if (parameters.length > 0 && parameters[0] instanceof ArrayCreationExpression) {
          PsiElement keyValue =
              PhpElementsUtil.getArrayValue((ArrayCreationExpression) parameters[0], keyName);
          if (keyValue != null) {
            return keyValue;
          }
        }
      }
    }

    return null;
  }
  @NotNull
  public static Collection<SymfonyCommand> getCommands(@NotNull Project project) {

    Collection<SymfonyCommand> symfonyCommands = new ArrayList<SymfonyCommand>();

    for (PhpClass phpClass :
        PhpIndex.getInstance(project)
            .getAllSubclasses("\\Symfony\\Component\\Console\\Command\\Command")) {

      if (PhpElementsUtil.isTestClass(phpClass)) {
        continue;
      }

      Method method = phpClass.findOwnMethodByName("configure");
      if (method == null) {
        continue;
      }

      PsiElement[] psiElements =
          PsiTreeUtil.collectElements(
              method,
              new PsiElementFilter() {
                @Override
                public boolean isAccepted(PsiElement psiElement) {
                  return psiElement instanceof MethodReference
                      && "setName".equals(((MethodReference) psiElement).getName());
                }
              });

      for (PsiElement psiElement : psiElements) {

        if (!(psiElement instanceof MethodReference)) {
          continue;
        }

        PsiElement psiMethodParameter =
            PsiElementUtils.getMethodParameterPsiElementAt((MethodReference) psiElement, 0);
        if (psiMethodParameter == null) {
          continue;
        }

        String stringValue = PhpElementsUtil.getStringValue(psiMethodParameter);
        if (stringValue == null) {
          continue;
        }

        symfonyCommands.add(new SymfonyCommand(stringValue, psiElement));
      }
    }

    return symfonyCommands;
  }
  /** finishView, buildView: $this->vars */
  public static Set<String> getFormViewVars(Project project, String... formTypeNames) {

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

    ArrayList<PhpClass> phpClasses = new ArrayList<PhpClass>();

    // attach core form phpclass
    // @TODO: add formtype itself
    PhpClass coreForm = FormUtil.getFormTypeToClass(project, "form");
    if (coreForm != null) {
      phpClasses.add(coreForm);
    }

    // for extension can also provide vars
    for (Map.Entry<String, String> entry :
        FormOptionsUtil.getFormExtensionKeys(project, formTypeNames).entrySet()) {
      PhpClass phpClass = PhpElementsUtil.getClassInterface(project, entry.getValue());
      if (phpClass != null) {
        phpClasses.add(phpClass);
      }
    }

    for (PhpClass phpClass : phpClasses) {
      for (String stringMethod : new String[] {"finishView", "buildView"}) {
        Method method = PhpElementsUtil.getClassMethod(phpClass, stringMethod);
        if (method != null) {

          // self method
          getMethodVars(stringSet, method);

          // allow parent::
          // @TODO: provide global util method
          for (ClassReference classReference :
              PsiTreeUtil.collectElementsOfType(method, ClassReference.class)) {
            if ("parent".equals(classReference.getName())) {
              PsiElement methodReference = classReference.getContext();
              if (methodReference instanceof MethodReference) {
                PsiElement parentMethod = ((MethodReference) methodReference).resolve();
                if (parentMethod instanceof Method) {
                  getMethodVars(stringSet, (Method) parentMethod);
                }
              }
            }
          }
        }
      }
    }

    return stringSet;
  }
  private void buildPropertyMap(QueryBuilderScopeContext qb) {

    if (!collectProperties) {
      return;
    }

    for (QueryBuilderJoin join : qb.getJoinMap().values()) {
      String className = join.getResolvedClass();
      if (className != null) {
        PhpClass phpClass = PhpElementsUtil.getClassInterface(project, className);
        if (phpClass != null) {
          qb.addPropertyAlias(
              join.getAlias(),
              new QueryBuilderPropertyAlias(
                  join.getAlias(),
                  null,
                  new DoctrineModelField(join.getAlias())
                      .addTarget(phpClass)
                      .setTypeName(phpClass.getPresentableFQN())));

          // add entity properties
          for (DoctrineModelField field : EntityHelper.getModelFields(phpClass)) {
            qb.addPropertyAlias(
                join.getAlias() + "." + field.getName(),
                new QueryBuilderPropertyAlias(join.getAlias(), field.getName(), field));
          }
        }
      }
    }
  }
  @Nullable
  @Override
  public String getType(PsiElement e) {
    if (DumbService.getInstance(e.getProject()).isDumb()
        || !Settings.getInstance(e.getProject()).pluginEnabled
        || !Settings.getInstance(e.getProject()).objectManagerFindTypeProvider) {
      return null;
    }

    if (!(e instanceof MethodReference)
        || !PhpElementsUtil.isMethodWithFirstStringOrFieldReference(e, "find")) {
      return null;
    }

    String refSignature = ((MethodReference) e).getSignature();
    if (StringUtil.isEmpty(refSignature)) {
      return null;
    }

    // we need the param key on getBySignature(), since we are already in the resolved method there
    // attach it to signature
    // param can have dotted values split with \
    PsiElement[] parameters = ((MethodReference) e).getParameters();
    if (parameters.length == 2) {
      return PhpTypeProviderUtil.getReferenceSignature((MethodReference) e, TRIM_KEY, 2);
    }

    return null;
  }
  public static ArrayList<String> getExtendedTypeClasses(Project project, String... formTypeNames) {

    List<String> formTypeNamesList = Arrays.asList(formTypeNames);

    ArrayList<String> extendedTypeClasses = new ArrayList<String>();

    FormExtensionServiceParser formExtensionServiceParser =
        ServiceXmlParserFactory.getInstance(project, FormExtensionServiceParser.class);
    for (String formClass : formExtensionServiceParser.getFormExtensions().keySet()) {

      PsiElement psiElements[] =
          PhpElementsUtil.getPsiElementsBySignature(
              project, "#M#C\\" + formClass + ".getExtendedType");
      for (PsiElement psiElement : psiElements) {
        PhpReturn phpReturn = PsiTreeUtil.findChildOfType(psiElement, PhpReturn.class);
        if (phpReturn != null) {
          PhpPsiElement returnValue = phpReturn.getFirstPsiChild();
          if (returnValue instanceof StringLiteralExpression
              && formTypeNamesList.contains(
                  ((StringLiteralExpression) returnValue).getContents())) {
            extendedTypeClasses.add(formClass);
          }
        }
      }
    }

    return extendedTypeClasses;
  }
  /** $this->vars['test'] $view->vars = array_replace($view->vars, array(...)); */
  private static void getFormViewVarsAttachKeys(
      Set<String> stringSet, FieldReference fieldReference) {

    // $this->vars['test']
    PsiElement context = fieldReference.getContext();
    if (context instanceof ArrayAccessExpression) {
      ArrayIndex arrayIndex = PsiTreeUtil.findChildOfType(context, ArrayIndex.class);
      if (arrayIndex != null) {
        PsiElement psiElement = arrayIndex.getFirstChild();
        if (psiElement instanceof StringLiteralExpression) {
          String contents = ((StringLiteralExpression) psiElement).getContents();
          if (StringUtils.isNotBlank(contents)) {
            stringSet.add(contents);
          }
        }
      }
    }

    // array_replace($view->vars, array(...))
    if (context instanceof ParameterList) {
      PsiElement functionReference = context.getContext();
      if (functionReference instanceof FunctionReference
          && "array_replace".equals(((FunctionReference) functionReference).getName())) {
        PsiElement[] psiElements = ((ParameterList) context).getParameters();
        if (psiElements.length > 1) {
          if (psiElements[1] instanceof ArrayCreationExpression) {
            stringSet.addAll(
                PhpElementsUtil.getArrayCreationKeys((ArrayCreationExpression) psiElements[1]));
          }
        }
      }
    }
  }
    @Override
    protected void addCompletions(
        @NotNull CompletionParameters completionParameters,
        ProcessingContext processingContext,
        @NotNull CompletionResultSet completionResultSet) {

      PsiElement position = completionParameters.getPosition();
      if (!Symfony2ProjectComponent.isEnabled(position)) {
        return;
      }

      YAMLCompoundValue yamlCompoundValue =
          PsiTreeUtil.getParentOfType(position, YAMLCompoundValue.class);
      if (yamlCompoundValue == null) {
        return;
      }

      String className =
          YamlHelper.getYamlKeyValueAsString(yamlCompoundValue, "targetEntity", false);
      if (className == null) {
        return;
      }

      PhpClass phpClass = PhpElementsUtil.getClass(position.getProject(), className);
      if (phpClass == null) {
        return;
      }

      for (DoctrineModelField field : EntityHelper.getModelFields(phpClass)) {
        if (field.getRelation() != null) {
          completionResultSet.addElement(new DoctrineModelFieldLookupElement(field));
        }
      }
    }
  public static Collection<PhpClass> getClassFromPhpTypeSetArrayClean(
      Project project, Set<String> types) {

    PhpType phpType = new PhpType();
    phpType.add(types);

    ArrayList<PhpClass> phpClasses = new ArrayList<PhpClass>();

    for (String typeName :
        PhpIndex.getInstance(project)
            .completeType(project, phpType, new HashSet<String>())
            .getTypes()) {
      if (typeName.startsWith("\\")) {

        // we clean array types \Foo[]
        if (typeName.endsWith("[]")) {
          typeName = typeName.substring(0, typeName.length() - 2);
        }

        PhpClass phpClass = PhpElementsUtil.getClassInterface(project, typeName);
        if (phpClass != null) {
          phpClasses.add(phpClass);
        }
      }
    }

    return phpClasses;
  }
  @Nullable
  public static PsiElement getArrayKeyValueInsideSignaturePsi(
      PsiElement psiElementInsideClass, String callTo[], String methodName, String keyName) {
    PhpClass phpClass = PsiTreeUtil.getParentOfType(psiElementInsideClass, PhpClass.class);
    if (phpClass == null) {
      return null;
    }

    String className = phpClass.getPresentableFQN();
    if (className == null) {
      return null;
    }

    for (String s : callTo) {
      // @TODO: replace signature
      PsiElement arrayKeyValueInsideSignature =
          PhpElementsUtil.getArrayKeyValueInsideSignaturePsi(
              psiElementInsideClass.getProject(),
              "#M#C\\" + className + "." + s,
              methodName,
              keyName);
      if (arrayKeyValueInsideSignature != null) {
        return arrayKeyValueInsideSignature;
      }
    }

    return null;
  }
  /**
   * Get array string values mapped with their PsiElements
   *
   * <p>["value", "value2"]
   */
  @NotNull
  public static Map<String, PsiElement> getArrayValuesAsMap(
      @NotNull ArrayCreationExpression arrayCreationExpression) {

    List<PsiElement> arrayValues =
        PhpPsiUtil.getChildren(
            arrayCreationExpression,
            new Condition<PsiElement>() {
              @Override
              public boolean value(PsiElement psiElement) {
                return psiElement.getNode().getElementType() == PhpElementTypes.ARRAY_VALUE;
              }
            });

    if (arrayValues == null) {
      return Collections.emptyMap();
    }

    Map<String, PsiElement> keys = new HashMap<String, PsiElement>();
    for (PsiElement child : arrayValues) {
      String stringValue = PhpElementsUtil.getStringValue(child.getFirstChild());
      if (stringValue != null && StringUtils.isNotBlank(stringValue)) {
        keys.put(stringValue, child);
      }
    }

    return keys;
  }
  @Nullable
  public static Method findTwigFileController(TwigFile twigFile) {

    SymfonyBundle symfonyBundle =
        new SymfonyBundleUtil(twigFile.getProject()).getContainingBundle(twigFile);
    if (symfonyBundle == null) {
      return null;
    }

    String relativePath = symfonyBundle.getRelativePath(twigFile.getVirtualFile());
    if (relativePath == null || !relativePath.startsWith("Resources/views/")) {
      return null;
    }

    String viewPath = relativePath.substring("Resources/views/".length());

    Matcher simpleFilter = Pattern.compile(".*/(\\w+)\\.\\w+\\.twig").matcher(viewPath);
    if (!simpleFilter.find()) {
      return null;
    }

    String methodName = simpleFilter.group(1) + "Action";
    String className =
        symfonyBundle.getNamespaceName()
            + "Controller\\"
            + viewPath.substring(0, viewPath.lastIndexOf("/")).replace("/", "\\")
            + "Controller";

    return PhpElementsUtil.getClassMethod(twigFile.getProject(), className, methodName);
  }
  private static HashMap<String, String> getFormDefaultKeys(
      Project project, String formTypeName, HashMap<String, String> defaultValues, int depth) {

    PhpClass phpClass = FormUtil.getFormTypeToClass(project, formTypeName);
    if (phpClass == null) {
      return defaultValues;
    }

    String typeClass = phpClass.getPresentableFQN();
    attachOnDefaultOptions(project, defaultValues, typeClass);

    // recursive search for parent form types
    PsiElement getParent =
        PhpElementsUtil.getPsiElementsBySignatureSingle(
            project, "#M#C\\" + phpClass.getPresentableFQN() + ".getParent");
    if (getParent != null && depth < 10) {
      PhpReturn phpReturn = PsiTreeUtil.findChildOfType(getParent, PhpReturn.class);
      if (phpReturn != null) {
        PhpPsiElement returnValue = phpReturn.getFirstPsiChild();
        if (returnValue instanceof StringLiteralExpression) {
          getFormDefaultKeys(
              project,
              ((StringLiteralExpression) returnValue).getContents(),
              defaultValues,
              depth++);
        }
      }
    }

    return defaultValues;
  }
  @Nullable
  @Override
  public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int offset, Editor editor) {
    if (!Symfony2ProjectComponent.isEnabled(psiElement)
        || !PhpElementsUtil.getClassNamePattern().accepts(psiElement)) {
      return new PsiElement[0];
    }

    return ServiceIndexUtil.findServiceDefinitions((PhpClass) psiElement.getContext());
  }
  private static void attachOnDefaultOptions(
      Project project, HashMap<String, String> defaultValues, String typeClass) {

    PsiElement setDefaultOptions =
        PhpElementsUtil.getPsiElementsBySignatureSingle(
            project, "#M#C\\" + typeClass + ".setDefaultOptions");
    if (setDefaultOptions == null) {
      return;
    }

    Collection<MethodReference> tests =
        PsiTreeUtil.findChildrenOfType(setDefaultOptions, MethodReference.class);
    for (MethodReference methodReference : tests) {
      // instance check
      // methodReference.getSignature().equals("#M#C\\Symfony\\Component\\OptionsResolver\\OptionsResolverInterface.setDefaults")
      if (PhpElementsUtil.isEqualMethodReferenceName(methodReference, "setDefaults")) {
        PsiElement[] parameters = methodReference.getParameters();
        if (parameters.length > 0 && parameters[0] instanceof ArrayCreationExpression) {
          for (String key :
              PhpElementsUtil.getArrayCreationKeys((ArrayCreationExpression) parameters[0])) {
            defaultValues.put(key, typeClass);
          }
        }
      }

      // support: parent::setDefaultOptions($resolver)
      // Symfony\Component\Form\Extension\Core\Type\FormType:setDefaultOptions
      if (PhpElementsUtil.isEqualMethodReferenceName(methodReference, "setDefaultOptions")
          && methodReference.getReferenceType() == PhpModifier.State.PARENT) {
        PsiElement parentMethod =
            PhpElementsUtil.getPsiElementsBySignatureSingle(
                project, methodReference.getSignature());
        if (parentMethod instanceof Method) {
          PhpClass phpClass = ((Method) parentMethod).getContainingClass();
          if (phpClass != null) {
            attachOnDefaultOptions(project, defaultValues, phpClass.getPresentableFQN());
          }
        }
      }
    }
  }
  /** new FooClass() */
  @Nullable
  private static PhpClass getNewExpressionPhpClass(@NotNull NewExpression newExpression) {
    ClassReference classReference = newExpression.getClassReference();
    if (classReference != null) {
      String fqn = classReference.getFQN();
      if (fqn != null) {
        return PhpElementsUtil.getClass(newExpression.getProject(), fqn);
      }
    }

    return null;
  }
  public void testCreate() {
    DoctrineRepositoryLookupElement element =
        DoctrineRepositoryLookupElement.create(
            PhpElementsUtil.getClass(getProject(), "\\Foo\\Bar\\BarRepository"));
    LookupElementPresentation presentation = new LookupElementPresentation();
    element.renderElement(presentation);

    assertEquals("Foo\\Bar\\BarRepository", element.getLookupString());
    assertEquals(Symfony2Icons.DOCTRINE, presentation.getIcon());
    assertEquals("BarRepository", presentation.getItemText());
    assertEquals("Foo\\Bar\\BarRepository", presentation.getTypeText());
    assertTrue(presentation.isTypeGrayed());
  }
  @Nullable
  public static PsiElement getArrayKeyValueInsideSignaturePsi(
      Project project, String signature, String methodName, String keyName) {

    PsiElement psiElement = PhpElementsUtil.getPsiElementsBySignatureSingle(project, signature);
    if (psiElement == null) {
      return null;
    }

    for (MethodReference methodReference :
        PsiTreeUtil.findChildrenOfType(psiElement, MethodReference.class)) {

      if (PhpElementsUtil.isEqualMethodReferenceName(methodReference, methodName)) {
        PsiElement[] parameters = methodReference.getParameters();
        if (parameters.length > 0 && parameters[0] instanceof ArrayCreationExpression) {
          return PhpElementsUtil.getArrayValue((ArrayCreationExpression) parameters[0], keyName);
        }
      }
    }

    return null;
  }
  @Nullable
  public static PhpClass getPhpClassForCreateCompilerScope(@Nullable PhpClass phpClass) {

    if (phpClass == null) {
      return null;
    }

    if (!PhpElementsUtil.isInstanceOf(
        phpClass, "\\Symfony\\Component\\HttpKernel\\Bundle\\BundleInterface")) {
      return null;
    }

    return phpClass;
  }
  private void collectSelectInForm(
      QueryBuilderScopeContext qb, MethodReference methodReference, String name) {

    // $qb->from('foo', 'select')
    if (!"from".equals(name)) {
      return;
    }

    PsiElement psiElement = PsiElementUtils.getMethodParameterPsiElementAt(methodReference, 1);
    String literalValue = PhpElementsUtil.getStringValue(psiElement);
    if (literalValue != null) {
      qb.addSelect(literalValue);
    }
  }
  /**
   * Get class by shortcut namespace, on a scoped namespace
   *
   * @param project current project
   * @param classNameScope Namespace fo search "\Foo\Foo", "Foo\Foo", "Foo\Foo\", last "\*" is
   *     stripped
   * @param className Class name inside namespace also fqn is supported
   * @return PhpClass matched
   */
  public static PhpClass getClassInsideNamespaceScope(
      @NotNull Project project, @NotNull String classNameScope, @NotNull String className) {

    if (className.startsWith("\\")) {
      return PhpElementsUtil.getClassInterface(project, className);
    }

    // strip class name we namespace
    String strip = StringUtils.strip(classNameScope, "\\");
    int i = strip.lastIndexOf("\\");
    if (i <= 0) {
      return PhpElementsUtil.getClassInterface(project, className);
    }

    PhpClass phpClass =
        PhpElementsUtil.getClassInterface(
            project, strip.substring(0, i) + "\\" + StringUtils.strip(className, "\\"));
    if (phpClass != null) {
      return phpClass;
    }

    return PhpElementsUtil.getClassInterface(project, className);
  }
  protected PsiElement[] serviceGoToDeclaration(PsiElement psiElement, String serviceId) {

    serviceId = YamlHelper.trimSpecialSyntaxServiceName(serviceId).toLowerCase();

    String serviceClass =
        ContainerCollectionResolver.resolveService(psiElement.getProject(), serviceId);

    if (serviceClass != null) {
      PsiElement[] targetElements =
          PhpElementsUtil.getClassInterfacePsiElements(psiElement.getProject(), serviceClass);
      if (targetElements.length > 0) {
        return targetElements;
      }
    }

    // get container target on indexes
    List<PsiElement> possibleServiceTargets =
        ServiceIndexUtil.findServiceDefinitions(psiElement.getProject(), serviceId);
    return possibleServiceTargets.toArray(new PsiElement[possibleServiceTargets.size()]);
  }
  private static void addYamlClassMethods(
      @Nullable PsiElement psiElement, CompletionResultSet completionResultSet, String classTag) {

    if (psiElement == null) {
      return;
    }

    YAMLKeyValue classKeyValue =
        PsiElementUtils.getChildrenOfType(
            psiElement, PlatformPatterns.psiElement(YAMLKeyValue.class).withName(classTag));
    if (classKeyValue == null) {
      return;
    }

    PhpClass phpClass =
        ServiceUtil.getResolvedClassDefinition(
            psiElement.getProject(), classKeyValue.getValueText());
    if (phpClass != null) {
      PhpElementsUtil.addClassPublicMethodCompletion(completionResultSet, phpClass);
    }
  }
  public static Collection<PhpClass> getClassFromPhpTypeSet(Project project, Set<String> types) {

    PhpType phpType = new PhpType();
    phpType.add(types);

    List<PhpClass> phpClasses = new ArrayList<PhpClass>();

    for (String typeName :
        PhpIndex.getInstance(project)
            .completeType(project, phpType, new HashSet<String>())
            .getTypes()) {
      if (typeName.startsWith("\\")) {
        PhpClass phpClass = PhpElementsUtil.getClassInterface(project, typeName);
        if (phpClass != null) {
          phpClasses.add(phpClass);
        }
      }
    }

    return phpClasses;
  }
  private Map<String, String> findRootDefinition(Collection<MethodReference> methodReferences) {

    Map<String, String> roots = new HashMap<String, String>();

    if (methodReferences.size() == 0) {
      return roots;
    }

    String rootAlias = null;
    String repository = null;

    for (MethodReference methodReference : methodReferences) {
      String methodReferenceName = methodReference.getName();

      // get alias
      // ->createQueryBuilder('test');
      if ("createQueryBuilder".equals(methodReferenceName)) {
        String possibleAlias =
            PhpElementsUtil.getStringValue(
                PsiElementUtils.getMethodParameterPsiElementAt(methodReference, 0));
        if (possibleAlias != null) {
          rootAlias = possibleAlias;
        }
      }

      // find repository class
      // getRepository('Foo')->createQueryBuilder('test');
      if ("getRepository".equals(methodReferenceName)) {
        String possibleRepository =
            PhpElementsUtil.getStringValue(
                PsiElementUtils.getMethodParameterPsiElementAt(methodReference, 0));
        if (possibleRepository != null) {
          repository = possibleRepository;
          PhpClass phpClass = EntityHelper.resolveShortcutName(project, repository);
          if (phpClass != null) {
            repository = phpClass.getPresentableFQN();
          }
        }
      }

      // $qb->from('Foo\Class', 'article')
      if ("from".equals(methodReferenceName)) {
        String table =
            PhpElementsUtil.getStringValue(
                PsiElementUtils.getMethodParameterPsiElementAt(methodReference, 0));
        String alias =
            PhpElementsUtil.getStringValue(
                PsiElementUtils.getMethodParameterPsiElementAt(methodReference, 1));
        if (table != null && alias != null) {
          PhpClass phpClass = EntityHelper.resolveShortcutName(project, table);
          if (phpClass != null) {
            table = phpClass.getPresentableFQN();
          }

          roots.put(table, alias);
        }
      }
    }

    // we have a valid root so add it
    if (rootAlias != null && repository != null) {
      roots.put(repository, rootAlias);
    }

    // we found a alias but not a repository name, so try a scope search if we are inside repository
    // class
    // class implements \Doctrine\Common\Persistence\ObjectRepository, so search for model name of
    // "repositoryClass"
    if (rootAlias != null && repository == null) {
      MethodReference methodReference = methodReferences.iterator().next();
      PhpClass phpClass = PsiTreeUtil.getParentOfType(methodReference, PhpClass.class);
      if (new Symfony2InterfacesUtil()
          .isInstanceOf(phpClass, "\\Doctrine\\Common\\Persistence\\ObjectRepository")) {
        for (DoctrineModel model : EntityHelper.getModelClasses(project)) {
          String className = model.getPhpClass().getPresentableFQN();
          if (className != null) {
            PhpClass resolvedRepoName = EntityHelper.getEntityRepositoryClass(project, className);
            if (PhpElementsUtil.isEqualClassName(resolvedRepoName, phpClass.getPresentableFQN())) {
              roots.put(className, rootAlias);
              return roots;
            }
          }
        }
      }
    }

    // search on PhpTypeProvider
    // $er->createQueryBuilder()
    if (rootAlias != null && repository == null) {
      for (MethodReference methodReference : methodReferences) {
        if ("createQueryBuilder".equals(methodReference.getName())) {
          String signature = methodReference.getSignature();
          int endIndex = signature.lastIndexOf(ObjectRepositoryTypeProvider.TRIM_KEY);
          if (endIndex != -1) {
            String parameter = signature.substring(endIndex + 1);
            int point = parameter.indexOf(".");
            if (point > -1) {
              parameter = parameter.substring(0, point);
              parameter =
                  PhpTypeProviderUtil.getResolvedParameter(
                      PhpIndex.getInstance(project), parameter);
              if (parameter != null) {
                PhpClass phpClass = EntityHelper.resolveShortcutName(project, parameter);
                if (phpClass != null && phpClass.getPresentableFQN() != null) {
                  roots.put(phpClass.getPresentableFQN(), rootAlias);
                  return roots;
                }
              }
            }
          }
        }
      }
    }

    return roots;
  }
  public QueryBuilderScopeContext collect() {
    QueryBuilderScopeContext qb = new QueryBuilderScopeContext();

    // doctrine needs valid root with an alias, try to find one in method references or scope
    Map<String, String> map = this.findRootDefinition(methodReferences);
    if (map.size() > 0) {
      Map.Entry<String, String> entry = map.entrySet().iterator().next();
      qb.addTable(entry.getKey(), entry.getValue());
    }

    for (MethodReference methodReference : methodReferences) {

      String name = methodReference.getName();
      if (name != null) {
        collectParameter(qb, methodReference, name);
        collectJoins(qb, methodReference, name);
        collectSelects(qb, methodReference, name);
        collectSelectInForm(qb, methodReference, name);
      }
    }

    // first tableMap entry is root, we add several initial data
    if (qb.getTableMap().size() > 0) {
      Map.Entry<String, String> entry = qb.getTableMap().entrySet().iterator().next();
      String className = entry.getKey();
      PhpClass phpClass = PhpElementsUtil.getClassInterface(project, className);

      // add root select fields
      if (phpClass != null) {

        qb.addPropertyAlias(
            entry.getValue(),
            new QueryBuilderPropertyAlias(
                entry.getValue(),
                null,
                new DoctrineModelField(entry.getValue())
                    .addTarget(phpClass)
                    .setTypeName(phpClass.getPresentableFQN())));

        List<QueryBuilderRelation> relationList = new ArrayList<QueryBuilderRelation>();

        // qb.addRelation(entry.getValue(), attachRelationFields(phpClass));
        for (DoctrineModelField field : EntityHelper.getModelFields(phpClass)) {
          qb.addPropertyAlias(
              entry.getValue() + "." + field.getName(),
              new QueryBuilderPropertyAlias(entry.getValue(), field.getName(), field));
          if (field.getRelation() != null && field.getRelationType() != null) {
            relationList.add(new QueryBuilderRelation(field.getName(), field.getRelation()));
          }
        }

        qb.addRelation(entry.getValue(), relationList);
      }

      QueryBuilderRelationClassResolver resolver =
          new QueryBuilderRelationClassResolver(
              project, entry.getValue(), entry.getKey(), qb.getRelationMap(), qb.getJoinMap());
      resolver.collect();
    }

    // we have a querybuilder which complete known elements now
    // se we can builder a property (field) map table from it
    this.buildPropertyMap(qb);

    return qb;
  }