001    /*
002     * Copyright 2010-2015 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.kotlin.descriptors.impl;
018    
019    import kotlin.jvm.functions.Function0;
020    import kotlin.jvm.functions.Function1;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.kotlin.descriptors.*;
024    import org.jetbrains.kotlin.descriptors.annotations.Annotations;
025    import org.jetbrains.kotlin.incremental.components.LookupLocation;
026    import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
027    import org.jetbrains.kotlin.name.Name;
028    import org.jetbrains.kotlin.resolve.DescriptorFactory;
029    import org.jetbrains.kotlin.resolve.OverridingUtil;
030    import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter;
031    import org.jetbrains.kotlin.resolve.scopes.MemberScope;
032    import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl;
033    import org.jetbrains.kotlin.resolve.scopes.StaticScopeForKotlinClass;
034    import org.jetbrains.kotlin.storage.MemoizedFunctionToNotNull;
035    import org.jetbrains.kotlin.storage.NotNullLazyValue;
036    import org.jetbrains.kotlin.storage.StorageManager;
037    import org.jetbrains.kotlin.types.KotlinType;
038    import org.jetbrains.kotlin.types.TypeConstructor;
039    import org.jetbrains.kotlin.types.TypeConstructorImpl;
040    import org.jetbrains.kotlin.utils.Printer;
041    
042    import java.util.*;
043    
044    public class EnumEntrySyntheticClassDescriptor extends ClassDescriptorBase {
045        private final TypeConstructor typeConstructor;
046        private final ConstructorDescriptor primaryConstructor;
047        private final MemberScope scope;
048        private final MemberScope staticScope = new StaticScopeForKotlinClass(this);
049        private final NotNullLazyValue<Collection<Name>> enumMemberNames;
050        private final Annotations annotations;
051    
052        /**
053         * Creates and initializes descriptors for enum entry with the given name and its companion object
054         * @param enumMemberNames needed for fake overrides resolution
055         */
056        @NotNull
057        public static EnumEntrySyntheticClassDescriptor create(
058                @NotNull StorageManager storageManager,
059                @NotNull ClassDescriptor enumClass,
060                @NotNull Name name,
061                @NotNull NotNullLazyValue<Collection<Name>> enumMemberNames,
062                @NotNull Annotations annotations,
063                @NotNull SourceElement source
064        ) {
065            KotlinType enumType = enumClass.getDefaultType();
066    
067            return new EnumEntrySyntheticClassDescriptor(storageManager, enumClass, enumType, name, enumMemberNames, annotations, source);
068        }
069    
070        private EnumEntrySyntheticClassDescriptor(
071                @NotNull StorageManager storageManager,
072                @NotNull ClassDescriptor containingClass,
073                @NotNull KotlinType supertype,
074                @NotNull Name name,
075                @NotNull NotNullLazyValue<Collection<Name>> enumMemberNames,
076                @NotNull Annotations annotations,
077                @NotNull SourceElement source
078        ) {
079            super(storageManager, containingClass, name, source);
080            assert containingClass.getKind() == ClassKind.ENUM_CLASS;
081    
082            this.annotations = annotations;
083            this.typeConstructor =
084                    TypeConstructorImpl.createForClass(this, getAnnotations(), true, "enum entry", Collections.<TypeParameterDescriptor>emptyList(),
085                                            Collections.singleton(supertype));
086    
087            this.scope = new EnumEntryScope(storageManager);
088            this.enumMemberNames = enumMemberNames;
089    
090            ConstructorDescriptorImpl primaryConstructor = DescriptorFactory.createPrimaryConstructorForObject(this, source);
091            primaryConstructor.setReturnType(getDefaultType());
092            this.primaryConstructor = primaryConstructor;
093        }
094    
095        @NotNull
096        @Override
097        public MemberScope getUnsubstitutedMemberScope() {
098            return scope;
099        }
100    
101        @NotNull
102        @Override
103        public MemberScope getStaticScope() {
104            return staticScope;
105        }
106    
107        @NotNull
108        @Override
109        public Collection<ConstructorDescriptor> getConstructors() {
110            return Collections.singleton(primaryConstructor);
111        }
112    
113        @NotNull
114        @Override
115        public TypeConstructor getTypeConstructor() {
116            return typeConstructor;
117        }
118    
119        @Nullable
120        @Override
121        public ClassDescriptor getCompanionObjectDescriptor() {
122            return null;
123        }
124    
125        @NotNull
126        @Override
127        public ClassKind getKind() {
128            return ClassKind.ENUM_ENTRY;
129        }
130    
131        @NotNull
132        @Override
133        public Modality getModality() {
134            return Modality.FINAL;
135        }
136    
137        @NotNull
138        @Override
139        public Visibility getVisibility() {
140            return Visibilities.PUBLIC;
141        }
142    
143        @Override
144        public boolean isInner() {
145            return false;
146        }
147    
148        @Override
149        public boolean isData() {
150            return false;
151        }
152    
153        @Override
154        public boolean isCompanionObject() {
155            return false;
156        }
157    
158        @Nullable
159        @Override
160        public ConstructorDescriptor getUnsubstitutedPrimaryConstructor() {
161            return primaryConstructor;
162        }
163    
164        @NotNull
165        @Override
166        public Annotations getAnnotations() {
167            return annotations;
168        }
169    
170        @Override
171        public String toString() {
172            return "enum entry " + getName();
173        }
174    
175        @NotNull
176        @Override
177        public List<TypeParameterDescriptor> getDeclaredTypeParameters() {
178            return Collections.emptyList();
179        }
180    
181        private class EnumEntryScope extends MemberScopeImpl {
182            private final MemoizedFunctionToNotNull<Name, Collection<FunctionDescriptor>> functions;
183            private final MemoizedFunctionToNotNull<Name, Collection<PropertyDescriptor>> properties;
184            private final NotNullLazyValue<Collection<DeclarationDescriptor>> allDescriptors;
185    
186            public EnumEntryScope(@NotNull StorageManager storageManager) {
187                this.functions = storageManager.createMemoizedFunction(new Function1<Name, Collection<FunctionDescriptor>>() {
188                    @Override
189                    public Collection<FunctionDescriptor> invoke(Name name) {
190                        return computeFunctions(name);
191                    }
192                });
193    
194                this.properties = storageManager.createMemoizedFunction(new Function1<Name, Collection<PropertyDescriptor>>() {
195                    @Override
196                    public Collection<PropertyDescriptor> invoke(Name name) {
197                        return computeProperties(name);
198                    }
199                });
200                this.allDescriptors = storageManager.createLazyValue(new Function0<Collection<DeclarationDescriptor>>() {
201                    @Override
202                    public Collection<DeclarationDescriptor> invoke() {
203                        return computeAllDeclarations();
204                    }
205                });
206            }
207    
208            @NotNull
209            @Override
210            @SuppressWarnings({"unchecked"}) // KT-9898 Impossible implement kotlin interface in java
211            public Collection getContributedVariables(@NotNull Name name, @NotNull LookupLocation location) {
212                return properties.invoke(name);
213            }
214    
215            @NotNull
216            @SuppressWarnings("unchecked")
217            private Collection<PropertyDescriptor> computeProperties(@NotNull Name name) {
218                return resolveFakeOverrides(name, (Collection) getSupertypeScope().getContributedVariables(name, NoLookupLocation.FOR_NON_TRACKED_SCOPE));
219            }
220    
221            @NotNull
222            @Override
223            public Collection<FunctionDescriptor> getContributedFunctions(@NotNull Name name, @NotNull LookupLocation location) {
224                return functions.invoke(name);
225            }
226    
227            @NotNull
228            private Collection<FunctionDescriptor> computeFunctions(@NotNull Name name) {
229                return resolveFakeOverrides(name, getSupertypeScope().getContributedFunctions(name, NoLookupLocation.FOR_NON_TRACKED_SCOPE));
230            }
231    
232            @NotNull
233            private MemberScope getSupertypeScope() {
234                Collection<KotlinType> supertype = getTypeConstructor().getSupertypes();
235                assert supertype.size() == 1 : "Enum entry and its companion object both should have exactly one supertype: " + supertype;
236                return supertype.iterator().next().getMemberScope();
237            }
238    
239            @NotNull
240            private <D extends CallableMemberDescriptor> Collection<D> resolveFakeOverrides(
241                    @NotNull Name name,
242                    @NotNull Collection<D> fromSupertypes
243            ) {
244                final Set<D> result = new LinkedHashSet<D>();
245    
246                OverridingUtil.generateOverridesInFunctionGroup(
247                        name, fromSupertypes, Collections.<D>emptySet(), EnumEntrySyntheticClassDescriptor.this,
248                        new OverridingUtil.DescriptorSink() {
249                            @Override
250                            @SuppressWarnings("unchecked")
251                            public void addFakeOverride(@NotNull CallableMemberDescriptor fakeOverride) {
252                                OverridingUtil.resolveUnknownVisibilityForMember(fakeOverride, null);
253                                result.add((D) fakeOverride);
254                            }
255    
256                            @Override
257                            public void conflict(@NotNull CallableMemberDescriptor fromSuper, @NotNull CallableMemberDescriptor fromCurrent) {
258                                // Do nothing
259                            }
260                        }
261                );
262    
263                return result;
264            }
265    
266            @NotNull
267            @Override
268            public Collection<DeclarationDescriptor> getContributedDescriptors(
269                    @NotNull DescriptorKindFilter kindFilter,
270                    @NotNull Function1<? super Name, Boolean> nameFilter
271            ) {
272                return allDescriptors.invoke();
273            }
274    
275            @NotNull
276            private Collection<DeclarationDescriptor> computeAllDeclarations() {
277                Collection<DeclarationDescriptor> result = new HashSet<DeclarationDescriptor>();
278                for (Name name : enumMemberNames.invoke()) {
279                    result.addAll(getContributedFunctions(name, NoLookupLocation.FOR_NON_TRACKED_SCOPE));
280                    result.addAll(getContributedVariables(name, NoLookupLocation.FOR_NON_TRACKED_SCOPE));
281                }
282                return result;
283            }
284    
285            @Override
286            public void printScopeStructure(@NotNull Printer p) {
287                p.println("enum entry scope for " + EnumEntrySyntheticClassDescriptor.this);
288            }
289        }
290    }