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 }