@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;
  }
  @NotNull
  @Override
  public Collection<PsiElement> getPsiTargets(
      @NotNull SourceContributorDeclarationHandlerParameter parameter) {
    String contents = parameter.getHandlerParameter().getContents();
    if (StringUtils.isBlank(contents)) {
      return Collections.emptyList();
    }

    String sourceParameter = parameter.getSourceParameter();
    if (sourceParameter == null) {
      return Collections.emptyList();
    }

    final Collection<PsiElement> psiElements = new ArrayList<PsiElement>();

    for (PhpClass phpClass :
        PhpIndex.getInstance(parameter.getProject()).getAllSubclasses(sourceParameter)) {
      if (StringUtils.stripStart(contents, "\\")
          .equalsIgnoreCase(StringUtils.stripStart(phpClass.getPresentableFQN(), "\\"))) {
        psiElements.add(phpClass);
      }
    }

    return psiElements;
  }
  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;
  }
    @Override
    protected void addCompletions(
        @NotNull CompletionParameters parameters,
        ProcessingContext processingContext,
        @NotNull CompletionResultSet completionResultSet) {

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

      PsiElement prevSiblingOfType =
          PsiElementUtils.getPrevSiblingOfType(
              position, YamlElementPatternHelper.getPreviousCommaSibling());
      if (prevSiblingOfType == null) {
        return;
      }

      String service = PsiElementUtils.trimQuote(prevSiblingOfType.getText());
      if (StringUtils.isBlank(service)) {
        return;
      }

      PhpClass phpClass = ServiceUtil.getServiceClass(prevSiblingOfType.getProject(), service);
      if (phpClass == null) {
        return;
      }

      for (Method method : phpClass.getMethods()) {
        if (method.getAccess().isPublic() && !(method.getName().startsWith("__"))) {
          completionResultSet.addElement(new PhpLookupElement(method));
        }
      }
    }
  public ControllerAction(PhpClass phpClass, int startOffset) {
    this.currentControllerName = phpClass.getName();
    Pattern pattern = Pattern.compile(".*?this->render\\((.*?)\\).*?");

    for (Method method : phpClass.getOwnMethods()) {
      TextRange textRange = method.getTextRange();
      final String methodName = method.getName();
      Matcher matcher = pattern.matcher(method.getText());
      List<String> renderViews =
          new ArrayList<String>() {
            {
              add(methodName);
            }
          };

      while (matcher.find()) {
        renderViews.add(matcher.group(1).replace("'", ""));
      }

      if (textRange.getStartOffset() <= startOffset && startOffset <= textRange.getEndOffset()) {
        this.currentActionName = methodName;
      }

      // Method name will be unique. Because PHP doesn't have [overroad] like java.
      actions.put(methodName, new Function(method.getName(), textRange, renderViews));
    }
  }
  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));
          }
        }
      }
    }
  }
  private void parseTypes2(
      Map<String, Collection<String>> map, Iterable<ArrayHashElement> elements, String parent) {
    Collection<String> types = map.get(parent);
    if (types == null) {
      types = new ArrayList<String>();
      map.put(parent, types);
    }

    for (ArrayHashElement element : elements) {
      PhpPsiElement key = element.getKey();
      if (key instanceof StringLiteralExpression) {
        // key
        String keyName = ((StringLiteralExpression) key).getContents();
        types.add(keyName);

        String fullKeyName = parent.length() > 0 ? (parent + "." + keyName) : keyName;

        // value
        PhpPsiElement val = element.getValue();
        if (val instanceof ArrayCreationExpression) { // recursive
          Iterable<ArrayHashElement> subElements =
              ((ArrayCreationExpression) val).getHashElements();
          parseTypes2(map, subElements, fullKeyName);

        } else if (val instanceof FieldReference) { // reference to a field, where it's defined
          String classFqn = ((ClassReference) ((FieldReference) val).getClassReference()).getFQN();
          for (PhpClass phpClass :
              PhpIndex.getInstance(element.getProject()).getClassesByFQN(classFqn)) {
            Field field = phpClass.findFieldByName(((FieldReference) val).getNameCS(), false);
            if (field.getDefaultValue() instanceof ArrayCreationExpression) {
              Iterable<ArrayHashElement> subElements =
                  ((ArrayCreationExpression) field.getDefaultValue()).getHashElements();
              parseTypes2(map, subElements, fullKeyName);
            }
          }

        } else { // get value type
          parseValueType(val);

          // try annotation
          PsiElement el2 = element;
          while (el2 != null
              && (el2 instanceof LeafPsiElement
                      && ((LeafPsiElement) el2).getElementType() == PhpTokenTypes.opCOMMA
                  || el2 instanceof PsiWhiteSpace)) {
            el2 = el2.getNextSibling();
          }
          if (el2 instanceof PsiComment) {
            System.out.println("Comment for " + fullKeyName + ": " + el2.getText());
          }
        }
      }
    }
  }
  /**
   * Try to visit possible class name for PsiElements with text like "Foo\|Bar", "Foo|\Bar",
   * "\Foo|\Bar" Cursor must have position in PsiElement
   *
   * @param psiElement the element context, cursor should be in it
   * @param cursorOffset current cursor editor eg from completion context
   * @param visitor callback on matching class
   */
  public static void visitNamespaceClassForCompletion(
      PsiElement psiElement, int cursorOffset, ClassForCompletionVisitor visitor) {

    int cursorOffsetClean = cursorOffset - psiElement.getTextOffset();
    if (cursorOffsetClean < 1) {
      return;
    }

    String content = psiElement.getText();
    int length = content.length();
    if (!(length >= cursorOffsetClean)) {
      return;
    }

    String beforeCursor = content.substring(0, cursorOffsetClean);
    boolean isValid;

    // espend\|Container, espend\Cont|ainer <- fallback to last full namespace
    // espend|\Container <- only on known namespace "espend"
    String namespace = beforeCursor;

    // if no backslash or its equal in first position, fallback on namespace completion
    int lastSlash = beforeCursor.lastIndexOf("\\");
    if (lastSlash <= 0) {
      isValid = PhpIndexUtil.hasNamespace(psiElement.getProject(), beforeCursor);
    } else {
      isValid = true;
      namespace = beforeCursor.substring(0, lastSlash);
    }

    if (!isValid) {
      return;
    }

    // format namespaces and add prefix for fluent completion
    String prefix = "";
    if (namespace.startsWith("\\")) {
      prefix = "\\";
    } else {
      namespace = "\\" + namespace;
    }

    // search classes in current namespace and child namespaces
    for (PhpClass phpClass :
        PhpIndexUtil.getPhpClassInsideNamespace(psiElement.getProject(), namespace)) {
      String presentableFQN = phpClass.getPresentableFQN();
      if (presentableFQN != null
          && fr.adrienbrault.idea.symfony2plugin.util.StringUtils.startWithEqualClassname(
              presentableFQN, beforeCursor)) {
        visitor.visit(phpClass, presentableFQN, prefix);
      }
    }
  }
  @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;
  }
  @Nullable
  public static Method getClassMethod(
      @NotNull Project project, @NotNull String phpClassName, @NotNull String methodName) {

    // we need here an each; because eg Command is non unique because phar file
    for (PhpClass phpClass : PhpIndex.getInstance(project).getClassesByFQN(phpClassName)) {
      Method method = phpClass.findMethodByName(methodName);
      if (method != null) {
        return method;
      }
    }

    return null;
  }
  @Nullable
  public static PsiElement invokeCreateCompilerPass(
      @NotNull PhpClass bundleClass, @Nullable Editor editor) {
    String className =
        Messages.showInputDialog(
            "Class name for CompilerPass (no namespace needed): ",
            "New File",
            Symfony2Icons.SYMFONY);
    if (StringUtils.isBlank(className)) {
      return null;
    }

    if (!PhpNameUtil.isValidClassName(className)) {
      Messages.showMessageDialog(
          bundleClass.getProject(), "Invalid class name", "Error", Symfony2Icons.SYMFONY);
    }

    try {
      return PhpBundleFileFactory.createCompilerPass(bundleClass, className);
    } catch (Exception e) {
      if (editor != null) {
        HintManager.getInstance().showErrorHint(editor, "Error:" + e.getMessage());
      } else {
        JOptionPane.showMessageDialog(null, "Error:" + e.getMessage());
      }
    }

    return null;
  }
  /**
   * Find a string return value of a method context "function() { return 'foo'}" First match wins
   */
  @Nullable
  public static String getMethodReturnAsString(
      @NotNull PhpClass phpClass, @NotNull String methodName) {

    Method method = phpClass.findMethodByName(methodName);
    if (method == null) {
      return null;
    }

    final Set<String> values = new HashSet<String>();
    method.acceptChildren(
        new PsiRecursiveElementWalkingVisitor() {
          @Override
          public void visitElement(PsiElement element) {

            if (PhpElementsUtil.getMethodReturnPattern().accepts(element)) {
              String value = PhpElementsUtil.getStringValue(element);
              if (value != null && StringUtils.isNotBlank(value)) {
                values.add(value);
              }
            }

            super.visitElement(element);
          }
        });

    if (values.size() == 0) {
      return null;
    }

    // we support only first item
    return values.iterator().next();
  }
  @Override
  public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement psiElement)
      throws IncorrectOperationException {

    PsiElement parentByCondition = PhpPsiUtil.getParentByCondition(psiElement, Method.INSTANCEOF);
    if (parentByCondition == null) {
      return;
    }

    PhpClass phpClass = PhpPsiUtil.getParentByCondition(psiElement, PhpClass.INSTANCEOF);
    if (phpClass == null) {
      return;
    }

    ServiceGenerateAction.invokeServiceGenerator(project, phpClass.getContainingFile(), phpClass);
  }
  @NotNull
  public static PsiElement createBundleFile(
      @NotNull PhpClass bundleClass,
      @NotNull String template,
      @NotNull String className,
      Map<String, String> vars)
      throws Exception {

    VirtualFile directory =
        bundleClass.getContainingFile().getContainingDirectory().getVirtualFile();
    if (fileExists(directory, new String[] {className})) {
      throw new Exception("File already exists");
    }

    String COMPILER_TEMPLATE = "/resources/fileTemplates/" + template + ".php";
    String fileTemplateContent = getFileTemplateContent(COMPILER_TEMPLATE);
    if (fileTemplateContent == null) {
      throw new Exception("Template content error");
    }

    String[] split = className.split("\\\\");

    String ns = bundleClass.getNamespaceName();
    String join = StringUtils.join(Arrays.copyOf(split, split.length - 1), "/");

    vars.put("ns", (ns.startsWith("\\") ? ns.substring(1) : ns) + join.replace("/", "\\"));
    vars.put("class", split[split.length - 1]);
    for (Map.Entry<String, String> entry : vars.entrySet()) {
      fileTemplateContent =
          fileTemplateContent.replace("{{ " + entry.getKey() + " }}", entry.getValue());
    }

    VirtualFile compilerDirectory = getAndCreateDirectory(directory, join);
    if (compilerDirectory == null) {
      throw new Exception("Directory creation failed");
    }

    Project project = bundleClass.getProject();
    PsiFile fileFromText =
        PsiFileFactory.getInstance(project)
            .createFileFromText(
                split[split.length - 1] + ".php", PhpFileType.INSTANCE, fileTemplateContent);
    CodeStyleManager.getInstance(project).reformat(fileFromText);
    return PsiDirectoryFactory.getInstance(project)
        .createDirectory(compilerDirectory)
        .add(fileFromText);
  }
 @Nullable
 public static Method getClassMethod(PhpClass phpClass, String methodName) {
   for (Method method : phpClass.getMethods()) {
     if (method.getName().equals(methodName)) {
       return method;
     }
   }
   return null;
 }
  @NotNull
  @Override
  public Collection<LookupElement> getLookupElements(
      @NotNull SourceContributorParameter parameter) {
    String sourceParameter = parameter.getSourceParameter();
    if (sourceParameter == null) {
      return Collections.emptyList();
    }

    Collection<LookupElement> lookupElements = new ArrayList<LookupElement>();
    for (PhpClass phpClass :
        PhpIndex.getInstance(parameter.getProject()).getAllSubclasses(sourceParameter)) {
      lookupElements.add(
          LookupElementBuilder.create(phpClass.getPresentableFQN()).withIcon(phpClass.getIcon()));
    }

    return lookupElements;
  }
  @Nullable
  public static String getControllerMethodShortcut(Method method) {

    // indexAction
    String methodName = method.getName();
    if (!methodName.endsWith("Action")) {
      return null;
    }

    PhpClass phpClass = method.getContainingClass();
    if (null == phpClass) {
      return null;
    }

    // defaultController
    // default/Folder/FolderController
    String className = phpClass.getName();
    if (!className.endsWith("Controller")) {
      return null;
    }

    SymfonyBundleUtil symfonyBundleUtil =
        new SymfonyBundleUtil(PhpIndex.getInstance(method.getProject()));
    SymfonyBundle symfonyBundle = symfonyBundleUtil.getContainingBundle(phpClass);
    if (symfonyBundle == null) {
      return null;
    }

    // find the bundle name of file
    PhpClass BundleClass = symfonyBundle.getPhpClass();
    if (null == BundleClass) {
      return null;
    }

    // check if files is in <Bundle>/Controller/*
    if (!phpClass.getNamespaceName().startsWith(BundleClass.getNamespaceName() + "Controller\\")) {
      return null;
    }

    // strip the controller folder name
    String templateFolderName =
        phpClass.getNamespaceName().substring(BundleClass.getNamespaceName().length() + 11);

    // HomeBundle:default:indexes
    // HomeBundle:default/Test:indexes
    templateFolderName = templateFolderName.replace("\\", "/");
    String shortcutName =
        symfonyBundle.getName()
            + ":"
            + templateFolderName
            + className.substring(0, className.lastIndexOf("Controller"))
            + ":"
            + methodName.substring(0, methodName.lastIndexOf("Action"));

    // we should support types later on
    // HomeBundle:default:indexes.html.twig
    return shortcutName + ".html.twig";
  }
  public static ArrayList<Method> getClassPublicMethod(PhpClass phpClass) {
    ArrayList<Method> methods = new ArrayList<Method>();

    for (Method method : phpClass.getMethods()) {
      if (method.getAccess().isPublic() && !method.getName().startsWith("__")) {
        methods.add(method);
      }
    }

    return methods;
  }
  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());
          }
        }
      }
    }
  }
  private static ArrayList<Method> getImplementedMethods(
      @Nullable PhpClass phpClass, @NotNull Method method, ArrayList<Method> implementedMethods) {
    if (phpClass == null) {
      return implementedMethods;
    }

    Method[] methods = phpClass.getOwnMethods();
    for (Method ownMethod : methods) {
      if (PhpLangUtil.equalsMethodNames(ownMethod.getName(), method.getName())) {
        implementedMethods.add(ownMethod);
      }
    }

    for (PhpClass interfaceClass : phpClass.getImplementedInterfaces()) {
      getImplementedMethods(interfaceClass, method, implementedMethods);
    }

    getImplementedMethods(phpClass.getSuperClass(), method, implementedMethods);

    return implementedMethods;
  }
    @Override
    protected void addCompletions(
        @NotNull CompletionParameters parameters,
        ProcessingContext processingContext,
        @NotNull CompletionResultSet completionResultSet) {

      if (!Symfony2ProjectComponent.isEnabled(parameters.getPosition())) {
        return;
      }

      PhpIndex phpIndex = PhpIndex.getInstance(parameters.getOriginalFile().getProject());
      for (PhpClass phpClass : phpIndex.getAllSubclasses("\\Doctrine\\ORM\\EntityRepository")) {
        String presentableFQN = phpClass.getPresentableFQN();
        if (presentableFQN != null) {
          completionResultSet.addElement(
              LookupElementBuilder.create(phpClass.getName())
                  .withTypeText(phpClass.getPresentableFQN(), true)
                  .withIcon(phpClass.getIcon()));
        }
      }
    }
  public static boolean isTestClass(@NotNull PhpClass phpClass) {

    if (PhpUnitUtil.isTestClass(phpClass)) {
      return true;
    }

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

    return fqn.contains("\\Test\\") || fqn.contains("\\Tests\\");
  }
  public static boolean isEqualClassName(
      @Nullable PhpClass phpClass, @Nullable String compareClassName) {

    if (phpClass == null || compareClassName == null) {
      return false;
    }

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

    if (phpClassName.startsWith("\\")) {
      phpClassName = phpClassName.substring(1);
    }

    if (compareClassName.startsWith("\\")) {
      compareClassName = compareClassName.substring(1);
    }

    return phpClassName.equals(compareClassName);
  }
  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;
  }
 /** There is no need for this proxy method. We are api safe now */
 @Deprecated
 @Nullable
 public static Method getClassMethod(PhpClass phpClass, String methodName) {
   return phpClass.findMethodByName(methodName);
 }
  @NotNull
  public static PsiElement createCompilerPass(
      @NotNull PhpClass bundleClass, @NotNull String className) throws Exception {

    VirtualFile directory =
        bundleClass.getContainingFile().getContainingDirectory().getVirtualFile();
    if (fileExists(directory, className)) {
      throw new Exception("File already exists");
    }

    PhpPsiElement scopeForUseOperator = PhpCodeInsightUtil.findScopeForUseOperator(bundleClass);
    if (scopeForUseOperator == null) {
      throw new Exception("No 'use' scope found");
    }

    VirtualFile compilerDirectory = getAndCreateCompilerDirectory(directory);
    if (compilerDirectory == null) {
      throw new Exception("Directory creation failed");
    }

    Project project = bundleClass.getProject();

    if (bundleClass.findOwnMethodByName("build") == null) {

      insertUseIfNecessary(
          scopeForUseOperator, "\\Symfony\\Component\\DependencyInjection\\ContainerBuilder");

      Method method =
          PhpPsiElementFactory.createMethod(
              project,
              ""
                  + "public function build(ContainerBuilder $container)\n"
                  + "    {\n"
                  + "        parent::build($container);\n"
                  + "    }");

      PhpCodeEditUtil.insertClassMember(bundleClass, method);
    }

    Method buildMethod = bundleClass.findOwnMethodByName("build");
    if (buildMethod == null) {
      throw new Exception("No 'build' method found");
    }

    String relativePath = VfsUtil.getRelativePath(compilerDirectory, directory);
    if (relativePath == null) {
      throw new Exception("path error");
    }

    MethodReference methodReference =
        PhpPsiElementFactory.createMethodReference(
            project, "$container->addCompilerPass(new " + className + "());");

    String ns = bundleClass.getNamespaceName() + relativePath.replace("/", "\\");
    String nsClass = ns + "\\" + className;

    insertUseIfNecessary(scopeForUseOperator, nsClass);

    GroupStatement groupStatement =
        PhpPsiUtil.getChildByCondition(buildMethod, GroupStatement.INSTANCEOF);
    if (groupStatement != null) {
      PsiElement semicolon = methodReference.getNextSibling();
      groupStatement.addRangeBefore(methodReference, semicolon, groupStatement.getLastChild());
    }

    String COMPILER_TEMPLATE = "/resources/fileTemplates/compiler_pass.php";
    String fileTemplateContent = getFileTemplateContent(COMPILER_TEMPLATE);
    if (fileTemplateContent == null) {
      throw new Exception("Template content error");
    }

    String replace =
        fileTemplateContent
            .replace("{{ ns }}", ns.startsWith("\\") ? ns.substring(1) : ns)
            .replace("{{ class }}", className);
    PsiFile fileFromText =
        PsiFileFactory.getInstance(project)
            .createFileFromText(className + ".php", PhpFileType.INSTANCE, replace);
    CodeStyleManager.getInstance(project).reformat(fileFromText);

    return PsiDirectoryFactory.getInstance(project)
        .createDirectory(compilerDirectory)
        .add(fileFromText);
  }
 public static boolean isEqualClassName(
     @NotNull PhpClass phpClass, @NotNull PhpClass compareClassName) {
   return isEqualClassName(phpClass, compareClassName.getPresentableFQN());
 }