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.java.scope;
018
019import com.google.common.collect.Lists;
020import com.google.common.collect.Maps;
021import com.google.common.collect.Sets;
022import com.intellij.openapi.progress.ProgressIndicatorProvider;
023import com.intellij.openapi.util.Condition;
024import com.intellij.psi.PsiElement;
025import com.intellij.util.containers.ContainerUtil;
026import org.jetbrains.annotations.NotNull;
027import org.jetbrains.annotations.Nullable;
028import org.jetbrains.jet.lang.descriptors.*;
029import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver;
030import org.jetbrains.jet.lang.resolve.java.JavaSemanticServices;
031import org.jetbrains.jet.lang.resolve.java.provider.ClassPsiDeclarationProvider;
032import org.jetbrains.jet.lang.resolve.java.provider.NamedMembers;
033import org.jetbrains.jet.lang.resolve.java.provider.PackagePsiDeclarationProvider;
034import org.jetbrains.jet.lang.resolve.java.provider.PsiDeclarationProvider;
035import org.jetbrains.jet.lang.resolve.name.Name;
036import org.jetbrains.jet.lang.resolve.scopes.JetScopeImpl;
037
038import java.util.*;
039
040public abstract class JavaBaseScope extends JetScopeImpl {
041
042    @NotNull
043    protected final JavaSemanticServices semanticServices;
044    @NotNull
045    protected final PsiDeclarationProvider declarationProvider;
046    @NotNull
047    private final Map<Name, Set<FunctionDescriptor>> functionDescriptors = Maps.newHashMap();
048    @NotNull
049    private final Map<Name, Set<VariableDescriptor>> propertyDescriptors = Maps.newHashMap();
050    @Nullable
051    private Collection<DeclarationDescriptor> allDescriptors = null;
052    @Nullable
053    private Set<ClassDescriptor> objectDescriptors = null;
054    @NotNull
055    protected final ClassOrNamespaceDescriptor descriptor;
056
057    private Collection<ClassDescriptor> innerClasses = null;
058
059
060    protected JavaBaseScope(
061            @NotNull ClassOrNamespaceDescriptor descriptor,
062            @NotNull JavaSemanticServices semanticServices,
063            @NotNull PsiDeclarationProvider declarationProvider
064    ) {
065        this.semanticServices = semanticServices;
066        this.declarationProvider = declarationProvider;
067        this.descriptor = descriptor;
068    }
069
070    @NotNull
071    @Override
072    public DeclarationDescriptor getContainingDeclaration() {
073        return descriptor;
074    }
075
076    @NotNull
077    @Override
078    public Collection<VariableDescriptor> getProperties(@NotNull Name name) {
079        Set<VariableDescriptor> cached = propertyDescriptors.get(name);
080        if (cached != null) return cached;
081
082        if (allDescriptorsComputed()) {
083            return Collections.emptySet();
084        }
085
086        Set<VariableDescriptor> computedDescriptors = computePropertyDescriptors(name);
087        propertyDescriptors.put(name, computedDescriptors);
088        return computedDescriptors;
089    }
090
091    @NotNull
092    private Set<VariableDescriptor> computePropertyDescriptors(@NotNull Name name) {
093        return getResolver().resolveFieldGroupByName(name, declarationProvider, descriptor);
094    }
095
096    @NotNull
097    @Override
098    public Collection<FunctionDescriptor> getFunctions(@NotNull Name name) {
099        Set<FunctionDescriptor> cached = functionDescriptors.get(name);
100        if (cached != null) return cached;
101
102        if (allDescriptorsComputed()) {
103            return Collections.emptySet();
104        }
105
106        Set<FunctionDescriptor> computedDescriptors = computeFunctionDescriptor(name);
107        functionDescriptors.put(name, computedDescriptors);
108        return computedDescriptors;
109    }
110
111    @NotNull
112    protected abstract Set<FunctionDescriptor> computeFunctionDescriptor(@NotNull Name name);
113
114    @NotNull
115    @Override
116    public Collection<DeclarationDescriptor> getAllDescriptors() {
117        if (allDescriptorsComputed()) {
118            return allDescriptors;
119        }
120
121        allDescriptors = computeAllDescriptors();
122
123        return allDescriptors;
124    }
125
126    private boolean allDescriptorsComputed() {
127        return allDescriptors != null;
128    }
129
130    @NotNull
131    protected Collection<DeclarationDescriptor> computeAllDescriptors() {
132        Collection<DeclarationDescriptor> result = Sets.newHashSet();
133        result.addAll(computeFieldAndFunctionDescriptors());
134        result.addAll(filterObjects(getInnerClasses(), false));
135        return result;
136    }
137
138    @NotNull
139    @Override
140    public Set<ClassDescriptor> getObjectDescriptors() {
141        if (objectDescriptors == null) {
142            objectDescriptors = new HashSet<ClassDescriptor>(filterObjects(getInnerClasses(), true));
143        }
144        return objectDescriptors;
145    }
146
147    @NotNull
148    protected abstract Collection<ClassDescriptor> computeInnerClasses();
149
150    @NotNull
151    private Collection<DeclarationDescriptor> computeFieldAndFunctionDescriptors() {
152        Collection<DeclarationDescriptor> result = Lists.newArrayList();
153        for (NamedMembers members : declarationProvider.getMembersCache().allMembers()) {
154            Name name = members.getName();
155            ProgressIndicatorProvider.checkCanceled();
156            result.addAll(getFunctions(name));
157            ProgressIndicatorProvider.checkCanceled();
158            result.addAll(getProperties(name));
159        }
160        return result;
161    }
162
163    @NotNull
164    protected JavaDescriptorResolver getResolver() {
165        return semanticServices.getDescriptorResolver();
166    }
167
168    //TODO: remove this method
169    @NotNull
170    public PsiElement getPsiElement() {
171        if (declarationProvider instanceof ClassPsiDeclarationProvider) {
172            return ((ClassPsiDeclarationProvider) declarationProvider).getPsiClass();
173        }
174        if (declarationProvider instanceof PackagePsiDeclarationProvider) {
175            return ((PackagePsiDeclarationProvider) declarationProvider).getPsiPackage();
176        }
177        throw new IllegalStateException();
178    }
179
180    @NotNull
181    protected Collection<ClassDescriptor> getInnerClasses() {
182        if (innerClasses == null) {
183            innerClasses = computeInnerClasses();
184        }
185        return innerClasses;
186    }
187
188    private static <T extends ClassDescriptor> Collection<T> filterObjects(Collection<T> classes, final boolean objects) {
189        return ContainerUtil.filter(classes, new Condition<T>() {
190            @Override
191            public boolean value(T classDescriptor) {
192                return classDescriptor.getKind().isObject() == objects;
193            }
194        });
195    }
196}