| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions; |
| |
| import com.intellij.openapi.progress.ProgressManager; |
| import com.intellij.psi.*; |
| import com.intellij.psi.util.InheritanceUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; |
| import org.jetbrains.plugins.groovy.lang.psi.api.SpreadState; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.GrTraitType; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; |
| import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.ClosureParameterEnhancer; |
| import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; |
| import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; |
| import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint; |
| import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor; |
| |
| import java.util.List; |
| import java.util.ListIterator; |
| |
| /** |
| * @author Medvedev Max |
| */ |
| public class GrReferenceResolveRunner { |
| |
| private final GrReferenceExpression place; |
| private ResolverProcessor processor; |
| |
| public GrReferenceResolveRunner(@NotNull GrReferenceExpression _place) { |
| place = _place; |
| } |
| |
| public boolean resolveImpl(@NotNull ResolverProcessor _processor) { |
| processor = _processor; |
| try { |
| boolean result = doResolve(); |
| ProgressManager.checkCanceled(); |
| return result; |
| } |
| finally { |
| processor = null; |
| } |
| } |
| |
| private boolean doResolve() { |
| GrExpression qualifier = place.getQualifier(); |
| if (qualifier == null) { |
| if (!ResolveUtil.treeWalkUp(place, processor, true)) return false; |
| if (!processor.hasCandidates()) { |
| GrExpression runtimeQualifier = PsiImplUtil.getRuntimeQualifier(place); |
| if (runtimeQualifier != null) { |
| if (!processQualifier(runtimeQualifier)) return false; |
| } |
| } |
| } |
| else { |
| if (place.getDotTokenType() == GroovyTokenTypes.mSPREAD_DOT) { |
| final PsiType qtype = qualifier.getType(); |
| final PsiType componentType = ClosureParameterEnhancer.findTypeForIteration(qtype, place); |
| if (componentType != null) { |
| final ResolveState state = ResolveState.initial() |
| .put(ClassHint.RESOLVE_CONTEXT, qualifier) |
| .put(SpreadState.SPREAD_STATE, SpreadState.create(qtype, null)); |
| if (!processQualifierType(componentType, state)) return false; |
| } |
| } |
| else { |
| if (ResolveUtil.isClassReference(place)) return true; |
| if (!processQualifier(qualifier)) return false; |
| if (!processJavaLangClass(qualifier)) return false; |
| } |
| } |
| return true; |
| } |
| |
| private boolean processJavaLangClass(@NotNull GrExpression qualifier) { |
| if (!(qualifier instanceof GrReferenceExpression)) return true; |
| |
| //optimization: only 'class' or 'this' in static context can be an alias of java.lang.Class |
| if (!("class".equals(((GrReferenceExpression)qualifier).getReferenceName()) || |
| PsiUtil.isThisReference(qualifier))) { |
| return true; |
| } |
| |
| PsiType type = qualifier.getType(); |
| if (!(type instanceof PsiClassType)) return true; |
| |
| final PsiClass psiClass = ((PsiClassType)type).resolve(); |
| if (psiClass == null || !CommonClassNames.JAVA_LANG_CLASS.equals(psiClass.getQualifiedName())) return true; |
| |
| final PsiType[] params = ((PsiClassType)type).getParameters(); |
| if (params.length != 1) return true; |
| |
| if (!processQualifierType(params[0], ResolveState.initial().put(ClassHint.RESOLVE_CONTEXT, qualifier))) { |
| return false; |
| } |
| return true; |
| } |
| |
| private boolean processQualifier(@NotNull GrExpression qualifier) { |
| PsiType qualifierType = qualifier.getType(); |
| ResolveState state = ResolveState.initial().put(ClassHint.RESOLVE_CONTEXT, qualifier); |
| if (qualifierType == null || qualifierType == PsiType.VOID) { |
| if (qualifier instanceof GrReferenceExpression) { |
| PsiElement resolved = ((GrReferenceExpression)qualifier).resolve(); |
| if (resolved != null && !resolved.processDeclarations(processor, state, null, place)) return false; |
| if (!(resolved instanceof PsiPackage)) { |
| PsiType objectQualifier = TypesUtil.getJavaLangObject(place); |
| if (!processQualifierType(objectQualifier, state)) return false; |
| } |
| } |
| } |
| else if (qualifierType instanceof PsiIntersectionType) { |
| for (PsiType conjunct : ((PsiIntersectionType)qualifierType).getConjuncts()) { |
| if (!processQualifierType(conjunct, state)) return false; |
| } |
| } |
| else { |
| if (!processQualifierType(qualifierType, state)) return false; |
| if (qualifier instanceof GrReferenceExpression && !PsiUtil.isSuperReference(qualifier) && !PsiUtil.isInstanceThisRef(qualifier)) { |
| PsiElement resolved = ((GrReferenceExpression)qualifier).resolve(); |
| if (resolved instanceof PsiClass) { |
| if (!processJavaLangClass(qualifierType, state)) return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| private boolean processJavaLangClass(@NotNull PsiType qualifierType, |
| @NotNull ResolveState state) { |
| //omitted .class |
| PsiClass javaLangClass = PsiUtil.getJavaLangClass(place, place.getResolveScope()); |
| if (javaLangClass == null) return true; |
| |
| PsiTypeParameter[] typeParameters = javaLangClass.getTypeParameters(); |
| PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY); |
| if (substitutor == null) substitutor = PsiSubstitutor.EMPTY; |
| if (typeParameters.length == 1) { |
| substitutor = substitutor.put(typeParameters[0], qualifierType); |
| state = state.put(PsiSubstitutor.KEY, substitutor); |
| } |
| if (!javaLangClass.processDeclarations(processor, state, null, place)) return false; |
| |
| PsiType javaLangClassType = JavaPsiFacade.getElementFactory(place.getProject()).createType(javaLangClass, substitutor); |
| |
| if (!ResolveUtil.processNonCodeMembers(javaLangClassType, processor, place, state)) return false; |
| |
| return true; |
| } |
| |
| private boolean processQualifierType(@NotNull PsiType originalQualifierType, |
| @NotNull ResolveState state) { |
| PsiType qualifierType = originalQualifierType instanceof PsiDisjunctionType |
| ? ((PsiDisjunctionType)originalQualifierType).getLeastUpperBound() |
| : originalQualifierType; |
| |
| if (qualifierType instanceof PsiIntersectionType) { |
| for (PsiType conjunct : ((PsiIntersectionType)qualifierType).getConjuncts()) { |
| if (!processQualifierType(conjunct, state)) return false; |
| } |
| return true; |
| } |
| |
| if (qualifierType instanceof GrTraitType) { |
| if (!processTraitType((GrTraitType)qualifierType, state)) { |
| return false; |
| } |
| return true; |
| } |
| |
| if (qualifierType instanceof PsiClassType) { |
| PsiClassType.ClassResolveResult qualifierResult = ((PsiClassType)qualifierType).resolveGenerics(); |
| PsiClass qualifierClass = qualifierResult.getElement(); |
| if (qualifierClass != null) { |
| if (!qualifierClass.processDeclarations(processor, state.put(PsiSubstitutor.KEY, qualifierResult.getSubstitutor()), null, place)) { |
| return false; |
| } |
| } |
| } |
| else if (qualifierType instanceof PsiArrayType) { |
| final GroovyPsiManager gmanager = GroovyPsiManager.getInstance(place.getProject()); |
| final GrTypeDefinition arrayClass = gmanager.getArrayClass(((PsiArrayType)qualifierType).getComponentType()); |
| if (arrayClass != null && !arrayClass.processDeclarations(processor, state, null, place)) return false; |
| } |
| |
| if (!(place.getParent() instanceof GrMethodCall) && InheritanceUtil.isInheritor(qualifierType, CommonClassNames.JAVA_UTIL_COLLECTION)) { |
| final PsiType componentType = ClosureParameterEnhancer.findTypeForIteration(qualifierType, place); |
| if (componentType != null) { |
| final SpreadState spreadState = state.get(SpreadState.SPREAD_STATE); |
| processQualifierType(componentType, state.put(SpreadState.SPREAD_STATE, SpreadState.create(qualifierType, spreadState))); |
| } |
| } |
| |
| if (!ResolveUtil.processCategoryMembers(place, processor, state)) return false; |
| if (!ResolveUtil.processNonCodeMembers(qualifierType, processor, place, state)) return false; |
| return true; |
| } |
| |
| private boolean processTraitType(@NotNull GrTraitType traitType, @NotNull ResolveState state) { |
| GrTypeDefinition mockDefinition = traitType.getMockTypeDefinition(); |
| if (mockDefinition != null) { |
| if (!mockDefinition.processDeclarations(processor, state, null, place)) { |
| return false; |
| } |
| } |
| else { |
| PsiClassType exprType = traitType.getExprType(); |
| |
| if (!processQualifierType(exprType, state)) return false; |
| |
| List<PsiClassType> traitTypes = traitType.getTraitTypes(); |
| for (ListIterator<PsiClassType> iterator = traitTypes.listIterator(); iterator.hasPrevious(); ) { |
| PsiClassType type = iterator.previous(); |
| if (!processQualifierType(type, state)) return false; |
| } |
| } |
| |
| return true; |
| } |
| } |