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.declarations;
018
019 import com.google.common.base.Predicate;
020 import com.google.common.base.Predicates;
021 import com.google.common.collect.*;
022 import com.intellij.psi.NavigatablePsiElement;
023 import com.intellij.util.Function;
024 import com.intellij.util.containers.ContainerUtil;
025 import jet.Function0;
026 import jet.Function1;
027 import org.jetbrains.annotations.NotNull;
028 import org.jetbrains.annotations.Nullable;
029 import org.jetbrains.jet.lang.psi.JetFile;
030 import org.jetbrains.jet.lang.psi.JetNamespaceHeader;
031 import org.jetbrains.jet.lang.psi.JetPsiUtil;
032 import org.jetbrains.jet.lang.resolve.lazy.data.JetClassLikeInfo;
033 import org.jetbrains.jet.lang.resolve.name.FqName;
034 import org.jetbrains.jet.storage.MemoizedFunctionToNullable;
035 import org.jetbrains.jet.storage.NotNullLazyValue;
036 import org.jetbrains.jet.storage.StorageManager;
037 import org.jetbrains.jet.util.QualifiedNamesUtil;
038
039 import java.util.Collection;
040 import java.util.Collections;
041 import java.util.Set;
042
043 public class FileBasedDeclarationProviderFactory implements DeclarationProviderFactory {
044
045 private static class Index {
046 private final Multimap<FqName, JetFile> filesByPackage = HashMultimap.create();
047 private final Set<FqName> declaredPackages = Sets.newHashSet();
048 }
049
050 private final Predicate<FqName> isPackageDeclaredExternally;
051
052 private final StorageManager storageManager;
053 private final NotNullLazyValue<Index> index;
054
055 private final MemoizedFunctionToNullable<FqName, PackageMemberDeclarationProvider> packageDeclarationProviders;
056
057 public FileBasedDeclarationProviderFactory(@NotNull StorageManager storageManager, @NotNull Collection<JetFile> files) {
058 this(storageManager, files, Predicates.<FqName>alwaysFalse());
059 }
060
061 public FileBasedDeclarationProviderFactory(
062 @NotNull StorageManager storageManager,
063 @NotNull final Collection<JetFile> files,
064 @NotNull Predicate<FqName> isPackageDeclaredExternally
065 ) {
066 this.storageManager = storageManager;
067 this.isPackageDeclaredExternally = isPackageDeclaredExternally;
068 this.index = storageManager.createLazyValue(new Function0<Index>() {
069 @Override
070 public Index invoke() {
071 return computeFilesByPackage(files);
072 }
073 });
074 this.packageDeclarationProviders = storageManager.createMemoizedFunctionWithNullableValues(new Function1<FqName, PackageMemberDeclarationProvider>() {
075 @Override
076 public PackageMemberDeclarationProvider invoke(FqName fqName) {
077 return createPackageMemberDeclarationProvider(fqName);
078 }
079 });
080 }
081
082 @NotNull
083 private static Index computeFilesByPackage(@NotNull Collection<JetFile> files) {
084 Index index = new Index();
085 for (JetFile file : files) {
086 JetNamespaceHeader header = file.getNamespaceHeader();
087 if (header == null) {
088 throw new IllegalArgumentException("Scripts are not supported");
089 }
090
091 FqName packageFqName = new FqName(header.getQualifiedName());
092 addMeAndParentPackages(index, packageFqName);
093 index.filesByPackage.put(packageFqName, file);
094 }
095 return index;
096 }
097
098 private static void addMeAndParentPackages(@NotNull Index index, @NotNull FqName name) {
099 index.declaredPackages.add(name);
100 if (!name.isRoot()) {
101 addMeAndParentPackages(index, name.parent());
102 }
103 }
104
105 /*package*/ boolean isPackageDeclaredExplicitly(@NotNull FqName packageFqName) {
106 return index.invoke().declaredPackages.contains(packageFqName);
107 }
108
109 /*package*/ boolean isPackageDeclared(@NotNull FqName packageFqName) {
110 return isPackageDeclaredExplicitly(packageFqName) || isPackageDeclaredExternally.apply(packageFqName);
111 }
112
113 /*package*/ Collection<FqName> getAllDeclaredSubPackagesOf(@NotNull final FqName parent) {
114 return Collections2.filter(index.invoke().declaredPackages, new Predicate<FqName>() {
115 @Override
116 public boolean apply(FqName fqName) {
117 return !fqName.isRoot() && fqName.parent().equals(parent);
118 }
119 });
120 }
121
122 /*package*/ Collection<NavigatablePsiElement> getPackageDeclarations(@NotNull final FqName fqName) {
123 if (fqName.isRoot()) {
124 return Collections.emptyList();
125 }
126
127 Collection<NavigatablePsiElement> resultElements = Lists.newArrayList();
128 for (FqName declaredPackage : index.invoke().filesByPackage.keys()) {
129 if (QualifiedNamesUtil.isSubpackageOf(declaredPackage, fqName)) {
130 Collection<JetFile> files = index.invoke().filesByPackage.get(declaredPackage);
131 resultElements.addAll(ContainerUtil.map(files, new Function<JetFile, NavigatablePsiElement>() {
132 @Override
133 public NavigatablePsiElement fun(JetFile file) {
134 return JetPsiUtil.getPackageReference(file, QualifiedNamesUtil.numberOfSegments(fqName) - 1);
135 }
136 }));
137 }
138 }
139
140 return resultElements;
141 }
142
143 @Override
144 public PackageMemberDeclarationProvider getPackageMemberDeclarationProvider(@NotNull FqName packageFqName) {
145 return packageDeclarationProviders.invoke(packageFqName);
146 }
147
148 @Nullable
149 public PackageMemberDeclarationProvider createPackageMemberDeclarationProvider(@NotNull FqName packageFqName) {
150 if (!isPackageDeclaredExplicitly(packageFqName)) {
151 if (isPackageDeclaredExternally.apply(packageFqName)) {
152 return EmptyPackageMemberDeclarationProvider.INSTANCE;
153 }
154 return null;
155 }
156
157 return new FileBasedPackageMemberDeclarationProvider(storageManager, packageFqName, this, index.invoke().filesByPackage.get(packageFqName));
158 }
159
160 @NotNull
161 @Override
162 public ClassMemberDeclarationProvider getClassMemberDeclarationProvider(@NotNull JetClassLikeInfo classLikeInfo) {
163 if (!index.invoke().filesByPackage.containsKey(classLikeInfo.getContainingPackageFqName())) {
164 throw new IllegalStateException("This factory doesn't know about this class: " + classLikeInfo);
165 }
166
167 return new PsiBasedClassMemberDeclarationProvider(storageManager, classLikeInfo);
168 }
169 }