public String toString() {
   @NonNls StringBuilder buffer = new StringBuilder();
   final Set<Map.Entry<PsiTypeParameter, PsiType>> set = mySubstitutionMap.entrySet();
   for (Map.Entry<PsiTypeParameter, PsiType> entry : set) {
     final PsiTypeParameter typeParameter = entry.getKey();
     buffer.append(typeParameter.getName());
     final PsiElement owner = typeParameter.getOwner();
     if (owner instanceof PsiClass) {
       buffer.append(" of ");
       buffer.append(((PsiClass) owner).getQualifiedName());
     } else if (owner instanceof PsiMethod) {
       buffer.append(" of ");
       buffer.append(((PsiMethod) owner).getName());
       buffer.append(" in ");
       buffer.append(((PsiMethod) owner).getContainingClass().getQualifiedName());
     }
     buffer.append(" -> ");
     if (entry.getValue() != null) {
       buffer.append(entry.getValue().getCanonicalText());
     } else {
       buffer.append("null");
     }
     buffer.append('\n');
   }
   return buffer.toString();
 }
  public String calcGenerics(@NotNull PsiElement context, InsertionContext insertionContext) {
    if (insertionContext.getCompletionChar() == '<') {
      return "";
    }

    assert context.isValid();
    if (myDiamond) {
      return "<>";
    }

    if (getObject() instanceof PsiClass) {
      PsiClass psiClass = (PsiClass) getObject();
      PsiResolveHelper resolveHelper =
          JavaPsiFacade.getInstance(psiClass.getProject()).getResolveHelper();
      PsiSubstitutor substitutor = getSubstitutor();
      StringBuilder builder = new StringBuilder();
      for (PsiTypeParameter parameter : psiClass.getTypeParameters()) {
        PsiType substitute = substitutor.substitute(parameter);
        if (substitute == null
            || (PsiUtil.resolveClassInType(substitute) == parameter
                && resolveHelper.resolveReferencedClass(parameter.getName(), context)
                    != CompletionUtil.getOriginalOrSelf(parameter))) {
          return "";
        }
        if (builder.length() > 0) {
          builder.append(", ");
        }
        builder.append(substitute.getCanonicalText());
      }
      if (builder.length() > 0) {
        return "<" + builder + ">";
      }
    }
    return "";
  }
 @Nullable
 private PsiElement resolveElement() {
   PsiElement element = getParent();
   while (element != null
       && (!(element instanceof PsiClass) || element instanceof PsiTypeParameter)) {
     if (element instanceof PsiMethod) {
       final PsiMethod method = (PsiMethod) element;
       final PsiTypeParameterList list = method.getTypeParameterList();
       if (list != null) {
         final PsiTypeParameter[] parameters = list.getTypeParameters();
         for (int i = 0; parameters != null && i < parameters.length; i++) {
           final PsiTypeParameter parameter = parameters[i];
           if (myQualifiedName.equals(parameter.getName())) return parameter;
         }
       }
     }
     element = element.getParent();
   }
   if (element == null) return null;
   for (PsiTypeParameter parameter :
       PsiUtil.typeParametersIterable((PsiTypeParameterListOwner) element)) {
     if (myQualifiedName.equals(parameter.getName())) return parameter;
   }
   return resolveClassPreferringMyJar();
 }
 private JavaResolveResult advancedResolveImpl() {
   final PsiElement resolve = resolveElement();
   if (resolve instanceof PsiClass) {
     final Map<PsiTypeParameter, PsiType> substitutionMap =
         new HashMap<PsiTypeParameter, PsiType>();
     int index = 0;
     for (PsiTypeParameter parameter : PsiUtil.typeParametersIterable((PsiClass) resolve)) {
       if (index >= myTypeParameters.length) {
         final PsiTypeParameterListOwner parameterOwner = parameter.getOwner();
         if (parameterOwner == resolve) {
           substitutionMap.put(parameter, null);
         } else if (parameterOwner instanceof PsiClass) {
           PsiElement containingClass = myParent;
           while ((containingClass =
                   PsiTreeUtil.getParentOfType(containingClass, PsiClass.class, true))
               != null) {
             final PsiSubstitutor superClassSubstitutor =
                 TypeConversionUtil.getClassSubstitutor(
                     (PsiClass) parameterOwner, (PsiClass) containingClass, PsiSubstitutor.EMPTY);
             if (superClassSubstitutor != null) {
               substitutionMap.put(parameter, superClassSubstitutor.substitute(parameter));
               break;
             }
           }
         }
       } else {
         substitutionMap.put(parameter, myTypeParameters[index].getType());
       }
       index++;
     }
     return new CandidateInfo(resolve, PsiSubstitutorImpl.createSubstitutor(substitutionMap));
   } else {
     return new CandidateInfo(resolve, PsiSubstitutor.EMPTY);
   }
 }
 private PsiType rawTypeForTypeParameter(final PsiTypeParameter typeParameter) {
   final PsiClassType[] extendsTypes = typeParameter.getExtendsListTypes();
   if (extendsTypes.length > 0) {
     // First bound
     return substitute(extendsTypes[0]);
   }
   // Object
   return PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope());
 }
 private static void createTypeParamsListComment(
     final StringBuilder buffer,
     final Project project,
     final CodeDocumentationAwareCommenter commenter,
     final PsiTypeParameterList typeParameterList) {
   final PsiTypeParameter[] typeParameters = typeParameterList.getTypeParameters();
   for (PsiTypeParameter typeParameter : typeParameters) {
     buffer.append(CodeDocumentationUtil.createDocCommentLine(PARAM_TAG, project, commenter));
     buffer.append("<").append(typeParameter.getName()).append(">");
     buffer.append(LINE_SEPARATOR);
   }
 }
  private static void findTypeParameterExternalUsages(
      final PsiTypeParameter typeParameter, final Collection<UsageInfo> usages) {
    PsiTypeParameterListOwner owner = typeParameter.getOwner();
    if (owner != null) {
      final PsiTypeParameterList parameterList = owner.getTypeParameterList();
      if (parameterList != null) {
        final int paramsCount = parameterList.getTypeParameters().length;
        final int index = parameterList.getTypeParameterIndex(typeParameter);

        ReferencesSearch.search(owner)
            .forEach(
                reference -> {
                  if (reference instanceof PsiJavaCodeReferenceElement) {
                    final PsiReferenceParameterList parameterList1 =
                        ((PsiJavaCodeReferenceElement) reference).getParameterList();
                    if (parameterList1 != null) {
                      PsiTypeElement[] typeArgs = parameterList1.getTypeParameterElements();
                      if (typeArgs.length > index) {
                        if (typeArgs.length == 1
                            && paramsCount > 1
                            && typeArgs[0].getType() instanceof PsiDiamondType) return true;
                        usages.add(
                            new SafeDeleteReferenceJavaDeleteUsageInfo(
                                typeArgs[index], typeParameter, true));
                      }
                    }
                  }
                  return true;
                });
      }
    }
  }
  @Nullable
  public static PsiType getSubstitutedType(@Nullable PsiParameter parameter) {
    if (parameter == null) return null;

    final PsiType type = getType(parameter);

    if (type instanceof PsiArrayType) {
      return type;
    }

    final PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(type);
    final PsiClass psiClass = result.getElement();
    if (psiClass == null) return type;

    final Set<PsiTypeParameter> usedTypeParameters = new HashSet<PsiTypeParameter>();
    RefactoringUtil.collectTypeParameters(usedTypeParameters, parameter);
    for (Iterator<PsiTypeParameter> iterator = usedTypeParameters.iterator();
        iterator.hasNext(); ) {
      PsiTypeParameter usedTypeParameter = iterator.next();
      if (parameter.getDeclarationScope() != usedTypeParameter.getOwner()) {
        iterator.remove();
      }
    }

    PsiSubstitutor subst = PsiSubstitutor.EMPTY;
    for (PsiTypeParameter usedTypeParameter : usedTypeParameters) {
      subst =
          subst.put(usedTypeParameter, TypeConversionUtil.typeParameterErasure(usedTypeParameter));
    }

    PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
    final Map<PsiTypeParameter, PsiType> typeMap = result.getSubstitutor().getSubstitutionMap();
    for (PsiTypeParameter typeParameter : typeMap.keySet()) {
      final PsiType psiType = typeMap.get(typeParameter);
      substitutor =
          substitutor.put(typeParameter, psiType != null ? subst.substitute(psiType) : null);
    }

    if (psiClass instanceof PsiTypeParameter) {
      return subst.substitute((PsiTypeParameter) psiClass);
    } else {
      return JavaPsiFacade.getElementFactory(parameter.getProject())
          .createType(psiClass, substitutor);
    }
  }
 @Override
 public void visitTypeParameter(PsiTypeParameter parameter) {
   super.visitTypeParameter(parameter);
   final String name = parameter.getName();
   if (!PsiKeyword.ASSERT.equals(name)) {
     return;
   }
   registerTypeParameterError(parameter);
 }
 protected final void registerTypeParameterError(
     @NotNull PsiTypeParameter typeParameter, Object... infos) {
   final PsiElement nameIdentifier = typeParameter.getNameIdentifier();
   if (nameIdentifier == null) {
     registerError(typeParameter, infos);
   } else {
     registerError(nameIdentifier, infos);
   }
 }
 @Override
 public PsiType substitute(@NotNull PsiTypeParameter typeParameter) {
   if (containsInMap(typeParameter)) {
     return getFromMap(typeParameter);
   }
   return JavaPsiFacade.getInstance(typeParameter.getProject())
       .getElementFactory()
       .createType(typeParameter);
 }
 public static int getTypeParameterIndex(
     @NotNull PsiTypeParameter typeParameter, @NotNull PsiTypeParameterList typeParameterList) {
   PsiTypeParameter[] typeParameters = typeParameterList.getTypeParameters();
   for (int i = 0; i < typeParameters.length; i++) {
     if (typeParameter.equals(typeParameters[i])) return i;
   }
   LOG.assertTrue(false);
   return -1;
 }
  private void buildDelegate() {
    final PsiManager manager = sourceClass.getManager();
    final PsiElementFactory factory =
        JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
    final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(manager.getProject());
    @NonNls final StringBuilder fieldBuffer = new StringBuilder();
    final String delegateVisibility = calculateDelegateVisibility();
    if (delegateVisibility.length() > 0) fieldBuffer.append(delegateVisibility).append(' ');
    fieldBuffer.append("final ");
    final String fullyQualifiedName = getQualifiedName();
    fieldBuffer.append(fullyQualifiedName);
    if (!typeParams.isEmpty()) {
      fieldBuffer.append('<');
      for (PsiTypeParameter typeParameter : typeParams) {
        fieldBuffer.append(typeParameter.getName());
      }
      fieldBuffer.append('>');
    }
    fieldBuffer.append(' ');
    fieldBuffer.append(delegateFieldName);
    fieldBuffer.append(" = new ").append(fullyQualifiedName);
    if (!typeParams.isEmpty()) {
      fieldBuffer.append('<');
      for (PsiTypeParameter typeParameter : typeParams) {
        fieldBuffer.append(typeParameter.getName());
      }
      fieldBuffer.append('>');
    }
    fieldBuffer.append('(');
    if (requiresBackpointer) {
      fieldBuffer.append("this");
    }

    fieldBuffer.append(");");
    try {
      final String fieldString = fieldBuffer.toString();
      final PsiField field = factory.createFieldFromText(fieldString, sourceClass);
      final PsiElement newField = sourceClass.add(field);
      codeStyleManager.reformat(
          JavaCodeStyleManager.getInstance(myProject).shortenClassReferences(newField));
    } catch (IncorrectOperationException e) {
      logger.error(e);
    }
  }
  private PsiType addBounds(PsiType substituted, final PsiTypeParameter typeParameter) {
    PsiElement captureContext = null;
    if (substituted instanceof PsiCapturedWildcardType) {
      final PsiCapturedWildcardType captured = (PsiCapturedWildcardType) substituted;
      substituted = captured.getWildcard();
      captureContext = captured.getContext();
    }
    if (substituted instanceof PsiWildcardType && !((PsiWildcardType) substituted).isSuper()) {
      PsiType originalBound = ((PsiWildcardType) substituted).getBound();
      PsiManager manager = typeParameter.getManager();
      final PsiType[] boundTypes = typeParameter.getExtendsListTypes();
      for (PsiType boundType : boundTypes) {
        PsiType substitutedBoundType = boundType.accept(mySimpleSubstitutionVisitor);
        PsiWildcardType wildcardType = (PsiWildcardType) substituted;
        if (substitutedBoundType != null
            && !(substitutedBoundType instanceof PsiWildcardType)
            && !substitutedBoundType.equalsToText("java.lang.Object")) {
          if (originalBound == null
              || (!TypeConversionUtil.erasure(substitutedBoundType)
                      .isAssignableFrom(TypeConversionUtil.erasure(originalBound))
                  && !TypeConversionUtil.erasure(substitutedBoundType)
                      .isAssignableFrom(
                          originalBound))) { // erasure is essential to avoid infinite recursion
            if (wildcardType.isExtends()) {
              final PsiType glb =
                  GenericsUtil.getGreatestLowerBound(wildcardType.getBound(), substitutedBoundType);
              if (glb != null) {
                substituted = PsiWildcardType.createExtends(manager, glb);
              }
            } else {
              // unbounded
              substituted = PsiWildcardType.createExtends(manager, substitutedBoundType);
            }
          }
        }
      }
    }

    if (captureContext != null) {
      LOG.assertTrue(substituted instanceof PsiWildcardType);
      substituted = PsiCapturedWildcardType.create((PsiWildcardType) substituted, captureContext);
    }
    return substituted;
  }
 @NotNull
 private static PsiSubstitutor calculateMethodSubstitutor(
     @NotNull PsiTypeParameter[] typeParameters,
     @NotNull PsiMethod method,
     @NotNull PsiSubstitutor siteSubstitutor,
     @NotNull PsiType[] types1,
     @NotNull PsiType[] types2,
     @NotNull LanguageLevel languageLevel) {
   PsiSubstitutor substitutor =
       PsiResolveHelper.SERVICE
           .getInstance(method.getProject())
           .inferTypeArguments(typeParameters, types1, types2, languageLevel);
   for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(method)) {
     ProgressManager.checkCanceled();
     LOG.assertTrue(typeParameter != null);
     if (!substitutor.getSubstitutionMap().containsKey(typeParameter)) {
       PsiType type = siteSubstitutor.substitute(typeParameter);
       if (type instanceof PsiClassType && typeParameter.getOwner() == method) {
         final PsiClass aClass = ((PsiClassType) type).resolve();
         if (aClass instanceof PsiTypeParameter
             && ((PsiTypeParameter) aClass).getOwner() == method) {
           type = TypeConversionUtil.erasure(type, siteSubstitutor);
         }
       }
       substitutor = substitutor.put(typeParameter, type);
     } else {
       final PsiType type = substitutor.substitute(typeParameter);
       if (type instanceof PsiClassType) {
         final PsiClass aClass = ((PsiClassType) type).resolve();
         if (aClass instanceof PsiTypeParameter) {
           substitutor =
               substitutor.put(
                   typeParameter,
                   JavaPsiFacade.getElementFactory(aClass.getProject())
                       .createType(aClass, siteSubstitutor));
         }
       }
     }
   }
   return substitutor;
 }
  @Nullable
  private static PsiSubstitutor getSuperMethodSignatureSubstitutorImpl(
      @NotNull MethodSignature methodSignature, @NotNull MethodSignature superSignature) {
    // normalize generic method declarations: correlate type parameters
    // todo: correlate type params by name?
    PsiTypeParameter[] methodTypeParameters = methodSignature.getTypeParameters();
    PsiTypeParameter[] superTypeParameters = superSignature.getTypeParameters();

    // both methods are parameterized and number of parameters mismatch
    if (methodTypeParameters.length != superTypeParameters.length) return null;

    PsiSubstitutor result = superSignature.getSubstitutor();
    for (int i = 0; i < methodTypeParameters.length; i++) {
      PsiTypeParameter methodTypeParameter = methodTypeParameters[i];
      PsiElementFactory factory =
          JavaPsiFacade.getInstance(methodTypeParameter.getProject()).getElementFactory();
      result = result.put(superTypeParameters[i], factory.createType(methodTypeParameter));
    }

    return result;
  }
  public static boolean isClassEquivalentTo(@NotNull PsiClass aClass, PsiElement another) {
    if (aClass == another) return true;
    if (!(another instanceof PsiClass)) return false;
    String name1 = aClass.getName();
    if (name1 == null) return false;
    if (!another.isValid()) return false;
    String name2 = ((PsiClass) another).getName();
    if (name2 == null) return false;
    if (name1.hashCode() != name2.hashCode()) return false;
    if (!name1.equals(name2)) return false;
    String qName1 = aClass.getQualifiedName();
    String qName2 = ((PsiClass) another).getQualifiedName();
    if (qName1 == null || qName2 == null) {
      //noinspection StringEquality
      if (qName1 != qName2) return false;

      if (aClass instanceof PsiTypeParameter && another instanceof PsiTypeParameter) {
        PsiTypeParameter p1 = (PsiTypeParameter) aClass;
        PsiTypeParameter p2 = (PsiTypeParameter) another;

        return p1.getIndex() == p2.getIndex()
            && aClass.getManager().areElementsEquivalent(p1.getOwner(), p2.getOwner());
      } else {
        return false;
      }
    }
    if (qName1.hashCode() != qName2.hashCode() || !qName1.equals(qName2)) {
      return false;
    }

    if (originalElement(aClass).equals(originalElement((PsiClass) another))) {
      return true;
    }

    final PsiFile file1 = aClass.getContainingFile().getOriginalFile();
    final PsiFile file2 = another.getContainingFile().getOriginalFile();

    // see com.intellij.openapi.vcs.changes.PsiChangeTracker
    // see com.intellij.psi.impl.PsiFileFactoryImpl#createFileFromText(CharSequence,PsiFile)
    final PsiFile original1 = file1.getUserData(PsiFileFactory.ORIGINAL_FILE);
    final PsiFile original2 = file2.getUserData(PsiFileFactory.ORIGINAL_FILE);
    if (original1 == original2 && original1 != null
        || original1 == file2
        || original2 == file1
        || file1 == file2) {
      return compareClassSeqNumber(aClass, (PsiClass) another);
    }

    final FileIndexFacade fileIndex =
        ServiceManager.getService(file1.getProject(), FileIndexFacade.class);
    final VirtualFile vfile1 = file1.getViewProvider().getVirtualFile();
    final VirtualFile vfile2 = file2.getViewProvider().getVirtualFile();
    boolean lib1 = fileIndex.isInLibraryClasses(vfile1);
    boolean lib2 = fileIndex.isInLibraryClasses(vfile2);

    return (fileIndex.isInSource(vfile1) || lib1) && (fileIndex.isInSource(vfile2) || lib2);
  }
 @NotNull
 public static PsiClass[] getInterfaces(@NotNull PsiTypeParameter typeParameter) {
   final PsiClassType[] referencedTypes = typeParameter.getExtendsListTypes();
   if (referencedTypes.length == 0) {
     return PsiClass.EMPTY_ARRAY;
   }
   final List<PsiClass> result = new ArrayList<PsiClass>(referencedTypes.length);
   for (PsiClassType referencedType : referencedTypes) {
     final PsiClass psiClass = referencedType.resolve();
     if (psiClass != null && psiClass.isInterface()) {
       result.add(psiClass);
     }
   }
   return result.toArray(new PsiClass[result.size()]);
 }
