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 }