| /* |
| * 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.completion; |
| |
| import com.intellij.codeInsight.completion.CompletionParameters; |
| import com.intellij.codeInsight.completion.JavaClassNameCompletionContributor; |
| import com.intellij.codeInsight.completion.PrefixMatcher; |
| import com.intellij.codeInsight.lookup.LookupElement; |
| import com.intellij.codeInsight.lookup.LookupElementBuilder; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.source.resolve.FileContextUtil; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.util.InheritanceUtil; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.util.Consumer; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.containers.hash.HashSet; |
| import icons.JetgroovyIcons; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.plugins.groovy.GroovyLanguage; |
| import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; |
| import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; |
| import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor; |
| import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; |
| import org.jetbrains.plugins.groovy.lang.psi.api.SpreadState; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationNameValuePair; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; |
| 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.api.statements.typedef.members.GrMethod; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrReflectedMethod; |
| 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.GroovyResolveResultImpl; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrReferenceExpressionImpl; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrBindingVariable; |
| import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.ClosureParameterEnhancer; |
| import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils; |
| import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; |
| import org.jetbrains.plugins.groovy.lang.resolve.ClosureMissingMethodContributor; |
| import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; |
| import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor; |
| import org.jetbrains.plugins.groovy.lang.resolve.processors.SubstitutorComputer; |
| |
| import java.util.*; |
| |
| /** |
| * @author ven |
| */ |
| public class CompleteReferenceExpression { |
| private static final Logger LOG = Logger.getInstance(CompleteReferenceExpression.class); |
| |
| private final PrefixMatcher myMatcher; |
| private final Consumer<LookupElement> myConsumer; |
| private final GrReferenceExpressionImpl myRefExpr; |
| private final CompletionParameters myParameters; |
| private final CompleteReferenceProcessor myProcessor; |
| |
| private CompleteReferenceExpression(@NotNull PrefixMatcher matcher, |
| @NotNull Consumer<LookupElement> consumer, |
| @NotNull GrReferenceExpressionImpl refExpr, |
| @NotNull CompletionParameters parameters) { |
| myMatcher = matcher; |
| myConsumer = consumer; |
| myParameters = parameters; |
| myRefExpr = refExpr; |
| |
| myProcessor = new CompleteReferenceProcessor(); |
| |
| } |
| |
| public static void processVariants(@NotNull PrefixMatcher matcher, |
| @NotNull Consumer<LookupElement> consumer, |
| @NotNull GrReferenceExpressionImpl refExpr, |
| @NotNull CompletionParameters parameters) { |
| new CompleteReferenceExpression(matcher, consumer, refExpr, parameters).processVariantsImpl(); |
| } |
| |
| private void processVariantsImpl() { |
| processRefInAnnotationImpl(); |
| |
| getVariantsImpl(); |
| final GroovyResolveResult[] candidates = myProcessor.getCandidates(); |
| List<LookupElement> results = |
| GroovyCompletionUtil.getCompletionVariants(candidates, |
| JavaClassNameCompletionContributor.AFTER_NEW.accepts(myRefExpr), myMatcher, myRefExpr); |
| |
| if (myProcessor.isEmpty() && results.isEmpty()) { |
| results = GroovyCompletionUtil.getCompletionVariants(myProcessor.getInapplicableResults(), |
| JavaClassNameCompletionContributor.AFTER_NEW.accepts(myRefExpr), myMatcher, |
| myRefExpr); |
| } |
| for (LookupElement o : results) { |
| myConsumer.consume(o); |
| } |
| } |
| |
| public static void processRefInAnnotation(@NotNull GrReferenceExpression refExpr, |
| @NotNull PrefixMatcher matcher, |
| @NotNull Consumer<LookupElement> consumer, |
| @NotNull CompletionParameters parameters) { |
| new CompleteReferenceExpression(matcher, consumer, (GrReferenceExpressionImpl)refExpr, parameters).processRefInAnnotationImpl(); |
| } |
| |
| private void processRefInAnnotationImpl() { |
| if (myRefExpr.getParent() instanceof GrAnnotationNameValuePair && |
| ((GrAnnotationNameValuePair)myRefExpr.getParent()).getNameIdentifierGroovy() == null) { |
| PsiElement parent = myRefExpr.getParent().getParent(); |
| if (!(parent instanceof GrAnnotation)) { |
| parent = parent.getParent(); |
| } |
| if (parent instanceof GrAnnotation) { |
| new AnnotationAttributeCompletionResultProcessor((GrAnnotation)parent).process(myConsumer, myMatcher); |
| } |
| } |
| } |
| |
| private void processIfJavaLangClass(@Nullable PsiType type) { |
| if (!(type instanceof PsiClassType)) return; |
| |
| final PsiClass psiClass = ((PsiClassType)type).resolve(); |
| if (psiClass == null || !CommonClassNames.JAVA_LANG_CLASS.equals(psiClass.getQualifiedName())) return; |
| |
| final PsiType[] params = ((PsiClassType)type).getParameters(); |
| if (params.length != 1) return; |
| |
| getVariantsFromQualifierType(params[0], myRefExpr.getProject()); |
| } |
| |
| private void getVariantsImpl() { |
| GrExpression qualifier = myRefExpr.getQualifierExpression(); |
| if (qualifier == null) { |
| ResolveUtil.treeWalkUp(myRefExpr, myProcessor, true); |
| |
| ClosureMissingMethodContributor.processMethodsFromClosures(myRefExpr, myProcessor); |
| |
| GrExpression runtimeQualifier = PsiImplUtil.getRuntimeQualifier(myRefExpr); |
| if (runtimeQualifier != null) { |
| getVariantsFromQualifier(runtimeQualifier); |
| } |
| |
| getBindings(); |
| } |
| else { |
| if (myRefExpr.getDotTokenType() != GroovyTokenTypes.mSPREAD_DOT) { |
| getVariantsFromQualifier(qualifier); |
| |
| if (qualifier instanceof GrReferenceExpression && |
| ("class".equals(((GrReferenceExpression)qualifier).getReferenceName()) || PsiUtil.isThisReference(qualifier) && !PsiUtil.isInstanceThisRef(qualifier))) { |
| processIfJavaLangClass(qualifier.getType()); |
| } |
| } |
| else { |
| getVariantsFromQualifierForSpreadOperator(qualifier); |
| } |
| } |
| ResolveUtil.processCategoryMembers(myRefExpr, myProcessor, ResolveState.initial()); |
| } |
| |
| private void getBindings() { |
| final PsiClass containingClass = PsiTreeUtil.getParentOfType(myRefExpr, PsiClass.class); |
| if (containingClass != null) return; |
| |
| final PsiFile file = FileContextUtil.getContextFile(myRefExpr); |
| if (file instanceof GroovyFile) { |
| ((GroovyFile)file).accept(new GroovyRecursiveElementVisitor() { |
| @Override |
| public void visitAssignmentExpression(GrAssignmentExpression expression) { |
| super.visitAssignmentExpression(expression); |
| |
| final GrExpression value = expression.getLValue(); |
| if (value instanceof GrReferenceExpression && !((GrReferenceExpression)value).isQualified()) { |
| final PsiElement resolved = ((GrReferenceExpression)value).resolve(); |
| if (resolved instanceof GrBindingVariable) { |
| myProcessor.execute(resolved, ResolveState.initial()); |
| } |
| else if (resolved == null) { |
| myProcessor.execute(new GrBindingVariable((GroovyFile)file, ((GrReferenceExpression)value).getReferenceName(), true), |
| ResolveState.initial()); |
| } |
| } |
| } |
| |
| @Override |
| public void visitTypeDefinition(GrTypeDefinition typeDefinition) { |
| //don't go into classes |
| } |
| }); |
| } |
| } |
| |
| private void getVariantsFromQualifierForSpreadOperator(@NotNull GrExpression qualifier) { |
| final PsiType spreadType = ClosureParameterEnhancer.findTypeForIteration(qualifier, myRefExpr); |
| if (spreadType != null) { |
| getVariantsFromQualifierType(spreadType, myRefExpr.getProject()); |
| } |
| } |
| |
| @NotNull |
| public static LookupElementBuilder createPropertyLookupElement(@NotNull String name, @Nullable PsiType type) { |
| LookupElementBuilder res = LookupElementBuilder.create(name).withIcon(JetgroovyIcons.Groovy.Property); |
| if (type != null) { |
| res = res.withTypeText(type.getPresentableText()); |
| } |
| return res; |
| } |
| |
| @Nullable |
| public static LookupElementBuilder createPropertyLookupElement(@NotNull PsiMethod accessor, |
| @Nullable GroovyResolveResult resolveResult, |
| @Nullable PrefixMatcher matcher) { |
| String propName; |
| PsiType propType; |
| final boolean getter = GroovyPropertyUtils.isSimplePropertyGetter(accessor, null); |
| if (getter) { |
| propName = GroovyPropertyUtils.getPropertyNameByGetter(accessor); |
| } |
| else if (GroovyPropertyUtils.isSimplePropertySetter(accessor, null)) { |
| propName = GroovyPropertyUtils.getPropertyNameBySetter(accessor); |
| } |
| else { |
| return null; |
| } |
| assert propName != null; |
| if (!PsiUtil.isValidReferenceName(propName)) { |
| propName = "'" + propName + "'"; |
| } |
| |
| if (matcher != null && !matcher.prefixMatches(propName)) { |
| return null; |
| } |
| |
| if (getter) { |
| propType = PsiUtil.getSmartReturnType(accessor); |
| } else { |
| propType = accessor.getParameterList().getParameters()[0].getType(); |
| } |
| |
| final PsiType substituted = resolveResult != null ? resolveResult.getSubstitutor().substitute(propType) : propType; |
| |
| LookupElementBuilder builder = |
| LookupElementBuilder.create(generatePropertyResolveResult(propName, accessor, propType, resolveResult), propName) |
| .withIcon(JetgroovyIcons.Groovy.Property); |
| if (substituted != null) { |
| builder = builder.withTypeText(substituted.getPresentableText()); |
| } |
| return builder; |
| } |
| |
| @NotNull |
| private static GroovyResolveResult generatePropertyResolveResult(@NotNull String name, |
| @NotNull PsiMethod method, |
| @Nullable PsiType type, |
| @Nullable GroovyResolveResult resolveResult) { |
| PsiType nonNullType = type != null ? type : TypesUtil.getJavaLangObject(method); |
| |
| final GrPropertyForCompletion field = new GrPropertyForCompletion(method, name, nonNullType); |
| if (resolveResult != null) { |
| return new GroovyResolveResultImpl(field, resolveResult.getCurrentFileResolveContext(), resolveResult.getSpreadState(), |
| resolveResult.getSubstitutor(), resolveResult.isAccessible(), resolveResult.isStaticsOK()); |
| } |
| else { |
| return new GroovyResolveResultImpl(field, true); |
| } |
| } |
| |
| private void getVariantsFromQualifier(@NotNull GrExpression qualifier) { |
| Project project = qualifier.getProject(); |
| PsiType qualifierType = qualifier.getType(); |
| final ResolveState state = ResolveState.initial(); |
| if (qualifierType == null || qualifierType == PsiType.VOID) { |
| if (qualifier instanceof GrReferenceExpression) { |
| PsiElement resolved = ((GrReferenceExpression)qualifier).resolve(); |
| if (resolved instanceof PsiPackage || resolved instanceof PsiVariable) { |
| resolved.processDeclarations(myProcessor, state, null, myRefExpr); |
| return; |
| } |
| } |
| getVariantsFromQualifierType(TypesUtil.getJavaLangObject(qualifier), project); |
| } |
| else if (qualifierType instanceof PsiIntersectionType) { |
| for (PsiType conjunct : ((PsiIntersectionType)qualifierType).getConjuncts()) { |
| getVariantsFromQualifierType(conjunct, project); |
| } |
| } |
| else if (qualifierType instanceof GrTraitType) { |
| GrTypeDefinition definition = ((GrTraitType)qualifierType).getMockTypeDefinition(); |
| if (definition != null) { |
| PsiClassType classType = JavaPsiFacade.getElementFactory(project).createType(definition); |
| getVariantsFromQualifierType(classType, project); |
| } |
| else { |
| getVariantsFromQualifierType(((GrTraitType)qualifierType).getExprType(), project); |
| for (PsiClassType traitType : ((GrTraitType)qualifierType).getTraitTypes()) { |
| getVariantsFromQualifierType(traitType, project); |
| } |
| } |
| } |
| else { |
| getVariantsFromQualifierType(qualifierType, project); |
| if (qualifier instanceof GrReferenceExpression && !PsiUtil.isSuperReference(qualifier) && !PsiUtil.isInstanceThisRef(qualifier)) { |
| PsiElement resolved = ((GrReferenceExpression)qualifier).resolve(); |
| if (resolved instanceof PsiClass) { ////omitted .class |
| GlobalSearchScope scope = myRefExpr.getResolveScope(); |
| PsiClass javaLangClass = PsiUtil.getJavaLangClass(resolved, scope); |
| if (javaLangClass != null) { |
| PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; |
| PsiTypeParameter[] typeParameters = javaLangClass.getTypeParameters(); |
| if (typeParameters.length == 1) { |
| substitutor = substitutor.put(typeParameters[0], qualifierType); |
| } |
| PsiType javaLangClassType = JavaPsiFacade.getElementFactory(myRefExpr.getProject()).createType(javaLangClass, substitutor); |
| ResolveUtil.processAllDeclarations(javaLangClassType, myProcessor, state, myRefExpr); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| private void getVariantsFromQualifierType(@NotNull PsiType qualifierType, |
| @NotNull Project project) { |
| final ResolveState state = ResolveState.initial(); |
| if (qualifierType instanceof PsiClassType) { |
| PsiClassType.ClassResolveResult result = ((PsiClassType)qualifierType).resolveGenerics(); |
| PsiClass qualifierClass = result.getElement(); |
| if (qualifierClass != null) { |
| qualifierClass.processDeclarations(myProcessor, state.put(PsiSubstitutor.KEY, result.getSubstitutor()), null, myRefExpr); |
| } |
| } |
| else if (qualifierType instanceof PsiArrayType) { |
| final GrTypeDefinition arrayClass = |
| GroovyPsiManager.getInstance(project).getArrayClass(((PsiArrayType)qualifierType).getComponentType()); |
| if (arrayClass != null) { |
| if (!arrayClass.processDeclarations(myProcessor, state, null, myRefExpr)) return; |
| } |
| } |
| else if (qualifierType instanceof PsiIntersectionType) { |
| for (PsiType conjunct : ((PsiIntersectionType)qualifierType).getConjuncts()) { |
| getVariantsFromQualifierType(conjunct, project); |
| } |
| return; |
| } |
| ResolveUtil.processNonCodeMembers(qualifierType, myProcessor, myRefExpr, state); |
| } |
| |
| @NotNull |
| private Set<String> addAllRestrictedProperties() { |
| if (myRefExpr.getQualifier() != null) { |
| return Collections.emptySet(); |
| } |
| |
| Set<String> propertyNames = new HashSet<String>(); |
| for (GrTypeDefinition containingClass = PsiTreeUtil.getParentOfType(myRefExpr, GrTypeDefinition.class); |
| containingClass != null; |
| containingClass = PsiTreeUtil.getParentOfType(containingClass, GrTypeDefinition.class)) { |
| for (PsiField field : containingClass.getFields()) { |
| propertyNames.add(field.getName()); |
| } |
| } |
| return propertyNames; |
| } |
| |
| private boolean isMap() { |
| final PsiType qType = PsiImplUtil.getQualifierType(myRefExpr); |
| return InheritanceUtil.isInheritor(qType, CommonClassNames.JAVA_UTIL_MAP); |
| } |
| |
| private class CompleteReferenceProcessor extends ResolverProcessor implements Consumer<Object> { |
| |
| private final Consumer<LookupElement> myConsumer; |
| |
| private final boolean mySkipPackages; |
| private final PsiClass myEventListener; |
| private final boolean myMethodPointerOperator; |
| private final boolean myFieldPointerOperator; |
| private final boolean myIsMap; |
| |
| private final SubstitutorComputer mySubstitutorComputer; |
| |
| private final Collection<String> myPreferredFieldNames; //Reference is inside classes with such fields so don't suggest properties with such names. |
| private final Set<String> myPropertyNames = new HashSet<String>(); |
| private final Set<String> myLocalVars = new HashSet<String>(); |
| private final Set<GrMethod> myProcessedMethodWithOptionalParams = new HashSet<GrMethod>(); |
| |
| private List<GroovyResolveResult> myInapplicable; |
| |
| private boolean myIsEmpty = true; |
| |
| protected CompleteReferenceProcessor() { |
| super(null, EnumSet.allOf(ResolveKind.class), myRefExpr, PsiType.EMPTY_ARRAY); |
| myConsumer = new Consumer<LookupElement>() { |
| @Override |
| public void consume(LookupElement element) { |
| myIsEmpty = false; |
| CompleteReferenceExpression.this.myConsumer.consume(element); |
| } |
| }; |
| myPreferredFieldNames = addAllRestrictedProperties(); |
| mySkipPackages = shouldSkipPackages(); |
| myEventListener = JavaPsiFacade.getInstance(myRefExpr.getProject()).findClass("java.util.EventListener", myRefExpr.getResolveScope()); |
| myPropertyNames.addAll(myPreferredFieldNames); |
| |
| myFieldPointerOperator = myRefExpr.hasAt(); |
| myMethodPointerOperator = myRefExpr.getDotTokenType() == GroovyTokenTypes.mMEMBER_POINTER; |
| myIsMap = isMap(); |
| final PsiType thisType = PsiImplUtil.getQualifierType(myRefExpr); |
| mySubstitutorComputer = new SubstitutorComputer(thisType, PsiType.EMPTY_ARRAY, PsiType.EMPTY_ARRAY, myRefExpr, myRefExpr.getParent()); |
| } |
| |
| public boolean isEmpty() { |
| return myIsEmpty; |
| } |
| |
| private boolean shouldSkipPackages() { |
| if (PsiImplUtil.getRuntimeQualifier(myRefExpr) != null) { |
| return false; |
| } |
| |
| PsiElement parent = myRefExpr.getParent(); |
| return parent == null || parent.getLanguage().isKindOf(GroovyLanguage.INSTANCE); //don't skip in Play! |
| } |
| |
| @Override |
| public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) { |
| if (element instanceof PsiMethod && ((PsiMethod)element).isConstructor()) return true; |
| if (element instanceof PsiNamedElement) { |
| |
| PsiNamedElement namedElement = (PsiNamedElement)element; |
| |
| boolean isAccessible = isAccessible(namedElement); |
| final PsiElement resolveContext = state.get(RESOLVE_CONTEXT); |
| final SpreadState spreadState = state.get(SpreadState.SPREAD_STATE); |
| boolean isStaticsOK = isStaticsOK(namedElement, resolveContext, myParameters.getInvocationCount() <= 1); |
| |
| PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY); |
| if (substitutor == null) substitutor = PsiSubstitutor.EMPTY; |
| if (element instanceof PsiMethod) { |
| substitutor = mySubstitutorComputer.obtainSubstitutor(substitutor, (PsiMethod)element, state); |
| } |
| |
| consume(new GroovyResolveResultImpl(namedElement, resolveContext, spreadState, substitutor, isAccessible, isStaticsOK)); |
| } |
| return true; |
| } |
| |
| @Override |
| public void consume(Object o) { |
| if (!(o instanceof GroovyResolveResult)) { |
| LOG.error(o); |
| return; |
| } |
| |
| GroovyResolveResult result = (GroovyResolveResult)o; |
| if (!result.isStaticsOK()) { |
| if (myInapplicable == null) myInapplicable = ContainerUtil.newArrayList(); |
| myInapplicable.add(result); |
| return; |
| } |
| if (!result.isAccessible() && myParameters.getInvocationCount() < 2) return; |
| |
| if (mySkipPackages && result.getElement() instanceof PsiPackage) return; |
| |
| PsiElement element = result.getElement(); |
| if (element instanceof PsiVariable && !myMatcher.prefixMatches(((PsiVariable)element).getName())) { |
| return; |
| } |
| |
| if (element instanceof GrReflectedMethod) { |
| element = ((GrReflectedMethod)element).getBaseMethod(); |
| if (!myProcessedMethodWithOptionalParams.add((GrMethod)element)) return; |
| |
| result = new GroovyResolveResultImpl(element, result.getCurrentFileResolveContext(), result.getSpreadState(), |
| result.getSubstitutor(), result.isAccessible(), result.isStaticsOK(), |
| result.isInvokedOnProperty(), result.isValidResult()); |
| } |
| |
| if (myFieldPointerOperator && !(element instanceof PsiVariable)) { |
| return; |
| } |
| if (myMethodPointerOperator && !(element instanceof PsiMethod)) { |
| return; |
| } |
| addCandidate(result); |
| |
| if (!myFieldPointerOperator && !myMethodPointerOperator) { |
| if (element instanceof PsiMethod) { |
| processProperty((PsiMethod)element, result); |
| } |
| else if (element instanceof GrField) { |
| if (((GrField)element).isProperty()) { |
| processPropertyFromField((GrField)element, result); |
| } |
| } |
| } |
| if (element instanceof GrVariable && !(element instanceof GrField)) { |
| myLocalVars.add(((GrVariable)element).getName()); |
| } |
| } |
| |
| private void processPropertyFromField(@NotNull GrField field, @NotNull GroovyResolveResult resolveResult) { |
| if (field.getGetters().length != 0 || field.getSetter() != null || !myPropertyNames.add(field.getName()) || myIsMap) return; |
| |
| for (LookupElement element : GroovyCompletionUtil.createLookupElements(resolveResult, false, myMatcher, null)) { |
| myConsumer.consume(((LookupElementBuilder)element).withIcon(JetgroovyIcons.Groovy.Property)); |
| } |
| |
| } |
| |
| private void processProperty(@NotNull PsiMethod method, @NotNull GroovyResolveResult resolveResult) { |
| if (myIsMap) return; |
| final LookupElementBuilder lookup = createPropertyLookupElement(method, resolveResult, myMatcher); |
| if (lookup != null) { |
| if (myPropertyNames.add(lookup.getLookupString())) { |
| myConsumer.consume(lookup); |
| } |
| } |
| else if (myEventListener != null) { |
| processListenerProperties(method); |
| } |
| } |
| |
| private void processListenerProperties(@NotNull PsiMethod method) { |
| if (!method.getName().startsWith("add") || method.getParameterList().getParametersCount() != 1) return; |
| |
| final PsiParameter parameter = method.getParameterList().getParameters()[0]; |
| final PsiType type = parameter.getType(); |
| if (!(type instanceof PsiClassType)) return; |
| |
| final PsiClassType classType = (PsiClassType)type; |
| final PsiClass listenerClass = classType.resolve(); |
| if (listenerClass == null) return; |
| |
| final PsiMethod[] listenerMethods = listenerClass.getMethods(); |
| if (!InheritanceUtil.isInheritorOrSelf(listenerClass, myEventListener, true)) return; |
| |
| for (PsiMethod listenerMethod : listenerMethods) { |
| final String name = listenerMethod.getName(); |
| if (myPropertyNames.add(name)) { |
| LookupElementBuilder builder = LookupElementBuilder |
| .create(generatePropertyResolveResult(name, listenerMethod, null, null), name) |
| .withIcon(JetgroovyIcons.Groovy.Property); |
| myConsumer.consume(builder); |
| } |
| } |
| } |
| |
| @NotNull |
| @Override |
| public GroovyResolveResult[] getCandidates() { |
| if (!hasCandidates()) return GroovyResolveResult.EMPTY_ARRAY; |
| final GroovyResolveResult[] results = ResolveUtil.filterSameSignatureCandidates(getCandidatesInternal()); |
| List<GroovyResolveResult> list = new ArrayList<GroovyResolveResult>(results.length); |
| myPropertyNames.removeAll(myPreferredFieldNames); |
| |
| Set<String> usedFields = ContainerUtil.newHashSet(); |
| for (GroovyResolveResult result : results) { |
| final PsiElement element = result.getElement(); |
| if (element instanceof PsiField) { |
| final String name = ((PsiField)element).getName(); |
| if (myPropertyNames.contains(name) || |
| myLocalVars.contains(name) || |
| usedFields.contains(name)) { |
| continue; |
| } |
| else { |
| usedFields.add(name); |
| } |
| } |
| |
| list.add(result); |
| } |
| return list.toArray(new GroovyResolveResult[list.size()]); |
| } |
| |
| @NotNull |
| private List<GroovyResolveResult> getInapplicableResults() { |
| if (myInapplicable == null) return Collections.emptyList(); |
| return myInapplicable; |
| } |
| } |
| } |