001    /*
002     * Copyright 2010-2013 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.collect.Lists;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
023    import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
024    import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
025    import org.jetbrains.jet.lang.descriptors.impl.ClassDescriptorBase;
026    import org.jetbrains.jet.lang.psi.JetNamed;
027    import org.jetbrains.jet.lang.resolve.name.FqName;
028    import org.jetbrains.jet.lang.resolve.name.Name;
029    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
030    import org.jetbrains.jet.util.QualifiedNamesUtil;
031    
032    import java.util.Arrays;
033    import java.util.Collection;
034    import java.util.Collections;
035    import java.util.List;
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        private ResolveSessionUtils() {
046        }
047    
048        @NotNull
049        public static Collection<ClassDescriptor> getClassDescriptorsByFqName(
050                    @NotNull KotlinCodeAnalyzer analyzer,
051                    @NotNull FqName fqName
052        ) {
053            return getClassOrObjectDescriptorsByFqName(analyzer, fqName, false);
054        }
055    
056        @NotNull
057        public static Collection<ClassDescriptor> getClassOrObjectDescriptorsByFqName(
058                @NotNull KotlinCodeAnalyzer analyzer,
059                @NotNull FqName fqName,
060                boolean includeObjectDeclarations
061        ) {
062            if (fqName.isRoot()) {
063                return Collections.emptyList();
064            }
065    
066            Collection<ClassDescriptor> classDescriptors = Lists.newArrayList();
067    
068            FqName packageFqName = fqName.parent();
069            while (true) {
070                NamespaceDescriptor packageDescriptor = analyzer.getPackageDescriptorByFqName(packageFqName);
071                if (packageDescriptor != null) {
072                    FqName classInPackagePath = new FqName(QualifiedNamesUtil.tail(packageFqName, fqName));
073                    Collection<ClassDescriptor> descriptors = getClassOrObjectDescriptorsByFqName(packageDescriptor, classInPackagePath,
074                                                                                                  includeObjectDeclarations);
075                    classDescriptors.addAll(descriptors);
076                }
077    
078                if (packageFqName.isRoot()) {
079                    break;
080                }
081                else {
082                    packageFqName = packageFqName.parent();
083                }
084            }
085    
086            return classDescriptors;
087        }
088    
089        private static Collection<ClassDescriptor> getClassOrObjectDescriptorsByFqName(
090                NamespaceDescriptor packageDescriptor,
091                FqName path,
092                boolean includeObjectDeclarations
093        ) {
094            if (path.isRoot()) {
095                return Collections.emptyList();
096            }
097    
098            Collection<JetScope> scopes = Arrays.asList(packageDescriptor.getMemberScope());
099    
100            List<Name> names = path.pathSegments();
101            if (names.size() > 1) {
102                for (Name subName : path.pathSegments().subList(0, names.size() - 1)) {
103                    Collection<JetScope> tempScopes = Lists.newArrayList();
104                    for (JetScope scope : scopes) {
105                        ClassifierDescriptor classifier = scope.getClassifier(subName);
106                        if (classifier instanceof ClassDescriptorBase) {
107                            ClassDescriptorBase classDescriptor = (ClassDescriptorBase) classifier;
108                            tempScopes.add(classDescriptor.getUnsubstitutedInnerClassesScope());
109                        }
110                    }
111                    scopes = tempScopes;
112                }
113            }
114    
115            Name shortName = path.shortName();
116            Collection<ClassDescriptor> resultClassifierDescriptors = Lists.newArrayList();
117            for (JetScope scope : scopes) {
118                ClassifierDescriptor classifier = scope.getClassifier(shortName);
119                if (classifier instanceof ClassDescriptor) {
120                    resultClassifierDescriptors.add((ClassDescriptor) classifier);
121                }
122                if (includeObjectDeclarations) {
123                    ClassDescriptor objectDescriptor = scope.getObjectDescriptor(shortName);
124                    if (objectDescriptor != null) {
125                        resultClassifierDescriptors.add(objectDescriptor);
126                    }
127                }
128            }
129    
130            return resultClassifierDescriptors;
131        }
132    
133        @NotNull
134        public static Name safeNameForLazyResolve(@NotNull JetNamed named) {
135            Name name = named.getNameAsName();
136            return safeNameForLazyResolve(name);
137        }
138    
139        @NotNull
140        public static Name safeNameForLazyResolve(@Nullable Name name) {
141            return name != null ? name : NO_NAME_FOR_LAZY_RESOLVE;
142        }
143    }