93fb11d11c608fa6ad6ebfbc29a91f8e7146c217
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / lang / psi / impl / auxiliary / GrListOrMapImpl.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary;
18
19 import com.intellij.lang.ASTNode;
20 import com.intellij.psi.*;
21 import com.intellij.psi.search.GlobalSearchScope;
22 import com.intellij.psi.tree.TokenSet;
23 import com.intellij.util.Function;
24 import org.jetbrains.annotations.NotNull;
25 import org.jetbrains.annotations.Nullable;
26 import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
27 import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mCOMMA;
28 import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
29 import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
30 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap;
31 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration;
32 import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel;
33 import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument;
34 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
35 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrParenthesizedExpression;
36 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement;
37 import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType;
38 import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager;
39 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrExpressionImpl;
40 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
41 import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
42
43 /**
44  * @author ilyas
45  */
46 public class GrListOrMapImpl extends GrExpressionImpl implements GrListOrMap {
47   private static final TokenSet MAP_LITERAL_TOKEN_SET = TokenSet.create(GroovyElementTypes.ARGUMENT, GroovyTokenTypes.mCOLON);
48   private static final Function<GrListOrMapImpl, PsiType> TYPES_CALCULATOR = new MyTypesCalculator();
49
50   public GrListOrMapImpl(@NotNull ASTNode node) {
51     super(node);
52   }
53
54   public void accept(GroovyElementVisitor visitor) {
55     visitor.visitListOrMap(this);
56   }
57
58   public String toString() {
59     return "Generalized list";
60   }
61
62   @Override
63   public ASTNode addInternal(ASTNode first, ASTNode last, ASTNode anchor, Boolean before) {
64     final GrExpression[] initializers = getInitializers();
65     if (initializers.length == 0) {
66       return super.addInternal(first, last, getNode().getFirstChildNode(), false);
67     }
68     final ASTNode lastChild = getNode().getLastChildNode();
69     getNode().addLeaf(mCOMMA, ",", lastChild);
70     return super.addInternal(first, last, lastChild.getTreePrev(), false);
71   }
72
73   public PsiType getType() {
74     return GroovyPsiManager.getInstance(getProject()).getType(this, TYPES_CALCULATOR);
75   }
76
77   public boolean isMap() {
78     return findChildByType(MAP_LITERAL_TOKEN_SET) != null;
79   }
80
81   @NotNull
82   public GrExpression[] getInitializers() {
83     return findChildrenByClass(GrExpression.class);
84   }
85
86   @NotNull
87   public GrNamedArgument[] getNamedArguments() {
88     return findChildrenByClass(GrNamedArgument.class);
89   }
90
91   private static class MyTypesCalculator implements Function<GrListOrMapImpl, PsiType> {
92     public PsiType fun(GrListOrMapImpl listOrMap) {
93       final GlobalSearchScope scope = listOrMap.getResolveScope();
94       if (listOrMap.isMap()) {
95         return inferMapInitializerType(listOrMap, JavaPsiFacade.getInstance(listOrMap.getProject()), scope);
96       }
97
98       PsiElement parent = listOrMap.getParent();
99       if (parent.getParent() instanceof GrVariableDeclaration) {
100         GrTypeElement typeElement = ((GrVariableDeclaration)parent.getParent()).getTypeElementGroovy();
101         if (typeElement != null) {
102           PsiType declaredType = typeElement.getType();
103           if (declaredType instanceof PsiArrayType) return declaredType;
104         }
105       }
106
107       return getTupleType(listOrMap.getInitializers(), listOrMap);
108     }
109
110     @Nullable
111     private static PsiClassType inferMapInitializerType(GrListOrMapImpl listOrMap, JavaPsiFacade facade, GlobalSearchScope scope) {
112       PsiClass mapClass = facade.findClass("java.util.Map", scope);
113       PsiElementFactory factory = facade.getElementFactory();
114       if (mapClass != null) {
115         PsiTypeParameter[] typeParameters = mapClass.getTypeParameters();
116         if (typeParameters.length == 2) {
117           GrNamedArgument[] namedArgs = listOrMap.getNamedArguments();
118           GrExpression[] values = new GrExpression[namedArgs.length];
119           GrArgumentLabel[] labels = new GrArgumentLabel[namedArgs.length];
120
121           for (int i = 0; i < values.length; i++) {
122             GrExpression expr = namedArgs[i].getExpression();
123             if (expr == null) return null;
124             values[i] = expr;
125             GrArgumentLabel label = namedArgs[i].getLabel();
126             if (label == null) return null;
127             labels[i] = label;
128           }
129
130           PsiType initializerType = getInitializerType(values);
131           PsiType labelType = getLabelsType(labels);
132           PsiSubstitutor substitutor = PsiSubstitutor.EMPTY.
133             put(typeParameters[0], labelType).
134             put(typeParameters[1], initializerType);
135           return factory.createType(mapClass, substitutor);
136         }
137         else {
138           return facade.getElementFactory().createType(mapClass);
139         }
140       }
141       return null;
142     }
143
144     @Nullable
145     private static PsiType getLabelsType(GrArgumentLabel[] labels) {
146       if (labels.length == 0) return null;
147       PsiType result = null;
148       PsiManager manager = labels[0].getManager();
149       for (GrArgumentLabel label : labels) {
150         PsiElement el = label.getNameElement();
151         final PsiType other;
152         if (el instanceof GrParenthesizedExpression) {
153           other = ((GrParenthesizedExpression)el).getType();
154         }
155         else {
156           if (el.getNode() != null) {
157             other = TypesUtil.getPsiType(el, el.getNode().getElementType());
158           }
159           else {
160             other = null;
161           }
162         }
163         result = getLeastUpperBound(result, other, manager);
164       }
165       return result;
166     }
167
168     private static PsiClassType getTupleType(GrExpression[] initializers, GrListOrMap listOrMap) {
169       PsiType[] result = new PsiType[initializers.length];
170       boolean isLValue = PsiUtil.isLValue(listOrMap);
171       for (int i = 0; i < result.length; i++) {
172         result[i] = isLValue ? initializers[i].getNominalType() : initializers[i].getType();
173       }
174       return new GrTupleType(result, JavaPsiFacade.getInstance(listOrMap.getProject()), listOrMap.getResolveScope());
175     }
176
177     @Nullable
178     private static PsiType getInitializerType(GrExpression[] initializers) {
179       if (initializers.length == 0) return null;
180       PsiManager manager = initializers[0].getManager();
181       PsiType result = initializers[0].getType();
182       for (int i = 1; i < initializers.length; i++) {
183         result = getLeastUpperBound(result, initializers[i].getType(), manager);
184       }
185
186       return result;
187     }
188
189     private static PsiType getLeastUpperBound(PsiType result, PsiType other, PsiManager manager) {
190       if (other == null) return result;
191       if (result == null) result = other;
192       if (result.isAssignableFrom(other)) return result;
193       if (other.isAssignableFrom(result)) result = other;
194
195       result = TypesUtil.getLeastUpperBound(result, other, manager);
196       return result;
197     }
198
199   }
200 }