001    /*
002     * Copyright 2010-2013 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.jet.lang.resolve;
018    
019    import com.google.common.base.Predicate;
020    import com.google.common.base.Predicates;
021    import com.google.common.collect.Collections2;
022    import com.google.common.collect.Lists;
023    import com.intellij.psi.PsiElement;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.annotations.Nullable;
026    import org.jetbrains.jet.lang.descriptors.*;
027    import org.jetbrains.jet.lang.descriptors.impl.MutableClassDescriptor;
028    import org.jetbrains.jet.lang.psi.JetClassOrObject;
029    import org.jetbrains.jet.lang.psi.JetDelegationSpecifier;
030    import org.jetbrains.jet.lang.psi.JetDelegatorByExpressionSpecifier;
031    import org.jetbrains.jet.lang.psi.JetTypeReference;
032    import org.jetbrains.jet.lang.types.JetType;
033    import org.jetbrains.jet.lang.types.TypeUtils;
034    
035    import java.util.Collection;
036    import java.util.Collections;
037    import java.util.HashSet;
038    import java.util.Set;
039    
040    import static org.jetbrains.jet.lang.diagnostics.Errors.MANY_IMPL_MEMBER_NOT_IMPLEMENTED;
041    import static org.jetbrains.jet.lang.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE;
042    
043    public final class DelegationResolver<T extends CallableMemberDescriptor> {
044    
045        public static void generateDelegatesInAClass(
046                @NotNull MutableClassDescriptor classDescriptor,
047                @NotNull final BindingTrace trace,
048                @NotNull JetClassOrObject jetClassOrObject
049        ) {
050            TypeResolver eagerTypeResolver = new TypeResolver() {
051                @Nullable
052                @Override
053                public JetType resolve(@NotNull JetTypeReference reference) {
054                    return trace.get(BindingContext.TYPE, reference);
055                }
056            };
057            MemberExtractor<CallableMemberDescriptor> eagerExtractor = new MemberExtractor<CallableMemberDescriptor>() {
058                @NotNull
059                @Override
060                public Collection<CallableMemberDescriptor> getMembersByType(@NotNull JetType type) {
061                    //noinspection unchecked
062                    return (Collection) Collections2.filter(type.getMemberScope().getAllDescriptors(),
063                                                            Predicates.instanceOf(CallableMemberDescriptor.class));
064                }
065            };
066            Set<CallableMemberDescriptor> existingMembers = classDescriptor.getAllCallableMembers();
067            Collection<CallableMemberDescriptor> delegatedMembers =
068                    generateDelegatedMembers(jetClassOrObject, classDescriptor, existingMembers, trace, eagerExtractor, eagerTypeResolver);
069            for (CallableMemberDescriptor descriptor : delegatedMembers) {
070                if (descriptor instanceof PropertyDescriptor) {
071                    PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
072                    classDescriptor.getBuilder().addPropertyDescriptor(propertyDescriptor);
073                }
074                else if (descriptor instanceof SimpleFunctionDescriptor) {
075                    SimpleFunctionDescriptor functionDescriptor = (SimpleFunctionDescriptor) descriptor;
076                    classDescriptor.getBuilder().addFunctionDescriptor(functionDescriptor);
077                }
078            }
079        }
080    
081    
082        @NotNull
083        public static <T extends CallableMemberDescriptor> Collection<T> generateDelegatedMembers(
084                @NotNull JetClassOrObject classOrObject,
085                @NotNull ClassDescriptor ownerDescriptor,
086                @NotNull Collection<? extends CallableDescriptor> existingMembers,
087                @NotNull BindingTrace trace,
088                @NotNull MemberExtractor<T> memberExtractor,
089                @NotNull TypeResolver typeResolver
090        ) {
091            return new DelegationResolver<T>(classOrObject, ownerDescriptor, existingMembers, trace, memberExtractor, typeResolver)
092                    .generateDelegatedMembers();
093        }
094    
095        @NotNull private final JetClassOrObject classOrObject;
096        @NotNull private final ClassDescriptor ownerDescriptor;
097        @NotNull private final Collection<? extends CallableDescriptor> existingMembers;
098        @NotNull private final BindingTrace trace;
099        @NotNull private final MemberExtractor<T> memberExtractor;
100        @NotNull private final TypeResolver typeResolver;
101    
102        private DelegationResolver(
103                @NotNull JetClassOrObject classOrObject,
104                @NotNull ClassDescriptor ownerDescriptor,
105                @NotNull Collection<? extends CallableDescriptor> existingMembers,
106                @NotNull BindingTrace trace,
107                @NotNull MemberExtractor<T> extractor,
108                @NotNull TypeResolver resolver
109        ) {
110    
111            this.classOrObject = classOrObject;
112            this.ownerDescriptor = ownerDescriptor;
113            this.existingMembers = existingMembers;
114            this.trace = trace;
115            this.memberExtractor = extractor;
116            this.typeResolver = resolver;
117        }
118    
119        @NotNull
120        private Collection<T> generateDelegatedMembers() {
121            Collection<T> delegatedMembers = new HashSet<T>();
122            for (JetDelegationSpecifier delegationSpecifier : classOrObject.getDelegationSpecifiers()) {
123                if (!(delegationSpecifier instanceof JetDelegatorByExpressionSpecifier)) {
124                    continue;
125                }
126                JetDelegatorByExpressionSpecifier specifier = (JetDelegatorByExpressionSpecifier) delegationSpecifier;
127                JetTypeReference typeReference = specifier.getTypeReference();
128                if (typeReference == null) {
129                    continue;
130                }
131                JetType delegatedTraitType = typeResolver.resolve(typeReference);
132                if (delegatedTraitType == null) {
133                    continue;
134                }
135                Collection<T> delegatesForTrait = generateDelegatesForTrait(delegatedMembers, delegatedTraitType);
136                delegatedMembers.addAll(delegatesForTrait);
137            }
138            return delegatedMembers;
139        }
140    
141        @NotNull
142        private Collection<T> generateDelegatesForTrait(
143                @NotNull Collection<T> existingDelegates,
144                @NotNull JetType delegatedTraitType
145        ) {
146            Collection<T> result = new HashSet<T>();
147            Collection<T> candidates = generateDelegationCandidates(delegatedTraitType);
148            for (T candidate : candidates) {
149                if (existingMemberOverridesDelegatedMember(candidate, existingMembers)) {
150                    continue;
151                }
152                //only leave the first delegated member
153                if (checkClashWithOtherDelegatedMember(existingDelegates, candidate)) {
154                    continue;
155                }
156    
157                result.add(candidate);
158            }
159            return result;
160        }
161    
162        @NotNull
163        private Collection<T> generateDelegationCandidates(@NotNull JetType delegatedTraitType) {
164            Collection<T> descriptorsToDelegate = overridableMembersNotFromSuperClassOfTrait(delegatedTraitType);
165            Collection<T> result = Lists.newArrayList();
166            for (T memberDescriptor : descriptorsToDelegate) {
167                Modality modality = DescriptorUtils.convertModality(memberDescriptor.getModality(), true);
168                @SuppressWarnings("unchecked")
169                T copy = (T) memberDescriptor.copy(ownerDescriptor, modality, memberDescriptor.getVisibility(),
170                                                   CallableMemberDescriptor.Kind.DELEGATION, false);
171                result.add(copy);
172            }
173            return result;
174        }
175    
176        private static boolean existingMemberOverridesDelegatedMember(
177                @NotNull CallableMemberDescriptor candidate,
178                @NotNull Collection<? extends CallableDescriptor> existingMembers
179        ) {
180            for (CallableDescriptor existingDescriptor : existingMembers) {
181                if (haveSameSignatures(existingDescriptor, candidate)) {
182                    return true;
183                }
184            }
185            return false;
186        }
187    
188        private boolean checkClashWithOtherDelegatedMember(
189                @NotNull Collection<T> delegatedMembers,
190                @NotNull T candidate
191        ) {
192            for (CallableMemberDescriptor alreadyDelegatedMember : delegatedMembers) {
193                if (haveSameSignatures(alreadyDelegatedMember, candidate)) {
194                    //trying to delegate to many traits with the same methods
195                    PsiElement nameIdentifier = classOrObject.getNameIdentifier();
196                    PsiElement element = nameIdentifier != null ? nameIdentifier : classOrObject;
197                    trace.report(MANY_IMPL_MEMBER_NOT_IMPLEMENTED.on(element, classOrObject, alreadyDelegatedMember));
198                    return true;
199                }
200            }
201            return false;
202        }
203    
204        @NotNull
205        private Collection<T> overridableMembersNotFromSuperClassOfTrait(@NotNull JetType trait) {
206            final Collection<T> membersToSkip = getMembersFromClassSupertypeOfTrait(trait);
207            return Collections2.filter(
208                    memberExtractor.getMembersByType(trait),
209                    new Predicate<CallableMemberDescriptor>() {
210                        @Override
211                        public boolean apply(CallableMemberDescriptor descriptor) {
212                            if (!descriptor.getModality().isOverridable()) {
213                                return false;
214                            }
215                            for (CallableMemberDescriptor memberToSkip : membersToSkip) {
216                                if (haveSameSignatures(memberToSkip, descriptor)) {
217                                    return false;
218                                }
219                            }
220                            return true;
221                        }
222                    });
223        }
224    
225        private static boolean haveSameSignatures(@NotNull CallableDescriptor memberOne, @NotNull CallableDescriptor memberTwo) {
226            //isOverridableBy ignores return types
227            return OverridingUtil.isOverridableBy(memberOne, memberTwo).getResult() == OVERRIDABLE;
228        }
229    
230        @NotNull
231        private Collection<T> getMembersFromClassSupertypeOfTrait(@NotNull JetType traitType) {
232            JetType classSupertype = null;
233            for (JetType supertype : TypeUtils.getAllSupertypes(traitType)) {
234                if (isNotTrait(supertype.getConstructor().getDeclarationDescriptor())) {
235                    classSupertype = supertype;
236                    break;
237                }
238            }
239            return classSupertype != null ? memberExtractor.getMembersByType(classSupertype) : Collections.<T>emptyList();
240        }
241    
242        private static boolean isNotTrait(@Nullable DeclarationDescriptor descriptor) {
243            if (descriptor instanceof ClassDescriptor) {
244                ClassKind kind = ((ClassDescriptor) descriptor).getKind();
245                return kind != ClassKind.TRAIT;
246            }
247            return false;
248        }
249    
250        public interface MemberExtractor<T extends CallableMemberDescriptor> {
251            @NotNull
252            Collection<T> getMembersByType(@NotNull JetType type);
253        }
254    
255        public interface TypeResolver {
256            @Nullable
257            JetType resolve(@NotNull JetTypeReference reference);
258        }
259    }