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.scopes.JetScope;
033    
034    import java.util.Collection;
035    import java.util.Collections;
036    
037    public class ResolveSessionUtils {
038    
039        // This name is used as a key for the case when something has no name _due to a syntactic error_
040        // Example: fun (x: Int) = 5
041        //          There's no name for this function in the PSI
042        // The name contains a GUID to avoid clashes, if a clash happens, it's not a big deal: the code does not compile anyway
043        public static final Name NO_NAME_FOR_LAZY_RESOLVE = Name.identifier("no_name_in_PSI_for_lazy_resolve_3d19d79d_1ba9_4cd0_b7f5_b46aa3cd5d40");
044    
045        public static final Predicate<ClassDescriptor> NON_SINGLETON_FILTER = new Predicate<ClassDescriptor>() {
046            @Override
047            public boolean apply(@Nullable ClassDescriptor descriptor) {
048                assert descriptor != null;
049                return !descriptor.getKind().isSingleton();
050            }
051        };
052    
053        public static final Predicate<ClassDescriptor> SINGLETON_FILTER = new Predicate<ClassDescriptor>() {
054            @Override
055            public boolean apply(@Nullable ClassDescriptor descriptor) {
056                assert descriptor != null;
057                return descriptor.getKind().isSingleton();
058            }
059        };
060    
061        private ResolveSessionUtils() {
062        }
063    
064        @NotNull
065        public static Collection<ClassDescriptor> getClassDescriptorsByFqName(@NotNull KotlinCodeAnalyzer analyzer, @NotNull FqName fqName) {
066            return getClassOrObjectDescriptorsByFqName(analyzer, fqName, NON_SINGLETON_FILTER);
067        }
068    
069        @NotNull
070        public static Collection<ClassDescriptor> getClassOrObjectDescriptorsByFqName(
071                @NotNull KotlinCodeAnalyzer analyzer,
072                @NotNull FqName fqName,
073                @NotNull Predicate<ClassDescriptor> filter
074        ) {
075            if (fqName.isRoot()) return Collections.emptyList();
076    
077            Collection<ClassDescriptor> classDescriptors = Lists.newArrayList();
078    
079            FqName packageFqName = fqName.parent();
080            while (true) {
081                PackageViewDescriptor packageDescriptor = analyzer.getModuleDescriptor().getPackage(packageFqName);
082                if (packageDescriptor != null) {
083                    FqName classInPackagePath = NamePackage.tail(fqName, packageFqName);
084                    ClassDescriptor classDescriptor = findByQualifiedName(packageDescriptor.getMemberScope(), classInPackagePath, filter);
085                    if (classDescriptor != null) {
086                        classDescriptors.add(classDescriptor);
087                    }
088                }
089    
090                if (packageFqName.isRoot()) {
091                    break;
092                }
093                else {
094                    packageFqName = packageFqName.parent();
095                }
096            }
097    
098            return classDescriptors;
099        }
100    
101        @Nullable
102        public static ClassDescriptor findByQualifiedName(@NotNull JetScope packageScope, @NotNull FqName path) {
103            return findByQualifiedName(packageScope, path, Predicates.<ClassDescriptor>alwaysTrue());
104        }
105    
106        @Nullable
107        private static ClassDescriptor findByQualifiedName(
108                @NotNull JetScope jetScope,
109                @NotNull FqName path,
110                @NotNull Predicate<ClassDescriptor> filter
111        ) {
112            if (path.isRoot()) return null;
113    
114            if (NamePackage.isOneSegmentFQN(path)) {
115                Name shortName = path.shortName();
116                ClassifierDescriptor classifier = jetScope.getClassifier(shortName);
117                if (classifier instanceof ClassDescriptor) {
118                    ClassDescriptor resultDescriptor = (ClassDescriptor) classifier;
119    
120                    if (filter.apply(resultDescriptor)) {
121                        return resultDescriptor;
122                    }
123                }
124    
125                return null;
126            }
127    
128            Name firstName = NamePackage.getFirstSegment(path);
129    
130            // Search in internal class
131            ClassifierDescriptor classifier = jetScope.getClassifier(firstName);
132            if (classifier instanceof ClassDescriptor) {
133                return findByQualifiedName(
134                        ((ClassDescriptor) classifier).getUnsubstitutedInnerClassesScope(),
135                        NamePackage.withoutFirstSegment(path),
136                        filter);
137            }
138    
139            // TODO: search in class object
140    
141            return null;
142        }
143    
144        @NotNull
145        public static Name safeNameForLazyResolve(@NotNull JetNamedDeclaration declaration) {
146            return safeNameForLazyResolve(declaration.getNameAsName());
147        }
148    
149        @NotNull
150        public static Name safeNameForLazyResolve(@Nullable Name name) {
151            return name != null ? name : NO_NAME_FOR_LAZY_RESOLVE;
152        }
153    
154        @Nullable
155        public static FqName safeFqNameForLazyResolve(@NotNull JetNamedDeclaration declaration) {
156            //NOTE: should only create special names for package level declarations, so we can safely rely on real fq name for parent
157            FqName parentFqName = JetNamedDeclarationUtil.getParentFqName(declaration);
158            return parentFqName != null ? parentFqName.child(safeNameForLazyResolve(declaration)) : null;
159        }
160    }