/* * 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.usages; import com.intellij.openapi.application.QueryExecutorBase; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiField; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiJavaFile; import com.intellij.psi.PsiManager; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiReference; import com.intellij.psi.impl.light.LightElement; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.search.searches.ReferencesSearch.SearchParameters; import com.intellij.util.Processor; import jetbrains.mps.baseLanguage.javastub.ASMNodeId; import jetbrains.mps.ide.project.ProjectHelper; import jetbrains.mps.idea.core.psi.impl.MPSPsiNode; import jetbrains.mps.idea.core.psi.impl.MPSPsiNodeBase; import jetbrains.mps.idea.core.psi.impl.MPSPsiProvider; import jetbrains.mps.idea.core.psi.impl.MPSPsiRef; import jetbrains.mps.idea.core.refactoring.NodePtr; import jetbrains.mps.idea.core.usages.IdeaSearchScope; import jetbrains.mps.idea.java.psiStubs.JavaForeignIdBuilder; import jetbrains.mps.persistence.java.library.JavaClassStubModelDescriptor; import jetbrains.mps.smodel.ModuleRepositoryFacade; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.model.SModel; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.model.SNodeId; import org.jetbrains.mps.openapi.model.SReference; import org.jetbrains.mps.openapi.module.FindUsagesFacade; import org.jetbrains.mps.openapi.module.SRepository; import java.util.HashSet; import java.util.Set; /** * danilla 2/13/13 */ // NOTE: It should be invoked by Idea only for classes and fields // For methods MethodReferenceSearch is used. // Local variables shouldn't bother us since we can't reference java code's local vars public class MPSReferenceSearch extends QueryExecutorBase<PsiReference, ReferencesSearch.SearchParameters> { public MPSReferenceSearch() { // flag: requires read action super(true); } @Override public void processQuery(@NotNull SearchParameters queryParameters, final @NotNull Processor<PsiReference> consumer) { if (!(queryParameters.getEffectiveSearchScope() instanceof GlobalSearchScope)) { return; } final GlobalSearchScope scope = (GlobalSearchScope) queryParameters.getEffectiveSearchScope(); final PsiElement psiTarget = queryParameters.getElementToSearch(); if (psiTarget instanceof MPSPsiNodeBase) return; final Project project = psiTarget.getProject(); final MPSPsiProvider psiProvider = MPSPsiProvider.getInstance(project); SRepository repository = ProjectHelper.getProjectRepository(project); repository.getModelAccess().runReadAction(new Runnable() { @Override public void run() { if (DumbService.getInstance(project).isDumb()) { return; } if (psiTarget instanceof LightElement) { // we don't handle light psi elements we don't know about // we may not be able to compute their node id and their Java meaning can be represented in baseLanguage // in a special way return; } // if MPSReferenceSearch is moved to mps-core, it will be MPS2PsiMapperUtil.getNodeId final SNode targetNode = getNodeForElement(psiTarget); if (targetNode == null) { // it can't be referenced from MPS return; } Set<SNode> targetNodes = new HashSet<SNode>(1); targetNodes.add(targetNode); Set<SReference> references; try { references = FindUsagesFacade.getInstance().findUsages(new IdeaSearchScope(scope), targetNodes, null); } catch (IndexNotReadyException e) { // DumbService doesn't seem to work return; } for (SReference sReference : references) { SNode source = sReference.getSourceNode(); MPSPsiNode psiNode = (MPSPsiNode) psiProvider.getPsi(source); // the source could have come from the psi stub itself if (psiNode == null) return; String refRole = sReference.getRole(); MPSPsiRef[] refs = psiNode.getReferences(refRole); if (refs.length == 0) continue; for (MPSPsiRef r : refs) { if (targetNode.getNodeId().equals(r.getNodeId())) { // it's our reference: giving it out to find usages consumer.process(r.getReference()); } } } } }); } // Maybe will go to MPS2PsiMapper @Nullable public static SNode getNodeForElement(PsiElement element) { // baseLanguage specific check if (!(element instanceof PsiClass || element instanceof PsiMethod || element instanceof PsiField)) { return null; } PsiFile psiFile = element.getContainingFile(); if (!(psiFile instanceof PsiJavaFile)) { return null; } if (PsiManager.getInstance(element.getProject()).isInProject(element)) { // It must be sources, try psi stubs // There might be psi stubs for this element, but there also might not (e.g. if it's inside a module // with no MPS facet) NodePtr nodePtr = JavaForeignIdBuilder.computeNodePtr(element); if (nodePtr == null) { return null; } SNode node = nodePtr.toSNodeReference().resolve(ProjectHelper.getProjectRepository(element.getProject())); return node; } else { // It must be from a library or SDK or something like that // Trying to find a suitable node in a class stub model SNodeId nodeId = null; if (element instanceof PsiClass) { nodeId = ASMNodeId.createId(((PsiClass) element).getQualifiedName()); } else if (element instanceof PsiField) { PsiClass clas = ((PsiField) element).getContainingClass(); String clasFqName = clas.getQualifiedName(); nodeId = ASMNodeId.createFieldId(clasFqName, ((PsiField) element).getName()); } else if (element instanceof PsiMethod) { // TODO argument types must be handled like they are in ASMNodeId (via asm classes) } if (nodeId == null) { // can't do anything about it return null; } String packageName = ((PsiJavaFile) psiFile).getPackageName(); for (SModel model : new ModuleRepositoryFacade(ProjectHelper.getProjectRepository(element.getProject())).getAllModels()) { if (!(model instanceof JavaClassStubModelDescriptor)) continue; if (!packageName.equals(model.getName().getLongName())) { continue; } SNode node = model.getNode(nodeId); if (node == null) continue; return node; } } return null; } }