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