update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / daemon / impl / quickfix / GuessTypeParameters.java
blob4d2493a058038f009fcc37a9425f9840f66e75fd
1 /*
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;
34 import java.util.Map;
36 /**
37 * @author ven
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) {
44 myFactory = 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];
54 if (val != null) {
55 switch (kind) {
56 case ExpectedTypeInfo.TYPE_STRICTLY:
57 if (val.equals(type)) result.add(myFactory.createType(params[i]));
58 break;
59 case ExpectedTypeInfo.TYPE_OR_SUBTYPE:
60 if (type.isAssignableFrom(val)) result.add(myFactory.createType(params[i]));
61 break;
62 case ExpectedTypeInfo.TYPE_OR_SUPERTYPE:
63 if (val.isAssignableFrom(type)) result.add(myFactory.createType(params[i]));
64 break;
69 return result;
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()])));
91 return;
93 else {
94 PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
95 PsiType type = info.getType();
96 PsiType defaultType = info.getDefaultType();
97 try {
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(),
113 info.getTailType());
114 MyTypeVisitor visitor = new MyTypeVisitor(manager, scope);
115 builder.replaceElement(refElement.getReferenceNameElement(),
116 new TypeExpression(project, ExpectedTypesProvider.processExpectedTypes(new ExpectedTypeInfo[]{info1}, visitor, project)));
119 return;
122 catch (IncorrectOperationException e) {
123 LOG.error(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();
145 return substitutor;
148 private static PsiClassType getComponentType (PsiType type) {
149 type = type.getDeepComponentType();
150 if (type instanceof PsiClassType) return (PsiClassType)type;
152 return null;
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,
161 PsiType[] paramVals,
162 PsiTypeParameter[] params,
163 TemplateBuilder builder,
164 PsiSubstitutor rawingSubstitutor,
165 boolean toplevel) {
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();
190 if (ref != null) {
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) {
208 myManager = manager;
209 myResolveScope = resolveScope;
212 public PsiType visitType(PsiType type) {
213 if (type.equals(PsiType.NULL)) return PsiType.getJavaLangObject(myManager, myResolveScope);
214 return type;
217 public PsiType visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) {
218 return capturedWildcardType.getUpperBound().accept(this);