2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.codeInsight
.daemon
.impl
.quickfix
;
18 import com
.intellij
.codeInsight
.ExpectedTypeInfo
;
19 import com
.intellij
.codeInsight
.ExpectedTypesProvider
;
20 import com
.intellij
.codeInsight
.intention
.impl
.TypeExpression
;
21 import com
.intellij
.codeInsight
.template
.TemplateBuilder
;
22 import com
.intellij
.openapi
.application
.ApplicationManager
;
23 import com
.intellij
.openapi
.diagnostic
.Logger
;
24 import com
.intellij
.openapi
.project
.Project
;
25 import com
.intellij
.psi
.*;
26 import com
.intellij
.psi
.search
.GlobalSearchScope
;
27 import com
.intellij
.psi
.util
.PsiTreeUtil
;
28 import com
.intellij
.util
.IncorrectOperationException
;
29 import org
.jetbrains
.annotations
.Nullable
;
31 import java
.util
.ArrayList
;
32 import java
.util
.Arrays
;
33 import java
.util
.List
;
39 public class GuessTypeParameters
{
40 private final PsiElementFactory myFactory
;
41 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.GuessTypeParameters");
43 public GuessTypeParameters(PsiElementFactory factory
) {
47 private List
<PsiType
> matchingTypeParameters (PsiType
[] paramVals
, PsiTypeParameter
[] params
, ExpectedTypeInfo info
) {
48 PsiType type
= info
.getType();
49 int kind
= info
.getKind();
51 List
<PsiType
> result
= new ArrayList
<PsiType
>();
52 for (int i
= 0; i
< paramVals
.length
; i
++) {
53 PsiType val
= paramVals
[i
];
56 case ExpectedTypeInfo
.TYPE_STRICTLY
:
57 if (val
.equals(type
)) result
.add(myFactory
.createType(params
[i
]));
59 case ExpectedTypeInfo
.TYPE_OR_SUBTYPE
:
60 if (type
.isAssignableFrom(val
)) result
.add(myFactory
.createType(params
[i
]));
62 case ExpectedTypeInfo
.TYPE_OR_SUPERTYPE
:
63 if (val
.isAssignableFrom(type
)) result
.add(myFactory
.createType(params
[i
]));
72 public void setupTypeElement (PsiTypeElement typeElement
, ExpectedTypeInfo
[] infos
, PsiSubstitutor substitutor
,
73 TemplateBuilder builder
, @Nullable PsiElement context
, PsiClass targetClass
) {
74 LOG
.assertTrue(typeElement
.isValid());
75 ApplicationManager
.getApplication().assertWriteAccessAllowed();
77 PsiManager manager
= typeElement
.getManager();
78 GlobalSearchScope scope
= typeElement
.getResolveScope();
79 Project project
= manager
.getProject();
81 if (infos
.length
== 1 && substitutor
!= null && substitutor
!= PsiSubstitutor
.EMPTY
) {
82 ExpectedTypeInfo info
= infos
[0];
83 Map
<PsiTypeParameter
, PsiType
> map
= substitutor
.getSubstitutionMap();
84 PsiType
[] vals
= map
.values().toArray(new PsiType
[map
.size()]);
85 PsiTypeParameter
[] params
= map
.keySet().toArray(new PsiTypeParameter
[map
.size()]);
87 List
<PsiType
> types
= matchingTypeParameters(vals
, params
, info
);
88 if (!types
.isEmpty()) {
89 types
.addAll(Arrays
.asList(ExpectedTypesProvider
.processExpectedTypes(infos
, new MyTypeVisitor(manager
, scope
), project
)));
90 builder
.replaceElement(typeElement
, new TypeExpression(project
, types
.toArray(new PsiType
[types
.size()])));
94 PsiElementFactory factory
= JavaPsiFacade
.getInstance(manager
.getProject()).getElementFactory();
95 PsiType type
= info
.getType();
96 PsiType defaultType
= info
.getDefaultType();
98 PsiTypeElement inplaceTypeElement
= ((PsiVariable
)factory
.createVariableDeclarationStatement("foo", type
, null).getDeclaredElements()[0]).getTypeElement();
100 PsiSubstitutor rawingSubstitutor
= getRawingSubstitutor (context
, targetClass
);
101 int substitionResult
= substituteToTypeParameters(typeElement
, inplaceTypeElement
, vals
, params
, builder
, rawingSubstitutor
, true);
102 if (substitionResult
!= SUBSTITUTED_NONE
) {
103 if (substitionResult
== SUBSTITUTED_IN_PARAMETERS
) {
104 PsiJavaCodeReferenceElement refElement
= typeElement
.getInnermostComponentReferenceElement();
105 LOG
.assertTrue(refElement
!= null && refElement
.getReferenceNameElement() != null);
106 type
= getComponentType(type
);
107 LOG
.assertTrue(type
!= null);
108 defaultType
= getComponentType(defaultType
);
109 LOG
.assertTrue(defaultType
!= null);
110 ExpectedTypeInfo info1
= ExpectedTypesProvider
.createInfo(((PsiClassType
)defaultType
).rawType(),
111 ExpectedTypeInfo
.TYPE_STRICTLY
,
112 ((PsiClassType
)defaultType
).rawType(),
114 MyTypeVisitor visitor
= new MyTypeVisitor(manager
, scope
);
115 builder
.replaceElement(refElement
.getReferenceNameElement(),
116 new TypeExpression(project
, ExpectedTypesProvider
.processExpectedTypes(new ExpectedTypeInfo
[]{info1
}, visitor
, project
)));
122 catch (IncorrectOperationException e
) {
128 PsiType
[] types
= infos
.length
== 0 ?
new PsiType
[] {typeElement
.getType()} : ExpectedTypesProvider
.processExpectedTypes(infos
, new MyTypeVisitor(manager
, scope
), project
);
129 builder
.replaceElement(typeElement
,
130 new TypeExpression(project
, types
));
133 private static PsiSubstitutor
getRawingSubstitutor(PsiElement context
, PsiClass targetClass
) {
134 if (context
== null || targetClass
== null) return PsiSubstitutor
.EMPTY
;
136 PsiTypeParameterListOwner currContext
= PsiTreeUtil
.getParentOfType(context
, PsiTypeParameterListOwner
.class);
137 PsiManager manager
= context
.getManager();
138 PsiSubstitutor substitutor
= PsiSubstitutor
.EMPTY
;
139 while (currContext
!= null && !manager
.areElementsEquivalent(currContext
, targetClass
)) {
140 PsiTypeParameter
[] typeParameters
= currContext
.getTypeParameters();
141 substitutor
= JavaPsiFacade
.getInstance(context
.getProject()).getElementFactory().createRawSubstitutor(substitutor
, typeParameters
);
142 currContext
= currContext
.getContainingClass();
148 private static PsiClassType
getComponentType (PsiType type
) {
149 type
= type
.getDeepComponentType();
150 if (type
instanceof PsiClassType
) return (PsiClassType
)type
;
155 private static final int SUBSTITUTED_NONE
= 0;
156 private static final int SUBSTITUTED_IN_REF
= 1;
157 private static final int SUBSTITUTED_IN_PARAMETERS
= 2;
159 private int substituteToTypeParameters (PsiTypeElement typeElement
,
160 PsiTypeElement inplaceTypeElement
,
162 PsiTypeParameter
[] params
,
163 TemplateBuilder builder
,
164 PsiSubstitutor rawingSubstitutor
,
166 PsiType type
= inplaceTypeElement
.getType();
167 List
<PsiType
> types
= new ArrayList
<PsiType
>();
168 for (int i
= 0; i
< paramVals
.length
; i
++) {
169 PsiType val
= paramVals
[i
];
170 if (val
== null) return SUBSTITUTED_NONE
;
171 if (type
.equals(val
)) {
172 types
.add(myFactory
.createType(params
[i
]));
176 if (!types
.isEmpty()) {
177 Project project
= typeElement
.getProject();
178 PsiType substituted
= rawingSubstitutor
.substitute(type
);
179 if (!"java.lang.Object".equals(substituted
.getCanonicalText()) && (toplevel
|| substituted
.equals(type
))) {
180 types
.add(substituted
);
183 builder
.replaceElement(typeElement
, new TypeExpression(project
, types
.toArray(new PsiType
[types
.size()])));
184 return toplevel ? SUBSTITUTED_IN_REF
: SUBSTITUTED_IN_PARAMETERS
;
187 boolean substituted
= false;
188 PsiJavaCodeReferenceElement ref
= typeElement
.getInnermostComponentReferenceElement();
189 PsiJavaCodeReferenceElement inplaceRef
= inplaceTypeElement
.getInnermostComponentReferenceElement();
191 LOG
.assertTrue(inplaceRef
!= null);
192 PsiTypeElement
[] innerTypeElements
= ref
.getParameterList().getTypeParameterElements();
193 PsiTypeElement
[] inplaceInnerTypeElements
= inplaceRef
.getParameterList().getTypeParameterElements();
194 for (int i
= 0; i
< innerTypeElements
.length
; i
++) {
195 substituted
|= substituteToTypeParameters(innerTypeElements
[i
], inplaceInnerTypeElements
[i
], paramVals
, params
, builder
,
196 rawingSubstitutor
, false) != SUBSTITUTED_NONE
;
200 return substituted ? SUBSTITUTED_IN_PARAMETERS
: SUBSTITUTED_NONE
;
203 public static class MyTypeVisitor
extends PsiTypeVisitor
<PsiType
> {
204 private final GlobalSearchScope myResolveScope
;
205 private final PsiManager myManager
;
207 public MyTypeVisitor(PsiManager manager
, GlobalSearchScope resolveScope
) {
209 myResolveScope
= resolveScope
;
212 public PsiType
visitType(PsiType type
) {
213 if (type
.equals(PsiType
.NULL
)) return PsiType
.getJavaLangObject(myManager
, myResolveScope
);
217 public PsiType
visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType
) {
218 return capturedWildcardType
.getUpperBound().accept(this);