Refactor DelegationResolver into method object to simplify code
This commit is contained in:
@@ -32,173 +32,15 @@ import org.jetbrains.jet.lang.psi.JetTypeReference;
|
|||||||
import org.jetbrains.jet.lang.types.JetType;
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
import org.jetbrains.jet.lang.types.TypeUtils;
|
import org.jetbrains.jet.lang.types.TypeUtils;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.jetbrains.jet.lang.diagnostics.Errors.MANY_IMPL_MEMBER_NOT_IMPLEMENTED;
|
import static org.jetbrains.jet.lang.diagnostics.Errors.MANY_IMPL_MEMBER_NOT_IMPLEMENTED;
|
||||||
import static org.jetbrains.jet.lang.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE;
|
import static org.jetbrains.jet.lang.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE;
|
||||||
|
|
||||||
public class DelegationResolver {
|
public final class DelegationResolver<T extends CallableMemberDescriptor> {
|
||||||
|
|
||||||
private DelegationResolver() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static <T extends CallableMemberDescriptor> Collection<T> generateDelegatedMembers(
|
|
||||||
@NotNull JetClassOrObject classOrObject,
|
|
||||||
@NotNull ClassDescriptor classDescriptor,
|
|
||||||
@NotNull Collection<? extends CallableDescriptor> existingMembers,
|
|
||||||
@NotNull BindingTrace trace,
|
|
||||||
@NotNull Callback<T> callback
|
|
||||||
) {
|
|
||||||
Collection<T> delegatedMembers = new HashSet<T>();
|
|
||||||
for (JetDelegationSpecifier delegationSpecifier : classOrObject.getDelegationSpecifiers()) {
|
|
||||||
if (!(delegationSpecifier instanceof JetDelegatorByExpressionSpecifier)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
JetDelegatorByExpressionSpecifier specifier = (JetDelegatorByExpressionSpecifier) delegationSpecifier;
|
|
||||||
JetTypeReference typeReference = specifier.getTypeReference();
|
|
||||||
if (typeReference == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
JetType delegatedTraitType = callback.getTypeByTypeReference(typeReference);
|
|
||||||
if (delegatedTraitType == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Collection<T> delegatesForTrait = generateDelegatesForOneTrait(classOrObject, classDescriptor, existingMembers,
|
|
||||||
trace, callback, delegatedMembers, delegatedTraitType);
|
|
||||||
delegatedMembers.addAll(delegatesForTrait);
|
|
||||||
}
|
|
||||||
return delegatedMembers;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static <T extends CallableMemberDescriptor> Collection<T> generateDelegatesForOneTrait(
|
|
||||||
@NotNull JetClassOrObject classOrObject,
|
|
||||||
@NotNull ClassDescriptor classDescriptor,
|
|
||||||
@NotNull Collection<? extends CallableDescriptor> existingMembers,
|
|
||||||
@NotNull BindingTrace trace,
|
|
||||||
@NotNull Callback<T> callback,
|
|
||||||
@NotNull Collection<T> existingDelegates,
|
|
||||||
@NotNull JetType delegatedTraitType
|
|
||||||
) {
|
|
||||||
Collection<T> result = new HashSet<T>();
|
|
||||||
Collection<T> candidates = generateDelegationCandidates(classDescriptor, delegatedTraitType, callback);
|
|
||||||
for (T candidate : candidates) {
|
|
||||||
if (existingMemberOverridesDelegatedMember(candidate, existingMembers)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
//only leave the first delegated member
|
|
||||||
if (checkClashWithOtherDelegatedMember(trace, classOrObject, existingDelegates, candidate)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.add(candidate);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <T extends CallableMemberDescriptor> boolean checkClashWithOtherDelegatedMember(
|
|
||||||
@NotNull BindingTrace trace,
|
|
||||||
@Nullable JetClassOrObject jetClass,
|
|
||||||
@NotNull Collection<T> delegatedMembers,
|
|
||||||
@NotNull T candidate
|
|
||||||
) {
|
|
||||||
for (CallableMemberDescriptor alreadyDelegatedMember : delegatedMembers) {
|
|
||||||
if (haveSameSignatures(alreadyDelegatedMember, candidate)) {
|
|
||||||
//trying to delegate to many traits with the same methods
|
|
||||||
if (jetClass != null) {
|
|
||||||
PsiElement nameIdentifier = jetClass.getNameIdentifier();
|
|
||||||
if (nameIdentifier != null) {
|
|
||||||
trace.report(MANY_IMPL_MEMBER_NOT_IMPLEMENTED.on(nameIdentifier, jetClass, alreadyDelegatedMember));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean existingMemberOverridesDelegatedMember(
|
|
||||||
@NotNull CallableMemberDescriptor candidate,
|
|
||||||
@NotNull Collection<? extends CallableDescriptor> existingMembers
|
|
||||||
) {
|
|
||||||
for (CallableDescriptor existingDescriptor : existingMembers) {
|
|
||||||
if (haveSameSignatures(existingDescriptor, candidate)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static <T extends CallableMemberDescriptor> Collection<T> generateDelegationCandidates(
|
|
||||||
@NotNull ClassDescriptor classDescriptor,
|
|
||||||
@NotNull JetType delegatedTraitType,
|
|
||||||
@NotNull Callback<T> callback
|
|
||||||
) {
|
|
||||||
Collection<T> membersFromTrait = callback.getMembersByType(delegatedTraitType);
|
|
||||||
Collection<T> descriptorsToDelegate = filterMembersFromSuperClassOfDelegatedTrait(delegatedTraitType, membersFromTrait, callback);
|
|
||||||
Collection<T> result = Lists.newArrayList();
|
|
||||||
for (T memberDescriptor : descriptorsToDelegate) {
|
|
||||||
if (memberDescriptor.getModality().isOverridable()) {
|
|
||||||
Modality modality = DescriptorUtils.convertModality(memberDescriptor.getModality(), true);
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
T copy = (T) memberDescriptor.copy(classDescriptor, modality, memberDescriptor.getVisibility(),
|
|
||||||
CallableMemberDescriptor.Kind.DELEGATION, false);
|
|
||||||
result.add(copy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static <T extends CallableMemberDescriptor> Collection<T> filterMembersFromSuperClassOfDelegatedTrait(
|
|
||||||
@NotNull JetType delegatedTraitType,
|
|
||||||
@NotNull Collection<T> membersFromTrait,
|
|
||||||
@NotNull Callback<T> callback
|
|
||||||
) {
|
|
||||||
final Collection<T> membersToSkip = getMembersFromClassSupertypeOfTrait(delegatedTraitType, callback);
|
|
||||||
return Collections2.filter(
|
|
||||||
membersFromTrait,
|
|
||||||
new Predicate<CallableMemberDescriptor>() {
|
|
||||||
@Override
|
|
||||||
public boolean apply(@Nullable CallableMemberDescriptor descriptor) {
|
|
||||||
for (CallableMemberDescriptor memberToSkip : membersToSkip) {
|
|
||||||
if (haveSameSignatures(memberToSkip, descriptor)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean haveSameSignatures(@NotNull CallableDescriptor memberOne, @NotNull CallableDescriptor memberTwo) {
|
|
||||||
return OverridingUtil.isOverridableBy(memberOne, memberTwo).getResult() == OVERRIDABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static <T extends CallableMemberDescriptor> Collection<T> getMembersFromClassSupertypeOfTrait(
|
|
||||||
@NotNull JetType delegateTraitType,
|
|
||||||
@NotNull Callback<T> callback
|
|
||||||
) {
|
|
||||||
JetType classSupertype = null;
|
|
||||||
for (JetType supertype : TypeUtils.getAllSupertypes(delegateTraitType)) {
|
|
||||||
if (isNotTrait(supertype.getConstructor().getDeclarationDescriptor())) {
|
|
||||||
classSupertype = supertype;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return classSupertype != null ? callback.getMembersByType(classSupertype) : Collections.<T>emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isNotTrait(DeclarationDescriptor descriptor) {
|
|
||||||
if (descriptor instanceof ClassDescriptor) {
|
|
||||||
ClassKind kind = ((ClassDescriptor) descriptor).getKind();
|
|
||||||
return kind != ClassKind.TRAIT;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void generateDelegatesInAClass(
|
public static void generateDelegatesInAClass(
|
||||||
@NotNull MutableClassDescriptor classDescriptor,
|
@NotNull MutableClassDescriptor classDescriptor,
|
||||||
@@ -235,6 +77,171 @@ public class DelegationResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static <T extends CallableMemberDescriptor> Collection<T> generateDelegatedMembers(
|
||||||
|
@NotNull JetClassOrObject classOrObject,
|
||||||
|
@NotNull ClassDescriptor ownerDescriptor,
|
||||||
|
@NotNull Collection<? extends CallableDescriptor> existingMembers,
|
||||||
|
@NotNull BindingTrace trace,
|
||||||
|
@NotNull Callback<T> callback
|
||||||
|
) {
|
||||||
|
return new DelegationResolver<T>(classOrObject, ownerDescriptor, existingMembers, trace, callback).generateDelegatedMembers();
|
||||||
|
}
|
||||||
|
@NotNull private final JetClassOrObject classOrObject;
|
||||||
|
@NotNull private final ClassDescriptor ownerDescriptor;
|
||||||
|
@NotNull private final Collection<? extends CallableDescriptor> existingMembers;
|
||||||
|
@NotNull private final BindingTrace trace;
|
||||||
|
|
||||||
|
@NotNull private final Callback<T> callback;
|
||||||
|
|
||||||
|
private DelegationResolver(
|
||||||
|
@NotNull JetClassOrObject classOrObject,
|
||||||
|
@NotNull ClassDescriptor ownerDescriptor,
|
||||||
|
@NotNull Collection<? extends CallableDescriptor> existingMembers,
|
||||||
|
@NotNull BindingTrace trace,
|
||||||
|
@NotNull Callback<T> callback
|
||||||
|
) {
|
||||||
|
|
||||||
|
this.classOrObject = classOrObject;
|
||||||
|
this.ownerDescriptor = ownerDescriptor;
|
||||||
|
this.existingMembers = existingMembers;
|
||||||
|
this.trace = trace;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private Collection<T> generateDelegatedMembers() {
|
||||||
|
Collection<T> delegatedMembers = new HashSet<T>();
|
||||||
|
for (JetDelegationSpecifier delegationSpecifier : classOrObject.getDelegationSpecifiers()) {
|
||||||
|
if (!(delegationSpecifier instanceof JetDelegatorByExpressionSpecifier)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
JetDelegatorByExpressionSpecifier specifier = (JetDelegatorByExpressionSpecifier) delegationSpecifier;
|
||||||
|
JetTypeReference typeReference = specifier.getTypeReference();
|
||||||
|
if (typeReference == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
JetType delegatedTraitType = callback.getTypeByTypeReference(typeReference);
|
||||||
|
if (delegatedTraitType == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Collection<T> delegatesForTrait = generateDelegatesForOneTrait(delegatedMembers, delegatedTraitType);
|
||||||
|
delegatedMembers.addAll(delegatesForTrait);
|
||||||
|
}
|
||||||
|
return delegatedMembers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private Collection<T> generateDelegatesForOneTrait(
|
||||||
|
@NotNull Collection<T> existingDelegates,
|
||||||
|
@NotNull JetType delegatedTraitType
|
||||||
|
) {
|
||||||
|
Collection<T> result = new HashSet<T>();
|
||||||
|
Collection<T> candidates = generateDelegationCandidates(delegatedTraitType);
|
||||||
|
for (T candidate : candidates) {
|
||||||
|
if (existingMemberOverridesDelegatedMember(candidate, existingMembers)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//only leave the first delegated member
|
||||||
|
if (checkClashWithOtherDelegatedMember(existingDelegates, candidate)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.add(candidate);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkClashWithOtherDelegatedMember(
|
||||||
|
@NotNull Collection<T> delegatedMembers,
|
||||||
|
@NotNull T candidate
|
||||||
|
) {
|
||||||
|
for (CallableMemberDescriptor alreadyDelegatedMember : delegatedMembers) {
|
||||||
|
if (haveSameSignatures(alreadyDelegatedMember, candidate)) {
|
||||||
|
//trying to delegate to many traits with the same methods
|
||||||
|
PsiElement nameIdentifier = classOrObject.getNameIdentifier();
|
||||||
|
if (nameIdentifier != null) {
|
||||||
|
trace.report(MANY_IMPL_MEMBER_NOT_IMPLEMENTED.on(nameIdentifier, classOrObject, alreadyDelegatedMember));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean existingMemberOverridesDelegatedMember(
|
||||||
|
@NotNull CallableMemberDescriptor candidate,
|
||||||
|
@NotNull Collection<? extends CallableDescriptor> existingMembers
|
||||||
|
) {
|
||||||
|
for (CallableDescriptor existingDescriptor : existingMembers) {
|
||||||
|
if (haveSameSignatures(existingDescriptor, candidate)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private Collection<T> generateDelegationCandidates(@NotNull JetType delegatedTraitType) {
|
||||||
|
Collection<T> descriptorsToDelegate = filterMembersFromSuperClassOfDelegatedTrait(delegatedTraitType);
|
||||||
|
Collection<T> result = Lists.newArrayList();
|
||||||
|
for (T memberDescriptor : descriptorsToDelegate) {
|
||||||
|
if (memberDescriptor.getModality().isOverridable()) {
|
||||||
|
Modality modality = DescriptorUtils.convertModality(memberDescriptor.getModality(), true);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
T copy = (T) memberDescriptor.copy(ownerDescriptor, modality, memberDescriptor.getVisibility(),
|
||||||
|
CallableMemberDescriptor.Kind.DELEGATION, false);
|
||||||
|
result.add(copy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private Collection<T> filterMembersFromSuperClassOfDelegatedTrait(
|
||||||
|
@NotNull JetType delegatedTraitType
|
||||||
|
) {
|
||||||
|
final Collection<T> membersToSkip = getMembersFromClassSupertypeOfTrait(delegatedTraitType);
|
||||||
|
return Collections2.filter(
|
||||||
|
callback.getMembersByType(delegatedTraitType),
|
||||||
|
new Predicate<CallableMemberDescriptor>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(@Nullable CallableMemberDescriptor descriptor) {
|
||||||
|
for (CallableMemberDescriptor memberToSkip : membersToSkip) {
|
||||||
|
if (haveSameSignatures(memberToSkip, descriptor)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean haveSameSignatures(@NotNull CallableDescriptor memberOne, @NotNull CallableDescriptor memberTwo) {
|
||||||
|
return OverridingUtil.isOverridableBy(memberOne, memberTwo).getResult() == OVERRIDABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private Collection<T> getMembersFromClassSupertypeOfTrait(@NotNull JetType delegateTraitType) {
|
||||||
|
JetType classSupertype = null;
|
||||||
|
for (JetType supertype : TypeUtils.getAllSupertypes(delegateTraitType)) {
|
||||||
|
if (isNotTrait(supertype.getConstructor().getDeclarationDescriptor())) {
|
||||||
|
classSupertype = supertype;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return classSupertype != null ? callback.getMembersByType(classSupertype) : Collections.<T>emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isNotTrait(@NotNull DeclarationDescriptor descriptor) {
|
||||||
|
if (descriptor instanceof ClassDescriptor) {
|
||||||
|
ClassKind kind = ((ClassDescriptor) descriptor).getKind();
|
||||||
|
return kind != ClassKind.TRAIT;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public interface Callback<T extends CallableMemberDescriptor> {
|
public interface Callback<T extends CallableMemberDescriptor> {
|
||||||
@Nullable
|
@Nullable
|
||||||
JetType getTypeByTypeReference(@NotNull JetTypeReference reference);
|
JetType getTypeByTypeReference(@NotNull JetTypeReference reference);
|
||||||
|
|||||||
Reference in New Issue
Block a user