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