diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/documentation/GroovyDocumentationProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/documentation/GroovyDocumentationProvider.java index 380d3015f770..dd0058bfee4f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/documentation/GroovyDocumentationProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/documentation/GroovyDocumentationProvider.java @@ -44,6 +44,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.dsl.CustomMembersGenerator; import org.jetbrains.plugins.groovy.dsl.holders.NonCodeMembersHolder; import org.jetbrains.plugins.groovy.extensions.NamedArgumentDescriptor; +import org.jetbrains.plugins.groovy.lang.completion.GrPropertyForCompletion; import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocComment; import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocCommentOwner; import org.jetbrains.plugins.groovy.lang.groovydoc.psi.impl.GrDocCommentUtil; @@ -59,8 +60,8 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefini import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.impl.GrTraitType; import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; -import org.jetbrains.plugins.groovy.lang.completion.GrPropertyForCompletion; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrImplicitVariable; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightVariable; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; @@ -234,7 +235,6 @@ public class GroovyDocumentationProvider implements CodeDocumentationProvider, E buffer.append(LINE_SEPARATOR); } - @SuppressWarnings({"HardCodedStringLiteral"}) private static String generateClassInfo(PsiClass aClass) { StringBuilder buffer = new StringBuilder(); GroovyFile file = (GroovyFile)aClass.getContainingFile(); @@ -303,8 +303,11 @@ public class GroovyDocumentationProvider implements CodeDocumentationProvider, E } - public static void appendTypeString(StringBuilder buffer, final PsiType type, PsiElement context) { - if (type != null) { + public static void appendTypeString(@NotNull StringBuilder buffer, @Nullable PsiType type, PsiElement context) { + if (type instanceof GrTraitType) { + generateTraitType(buffer, ((GrTraitType)type), context); + } + else if (type != null) { JavaDocInfoGenerator.generateType(buffer, type, context); } else { @@ -312,6 +315,18 @@ public class GroovyDocumentationProvider implements CodeDocumentationProvider, E } } + private static void generateTraitType(@NotNull StringBuilder buffer, @NotNull GrTraitType type, PsiElement context) { + PsiClassType exprType = type.getExprType(); + List traitTypes = type.getTraitTypes(); + appendTypeString(buffer, exprType, context); + buffer.append(" as "); + for (PsiClassType traitType : traitTypes) { + appendTypeString(buffer, traitType, context); + buffer.append(", "); + } + buffer.delete(buffer.length() - 2, buffer.length()); + } + @Override @Nullable public List getUrlFor(PsiElement element, PsiElement originalElement) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTraitType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTraitType.java index e8b2bf598521..a8bf62957332 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTraitType.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTraitType.java @@ -17,11 +17,10 @@ package org.jetbrains.plugins.groovy.lang.psi.impl; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.VolatileNotNullLazyValue; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.PsiAnnotation; -import com.intellij.psi.PsiClassType; -import com.intellij.psi.PsiType; -import com.intellij.psi.PsiTypeVisitor; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.*; import com.intellij.util.ArrayUtil; @@ -37,32 +36,49 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrSafeCa import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrImplementsClause; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrReferenceList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; -import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClassTypeElement; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeParameter; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeParameterList; import org.jetbrains.plugins.groovy.lang.psi.util.GrTraitUtil; import java.util.Collections; import java.util.List; +import java.util.Map; /** * Created by Max Medvedev on 20/05/14 */ -public class GrTraitType extends PsiType { +public class GrTraitType extends PsiClassType { private static final Logger LOG = Logger.getInstance(GrTraitType.class); private final GrExpression myOriginal; - private final GlobalSearchScope myResolveScope; + private final PsiClassType myExprType; private final List myTraitTypes; + private final GlobalSearchScope myResolveScope; + + private final VolatileNotNullLazyValue myParameters = new VolatileNotNullLazyValue() { + @NotNull + @Override + protected PsiType[] compute() { + List result = ContainerUtil.newArrayList(); + ContainerUtil.addAll(result, myExprType.getParameters()); + for (PsiClassType type : myTraitTypes) { + ContainerUtil.addAll(result, type.getParameters()); + } + return result.toArray(new PsiType[result.size()]); + } + }; + public GrTraitType(@NotNull GrExpression original, @NotNull PsiClassType exprType, @NotNull List traitTypes, - @NotNull GlobalSearchScope resolveScope) { - super(PsiAnnotation.EMPTY_ARRAY); + @NotNull GlobalSearchScope resolveScope, LanguageLevel languageLevel) { + super(languageLevel); myOriginal = original; myResolveScope = resolveScope; myExprType = exprType; @@ -112,9 +128,33 @@ public class GrTraitType extends PsiType { return false; } + @NotNull @Override - public A accept(@NotNull PsiTypeVisitor visitor) { - return visitor.visitType(this); + public LanguageLevel getLanguageLevel() { + return myLanguageLevel; + } + + @NotNull + @Override + public PsiClassType setLanguageLevel(@NotNull LanguageLevel languageLevel) { + return new GrTraitType(myOriginal, myExprType, myTraitTypes, myResolveScope, languageLevel); + } + + @Nullable + @Override + public PsiClass resolve() { + return getMockTypeDefinition(); + } + + @Override + public String getClassName() { + return null; + } + + @NotNull + @Override + public PsiType[] getParameters() { + return myParameters.getValue(); } @NotNull @@ -126,6 +166,32 @@ public class GrTraitType extends PsiType { return result; } + @NotNull + @Override + public ClassResolveResult resolveGenerics() { + return CachedValuesManager.getCachedValue(myOriginal, new CachedValueProvider() { + @Nullable + @Override + public Result compute() { + final GrTypeDefinition definition = new MockTypeBuilder().buildMockTypeDefinition(); + final PsiSubstitutor substitutor = new SubstitutorBuilder(definition).buildSubstitutor(); + + return Result.create(new TraitResolveResult(definition, substitutor), PsiModificationTracker.MODIFICATION_COUNT); + } + }); + } + + @NotNull + @Override + public PsiClassType rawType() { + return new GrTraitType(myOriginal, myExprType.rawType(), ContainerUtil.map(myTraitTypes, new Function() { + @Override + public PsiClassType fun(PsiClassType type) { + return type.rawType(); + } + }), myResolveScope, myLanguageLevel); + } + @NotNull @Override public GlobalSearchScope getResolveScope() { @@ -138,7 +204,7 @@ public class GrTraitType extends PsiType { @Nullable @Override public Result compute() { - return Result.create(buildMockTypeDefinition(), PsiModificationTracker.MODIFICATION_COUNT); + return Result.create(new MockTypeBuilder().buildMockTypeDefinition(), PsiModificationTracker.MODIFICATION_COUNT); } }); } @@ -151,39 +217,6 @@ public class GrTraitType extends PsiType { return Collections.unmodifiableList(myTraitTypes); } - @Nullable - private GrTypeDefinition buildMockTypeDefinition() { - GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myOriginal.getProject()); - try { - GrTypeDefinition definition = factory.createTypeDefinition("class ___________Temp______ extends Super implements Trait {}"); - replaceReferenceWith(factory, definition.getExtendsClause(), myExprType); - addReferencesWith(factory, definition.getImplementsClause(), myTraitTypes); - return definition; - } - catch (IncorrectOperationException e) { - return null; - } - } - - private static void addReferencesWith(GroovyPsiElementFactory factory, GrImplementsClause clause, List traitTypes) { - clause.getReferenceElementsGroovy()[0].delete(); - for (PsiClassType type : traitTypes) { - GrCodeReferenceElement ref = ((GrClassTypeElement)factory.createTypeElement(type)).getReferenceElement(); - clause.add(ref); - } - } - - private static void replaceReferenceWith(@NotNull GroovyPsiElementFactory factory, - @Nullable GrReferenceList clause, - @NotNull PsiClassType type) { - LOG.assertTrue(clause != null); - GrCodeReferenceElement mockSuperReference = clause.getReferenceElementsGroovy()[0]; - - GrClassTypeElement superTypeElement = ((GrClassTypeElement)factory.createTypeElement(type)); - GrCodeReferenceElement superReference = superTypeElement.getReferenceElement(); - mockSuperReference.replace(superReference); - } - public GrTraitType erasure() { PsiClassType exprType = (PsiClassType)TypeConversionUtil.erasure(myExprType); List traitTypes = ContainerUtil.map(myTraitTypes, new Function() { @@ -192,7 +225,7 @@ public class GrTraitType extends PsiType { return (PsiClassType)TypeConversionUtil.erasure(type); } }); - return new GrTraitType(myOriginal, exprType, traitTypes, myResolveScope); + return new GrTraitType(myOriginal, exprType, traitTypes, myResolveScope, LanguageLevel.JDK_1_5); } @Nullable @@ -206,7 +239,8 @@ public class GrTraitType extends PsiType { PsiType type = typeElement.getType(); if (!GrTraitUtil.isTrait(PsiTypesUtil.getPsiClass(type))) return null; - return new GrTraitType(safeCastExpression, ((PsiClassType)exprType), Collections.singletonList((PsiClassType)type), safeCastExpression.getResolveScope()); + return new GrTraitType(safeCastExpression, ((PsiClassType)exprType), Collections.singletonList((PsiClassType)type), safeCastExpression.getResolveScope(), + LanguageLevel.JDK_1_5); } @@ -215,6 +249,155 @@ public class GrTraitType extends PsiType { @NotNull PsiClassType exprType, @NotNull List traitTypes, @NotNull GlobalSearchScope resolveScope) { - return new GrTraitType(context, exprType, traitTypes, resolveScope); + return new GrTraitType(context, exprType, traitTypes, resolveScope, LanguageLevel.JDK_1_5); + } + + private static class TraitResolveResult implements ClassResolveResult { + + private final GrTypeDefinition myDefinition; + private final PsiSubstitutor mySubstitutor; + + public TraitResolveResult(GrTypeDefinition definition, PsiSubstitutor substitutor) { + myDefinition = definition; + mySubstitutor = substitutor; + } + + @Override + public GrTypeDefinition getElement() { + return myDefinition; + } + + @NotNull + @Override + public PsiSubstitutor getSubstitutor() { + return mySubstitutor; + } + + @Override + public boolean isPackagePrefixPackageReference() { + return false; + } + + @Override + public boolean isAccessible() { + return true; + } + + @Override + public boolean isStaticsScopeCorrect() { + return true; + } + + @Override + public PsiElement getCurrentFileResolveScope() { + return null; + } + + @Override + public boolean isValidResult() { + return true; + } + } + + private class MockTypeBuilder { + GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myOriginal.getProject()); + + @Nullable + public GrTypeDefinition buildMockTypeDefinition() { + try { + + StringBuilder buffer = new StringBuilder("class _____________Temp___________ "); + prepareGenerics(buffer); + buffer.append(" extends Super implements Trait {}"); + GrTypeDefinition definition = factory.createTypeDefinition(buffer.toString()); + replaceReferenceWith(definition.getExtendsClause(), myExprType); + addReferencesWith(definition.getImplementsClause(), myTraitTypes, myExprType.getParameterCount()); + return definition; + } + catch (IncorrectOperationException e) { + return null; + } + } + + private void prepareGenerics(StringBuilder buffer) { + int count = myExprType.getParameterCount(); + for (PsiClassType trait : myTraitTypes) { + count += trait.getParameterCount(); + } + if (count == 0) return; + + buffer.append('<'); + for (int i = 0; i < count; i++) { + buffer.append("T").append(i).append(","); + } + buffer.replace(buffer.length() - 1, buffer.length(), ">"); + } + + private void addReferencesWith(@Nullable GrImplementsClause clause, @NotNull List traitTypes, int parameterOffset) { + LOG.assertTrue(clause != null); + clause.getReferenceElementsGroovy()[0].delete(); + for (PsiClassType type : traitTypes) { + processType(clause, type, parameterOffset); + parameterOffset += type.getParameterCount(); + } + } + + private void replaceReferenceWith(@Nullable GrReferenceList clause, @NotNull PsiClassType type) { + LOG.assertTrue(clause != null); + clause.getReferenceElementsGroovy()[0].delete(); + processType(clause, type, 0); + } + + private void processType(@NotNull GrReferenceList clause, @NotNull PsiClassType type, int parameterOffset) { + PsiClass resolved = type.resolve(); + if (resolved != null) { + String qname = resolved.getQualifiedName(); + StringBuilder buffer = new StringBuilder(); + buffer.append(qname); + int parameterCount = type.getParameterCount(); + if (parameterCount > 0) { + buffer.append('<'); + for (int i = 0; i < parameterCount; i++) { + buffer.append("T").append(parameterOffset + i).append(','); + } + buffer.replace(buffer.length() - 1, buffer.length(), ">"); + } + + GrCodeReferenceElement ref = factory.createCodeReferenceElementFromText(buffer.toString()); + clause.add(ref); + } + } + } + + private class SubstitutorBuilder { + + private final GrTypeParameter[] myParameters; + private int myOffset = 0; + + public SubstitutorBuilder(@NotNull GrTypeDefinition definition) { + GrTypeParameterList typeParameterList = definition.getTypeParameterList(); + myParameters = typeParameterList != null ? typeParameterList.getTypeParameters() : GrTypeParameter.EMPTY_ARRAY; + } + + @NotNull + public PsiSubstitutor buildSubstitutor() { + if (myParameters.length == 0) return PsiSubstitutor.EMPTY; + + Map map = ContainerUtil.newLinkedHashMap(); + putMappingAndReturnOffset(map, myExprType); + for (PsiClassType type : myTraitTypes) { + putMappingAndReturnOffset(map, type); + } + + return JavaPsiFacade.getElementFactory(myOriginal.getProject()).createSubstitutor(map); + } + + private void putMappingAndReturnOffset(@NotNull Map map, @NotNull PsiClassType type) { + PsiType[] args = type.getParameters(); + for (int i = 0; i < args.length; i++) { + map.put(myParameters[myOffset + i], args[i]); + } + myOffset += args.length; + } } }