/*
* Copyright 2003-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.idea.java.psi.impl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.util.CollectConsumer;
import com.intellij.util.CommonProcessors;
import com.intellij.util.Consumer;
import com.intellij.util.Processor;
import com.intellij.util.containers.HashSet;
import com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.indexing.FileBasedIndex.ValueProcessor;
import jetbrains.mps.ide.project.ProjectHelper;
import jetbrains.mps.idea.core.psi.impl.MPSPsiProvider;
import jetbrains.mps.idea.java.index.MPSJavaFieldIndex;
import jetbrains.mps.idea.java.index.MPSJavaMethodIndex;
import jetbrains.mps.idea.java.index.MPSShortNameJavaClassIndex;
import jetbrains.mps.smodel.ModelAccessHelper;
import jetbrains.mps.util.Computable;
import jetbrains.mps.workbench.goTo.index.SNodeDescriptor;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.SNode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* evgeny, 1/25/13
*/
public class MPSJavaShortNamesCache extends PsiShortNamesCache {
private final Project myProject;
public MPSJavaShortNamesCache(Project project) {
myProject = project;
}
@NotNull
@Override
public PsiClass[] getClassesByName(@NotNull @NonNls final String name, final @NotNull GlobalSearchScope scope) {
ApplicationManager.getApplication().assertReadAccessAllowed();
return new ModelAccessHelper(ProjectHelper.getModelAccess(myProject)).runReadAction(new Computable<PsiClass[]>() {
@Override
public PsiClass[] compute() {
CollectConsumer<SNode> consumer = new CollectConsumer<SNode>(new ArrayList<SNode>());
findMPSClasses(name, consumer, scope);
return toPsiClasses(consumer.getResult());
}
});
}
@NotNull
@Override
public String[] getAllClassNames() {
Collection<String> allNames = FileBasedIndex.getInstance().getAllKeys(MPSShortNameJavaClassIndex.ID, myProject);
return allNames.toArray(new String[allNames.size()]);
}
@Override
public void getAllClassNames(@NotNull HashSet<String> dest) {
FileBasedIndex.getInstance().processAllKeys(MPSShortNameJavaClassIndex.ID, new CommonProcessors.CollectProcessor<String>(dest), myProject);
}
@NotNull
@Override
public PsiMethod[] getMethodsByName(@NonNls @NotNull final String name, @NotNull final GlobalSearchScope scope) {
ApplicationManager.getApplication().assertReadAccessAllowed();
return new ModelAccessHelper(ProjectHelper.getModelAccess(myProject)).runReadAction(new Computable<PsiMethod[]>() {
@Override
public PsiMethod[] compute() {
CollectConsumer<SNode> consumer = new CollectConsumer<SNode>(new ArrayList<SNode>());
findMPSMethods(name, consumer, scope);
return toPsiMethods(consumer.getResult());
}
});
}
@NotNull
@Override
public PsiMethod[] getMethodsByNameIfNotMoreThan(@NonNls @NotNull final String name, @NotNull final GlobalSearchScope scope, final int maxCount) {
ApplicationManager.getApplication().assertReadAccessAllowed();
return new ModelAccessHelper(ProjectHelper.getModelAccess(myProject)).runReadAction(new Computable<PsiMethod[]>() {
@Override
public PsiMethod[] compute() {
final CollectConsumer<SNode> consumer = new CollectConsumer<SNode>(new ArrayList<SNode>());
processMPSMethods(name, new Processor<SNode>() {
int count = 0;
@Override
public boolean process(SNode node) {
if (count++ >= maxCount) return false;
consumer.consume(node);
return true;
}
}, scope);
return toPsiMethods(consumer.getResult());
}
});
}
@NotNull
@Override
public PsiField[] getFieldsByNameIfNotMoreThan(@NonNls @NotNull final String name, @NotNull final GlobalSearchScope scope, final int maxCount) {
ApplicationManager.getApplication().assertReadAccessAllowed();
return new ModelAccessHelper(ProjectHelper.getModelAccess(myProject)).runReadAction(new Computable<PsiField[]>() {
@Override
public PsiField[] compute() {
final CollectConsumer<SNode> consumer = new CollectConsumer<SNode>(new ArrayList<SNode>());
processMPSFields(name, new Processor<SNode>() {
int count = 0;
@Override
public boolean process(SNode node) {
if (count++ >= maxCount) return false;
consumer.consume(node);
return true;
}
}, scope);
return toPsiFields(consumer.getResult());
}
});
}
@Override
public boolean processMethodsWithName(@NonNls @NotNull final String name, @NotNull final GlobalSearchScope scope, @NotNull final Processor<PsiMethod> processor) {
ApplicationManager.getApplication().assertReadAccessAllowed();
final MPSPsiProvider psiProvider = MPSPsiProvider.getInstance(myProject);
return new ModelAccessHelper(ProjectHelper.getModelAccess(myProject)).runReadAction(new Computable<Boolean>() {
@Override
public Boolean compute() {
return processMPSMethods(name, new Processor<SNode>() {
@Override
public boolean process(SNode node) {
PsiElement psi = psiProvider.getPsi(node);
if (psi instanceof PsiMethod) {
if (!processor.process((PsiMethod) psi)) return false;
}
return true;
}
}, scope);
}
});
}
@NotNull
@Override
public String[] getAllMethodNames() {
Collection<String> allNames = FileBasedIndex.getInstance().getAllKeys(MPSJavaMethodIndex.ID, myProject);
return allNames.toArray(new String[allNames.size()]);
}
@Override
public void getAllMethodNames(@NotNull HashSet<String> set) {
FileBasedIndex.getInstance().processAllKeys(MPSJavaMethodIndex.ID, new CommonProcessors.CollectProcessor<String>(set), myProject);
}
@NotNull
@Override
public PsiField[] getFieldsByName(@NotNull @NonNls final String name, @NotNull final GlobalSearchScope scope) {
ApplicationManager.getApplication().assertReadAccessAllowed();
return new ModelAccessHelper(ProjectHelper.getModelAccess(myProject)).runReadAction(new Computable<PsiField[]>() {
@Override
public PsiField[] compute() {
CollectConsumer<SNode> consumer = new CollectConsumer<SNode>(new ArrayList<SNode>());
findMPSFields(name, consumer, scope);
return toPsiFields(consumer.getResult());
}
});
}
@NotNull
@Override
public String[] getAllFieldNames() {
Collection<String> allNames = FileBasedIndex.getInstance().getAllKeys(MPSJavaFieldIndex.ID, myProject);
return allNames.toArray(new String[allNames.size()]);
}
@Override
public void getAllFieldNames(@NotNull HashSet<String> set) {
FileBasedIndex.getInstance().processAllKeys(MPSJavaFieldIndex.ID, new CommonProcessors.CollectProcessor<String>(set), myProject);
}
/** read access required */
private void findMPSClasses(String shortName, Consumer<SNode> consumer, GlobalSearchScope scope) {
final FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance();
List<Collection<SNodeDescriptor>> values = fileBasedIndex.getValues(MPSShortNameJavaClassIndex.ID, shortName, scope);
collectNodes(consumer, values);
}
/** read access required */
private void findMPSMethods(String shortName, Consumer<SNode> consumer, GlobalSearchScope scope) {
final FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance();
List<Collection<SNodeDescriptor>> values = fileBasedIndex.getValues(MPSJavaMethodIndex.ID, shortName, scope);
collectNodes(consumer, values);
}
/** read access required */
private boolean processMPSMethods(String shortName, final Processor<SNode> processor, GlobalSearchScope scope) {
final FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance();
return fileBasedIndex.processValues(MPSJavaMethodIndex.ID, shortName, null, new ValueProcessor<Collection<SNodeDescriptor>>() {
@Override
public boolean process(VirtualFile file, Collection<SNodeDescriptor> value) {
for (SNodeDescriptor descriptor : value) {
SNode node = descriptor.getNodeReference().resolve(ProjectHelper.getProjectRepository(myProject));
if (node == null) continue;
if (!processor.process(node)) return false;
}
return true;
}
}, scope);
}
/** read access required */
private void findMPSFields(String shortName, Consumer<SNode> consumer, GlobalSearchScope scope) {
final FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance();
List<Collection<SNodeDescriptor>> values = fileBasedIndex.getValues(MPSJavaFieldIndex.ID, shortName, scope);
collectNodes(consumer, values);
}
/** read access required */
private boolean processMPSFields(String shortName, final Processor<SNode> processor, GlobalSearchScope scope) {
final FileBasedIndex fileBasedIndex = FileBasedIndex.getInstance();
return fileBasedIndex.processValues(MPSJavaFieldIndex.ID, shortName, null, new ValueProcessor<Collection<SNodeDescriptor>>() {
@Override
public boolean process(VirtualFile file, Collection<SNodeDescriptor> value) {
for (SNodeDescriptor descriptor : value) {
SNode node = descriptor.getNodeReference().resolve(ProjectHelper.getProjectRepository(myProject));
if (node == null) continue;
if (!processor.process(node)) return false;
}
return true;
}
}, scope);
}
private void collectNodes(Consumer<SNode> consumer, List<Collection<SNodeDescriptor>> values) {
for (Collection<SNodeDescriptor> value : values) {
for (SNodeDescriptor descriptor : value) {
SNode node = descriptor.getNodeReference().resolve(ProjectHelper.getProjectRepository(myProject));
if (node == null) continue;
consumer.consume(node);
}
}
}
@SuppressWarnings("SuspiciousToArrayCall")
private PsiClass[] toPsiClasses(Iterable<SNode> classes) {
List<PsiElement> result = new ArrayList<PsiElement>();
collectPsiElements(classes, PsiClass.class, result);
return result.isEmpty() ? PsiClass.EMPTY_ARRAY : result.toArray(new PsiClass[result.size()]);
}
@SuppressWarnings("SuspiciousToArrayCall")
private PsiMethod[] toPsiMethods(Iterable<SNode> methods) {
List<PsiElement> result = new ArrayList<PsiElement>();
collectPsiElements(methods, PsiMethod.class, result);
return result.isEmpty() ? PsiMethod.EMPTY_ARRAY : result.toArray(new PsiMethod[result.size()]);
}
@SuppressWarnings("SuspiciousToArrayCall")
private PsiField[] toPsiFields(Iterable<SNode> fields) {
List<PsiElement> result = new ArrayList<PsiElement>();
collectPsiElements(fields, PsiField.class, result);
return result.isEmpty() ? PsiField.EMPTY_ARRAY : result.toArray(new PsiField[result.size()]);
}
private void collectPsiElements(Iterable<SNode> nodes, Class<? extends PsiElement> cls, List<PsiElement> yield) {
MPSPsiProvider psiProvider = MPSPsiProvider.getInstance(myProject);
for (SNode n : nodes) {
final PsiElement psi = psiProvider.getPsi(n);
if (cls.isInstance(psi)) {
PsiElement cast = cls.cast(psi);
yield.add(cast);
}
}
}
}