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    }