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 org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.lang.descriptors.*;
025    import org.jetbrains.jet.lang.descriptors.impl.MutableClassDescriptor;
026    import org.jetbrains.jet.lang.psi.JetClassOrObject;
027    import org.jetbrains.jet.lang.psi.JetDelegationSpecifier;
028    import org.jetbrains.jet.lang.psi.JetDelegatorByExpressionSpecifier;
029    import org.jetbrains.jet.lang.psi.JetTypeReference;
030    import org.jetbrains.jet.lang.types.JetType;
031    import org.jetbrains.jet.lang.types.TypeUtils;
032    
033    import java.util.*;
034    
035    import static org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor.Kind.DELEGATION;
036    import static org.jetbrains.jet.lang.diagnostics.Errors.MANY_IMPL_MEMBER_NOT_IMPLEMENTED;
037    import static org.jetbrains.jet.lang.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE;
038    
039    public final class DelegationResolver<T extends CallableMemberDescriptor> {
040    
041        public static void generateDelegatesInAClass(
042                @NotNull MutableClassDescriptor classDescriptor,
043                @NotNull final BindingTrace trace,
044                @NotNull JetClassOrObject jetClassOrObject
045        ) {
046            TypeResolver eagerTypeResolver = new TypeResolver() {
047                @Nullable
048                @Override
049                public JetType resolve(@NotNull JetTypeReference reference) {
050                    return trace.get(BindingContext.TYPE, reference);
051                }
052            };
053            MemberExtractor<CallableMemberDescriptor> eagerExtractor = new MemberExtractor<CallableMemberDescriptor>() {
054                @NotNull
055                @Override
056                public Collection<CallableMemberDescriptor> getMembersByType(@NotNull JetType type) {
057                    //noinspection unchecked
058                    return (Collection) Collections2.filter(type.getMemberScope().getAllDescriptors(),
059                                                            Predicates.instanceOf(CallableMemberDescriptor.class));
060                }
061            };
062            Set<CallableMemberDescriptor> existingMembers = classDescriptor.getAllCallableMembers();
063            Collection<CallableMemberDescriptor> delegatedMembers =
064                    generateDelegatedMembers(jetClassOrObject, classDescriptor, existingMembers, trace, eagerExtractor, eagerTypeResolver);
065            for (CallableMemberDescriptor descriptor : delegatedMembers) {
066                if (descriptor instanceof PropertyDescriptor) {
067                    PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
068                    classDescriptor.getBuilder().addPropertyDescriptor(propertyDescriptor);
069                }
070                else if (descriptor instanceof SimpleFunctionDescriptor) {
071                    SimpleFunctionDescriptor functionDescriptor = (SimpleFunctionDescriptor) descriptor;
072                    classDescriptor.getBuilder().addFunctionDescriptor(functionDescriptor);
073                }
074            }
075        }
076    
077    
078        @NotNull
079        public static <T extends CallableMemberDescriptor> Collection<T> generateDelegatedMembers(
080                @NotNull JetClassOrObject classOrObject,
081                @NotNull ClassDescriptor ownerDescriptor,
082                @NotNull Collection<? extends CallableDescriptor> existingMembers,
083                @NotNull BindingTrace trace,
084                @NotNull MemberExtractor<T> memberExtractor,
085                @NotNull TypeResolver typeResolver
086        ) {
087            return new DelegationResolver<T>(classOrObject, ownerDescriptor, existingMembers, trace, memberExtractor, typeResolver)
088                    .generateDelegatedMembers();
089        }
090    
091        @NotNull private final JetClassOrObject classOrObject;
092        @NotNull private final ClassDescriptor ownerDescriptor;
093        @NotNull private final Collection<? extends CallableDescriptor> existingMembers;
094        @NotNull private final BindingTrace trace;
095        @NotNull private final MemberExtractor<T> memberExtractor;
096        @NotNull private final TypeResolver typeResolver;
097    
098        private DelegationResolver(
099                @NotNull JetClassOrObject classOrObject,
100                @NotNull ClassDescriptor ownerDescriptor,
101                @NotNull Collection<? extends CallableDescriptor> existingMembers,
102                @NotNull BindingTrace trace,
103                @NotNull MemberExtractor<T> extractor,
104                @NotNull TypeResolver resolver
105        ) {
106    
107            this.classOrObject = classOrObject;
108            this.ownerDescriptor = ownerDescriptor;
109            this.existingMembers = existingMembers;
110            this.trace = trace;
111            this.memberExtractor = extractor;
112            this.typeResolver = resolver;
113        }
114    
115        @NotNull
116        private Collection<T> generateDelegatedMembers() {
117            Collection<T> delegatedMembers = new HashSet<T>();
118            for (JetDelegationSpecifier delegationSpecifier : classOrObject.getDelegationSpecifiers()) {
119                if (!(delegationSpecifier instanceof JetDelegatorByExpressionSpecifier)) {
120                    continue;
121                }
122                JetDelegatorByExpressionSpecifier specifier = (JetDelegatorByExpressionSpecifier) delegationSpecifier;
123                JetTypeReference typeReference = specifier.getTypeReference();
124                if (typeReference == null) {
125                    continue;
126                }
127                JetType delegatedTraitType = typeResolver.resolve(typeReference);
128                if (delegatedTraitType == null || delegatedTraitType.isError()) {
129                    continue;
130                }
131                Collection<T> delegatesForTrait = generateDelegatesForTrait(delegatedMembers, delegatedTraitType);
132                delegatedMembers.addAll(delegatesForTrait);
133            }
134            return delegatedMembers;
135        }
136    
137        @NotNull
138        private Collection<T> generateDelegatesForTrait(
139                @NotNull Collection<T> existingDelegates,
140                @NotNull JetType delegatedTraitType
141        ) {
142            Collection<T> result = new HashSet<T>();
143            Collection<T> candidates = generateDelegationCandidates(delegatedTraitType);
144            for (T candidate : candidates) {
145                if (existingMemberOverridesDelegatedMember(candidate, existingMembers)) {
146                    continue;
147                }
148                //only leave the first delegated member
149                if (checkClashWithOtherDelegatedMember(existingDelegates, candidate)) {
150                    continue;
151                }
152    
153                result.add(candidate);
154            }
155            return result;
156        }
157    
158        @NotNull
159        private Collection<T> generateDelegationCandidates(@NotNull JetType delegatedTraitType) {
160            Collection<T> descriptorsToDelegate = overridableMembersNotFromSuperClassOfTrait(delegatedTraitType);
161            Collection<T> result = new ArrayList<T>(descriptorsToDelegate.size());
162            for (T memberDescriptor : descriptorsToDelegate) {
163                Modality newModality = memberDescriptor.getModality() == Modality.ABSTRACT ? Modality.OPEN : memberDescriptor.getModality();
164                @SuppressWarnings("unchecked")
165                T copy = (T) memberDescriptor.copy(ownerDescriptor, newModality, Visibilities.INHERITED, DELEGATION, false);
166                result.add(copy);
167            }
168            return result;
169        }
170    
171        private static boolean existingMemberOverridesDelegatedMember(
172                @NotNull CallableMemberDescriptor candidate,
173                @NotNull Collection<? extends CallableDescriptor> existingMembers
174        ) {
175            for (CallableDescriptor existingDescriptor : existingMembers) {
176                if (haveSameSignatures(existingDescriptor, candidate)) {
177                    return true;
178                }
179            }
180            return false;
181        }
182    
183        private boolean checkClashWithOtherDelegatedMember(@NotNull Collection<T> delegatedMembers, @NotNull T candidate) {
184            for (CallableMemberDescriptor alreadyDelegatedMember : delegatedMembers) {
185                if (haveSameSignatures(alreadyDelegatedMember, candidate)) {
186                    //trying to delegate to many traits with the same methods
187                    trace.report(MANY_IMPL_MEMBER_NOT_IMPLEMENTED.on(classOrObject, classOrObject, alreadyDelegatedMember));
188                    return true;
189                }
190            }
191            return false;
192        }
193    
194        @NotNull
195        private Collection<T> overridableMembersNotFromSuperClassOfTrait(@NotNull JetType trait) {
196            final Collection<T> membersToSkip = getMembersFromClassSupertypeOfTrait(trait);
197            return Collections2.filter(
198                    memberExtractor.getMembersByType(trait),
199                    new Predicate<CallableMemberDescriptor>() {
200                        @Override
201                        public boolean apply(CallableMemberDescriptor descriptor) {
202                            if (!descriptor.getModality().isOverridable()) {
203                                return false;
204                            }
205                            for (CallableMemberDescriptor memberToSkip : membersToSkip) {
206                                if (haveSameSignatures(memberToSkip, descriptor)) {
207                                    return false;
208                                }
209                            }
210                            return true;
211                        }
212                    });
213        }
214    
215        private static boolean haveSameSignatures(@NotNull CallableDescriptor memberOne, @NotNull CallableDescriptor memberTwo) {
216            //isOverridableBy ignores return types
217            return OverridingUtil.isOverridableBy(memberOne, memberTwo).getResult() == OVERRIDABLE;
218        }
219    
220        @NotNull
221        private Collection<T> getMembersFromClassSupertypeOfTrait(@NotNull JetType traitType) {
222            JetType classSupertype = null;
223            for (JetType supertype : TypeUtils.getAllSupertypes(traitType)) {
224                if (isNotTrait(supertype.getConstructor().getDeclarationDescriptor())) {
225                    classSupertype = supertype;
226                    break;
227                }
228            }
229            return classSupertype != null ? memberExtractor.getMembersByType(classSupertype) : Collections.<T>emptyList();
230        }
231    
232        private static boolean isNotTrait(@Nullable DeclarationDescriptor descriptor) {
233            if (descriptor instanceof ClassDescriptor) {
234                ClassKind kind = ((ClassDescriptor) descriptor).getKind();
235                return kind != ClassKind.TRAIT;
236            }
237            return false;
238        }
239    
240        public interface MemberExtractor<T extends CallableMemberDescriptor> {
241            @NotNull
242            Collection<T> getMembersByType(@NotNull JetType type);
243        }
244    
245        public interface TypeResolver {
246            @Nullable
247            JetType resolve(@NotNull JetTypeReference reference);
248        }
249    }