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