private static boolean hasCorrectType(
        @Nullable PsiAnnotationMemberValue value, PsiType expectedType) {
      if (value == null) return false;

      if (expectedType instanceof PsiClassType
          && expectedType.equalsToText(CommonClassNames.JAVA_LANG_CLASS)
          && !(value instanceof PsiClassObjectAccessExpression)) {
        return false;
      }

      if (value instanceof PsiAnnotation) {
        final PsiJavaCodeReferenceElement nameRef =
            ((PsiAnnotation) value).getNameReferenceElement();
        if (nameRef == null) return true;

        if (expectedType instanceof PsiClassType) {
          final PsiClass aClass = ((PsiClassType) expectedType).resolve();
          if (aClass != null && nameRef.isReferenceTo(aClass)) return true;
        }

        if (expectedType instanceof PsiArrayType) {
          final PsiType componentType = ((PsiArrayType) expectedType).getComponentType();
          if (componentType instanceof PsiClassType) {
            final PsiClass aClass = ((PsiClassType) componentType).resolve();
            if (aClass != null && nameRef.isReferenceTo(aClass)) return true;
          }
        }
        return false;
      }
      if (value instanceof PsiArrayInitializerMemberValue) {
        return expectedType instanceof PsiArrayType;
      }
      if (value instanceof PsiExpression) {
        final PsiExpression expression = (PsiExpression) value;
        return expression.getType() != null
                && TypeConversionUtil.areTypesAssignmentCompatible(expectedType, expression)
            || expectedType instanceof PsiArrayType
                && TypeConversionUtil.areTypesAssignmentCompatible(
                    ((PsiArrayType) expectedType).getComponentType(), expression);
      }
      return true;
    }
 @Override
 public void visitThisExpression(PsiThisExpression expression) {
   super.visitThisExpression(expression);
   final PsiJavaCodeReferenceElement qualifier = expression.getQualifier();
   if (qualifier != null && qualifier.isReferenceTo(mySourceClass)) {
     try {
       qualifier.bindToElement(myTargetSuperClass);
     } catch (IncorrectOperationException e) {
       LOG.error(e);
     }
   }
 }
 @Override
 public void visitSuperExpression(PsiSuperExpression expression) {
   super.visitSuperExpression(expression);
   final PsiJavaCodeReferenceElement qualifier = expression.getQualifier();
   if (qualifier != null && qualifier.isReferenceTo(mySourceClass)) {
     try {
       expression.replace(
           JavaPsiFacade.getElementFactory(myProject)
               .createExpressionFromText(myTargetSuperClass.getName() + ".this", null));
     } catch (IncorrectOperationException e) {
       LOG.error(e);
     }
   }
 }
  public void handleInsert(
      final InsertionContext context, final JavaPsiClassReferenceElement item) {
    final char c = context.getCompletionChar();

    int offset = context.getTailOffset() - 1;
    final PsiFile file = context.getFile();
    if (PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiImportStatementBase.class, false)
        != null) {
      final PsiJavaCodeReferenceElement ref =
          PsiTreeUtil.findElementOfClassAtOffset(
              file, offset, PsiJavaCodeReferenceElement.class, false);
      final String qname = item.getQualifiedName();
      if (qname != null && (ref == null || !qname.equals(ref.getCanonicalText()))) {
        AllClassesGetter.INSERT_FQN.handleInsert(context, item);
      }
      return;
    }

    PsiElement position = file.findElementAt(offset);
    PsiClass psiClass = item.getObject();
    final Project project = context.getProject();
    final boolean annotation = insertingAnnotation(context, item);

    final Editor editor = context.getEditor();
    if (c == '#') {
      context.setLaterRunnable(
          new Runnable() {
            public void run() {
              new CodeCompletionHandlerBase(CompletionType.BASIC).invokeCompletion(project, editor);
            }
          });
    } else if (c == '.' && PsiTreeUtil.getParentOfType(position, PsiParameterList.class) == null) {
      AutoPopupController.getInstance(context.getProject())
          .autoPopupMemberLookup(context.getEditor(), null);
    }

    if (position != null) {
      PsiElement parent = position.getParent();
      if (parent instanceof PsiJavaCodeReferenceElement) {
        final PsiJavaCodeReferenceElement ref = (PsiJavaCodeReferenceElement) parent;
        if (PsiTreeUtil.getParentOfType(position, PsiDocTag.class) != null
            && ref.isReferenceTo(psiClass)) {
          return;
        }
      }
    }

    OffsetKey refEnd = context.trackOffset(context.getTailOffset(), false);

    boolean fillTypeArgs = context.getCompletionChar() == '<';
    if (fillTypeArgs) {
      context.setAddCompletionChar(false);
    }

    if (shouldInsertParentheses(psiClass, position)) {
      if (ConstructorInsertHandler.insertParentheses(context, item, psiClass, false)) {
        fillTypeArgs |=
            psiClass.hasTypeParameters()
                && PsiUtil.getLanguageLevel(file).isAtLeast(LanguageLevel.JDK_1_5);
      }
    } else if (insertingAnnotationWithParameters(context, item)) {
      JavaCompletionUtil.insertParentheses(context, item, false, true);
      AutoPopupController.getInstance(project).autoPopupParameterInfo(editor, null);
    }

    LOG.assertTrue(context.getTailOffset() >= 0);
    String docText = context.getDocument().getText();
    DefaultInsertHandler.addImportForItem(context, item);
    if (context.getTailOffset() < 0) {
      LOG.error(
          LogMessageEx.createEvent(
              "Tail offset degraded after insertion",
              "start=" + context.getStartOffset(),
              new Attachment(
                  context.getFile().getViewProvider().getVirtualFile().getPath(), docText)));
    }

    if (annotation) {
      // Check if someone inserts annotation class that require @
      PsiElement elementAt = file.findElementAt(context.getStartOffset());
      final PsiElement parentElement = elementAt != null ? elementAt.getParent() : null;

      if (elementAt instanceof PsiIdentifier
          && (PsiTreeUtil.getParentOfType(elementAt, PsiAnnotationParameterList.class) != null
              || parentElement instanceof PsiErrorElement
                  && parentElement.getParent()
                      instanceof PsiJavaFile // top level annotation without @
          )
          && isAtTokenNeeded(context)) {
        int expectedOffsetForAtToken = elementAt.getTextRange().getStartOffset();
        context.getDocument().insertString(expectedOffsetForAtToken, "@");
      }
    }

    if (fillTypeArgs) {
      PostprocessReformattingAspect.getInstance(project).doPostponedFormatting();
      int typeArgs = context.getOffset(refEnd);
      if (typeArgs >= 0) {
        context.getDocument().insertString(typeArgs, "<>");
        context.getEditor().getCaretModel().moveToOffset(typeArgs + 1);
      }
    }
  }
  @Nullable
  public static HighlightInfo checkMemberValueType(
      @Nullable PsiAnnotationMemberValue value, PsiType expectedType) {
    if (value == null) return null;

    if (expectedType instanceof PsiClassType
        && expectedType.equalsToText(CommonClassNames.JAVA_LANG_CLASS)) {
      if (!(value instanceof PsiClassObjectAccessExpression)) {
        String description =
            JavaErrorMessages.message("annotation.non.class.literal.attribute.value");
        return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
            .range(value)
            .descriptionAndTooltip(description)
            .create();
      }
    }

    if (value instanceof PsiAnnotation) {
      PsiJavaCodeReferenceElement nameRef = ((PsiAnnotation) value).getNameReferenceElement();
      if (nameRef == null) return null;

      if (expectedType instanceof PsiClassType) {
        PsiClass aClass = ((PsiClassType) expectedType).resolve();
        if (aClass != null && nameRef.isReferenceTo(aClass)) return null;
      }

      if (expectedType instanceof PsiArrayType) {
        PsiType componentType = ((PsiArrayType) expectedType).getComponentType();
        if (componentType instanceof PsiClassType) {
          PsiClass aClass = ((PsiClassType) componentType).resolve();
          if (aClass != null && nameRef.isReferenceTo(aClass)) return null;
        }
      }

      String description =
          JavaErrorMessages.message(
              "annotation.incompatible.types",
              formatReference(nameRef),
              JavaHighlightUtil.formatType(expectedType));
      return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
          .range(value)
          .descriptionAndTooltip(description)
          .create();
    }
    if (value instanceof PsiArrayInitializerMemberValue) {
      if (expectedType instanceof PsiArrayType) return null;
      String description =
          JavaErrorMessages.message(
              "annotation.illegal.array.initializer", JavaHighlightUtil.formatType(expectedType));
      return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
          .range(value)
          .descriptionAndTooltip(description)
          .create();
    }
    if (value instanceof PsiExpression) {
      PsiExpression expr = (PsiExpression) value;
      PsiType type = expr.getType();
      if (type != null && TypeConversionUtil.areTypesAssignmentCompatible(expectedType, expr)
          || expectedType instanceof PsiArrayType
              && TypeConversionUtil.areTypesAssignmentCompatible(
                  ((PsiArrayType) expectedType).getComponentType(), expr)) {
        return null;
      }

      String description =
          JavaErrorMessages.message(
              "annotation.incompatible.types",
              JavaHighlightUtil.formatType(type),
              JavaHighlightUtil.formatType(expectedType));
      return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR)
          .range(value)
          .descriptionAndTooltip(description)
          .create();
    }

    LOG.error("Unknown annotation member value: " + value);
    return null;
  }