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