blob: b6e34d9ab74db4081d11cf03f82ec28891e2b57b [file] [log] [blame]
/*
* 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.overrideImplement;
import com.intellij.codeInsight.generation.OverrideImplementExploreUtil;
import com.intellij.psi.*;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
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.impl.synthetic.GrTraitMethod;
import org.jetbrains.plugins.groovy.lang.psi.util.GrTraitUtil;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
/**
* Created by Max Medvedev on 16/05/14
*/
public class GroovyOverrideImplementExploreUtil {
@NotNull
public static Collection<MethodSignature> getMethodSignaturesToOverride(@NotNull GrTypeDefinition aClass) {
if (aClass.isAnnotationType()) return Collections.emptySet();
return getMapToOverrideImplement(aClass, false, true).keySet();
}
@NotNull
public static Collection<MethodSignature> getMethodSignaturesToImplement(@NotNull GrTypeDefinition aClass) {
return getMapToOverrideImplement(aClass, true, true).keySet();
}
@NotNull
public static Collection<CandidateInfo> getMethodsToOverrideImplement(GrTypeDefinition aClass, boolean toImplement) {
return getMapToOverrideImplement(aClass, toImplement, true).values();
}
@NotNull
public static Map<MethodSignature, CandidateInfo> getMapToOverrideImplement(GrTypeDefinition aClass, boolean toImplement, boolean skipImplemented) {
Map<MethodSignature, PsiMethod> abstracts = ContainerUtil.newLinkedHashMap();
Map<MethodSignature, PsiMethod> finals = ContainerUtil.newLinkedHashMap();
Map<MethodSignature, PsiMethod> concretes = ContainerUtil.newLinkedHashMap();
PsiUtilCore.ensureValid(aClass);
Collection<HierarchicalMethodSignature> allMethodSignatures = aClass.getVisibleSignatures();
PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(aClass.getProject()).getResolveHelper();
for (HierarchicalMethodSignature signature : allMethodSignatures) {
PsiMethod method = signature.getMethod();
if (method instanceof GrTraitMethod) {
for (HierarchicalMethodSignature superSignature : signature.getSuperSignatures()) {
processMethod(aClass, skipImplemented, abstracts, finals, concretes, resolveHelper, superSignature, superSignature.getMethod());
}
}
else {
processMethod(aClass, skipImplemented, abstracts, finals, concretes, resolveHelper, signature, method);
}
}
final Map<MethodSignature, CandidateInfo> result = new TreeMap<MethodSignature,CandidateInfo>(new OverrideImplementExploreUtil.MethodSignatureComparator());
if (toImplement || aClass.isInterface()) {
collectMethodsToImplement(aClass, abstracts, finals, concretes, result);
}
else {
for (Map.Entry<MethodSignature, PsiMethod> entry : concretes.entrySet()) {
MethodSignature signature = entry.getKey();
PsiMethod concrete = entry.getValue();
if (finals.get(signature) == null) {
PsiMethod abstractOne = abstracts.get(signature);
if (abstractOne == null || !abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true) ||
CommonClassNames.JAVA_LANG_OBJECT.equals(concrete.getContainingClass().getQualifiedName())) {
PsiSubstitutor subst = OverrideImplementExploreUtil.correctSubstitutor(concrete, signature.getSubstitutor());
CandidateInfo info = new CandidateInfo(concrete, subst);
result.put(signature, info);
}
}
}
}
return result;
}
private static void processMethod(GrTypeDefinition aClass,
boolean skipImplemented,
Map<MethodSignature, PsiMethod> abstracts,
Map<MethodSignature, PsiMethod> finals,
Map<MethodSignature, PsiMethod> concretes,
PsiResolveHelper resolveHelper, HierarchicalMethodSignature signature, PsiMethod method) {
PsiUtilCore.ensureValid(method);
if (method.hasModifierProperty(PsiModifier.STATIC) || !resolveHelper.isAccessible(method, aClass, aClass)) return;
PsiClass hisClass = method.getContainingClass();
if (hisClass == null) return;
// filter non-immediate super constructors
if (method.isConstructor() && (!aClass.isInheritor(hisClass, false) || aClass instanceof PsiAnonymousClass || aClass.isEnum())) {
return;
}
// filter already implemented
if (skipImplemented) {
PsiMethod implemented = MethodSignatureUtil.findMethodBySignature(aClass, signature, false);
if (implemented != null && !(implemented instanceof GrTraitMethod)) {
return;
}
}
if (method.hasModifierProperty(PsiModifier.FINAL)) {
finals.put(signature, method);
return;
}
Map<MethodSignature, PsiMethod> map = method.hasModifierProperty(PsiModifier.ABSTRACT) ? abstracts : concretes;
fillMap(signature, method, map);
if (isDefaultMethod(method)) {
fillMap(signature, method, concretes);
}
}
private static void fillMap(HierarchicalMethodSignature signature, PsiMethod method, Map<MethodSignature, PsiMethod> map) {
final PsiMethod other = map.get(signature);
if (other == null || preferLeftForImplement(method, other)) {
map.put(signature, method);
}
}
private static boolean preferLeftForImplement(PsiMethod left, PsiMethod right) {
if (PsiUtil.getAccessLevel(left.getModifierList()) > PsiUtil.getAccessLevel(right.getModifierList())) return true;
PsiClass lClass = left.getContainingClass();
PsiClass rClass = right.getContainingClass();
if (lClass != null && !lClass.isInterface()) return true;
if (rClass != null && !rClass.isInterface()) return false;
// implement annotated method
PsiAnnotation[] leftAnnotations = left.getModifierList().getAnnotations();
PsiAnnotation[] rightAnnotations = right.getModifierList().getAnnotations();
return leftAnnotations.length > rightAnnotations.length;
}
private static boolean isDefaultMethod(PsiMethod method) {
return method instanceof GrMethod && !method.getModifierList().hasExplicitModifier(PsiModifier.ABSTRACT) &&
GrTraitUtil.isTrait(method.getContainingClass());
}
public static void collectMethodsToImplement(PsiClass aClass,
Map<MethodSignature, PsiMethod> abstracts,
Map<MethodSignature, PsiMethod> finals,
Map<MethodSignature, PsiMethod> concretes,
Map<MethodSignature, CandidateInfo> result) {
for (Map.Entry<MethodSignature, PsiMethod> entry : abstracts.entrySet()) {
MethodSignature signature = entry.getKey();
PsiMethod abstractOne = entry.getValue();
PsiMethod concrete = concretes.get(signature);
if (concrete == null
|| PsiUtil.getAccessLevel(concrete.getModifierList()) < PsiUtil.getAccessLevel(abstractOne.getModifierList())
|| !abstractOne.getContainingClass().isInterface() && abstractOne.getContainingClass().isInheritor(concrete.getContainingClass(), true)
|| isDefaultMethod(abstractOne)) {
if (finals.get(signature) == null) {
PsiSubstitutor subst = OverrideImplementExploreUtil.correctSubstitutor(abstractOne, signature.getSubstitutor());
CandidateInfo info = new CandidateInfo(abstractOne, subst);
result.put(signature, info);
}
}
}
for (final PsiMethod method : new GroovyMethodImplementor().getMethodsToImplement(aClass)) {
MethodSignature signature = MethodSignatureUtil.createMethodSignature(method.getName(), method.getParameterList(),
method.getTypeParameterList(), PsiSubstitutor.EMPTY,
method.isConstructor());
CandidateInfo info = new CandidateInfo(method, PsiSubstitutor.EMPTY);
result.put(signature, info);
}
}
}