@Override
  public void addEntryPoint(@NotNull RefElement newEntryPoint, boolean isPersistent) {
    if (!newEntryPoint.isValid()) return;
    if (isPersistent) {
      if (newEntryPoint instanceof RefMethod && ((RefMethod) newEntryPoint).isConstructor()
          || newEntryPoint instanceof RefClass) {
        final ClassPattern classPattern = new ClassPattern();
        classPattern.pattern = new SmartRefElementPointerImpl(newEntryPoint, true).getFQName();
        getPatterns().add(classPattern);

        final EntryPointsManager entryPointsManager =
            getInstance(newEntryPoint.getElement().getProject());
        if (this != entryPointsManager) {
          entryPointsManager.addEntryPoint(newEntryPoint, true);
        }

        return;
      }
    }

    if (newEntryPoint instanceof RefClass) {
      RefClass refClass = (RefClass) newEntryPoint;

      if (refClass.isAnonymous()) {
        // Anonymous class cannot be an entry point.
        return;
      }

      List<RefMethod> refConstructors = refClass.getConstructors();
      if (refConstructors.size() == 1) {
        addEntryPoint(refConstructors.get(0), isPersistent);
      } else if (refConstructors.size() > 1) {
        // Many constructors here. Need to ask user which ones are used
        for (RefMethod refConstructor : refConstructors) {
          addEntryPoint(refConstructor, isPersistent);
        }
      }
    }

    if (!isPersistent) {
      myTemporaryEntryPoints.add(newEntryPoint);
      ((RefElementImpl) newEntryPoint).setEntry(true);
    } else {
      if (myPersistentEntryPoints.get(newEntryPoint.getExternalName()) == null) {
        final SmartRefElementPointerImpl entry =
            new SmartRefElementPointerImpl(newEntryPoint, true);
        myPersistentEntryPoints.put(entry.getFQName(), entry);
        ((RefElementImpl) newEntryPoint).setEntry(true);
        ((RefElementImpl) newEntryPoint).setPermanentEntry(true);
        if (entry.isPersistent()) { // do save entry points
          final EntryPointsManager entryPointsManager =
              getInstance(newEntryPoint.getElement().getProject());
          if (this != entryPointsManager) {
            entryPointsManager.addEntryPoint(newEntryPoint, true);
          }
        }
      }
    }
  }
 @Override
 public void onInitialize(RefElement refElement) {
   if (refElement instanceof RefMethod) {
     final PsiElement element = refElement.getElement();
     if (!(element instanceof PsiMethod)) return;
     if (!PsiType.BOOLEAN.equals(((PsiMethod) element).getReturnType())) return;
     refElement.putUserData(ALWAYS_INVERTED, Boolean.TRUE); // initial mark boolean methods
   }
 }
  @Override
  public Element export(
      @NotNull RefEntity refEntity, @NotNull final Element element, final int actualLine) {
    refEntity = getRefinedElement(refEntity);

    Element problem = new Element("problem");

    if (refEntity instanceof RefElement) {
      final RefElement refElement = (RefElement) refEntity;
      final SmartPsiElementPointer pointer = refElement.getPointer();
      PsiFile psiFile = pointer.getContainingFile();
      if (psiFile == null) return null;

      Element fileElement = new Element("file");
      Element lineElement = new Element("line");
      final VirtualFile virtualFile = psiFile.getVirtualFile();
      LOG.assertTrue(virtualFile != null);
      fileElement.addContent(virtualFile.getUrl());

      if (actualLine == -1) {
        final Document document =
            PsiDocumentManager.getInstance(pointer.getProject()).getDocument(psiFile);
        LOG.assertTrue(document != null);
        final Segment range = pointer.getRange();
        lineElement.addContent(
            String.valueOf(
                range != null ? document.getLineNumber(range.getStartOffset()) + 1 : -1));
      } else {
        lineElement.addContent(String.valueOf(actualLine));
      }

      problem.addContent(fileElement);
      problem.addContent(lineElement);

      appendModule(problem, refElement.getModule());
    } else if (refEntity instanceof RefModule) {
      final RefModule refModule = (RefModule) refEntity;
      final VirtualFile moduleFile = refModule.getModule().getModuleFile();
      final Element fileElement = new Element("file");
      fileElement.addContent(moduleFile != null ? moduleFile.getUrl() : refEntity.getName());
      problem.addContent(fileElement);
      appendModule(problem, refModule);
    }

    for (RefManagerExtension extension : myExtensions.values()) {
      extension.export(refEntity, problem);
    }

    new SmartRefElementPointerImpl(refEntity, true).writeExternal(problem);
    element.addContent(problem);
    return problem;
  }
 @Override
 public void iterate(@NotNull RefVisitor visitor) {
   for (RefElement refElement : getSortedElements()) {
     refElement.accept(visitor);
   }
   if (myModules != null) {
     for (RefModule refModule : myModules.values()) {
       refModule.accept(visitor);
     }
   }
   for (RefManagerExtension extension : myExtensions.values()) {
     extension.iterate(visitor);
   }
 }
  @Override
  public void removeRefElement(
      @NotNull RefElement refElement, @NotNull List<RefElement> deletedRefs) {
    List<RefEntity> children = refElement.getChildren();
    if (children != null) {
      RefElement[] refElements = children.toArray(new RefElement[children.size()]);
      for (RefElement refChild : refElements) {
        removeRefElement(refChild, deletedRefs);
      }
    }

    ((RefManagerImpl) refElement.getRefManager()).removeReference(refElement);
    ((RefElementImpl) refElement).referenceRemoved();
    if (!deletedRefs.contains(refElement)) deletedRefs.add(refElement);
  }
  @Override
  public CommonProblemDescriptor[] checkElement(
      @NotNull RefEntity refEntity,
      @NotNull AnalysisScope scope,
      @NotNull final InspectionManager manager,
      @NotNull final GlobalInspectionContext globalContext) {
    if (refEntity instanceof RefMethod) {
      RefMethod refMethod = (RefMethod) refEntity;
      if (!refMethod.isReferenced()) return null;
      if (hasNonInvertedCalls(refMethod)) return null;
      if (!refMethod.getSuperMethods().isEmpty()) return null;
      final PsiMethod psiMethod = (PsiMethod) refMethod.getElement();
      final PsiIdentifier psiIdentifier = psiMethod.getNameIdentifier();
      if (psiIdentifier != null) {
        final Collection<RefElement> inReferences = refMethod.getInReferences();
        if (inReferences.size() == 1) {
          final RefElement refElement = inReferences.iterator().next();
          final PsiElement usagesContainer = refElement.getElement();
          if (usagesContainer == null) return null;
          if (ReferencesSearch.search(psiMethod, new LocalSearchScope(usagesContainer))
              .forEach(
                  new Processor<PsiReference>() {
                    private final Set<PsiReference> myFoundRefs = new HashSet<>();

                    @Override
                    public boolean process(PsiReference reference) {
                      myFoundRefs.add(reference);
                      return myFoundRefs.size() < 2;
                    }
                  })) return null;
        }
        return new ProblemDescriptor[] {
          manager.createProblemDescriptor(
              psiIdentifier,
              InspectionsBundle.message("boolean.method.is.always.inverted.problem.descriptor"),
              (LocalQuickFix) getQuickFix(null),
              ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
              false)
        };
      }
    }
    return null;
  }
  @Override
  public void removeEntryPoint(@NotNull RefElement anEntryPoint) {
    myTemporaryEntryPoints.remove(anEntryPoint);

    Set<Map.Entry<String, SmartRefElementPointer>> set = myPersistentEntryPoints.entrySet();
    String key = null;
    for (Map.Entry<String, SmartRefElementPointer> entry : set) {
      SmartRefElementPointer value = entry.getValue();
      if (value.getRefElement() == anEntryPoint) {
        key = entry.getKey();
        break;
      }
    }

    if (key != null) {
      myPersistentEntryPoints.remove(key);
    }
    ((RefElementImpl) anEntryPoint).setEntry(false);

    if (anEntryPoint.isPermanentEntry() && anEntryPoint.isValid()) {
      final Project project = anEntryPoint.getElement().getProject();
      final EntryPointsManager entryPointsManager = getInstance(project);
      if (this != entryPointsManager) {
        entryPointsManager.removeEntryPoint(anEntryPoint);
      }
    }

    if (anEntryPoint instanceof RefMethod && ((RefMethod) anEntryPoint).isConstructor()
        || anEntryPoint instanceof RefClass) {
      final RefClass aClass =
          anEntryPoint instanceof RefClass
              ? (RefClass) anEntryPoint
              : ((RefMethod) anEntryPoint).getOwnerClass();
      final String qualifiedName = aClass.getQualifiedName();
      for (Iterator<ClassPattern> iterator = getPatterns().iterator(); iterator.hasNext(); ) {
        if (Comparing.equal(iterator.next().pattern, qualifiedName)) {
          // todo if inheritance or pattern?
          iterator.remove();
        }
      }
    }
  }
  @Override
  @Nullable
  public RefClass getOwnerClass(RefElement refElement) {
    RefEntity parent = refElement.getOwner();

    while (!(parent instanceof RefClass) && parent instanceof RefElement) {
      parent = parent.getOwner();
    }

    if (parent instanceof RefClass) return (RefClass) parent;

    return null;
  }
  @Override
  public RefClass getTopLevelClass(RefElement refElement) {
    RefEntity refParent = refElement.getOwner();

    while (refParent != null
        && refParent instanceof RefElement
        && !(refParent instanceof RefFile)) {
      refElement = (RefElementImpl) refParent;
      refParent = refParent.getOwner();
    }

    return refElement instanceof RefClass ? (RefClass) refElement : null;
  }
  private void validateEntryPoints() {
    long count = PsiManager.getInstance(myProject).getModificationTracker().getModificationCount();
    if (count != myLastModificationCount) {
      myLastModificationCount = count;
      Collection<SmartRefElementPointer> collection = myPersistentEntryPoints.values();
      SmartRefElementPointer[] entries =
          collection.toArray(new SmartRefElementPointer[collection.size()]);
      for (SmartRefElementPointer entry : entries) {
        RefElement refElement = (RefElement) entry.getRefElement();
        if (refElement != null && !refElement.isValid()) {
          myPersistentEntryPoints.remove(entry.getFQName());
        }
      }

      final Iterator<RefElement> it = myTemporaryEntryPoints.iterator();
      while (it.hasNext()) {
        RefElement refElement = it.next();
        if (!refElement.isValid()) {
          it.remove();
        }
      }
    }
  }
  private void updateRefMethod(
      PsiElement psiResolved,
      RefElement refResolved,
      PsiElement refExpression,
      final PsiElement psiFrom,
      final RefElement refFrom) {
    PsiMethod psiMethod = (PsiMethod) psiResolved;
    RefMethodImpl refMethod = (RefMethodImpl) refResolved;

    PsiMethodCallExpression call =
        PsiTreeUtil.getParentOfType(refExpression, PsiMethodCallExpression.class);
    if (call != null) {
      PsiType returnType = psiMethod.getReturnType();
      if (!psiMethod.isConstructor() && returnType != PsiType.VOID) {
        if (!(call.getParent() instanceof PsiExpressionStatement)) {
          refMethod.setReturnValueUsed(true);
        }

        addTypeReference(psiFrom, returnType, refFrom.getRefManager());
      }

      PsiExpressionList argumentList = call.getArgumentList();
      if (argumentList.getExpressions().length > 0) {
        refMethod.updateParameterValues(argumentList.getExpressions());
      }

      final PsiExpression psiExpression = call.getMethodExpression().getQualifierExpression();
      if (psiExpression != null) {
        final PsiType usedType = psiExpression.getType();
        if (usedType != null) {
          final String fqName = psiMethod.getContainingClass().getQualifiedName();
          if (fqName != null) {
            final PsiClassType methodOwnerType =
                JavaPsiFacade.getInstance(call.getProject())
                    .getElementFactory()
                    .createTypeByFQClassName(
                        fqName, GlobalSearchScope.allScope(psiMethod.getProject()));
            if (!usedType.equals(methodOwnerType)) {
              refMethod.setCalledOnSubClass(true);
            }
          }
        }
      }
    }
  }
 @Override
 public void onMarkReferenced(
     RefElement refWhat,
     RefElement refFrom,
     boolean referencedFromClassInitializer,
     boolean forReading,
     boolean forWriting) {
   if (!(refWhat instanceof RefField)) return;
   if (!(refFrom instanceof RefMethod)
       || !((RefMethod) refFrom).isConstructor()
       || ((PsiField) refWhat.getElement()).hasInitializer()
       || ((RefMethod) refFrom).getOwnerClass() != ((RefField) refWhat).getOwnerClass()
       || ((RefField) refWhat).isStatic()) {
     if (!referencedFromClassInitializer && forWriting) {
       ((RefFieldImpl) refWhat).setFlag(false, CAN_BE_FINAL_MASK);
     }
   }
 }
  @Override
  @Nullable
  public String getGroupName(final RefElement entity) {
    for (RefManagerExtension extension : myExtensions.values()) {
      final String groupName = extension.getGroupName(entity);
      if (groupName != null) return groupName;
    }

    final LinkedList<String> containingDirs = new LinkedList<>();
    RefEntity parent = entity.getOwner();
    while (parent != null && !(parent instanceof RefDirectory)) {
      parent = parent.getOwner();
    }
    while (parent instanceof RefDirectory) {
      containingDirs.addFirst(parent.getName());
      parent = parent.getOwner();
    }
    return containingDirs.isEmpty() ? null : StringUtil.join(containingDirs, File.separator);
  }
  void removeReference(@NotNull RefElement refElem) {
    final PsiElement element = refElem.getElement();
    final RefManagerExtension extension =
        element != null ? getExtension(element.getLanguage()) : null;
    if (extension != null) {
      extension.removeReference(refElem);
    }

    synchronized (myRefTable) {
      mySortedRefs = null;
      if (element != null && myRefTable.remove(createAnchor(element)) != null) return;

      // PsiElement may have been invalidated and new one returned by getElement() is different so
      // we need to do this stuff.
      for (Map.Entry<PsiAnchor, RefElement> entry : myRefTable.entrySet()) {
        RefElement value = entry.getValue();
        PsiAnchor anchor = entry.getKey();
        if (value == refElem) {
          myRefTable.remove(anchor);
          break;
        }
      }
    }
  }
  @Override
  public void onReferencesBuild(RefElement refElement) {
    if (refElement instanceof RefClass) {
      final PsiClass psiClass = (PsiClass) refElement.getElement();
      if (psiClass != null) {

        if (refElement.isEntry()) {
          ((RefClassImpl) refElement).setFlag(false, CAN_BE_FINAL_MASK);
        }

        PsiMethod[] psiMethods = psiClass.getMethods();
        PsiField[] psiFields = psiClass.getFields();

        HashSet<PsiVariable> allFields = new HashSet<PsiVariable>();
        ContainerUtil.addAll(allFields, psiFields);
        ArrayList<PsiVariable> instanceInitializerInitializedFields = new ArrayList<PsiVariable>();
        boolean hasInitializers = false;
        for (PsiClassInitializer initializer : psiClass.getInitializers()) {
          PsiCodeBlock body = initializer.getBody();
          hasInitializers = true;
          ControlFlow flow;
          try {
            flow =
                ControlFlowFactory.getInstance(body.getProject())
                    .getControlFlow(
                        body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false);
          } catch (AnalysisCanceledException e) {
            flow = ControlFlow.EMPTY;
          }
          Collection<PsiVariable> writtenVariables = new ArrayList<PsiVariable>();
          ControlFlowUtil.getWrittenVariables(flow, 0, flow.getSize(), false, writtenVariables);
          for (PsiVariable psiVariable : writtenVariables) {
            if (allFields.contains(psiVariable)) {
              if (instanceInitializerInitializedFields.contains(psiVariable)) {
                allFields.remove(psiVariable);
                instanceInitializerInitializedFields.remove(psiVariable);
              } else {
                instanceInitializerInitializedFields.add(psiVariable);
              }
            }
          }
          for (PsiVariable psiVariable : writtenVariables) {
            if (!instanceInitializerInitializedFields.contains(psiVariable)) {
              allFields.remove(psiVariable);
            }
          }
        }

        for (PsiMethod psiMethod : psiMethods) {
          if (psiMethod.isConstructor()) {
            PsiCodeBlock body = psiMethod.getBody();
            if (body != null) {
              hasInitializers = true;
              ControlFlow flow;
              try {
                flow =
                    ControlFlowFactory.getInstance(body.getProject())
                        .getControlFlow(
                            body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false);
              } catch (AnalysisCanceledException e) {
                flow = ControlFlow.EMPTY;
              }

              Collection<PsiVariable> writtenVariables =
                  ControlFlowUtil.getWrittenVariables(flow, 0, flow.getSize(), false);
              for (PsiVariable psiVariable : writtenVariables) {
                if (instanceInitializerInitializedFields.contains(psiVariable)) {
                  allFields.remove(psiVariable);
                  instanceInitializerInitializedFields.remove(psiVariable);
                }
              }
              List<PsiMethod> redirectedConstructors =
                  HighlightControlFlowUtil.getChainedConstructors(psiMethod);
              if (redirectedConstructors == null || redirectedConstructors.isEmpty()) {
                List<PsiVariable> ssaVariables = ControlFlowUtil.getSSAVariables(flow);
                ArrayList<PsiVariable> good = new ArrayList<PsiVariable>(ssaVariables);
                good.addAll(instanceInitializerInitializedFields);
                allFields.retainAll(good);
              } else {
                allFields.removeAll(writtenVariables);
              }
            }
          }
        }

        for (PsiField psiField : psiFields) {
          if ((!hasInitializers || !allFields.contains(psiField))
              && psiField.getInitializer() == null) {
            final RefFieldImpl refField = (RefFieldImpl) myManager.getReference(psiField);
            if (refField != null) {
              refField.setFlag(false, CAN_BE_FINAL_MASK);
            }
          }
        }
      }
    } else if (refElement instanceof RefMethod) {
      final RefMethod refMethod = (RefMethod) refElement;
      if (refMethod.isEntry()) {
        ((RefMethodImpl) refMethod).setFlag(false, CAN_BE_FINAL_MASK);
      }
    }
  }
 @Override
 public void onMarkReferenced(
     RefElement refWhat, RefElement refFrom, boolean referencedFromClassInitializer) {
   checkMethodCall(refWhat, refFrom.getElement());
 }