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