Beispiel #19
0
  public SNode convertTypeParameter(PsiTypeParameter x) {
    SNode typeVar =
        SConceptOperations.createNewNode(
            "jetbrains.mps.baseLanguage.structure.TypeVariableDeclaration", null);
    SPropertyOperations.set(typeVar, "name", x.getName());

    if (x.getExtendsListTypes().length > 0) {
      Iterable<PsiClassType> extend = Sequence.fromArray(x.getExtendsListTypes());
      SLinkOperations.setTarget(
          typeVar, "bound", resolveClass(Sequence.fromIterable(extend).first()), true);
      ListSequence.fromList(SLinkOperations.getTargets(typeVar, "auxBounds", true))
          .addSequence(
              Sequence.fromIterable(extend)
                  .skip(1)
                  .select(
                      new ISelector<PsiClassType, SNode>() {
                        public SNode select(PsiClassType it) {
                          return resolveClass(it);
                        }
                      }));
    }

    return typeVar;
  }
  /**
   * @param methodSignature method signature
   * @param superMethodSignature super method signature
   * @return null if signatures do not match
   */
  @Nullable
  public static PsiSubstitutor getSuperMethodSignatureSubstitutor(
      @NotNull MethodSignature methodSignature, @NotNull MethodSignature superMethodSignature) {
    PsiSubstitutor result =
        getSuperMethodSignatureSubstitutorImpl(methodSignature, superMethodSignature);
    if (result == null) return null;

    PsiTypeParameter[] methodTypeParameters = methodSignature.getTypeParameters();
    PsiTypeParameter[] superTypeParameters = superMethodSignature.getTypeParameters();
    PsiSubstitutor methodSubstitutor = methodSignature.getSubstitutor();

    // check bounds
    for (int i = 0; i < methodTypeParameters.length; i++) {
      PsiTypeParameter methodTypeParameter = methodTypeParameters[i];
      PsiTypeParameter superTypeParameter = superTypeParameters[i];
      final Set<PsiType> methodSupers = new HashSet<PsiType>();
      for (PsiClassType methodSuper : methodTypeParameter.getSuperTypes()) {
        methodSupers.add(methodSubstitutor.substitute(methodSuper));
      }

      final Set<PsiType> superSupers = new HashSet<PsiType>();
      for (PsiClassType superSuper : superTypeParameter.getSuperTypes()) {
        superSupers.add(
            methodSubstitutor.substitute(
                PsiUtil.captureToplevelWildcards(
                    result.substitute(superSuper), methodTypeParameter)));
      }
      methodSupers.remove(
          PsiType.getJavaLangObject(
              methodTypeParameter.getManager(), methodTypeParameter.getResolveScope()));
      superSupers.remove(
          PsiType.getJavaLangObject(
              superTypeParameter.getManager(), superTypeParameter.getResolveScope()));
      if (!methodSupers.equals(superSupers)) return null;
    }

    return result;
  }
  private void buildText(
      @NotNull PsiClass aClass,
      @NotNull PsiSubstitutor substitutor,
      @NotNull StringBuilder buffer,
      boolean canonical,
      boolean internal) {
    if (aClass instanceof PsiAnonymousClass) {
      ClassResolveResult baseResolveResult =
          ((PsiAnonymousClass) aClass).getBaseClassType().resolveGenerics();
      PsiClass baseClass = baseResolveResult.getElement();
      PsiSubstitutor baseSub = baseResolveResult.getSubstitutor();
      if (baseClass != null) {
        buildText(baseClass, baseSub, buffer, canonical, internal);
      }
      return;
    }

    if (canonical == internal) {
      buffer.append(getAnnotationsTextPrefix(internal, false, true));
    }

    PsiClass enclosingClass = null;
    if (!aClass.hasModifierProperty(PsiModifier.STATIC)) {
      final PsiElement parent = aClass.getParent();
      if (parent instanceof PsiClass && !(parent instanceof PsiAnonymousClass)) {
        enclosingClass = (PsiClass) parent;
      }
    }
    if (enclosingClass != null) {
      buildText(enclosingClass, substitutor, buffer, canonical, false);
      buffer.append('.');
      buffer.append(aClass.getName());
    } else {
      final String name;
      if (!canonical) {
        name = aClass.getName();
      } else {
        final String qualifiedName = aClass.getQualifiedName();
        if (qualifiedName == null) {
          name = aClass.getName();
        } else {
          name = qualifiedName;
        }
      }
      buffer.append(name);
    }

    PsiTypeParameter[] typeParameters = aClass.getTypeParameters();
    if (typeParameters.length > 0) {
      StringBuilder pineBuffer = new StringBuilder();
      pineBuffer.append('<');
      for (int i = 0; i < typeParameters.length; i++) {
        PsiTypeParameter typeParameter = typeParameters[i];
        assert typeParameter.isValid();
        if (i > 0) pineBuffer.append(',');
        final PsiType substitutionResult = substitutor.substitute(typeParameter);
        if (substitutionResult == null) {
          pineBuffer = null;
          break;
        }
        assert substitutionResult.isValid();
        if (canonical) {
          if (internal) {
            pineBuffer.append(substitutionResult.getInternalCanonicalText());
          } else {
            pineBuffer.append(substitutionResult.getCanonicalText());
          }
        } else {
          pineBuffer.append(substitutionResult.getPresentableText());
        }
      }
      if (pineBuffer != null) {
        buffer.append(pineBuffer);
        buffer.append('>');
      }
    }
  }
 @Override
 public int computeHashCode(PsiTypeParameter object) {
   String name = object.getName();
   return name == null ? 0 : name.hashCode();
 }
  @Override
  public PsiReference getReference() {
    final PsiDocComment comment = PsiTreeUtil.getParentOfType(this, PsiDocComment.class);
    if (comment == null) return null;
    final PsiDocCommentOwner owner = comment.getOwner();
    if (!(owner instanceof PsiMethod) && !(owner instanceof PsiClass)) return null;
    final ASTNode valueToken = findChildByType(JavaDocTokenType.DOC_TAG_VALUE_TOKEN);
    if (valueToken == null) return null;
    final String name = valueToken.getText();
    PsiElement reference = null;
    final PsiElement firstChild = getFirstChild();
    if (firstChild instanceof PsiDocToken
        && ((PsiDocToken) firstChild).getTokenType().equals(JavaDocTokenType.DOC_TAG_VALUE_LT)) {
      final PsiTypeParameter[] typeParameters =
          ((PsiTypeParameterListOwner) owner).getTypeParameters();
      for (PsiTypeParameter typeParameter : typeParameters) {
        if (typeParameter.getName().equals(name)) {
          reference = typeParameter;
        }
      }
    } else if (owner instanceof PsiMethod) {
      final PsiParameter[] parameters = ((PsiMethod) owner).getParameterList().getParameters();
      for (PsiParameter parameter : parameters) {
        if (parameter.getName().equals(name)) {
          reference = parameter;
        }
      }
    }

    final PsiElement resultReference = reference;
    return new PsiJavaReference() {
      @Override
      public PsiElement resolve() {
        return resultReference;
      }

      @Override
      @NotNull
      public String getCanonicalText() {
        return valueToken.getText();
      }

      @Override
      public PsiElement handleElementRename(String newElementName) {
        final CharTable charTableByTree = SharedImplUtil.findCharTableByTree(getNode());
        LeafElement newElement =
            Factory.createSingleLeafElement(
                JavaDocTokenType.DOC_TAG_VALUE_TOKEN,
                newElementName,
                charTableByTree,
                getManager());
        replaceChild(valueToken, newElement);
        return PsiDocParamRef.this;
      }

      @Override
      public PsiElement bindToElement(@NotNull PsiElement element)
          throws IncorrectOperationException {
        if (isReferenceTo(element)) return PsiDocParamRef.this;
        if (!(element instanceof PsiParameter)) {
          throw new IncorrectOperationException("Unsupported operation");
        }
        return handleElementRename(((PsiParameter) element).getName());
      }

      @Override
      public boolean isReferenceTo(PsiElement element) {
        if (!(element instanceof PsiNamedElement)) return false;
        PsiNamedElement namedElement = (PsiNamedElement) element;
        if (!getCanonicalText().equals(namedElement.getName())) return false;
        return getManager().areElementsEquivalent(resolve(), element);
      }

      @Override
      @NotNull
      public PsiElement[] getVariants() {
        final PsiElement firstChild = getFirstChild();

        Set<String> usedNames = new HashSet<String>();
        for (PsiDocTag tag : comment.getTags()) {
          if (tag.getName().equals("param")) {
            PsiDocTagValue valueElement = tag.getValueElement();
            if (valueElement != null) {
              usedNames.add(valueElement.getText());
            }
          }
        }

        PsiNamedElement[] result = PsiNamedElement.EMPTY_ARRAY;
        if (firstChild instanceof PsiDocToken
            && ((PsiDocToken) firstChild)
                .getTokenType()
                .equals(JavaDocTokenType.DOC_TAG_VALUE_LT)) {
          result = ((PsiTypeParameterListOwner) owner).getTypeParameters();
        } else if (owner instanceof PsiMethod) {
          result = ((PsiMethod) owner).getParameterList().getParameters();
        }
        List<PsiElement> filtered = new ArrayList<PsiElement>();
        for (PsiNamedElement namedElement : result) {
          if (!usedNames.contains(namedElement.getName())) {
            filtered.add(namedElement);
          }
        }
        return filtered.toArray(new PsiElement[filtered.size()]);
      }

      @Override
      public boolean isSoft() {
        return false;
      }

      @Override
      public TextRange getRangeInElement() {
        final int startOffsetInParent = valueToken.getPsi().getStartOffsetInParent();
        return new TextRange(startOffsetInParent, startOffsetInParent + valueToken.getTextLength());
      }

      @Override
      public PsiElement getElement() {
        return PsiDocParamRef.this;
      }

      @Override
      public void processVariants(PsiScopeProcessor processor) {
        for (final PsiElement element : getVariants()) {
          if (!processor.execute(element, ResolveState.initial())) {
            return;
          }
        }
      }

      @Override
      @NotNull
      public JavaResolveResult advancedResolve(boolean incompleteCode) {
        return resultReference == null
            ? JavaResolveResult.EMPTY
            : new CandidateInfo(resultReference, PsiSubstitutor.EMPTY);
      }

      @Override
      @NotNull
      public JavaResolveResult[] multiResolve(boolean incompleteCode) {
        return resultReference == null
            ? JavaResolveResult.EMPTY_ARRAY
            : new JavaResolveResult[] {new CandidateInfo(resultReference, PsiSubstitutor.EMPTY)};
      }
    };
  }
 @Override
 public boolean equals(PsiTypeParameter element1, PsiTypeParameter element2) {
   return element1.getManager().areElementsEquivalent(element1, element2);
 }
  @SuppressWarnings({"HardCodedStringLiteral"})
  private static String generateClassInfo(PsiClass aClass) {
    StringBuilder buffer = new StringBuilder();
    GroovyFile file = (GroovyFile) aClass.getContainingFile();

    String packageName = file.getPackageName();
    if (packageName.length() > 0) {
      buffer.append(packageName).append("\n");
    }

    final String classString =
        aClass.isInterface()
            ? "interface"
            : aClass instanceof PsiTypeParameter
                ? "type parameter"
                : aClass.isEnum() ? "enum" : "class";
    buffer.append(classString).append(" ").append(aClass.getName());

    if (aClass.hasTypeParameters()) {
      PsiTypeParameter[] typeParameters = aClass.getTypeParameters();

      buffer.append("<");

      for (int i = 0; i < typeParameters.length; i++) {
        if (i > 0) buffer.append(", ");

        PsiTypeParameter tp = typeParameters[i];

        buffer.append(tp.getName());

        PsiClassType[] refs = tp.getExtendsListTypes();

        if (refs.length > 0) {
          buffer.append(" extends ");

          for (int j = 0; j < refs.length; j++) {
            if (j > 0) buffer.append(" & ");
            appendTypeString(buffer, refs[j], aClass);
          }
        }
      }

      buffer.append(">");
    }

    PsiClassType[] refs = aClass.getExtendsListTypes();
    if (refs.length > 0
        || !aClass.isInterface()
            && !CommonClassNames.JAVA_LANG_OBJECT.equals(aClass.getQualifiedName())) {
      buffer.append(" extends ");
      if (refs.length == 0) {
        buffer.append("Object");
      } else {
        for (int i = 0; i < refs.length; i++) {
          if (i > 0) buffer.append(", ");
          appendTypeString(buffer, refs[i], aClass);
        }
      }
    }

    refs = aClass.getImplementsListTypes();
    if (refs.length > 0) {
      buffer.append("\nimplements ");
      for (int i = 0; i < refs.length; i++) {
        if (i > 0) buffer.append(", ");
        appendTypeString(buffer, refs[i], aClass);
      }
    }

    return buffer.toString();
  }