@Override
  public void processElementUsages(
      @NotNull final PsiElement element,
      @NotNull final Processor<UsageInfo> processor,
      @NotNull final FindUsagesOptions options) {
    if (options instanceof JavaVariableFindUsagesOptions) {
      final JavaVariableFindUsagesOptions varOptions = (JavaVariableFindUsagesOptions) options;
      if (varOptions.isReadAccess || varOptions.isWriteAccess) {
        if (varOptions.isReadAccess && varOptions.isWriteAccess) {
          addElementUsages(element, processor, options);
        } else {
          addElementUsages(
              element,
              new Processor<UsageInfo>() {
                @Override
                public boolean process(UsageInfo info) {
                  final PsiElement element = info.getElement();
                  boolean isWrite =
                      element instanceof PsiExpression
                          && PsiUtil.isAccessedForWriting((PsiExpression) element);
                  if (isWrite == varOptions.isWriteAccess) {
                    if (!processor.process(info)) return false;
                  }
                  return true;
                }
              },
              varOptions);
        }
      }
    } else if (options.isUsages) {
      addElementUsages(element, processor, options);
    }

    ApplicationManager.getApplication()
        .runReadAction(
            new Runnable() {
              @Override
              public void run() {
                if (ThrowSearchUtil.isSearchable(element)
                    && options instanceof JavaThrowFindUsagesOptions
                    && options.isUsages) {
                  ThrowSearchUtil.Root root =
                      options.getUserData(ThrowSearchUtil.THROW_SEARCH_ROOT_KEY);
                  if (root == null) {
                    final ThrowSearchUtil.Root[] roots = ThrowSearchUtil.getSearchRoots(element);
                    if (roots != null && roots.length > 0) {
                      root = roots[0];
                    }
                  }
                  if (root != null) {
                    ThrowSearchUtil.addThrowUsages(processor, root, options);
                  }
                }
              }
            });

    if (options instanceof JavaPackageFindUsagesOptions
        && ((JavaPackageFindUsagesOptions) options).isClassesUsages) {
      addClassesUsages((PsiPackage) element, processor, (JavaPackageFindUsagesOptions) options);
    }

    if (options instanceof JavaClassFindUsagesOptions) {
      final JavaClassFindUsagesOptions classOptions = (JavaClassFindUsagesOptions) options;
      final PsiClass psiClass = (PsiClass) element;
      if (classOptions.isMethodsUsages) {
        addMethodsUsages(psiClass, processor, classOptions);
      }
      if (classOptions.isFieldsUsages) {
        addFieldsUsages(psiClass, processor, classOptions);
      }
      if (psiClass.isInterface()) {
        if (classOptions.isDerivedInterfaces) {
          if (classOptions.isImplementingClasses) {
            addInheritors(psiClass, processor, classOptions);
          } else {
            addDerivedInterfaces(psiClass, processor, classOptions);
          }
        } else if (classOptions.isImplementingClasses) {
          addImplementingClasses(psiClass, processor, classOptions);
        }
      } else if (classOptions.isDerivedClasses) {
        addInheritors(psiClass, processor, classOptions);
      }
    }

    if (options instanceof JavaMethodFindUsagesOptions) {
      final PsiMethod psiMethod = (PsiMethod) element;
      boolean isAbstract =
          ApplicationManager.getApplication()
              .runReadAction(
                  new Computable<Boolean>() {
                    @Override
                    public Boolean compute() {
                      return psiMethod.hasModifierProperty(PsiModifier.ABSTRACT);
                    }
                  });
      final JavaMethodFindUsagesOptions methodOptions = (JavaMethodFindUsagesOptions) options;
      if (isAbstract && methodOptions.isImplementingMethods || methodOptions.isOverridingMethods) {
        processOverridingMethods(psiMethod, processor, methodOptions);
      }
    }

    if (element instanceof PomTarget) {
      addAliasingUsages((PomTarget) element, processor, options);
    }
    final Boolean isSearchable =
        ApplicationManager.getApplication()
            .runReadAction(
                new Computable<Boolean>() {
                  @Override
                  public Boolean compute() {
                    return ThrowSearchUtil.isSearchable(element);
                  }
                });
    if (!isSearchable
        && options.isSearchForTextOccurrences
        && options.searchScope instanceof GlobalSearchScope) {
      // todo add to fastTrack
      processUsagesInText(element, processor, (GlobalSearchScope) options.searchScope);
    }
  }