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.Collections2;
022    import com.google.common.collect.HashMultimap;
023    import com.google.common.collect.Multimap;
024    import com.google.common.collect.Sets;
025    import com.intellij.openapi.util.Computable;
026    import com.intellij.util.Function;
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.resolve.lazy.data.JetClassLikeInfo;
032    import org.jetbrains.jet.lang.resolve.lazy.storage.MemoizedFunctionToNullable;
033    import org.jetbrains.jet.lang.resolve.lazy.storage.NotNullLazyValue;
034    import org.jetbrains.jet.lang.resolve.lazy.storage.StorageManager;
035    import org.jetbrains.jet.lang.resolve.name.FqName;
036    
037    import java.util.Collection;
038    import java.util.Set;
039    
040    public 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    }