001    /*
002     * Copyright 2010-2014 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.lazy;
018    
019    import com.google.common.base.Predicate;
020    import com.google.common.base.Predicates;
021    import com.google.common.collect.Lists;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
025    import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
026    import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor;
027    import org.jetbrains.jet.lang.psi.JetNamedDeclaration;
028    import org.jetbrains.jet.lang.psi.JetNamedDeclarationUtil;
029    import org.jetbrains.jet.lang.resolve.name.FqName;
030    import org.jetbrains.jet.lang.resolve.name.Name;
031    import org.jetbrains.jet.lang.resolve.name.NamePackage;
032    import org.jetbrains.jet.lang.resolve.name.SpecialNames;
033    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
034    
035    import java.util.Collection;
036    import java.util.Collections;
037    
038    public class ResolveSessionUtils {
039    
040        public static final Predicate<ClassDescriptor> NON_SINGLETON_FILTER = new Predicate<ClassDescriptor>() {
041            @Override
042            public boolean apply(@Nullable ClassDescriptor descriptor) {
043                assert descriptor != null;
044                return !descriptor.getKind().isSingleton();
045            }
046        };
047    
048        public static final Predicate<ClassDescriptor> SINGLETON_FILTER = new Predicate<ClassDescriptor>() {
049            @Override
050            public boolean apply(@Nullable ClassDescriptor descriptor) {
051                assert descriptor != null;
052                return descriptor.getKind().isSingleton();
053            }
054        };
055    
056        private ResolveSessionUtils() {
057        }
058    
059        @NotNull
060        public static Collection<ClassDescriptor> getClassDescriptorsByFqName(@NotNull KotlinCodeAnalyzer analyzer, @NotNull FqName fqName) {
061            return getClassOrObjectDescriptorsByFqName(analyzer, fqName, NON_SINGLETON_FILTER);
062        }
063    
064        @NotNull
065        public static Collection<ClassDescriptor> getClassOrObjectDescriptorsByFqName(
066                @NotNull KotlinCodeAnalyzer analyzer,
067                @NotNull FqName fqName,
068                @NotNull Predicate<ClassDescriptor> filter
069        ) {
070            if (fqName.isRoot()) return Collections.emptyList();
071    
072            Collection<ClassDescriptor> classDescriptors = Lists.newArrayList();
073    
074            FqName packageFqName = fqName.parent();
075            while (true) {
076                PackageViewDescriptor packageDescriptor = analyzer.getModuleDescriptor().getPackage(packageFqName);
077                if (packageDescriptor != null) {
078                    FqName classInPackagePath = NamePackage.tail(fqName, packageFqName);
079                    ClassDescriptor classDescriptor = findByQualifiedName(packageDescriptor.getMemberScope(), classInPackagePath, filter);
080                    if (classDescriptor != null) {
081                        classDescriptors.add(classDescriptor);
082                    }
083                }
084    
085                if (packageFqName.isRoot()) {
086                    break;
087                }
088                else {
089                    packageFqName = packageFqName.parent();
090                }
091            }
092    
093            return classDescriptors;
094        }
095    
096        @Nullable
097        public static ClassDescriptor findByQualifiedName(@NotNull JetScope packageScope, @NotNull FqName path) {
098            return findByQualifiedName(packageScope, path, Predicates.<ClassDescriptor>alwaysTrue());
099        }
100    
101        @Nullable
102        private static ClassDescriptor findByQualifiedName(
103                @NotNull JetScope jetScope,
104                @NotNull FqName path,
105                @NotNull Predicate<ClassDescriptor> filter
106        ) {
107            if (path.isRoot()) return null;
108    
109            if (NamePackage.isOneSegmentFQN(path)) {
110                Name shortName = path.shortName();
111                ClassifierDescriptor classifier = jetScope.getClassifier(shortName);
112                if (classifier instanceof ClassDescriptor) {
113                    ClassDescriptor resultDescriptor = (ClassDescriptor) classifier;
114    
115                    if (filter.apply(resultDescriptor)) {
116                        return resultDescriptor;
117                    }
118                }
119    
120                return null;
121            }
122    
123            Name firstName = NamePackage.getFirstSegment(path);
124    
125            // Search in internal class
126            ClassifierDescriptor classifier = jetScope.getClassifier(firstName);
127            if (classifier instanceof ClassDescriptor) {
128                return findByQualifiedName(
129                        ((ClassDescriptor) classifier).getUnsubstitutedInnerClassesScope(),
130                        NamePackage.withoutFirstSegment(path),
131                        filter);
132            }
133    
134            // TODO: search in class object
135    
136            return null;
137        }
138    
139        @NotNull
140        public static Name safeNameForLazyResolve(@NotNull JetNamedDeclaration declaration) {
141            return safeNameForLazyResolve(declaration.getNameAsName());
142        }
143    
144        @NotNull
145        public static Name safeNameForLazyResolve(@Nullable Name name) {
146            return SpecialNames.safeIdentifier(name);
147        }
148    
149        @Nullable
150        public static FqName safeFqNameForLazyResolve(@NotNull JetNamedDeclaration declaration) {
151            //NOTE: should only create special names for package level declarations, so we can safely rely on real fq name for parent
152            FqName parentFqName = JetNamedDeclarationUtil.getParentFqName(declaration);
153            return parentFqName != null ? parentFqName.child(safeNameForLazyResolve(declaration)) : null;
154        }
155    }