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