@Override
  public void addReferencesTo(@NotNull final UElement decl, @NotNull final RefJavaElement ref, final UElement @Nullable ... findIn) {
    final RefJavaElementImpl refFrom = (RefJavaElementImpl)ref;
    final RefManagerImpl refManager = refFrom.getRefManager();
    if (findIn == null) {
      return;
    }
    for (UElement element : findIn) {
      if (element == null) continue;
      element.accept(new AbstractUastVisitor() {
                       @Override
                       public boolean visitEnumConstant(@NotNull UEnumConstant node) {
                         processNewLikeConstruct(node.resolve(), node);
                         return false;
                       }

                       @Override
                       public boolean visitNamedExpression(@NotNull UNamedExpression node) {
                         PsiElement source = node.getSourcePsi();
                         if (source instanceof PsiNameValuePair pair) {
                           PsiReference reference = pair.getReference();
                           if (reference != null) {
                             PsiElement target = reference.resolve();
                             final RefElement refElement = refManager.getReference(target);
                             if (refElement != null) {
                               refElement.initializeIfNeeded();
                               refFrom.addReference(refElement, target, decl, false, true, node);
                             }
                           }
                         }
                         return false;
                       }

                       @Override
                       public boolean visitAnnotation(@NotNull UAnnotation node) {
                         PsiClass javaClass = node.resolve();
                         if (javaClass != null) {
                           final RefElement refClass = refManager.getReference(javaClass.getOriginalElement());
                           if (refClass != null) refClass.initializeIfNeeded();
                           refFrom.addReference(refClass, javaClass.getOriginalElement(), decl, false, true, null);
                           checkRepeatableAnnotation(javaClass, node);
                         }
                         return false;
                       }

                       private void checkRepeatableAnnotation(PsiClass annotationClass, UAnnotation annotation) {
                         if (!CommonClassNames.JAVA_LANG_ANNOTATION_REPEATABLE.equals(annotationClass.getQualifiedName())) return;
                         UExpression value = annotation.findAttributeValue("value");
                         while (value instanceof UParenthesizedExpression parenth) {
                           value = parenth.getExpression();
                         }
                         if (!(value instanceof UClassLiteralExpression classLiteralExpression)) return;
                         PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(classLiteralExpression.getType());
                         if (aClass == null) return;
                         RefClassImpl refClass =
                           ObjectUtils.tryCast(refManager.getReference(aClass.getOriginalElement()), RefClassImpl.class);
                         if (refClass == null) return;
                         refClass.initializeIfNeeded();
                         for (RefEntity child : refClass.getChildren()) {
                           if (child instanceof RefMethod method && "value()".equals(method.getName())) {
                             refFrom.addReference(method, null, decl, false, true, null);
                             break;
                           }
                         }
                       }

                       @Override
                       public boolean visitTypeReferenceExpression(@NotNull UTypeReferenceExpression node) {
                         PsiType type = node.getType();
                         visitTypeRefs(type);
                         return false;
                       }

                       private void visitTypeRefs(PsiType type) {
                         type = type.getDeepComponentType();
                         if (type instanceof PsiClassType) {
                           type.accept(new PsiTypeVisitor<Void>() {
                             @Override
                             public Void visitClassType(@NotNull PsiClassType classType) {
                               for (PsiType parameter : classType.getParameters()) {
                                 parameter.accept(this);
                               }
                               PsiClass aClass = classType.resolve();
                               UClass target = UastContextKt.toUElement(aClass, UClass.class);
                               if (target != null) {
                                 final RefElement refElement = refManager.getReference(target.getSourcePsi());
                                 if (refElement != null) refElement.initializeIfNeeded();
                                 refFrom.addReference(refElement, aClass, decl, false, true, null);
                               }
                               return null;
                             }
                           });
                         }
                       }

                       @Override
                       public boolean visitVariable(@NotNull UVariable node) {
                         visitTypeRefs(node.getType());
                         return false;
                       }

                       @Override
                       public boolean visitSimpleNameReferenceExpression(@NotNull USimpleNameReferenceExpression node) {
                         visitReferenceExpression(node);
                         return false;
                       }

                       @Override
                       public boolean visitLiteralExpression(@NotNull ULiteralExpression node) {
                         PsiElement sourcePsi = node.getSourcePsi();
                         if (sourcePsi != null) {
                           for (PsiReference reference : sourcePsi.getReferences()) {
                             PsiElement resolve = reference.resolve();
                             if (resolve instanceof PsiMember) {
                               final RefElement refResolved = refManager.getReference(resolve);
                               if (refResolved != null) refResolved.initializeIfNeeded();
                               refFrom.addReference(refResolved, resolve, decl, false, true, null);
                               if (refResolved instanceof RefMethodImpl refMethod) {
                                 updateRefMethod(resolve, refMethod, node, decl);
                               }
                             }
                           }
                         }
                         return false;
                       }

                       @Override
                       public boolean visitPrefixExpression(@NotNull UPrefixExpression node) {
                         visitReferenceExpression(node);
                         return false;
                       }

                       @Override
                       public boolean visitPostfixExpression(@NotNull UPostfixExpression node) {
                         visitReferenceExpression(node);
                         return false;
                       }

                       @Override
                       public boolean visitUnaryExpression(@NotNull UUnaryExpression node) {
                         visitReferenceExpression(node);
                         return false;
                       }

                       @Override
                       public boolean visitBinaryExpression(@NotNull UBinaryExpression node) {
                         visitReferenceExpression(node);
                         return false;
                       }

                       @Override
                       public boolean visitQualifiedReferenceExpression(@NotNull UQualifiedReferenceExpression node) {
                         visitReferenceExpression(node);
                         return false;
                       }

                       @Override
                       public boolean visitObjectLiteralExpression(@NotNull UObjectLiteralExpression node) {
                         visitReferenceExpression(node);
                         visitClass(node.getDeclaration());
                         return false;
                       }

                       @Override
                       public boolean visitCallExpression(@NotNull UCallExpression node) {
                         visitReferenceExpression(node);
                         if (node instanceof UObjectLiteralExpression objectLiteralExpression) {
                           visitClass(objectLiteralExpression.getDeclaration());
                         }
                         if (node.getKind() == UastCallKind.CONSTRUCTOR_CALL) {
                           PsiElement resolvedMethod = returnToPhysical(node.resolve());
                           processNewLikeConstruct(resolvedMethod, node);
                         }
                         try {
                           node.getTypeArguments().forEach(this::visitTypeRefs);
                         }
                         catch (UnsupportedOperationException e) {
                           //TODO happens somewhere in kotlin plugin. Please assign those exception for Dmitry Batkovich
                           LOG.error(e);
                         }
                         return false;
                       }

                       private void visitReferenceExpression(@NotNull UExpression node) {
                         UElement uastParent = node.getUastParent();
                         if (uastParent instanceof UQualifiedReferenceExpression qualifiedReference && qualifiedReference.getSelector() == node) {
                           return;
                         }
                         PsiElement psiResolved = null;
                         if (node instanceof UCallExpression callExpression &&
                             "invoke".equals(callExpression.getMethodName()) &&
                             callExpression.getReceiver() instanceof UResolvable resolvable) {
                           psiResolved = resolvable.resolve();
                         }
                         else if (node instanceof UResolvable resolvable) {
                           psiResolved = resolvable.resolve();
                         }
                         else if (node instanceof UBinaryExpression binaryExpression) {
                           psiResolved = binaryExpression.resolveOperator();
                         }
                         else if (node instanceof UUnaryExpression unaryExpression) {
                           psiResolved = unaryExpression.resolveOperator();
                         }

                         psiResolved = returnToPhysical(psiResolved);
                         RefElement refResolved = refManager.getReference(psiResolved);
                         boolean writing = isAccessedForWriting(node);
                         boolean reading = isAccessedForReading(node);
                         if (refResolved != null) refResolved.initializeIfNeeded();
                         refFrom.addReference(refResolved, psiResolved, decl, writing, reading, node);

                         if (refResolved instanceof RefMethodImpl refMethod) {
                           updateRefMethod(psiResolved, refMethod, node, decl);
                         }
                         else if (refResolved instanceof RefField) {
                           if (node instanceof UResolvable resolvable) {
                             UMethod uProperty = UastContextKt.toUElement(resolvable.resolve(), UMethod.class);
                             if (uProperty != null) {
                             //  PsiMethod property = uProperty.getJavaPsi();
                             //  RefElement refProperty = refManager.getReference(uProperty);
                             //  if (refProperty != null) {
                             //    refProperty.waitForInitialized();
                             //    refFrom.addReference(refProperty, property, decl, false, true, node);
                             //  }
                             }
                           }
                         }
                         else if (refResolved ==  null && psiResolved instanceof PsiMethod method) {
                           markEnumUsedIfValuesMethod(method, node);
                         }

                         if (psiResolved instanceof PsiMember psiMember) {
                           //TODO support kotlin
                           addClassReferenceForStaticImport(node, psiMember, refFrom, decl);
                         }
                       }

                       @Override
                       public boolean visitLambdaExpression(@NotNull ULambdaExpression lambda) {
                         processFunctionalExpression(lambda, lambda.getFunctionalInterfaceType());
                         return true;
                       }

                       private void markEnumUsedIfValuesMethod(PsiMethod psiWhat, UExpression expression) {
                         //TODO support kotlin enums
                         final PsiClass containingClass = psiWhat.getContainingClass();
                         if (containingClass == null || !containingClass.isEnum()) {
                           return;
                         }
                         String methodName = psiWhat.getName();
                         if (!"values".equals(methodName) && !"valueOf".equals(methodName)) {
                           return;
                         }
                         for (PsiField enumConstant : containingClass.getFields()) {
                           if (enumConstant instanceof PsiEnumConstant) {
                             final RefJavaElementImpl enumConstantReference = (RefJavaElementImpl)refFrom.getRefManager().getReference(enumConstant);
                             if (enumConstantReference != null) {
                               refFrom.addOutReference(enumConstantReference);
                               enumConstantReference.markReferenced(refFrom, false, true, expression);
                             }
                           }
                         }
                       }

                       @Override
                       public boolean visitCallableReferenceExpression(@NotNull UCallableReferenceExpression methodRef) {
                         UExpression qualifierExpression = methodRef.getQualifierExpression();
                         if (qualifierExpression != null) {
                           qualifierExpression.accept(this);
                         }
                         RefElement refMethod = refManager.getReference(methodRef.getSourcePsi());
                         if (refFrom == refMethod) {
                           visitReferenceExpression(methodRef);
                           return false;
                         }
                         else {
                           processFunctionalExpression(methodRef, getFunctionalInterfaceType(methodRef));
                           return true;
                         }
                       }

                       private void processFunctionalExpression(@NotNull UExpression expression, @Nullable PsiType type) {
                         PsiElement aClass = PsiUtil.resolveClassInType(type);
                         if (aClass != null) {
                           aClass = ((PsiClass)aClass).getSourceElement();
                         }
                         if (aClass != null) {
                           final RefElement refWhat = refManager.getReference(aClass);
                           if (refWhat != null) refWhat.initializeIfNeeded();
                           refFrom.addReference(refWhat, aClass, decl, false, true, null);
                         }
                         PsiElement functionalExpr = expression.getSourcePsi();
                         RefElement refFunctionalExpr = refManager.getReference(functionalExpr);
                         if (refFunctionalExpr != null) refFunctionalExpr.initializeIfNeeded();
                         refFrom.addReference(refFunctionalExpr, functionalExpr, decl, false, true, expression);
                       }

                       private void processNewLikeConstruct(PsiElement javaConstructor, UCallExpression call) {
                         RefMethodImpl refConstructor = javaConstructor == null ? null :
                           ObjectUtils.tryCast(refManager.getReference(javaConstructor.getOriginalElement()), RefMethodImpl.class);

                         if (refConstructor != null) {
                           refConstructor.initializeIfNeeded();
                           refFrom.addReference(refConstructor, javaConstructor, decl, false, true, call);
                           refConstructor.updateParameterValues(call, javaConstructor);
                         }
                         else {
                           UReferenceExpression reference = call.getClassReference();
                           if (reference != null) {
                             PsiElement constructorClass = reference.resolve();
                             if (constructorClass instanceof PsiClass psiClass) {
                               processClassReference(psiClass, true, call);
                             }
                           }
                         }
                         for (UExpression arg : call.getValueArguments()) {
                           arg.accept(this);
                         }
                       }

                       @Override
                       public boolean visitClass(@NotNull UClass uClass) {
                         for (UTypeReferenceExpression type : uClass.getUastSuperTypes()) {
                           type.accept(this);
                         }
                         PsiElement sourcePsi = uClass.getSourcePsi();
                         RefElement refWhat = refManager.getReference(sourcePsi);
                         if (refWhat != null) refWhat.initializeIfNeeded();
                         refFrom.addReference(refWhat, sourcePsi, decl, false, true, null);
                         return true;
                       }

                       @Override
                       public boolean visitReturnExpression(@NotNull UReturnExpression node) {
                         RefMethodImpl refMethod = null;
                         if (refFrom instanceof RefMethodImpl &&
                             UastUtils.getParentOfType(node, UMethod.class, false, UClass.class, ULambdaExpression.class) == decl) {
                           refMethod = (RefMethodImpl)refFrom;
                         }
                         else if (refFrom instanceof RefFunctionalExpression) {
                           UElement target = node.getJumpTarget();
                           if (target instanceof UMethod) {
                             refMethod = ObjectUtils.tryCast(refManager.getReference(target.getSourcePsi()), RefMethodImpl.class);
                           }
                           else if (decl instanceof ULambdaExpression lambdaExpression) {
                             PsiMethod lambdaMethod = LambdaUtil.getFunctionalInterfaceMethod(lambdaExpression.getFunctionalInterfaceType());
                             refMethod = ObjectUtils.tryCast(refManager.getReference(lambdaMethod), RefMethodImpl.class);
                           }
                         }
                         if (refMethod != null) {
                           refMethod.initializeIfNeeded();
                           refMethod.updateReturnValueTemplate(node.getReturnExpression());
                         }
                         return false;
                       }

                       @Override
                       public boolean visitClassLiteralExpression(@NotNull UClassLiteralExpression node) {
                         processClassReference(PsiUtil.resolveClassInClassTypeOnly(node.getType()), false, node);
                         return false;
                       }

                       private void processClassReference(PsiClass psiClass, boolean defaultConstructorOnly, UExpression node) {
                         if (psiClass != null) {
                           RefClassImpl refClass =
                             ObjectUtils.tryCast(refManager.getReference(psiClass.getNavigationElement()), RefClassImpl.class);

                           if (refClass != null) {
                             boolean hasConstructorsMarked = false;
                             refClass.initializeIfNeeded();

                             if (defaultConstructorOnly) {
                               WritableRefElement refDefaultConstructor = (WritableRefElement)refClass.getDefaultConstructor();
                               if (refDefaultConstructor != null) {
                                 refDefaultConstructor.addInReference(refFrom);
                                 refFrom.addOutReference(refDefaultConstructor);
                                 hasConstructorsMarked = true;
                               }
                             }
                             else {
                               for (RefMethod cons : refClass.getConstructors()) {
                                 if (cons instanceof RefImplicitConstructor) continue;
                                 ((WritableRefElement)cons).addInReference(refFrom);
                                 refFrom.addOutReference(cons);
                                 hasConstructorsMarked = true;
                               }

                               if (refClass.isEnum()) {
                                 for (RefField field : refClass.getFields()) {
                                   if (field.isEnumConstant()) {
                                     ((RefFieldImpl)field).markReferenced(refFrom, false, true, node);
                                     refFrom.addOutReference(field);
                                   }
                                 }
                               }
                             }

                             if (!hasConstructorsMarked) {
                               refFrom.addReference(refClass, psiClass, decl, false, true, node);
                             }
                           }
                         }
                       }
                     }
      );
    }
  }
@Override
  public void addReferencesTo(@NotNull final UElement decl, @NotNull final RefJavaElement ref, final UElement @Nullable ... findIn)