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.
17 package org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary;
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;
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();
50 public GrListOrMapImpl(@NotNull ASTNode node) {
54 public void accept(GroovyElementVisitor visitor) {
55 visitor.visitListOrMap(this);
58 public String toString() {
59 return "Generalized list";
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);
68 final ASTNode lastChild = getNode().getLastChildNode();
69 getNode().addLeaf(mCOMMA, ",", lastChild);
70 return super.addInternal(first, last, lastChild.getTreePrev(), false);
73 public PsiType getType() {
74 return GroovyPsiManager.getInstance(getProject()).getType(this, TYPES_CALCULATOR);
77 public boolean isMap() {
78 return findChildByType(MAP_LITERAL_TOKEN_SET) != null;
82 public GrExpression[] getInitializers() {
83 return findChildrenByClass(GrExpression.class);
87 public GrNamedArgument[] getNamedArguments() {
88 return findChildrenByClass(GrNamedArgument.class);
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);
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;
107 return getTupleType(listOrMap.getInitializers(), listOrMap);
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];
121 for (int i = 0; i < values.length; i++) {
122 GrExpression expr = namedArgs[i].getExpression();
123 if (expr == null) return null;
125 GrArgumentLabel label = namedArgs[i].getLabel();
126 if (label == null) return null;
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);
138 return facade.getElementFactory().createType(mapClass);
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();
152 if (el instanceof GrParenthesizedExpression) {
153 other = ((GrParenthesizedExpression)el).getType();
156 if (el.getNode() != null) {
157 other = TypesUtil.getPsiType(el, el.getNode().getElementType());
163 result = getLeastUpperBound(result, other, manager);
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();
174 return new GrTupleType(result, JavaPsiFacade.getInstance(listOrMap.getProject()), listOrMap.getResolveScope());
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);
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;
195 result = TypesUtil.getLeastUpperBound(result, other, manager);