package jetbrains.mps.workbench.findusages; /*Generated by MPS */ import com.intellij.openapi.components.ApplicationComponent; import org.jetbrains.mps.openapi.persistence.FindUsagesParticipant; import org.apache.log4j.Logger; import org.apache.log4j.LogManager; import jetbrains.mps.persistence.PersistenceRegistry; import jetbrains.mps.ide.MPSCoreComponents; import org.jetbrains.annotations.NotNull; import java.util.Collection; import org.jetbrains.mps.openapi.model.SModel; import java.util.Set; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.util.Consumer; import org.jetbrains.mps.openapi.model.SReference; import jetbrains.mps.internal.collections.runtime.SetSequence; import java.util.HashSet; import jetbrains.mps.internal.collections.runtime.IWhereFilter; import jetbrains.mps.smodel.SNodeId; import jetbrains.mps.util.containers.MultiMap; import java.util.function.Function; import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations; import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory; import java.util.Map; import jetbrains.mps.findUsages.NodeUsageFinder; import org.jetbrains.mps.openapi.language.SAbstractConcept; import org.jetbrains.mps.openapi.language.SLanguage; import jetbrains.mps.findUsages.FindUsagesUtil; import org.jetbrains.mps.openapi.model.SModelReference; import jetbrains.mps.smodel.SModelStereotype; import org.jetbrains.annotations.Nullable; import jetbrains.mps.util.containers.SetBasedMultiMap; import jetbrains.mps.persistence.java.library.JavaClassStubModelDescriptor; import jetbrains.mps.util.containers.ManyToManyMap; import com.intellij.openapi.vfs.VirtualFile; import jetbrains.mps.extapi.persistence.FolderSetDataSource; import gnu.trove.THashSet; import jetbrains.mps.vfs.IFile; import java.util.ArrayList; import jetbrains.mps.ide.vfs.VirtualFileUtils; import org.apache.log4j.Level; import java.util.Arrays; import com.intellij.util.indexing.FileBasedIndex; import com.intellij.psi.impl.cache.impl.id.IdIndex; import com.intellij.psi.impl.cache.impl.id.IdIndexEntry; import com.intellij.openapi.progress.ProcessCanceledException; import java.util.Collections; public class StubModelsFastFindSupport implements ApplicationComponent, FindUsagesParticipant { private static final Logger LOG = LogManager.getLogger(StubModelsFastFindSupport.class); private final PersistenceRegistry myRegistry; public StubModelsFastFindSupport(MPSCoreComponents mpsCore) { myRegistry = mpsCore.getPlatform().findComponent(PersistenceRegistry.class); } @Override public void initComponent() { myRegistry.addFindUsagesParticipant(this); } @Override public void disposeComponent() { myRegistry.removeFindUsagesParticipant(this); } @NotNull @Override public String getComponentName() { return StubModelsFastFindSupport.class.getSimpleName(); } @Override public void findUsages(Collection<SModel> models, Set<SNode> nodes, Consumer<SReference> consumer, Consumer<SModel> processedConsumer) { nodes = SetSequence.fromSetWithValues(new HashSet<SNode>(), SetSequence.fromSet(nodes).where(new IWhereFilter<SNode>() { public boolean accept(SNode it) { return it.getNodeId() instanceof SNodeId.Foreign; } })); MultiMap<SModel, SNode> candidates = findCandidates(models, nodes, processedConsumer, new Function<SNode, String>() { public String apply(SNode key) { return key.getNodeId().toString(); } }); for (SNode node : SetSequence.fromSet(nodes)) { SNode snode = ((SNode) node); if (!(SNodeOperations.isInstanceOf(snode, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x1024639ed74L, "jetbrains.mps.baseLanguage.structure.TypeVariableDeclaration")))) { continue; } candidates.putValue(SNodeOperations.getModel(snode), node); } for (Map.Entry<SModel, Collection<SNode>> e : candidates.entrySet()) { new NodeUsageFinder(e.getValue(), consumer).collectUsages(e.getKey()); } } @Override public void findInstances(Collection<SModel> models, Set<SAbstractConcept> concepts, Consumer<SNode> consumer, Consumer<SModel> processedConsumer) { final SLanguage bl = MetaAdapterFactory.getLanguage(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, "jetbrains.mps.baseLanguage"); concepts = SetSequence.fromSetWithValues(new HashSet<SAbstractConcept>(), SetSequence.fromSet(concepts).where(new IWhereFilter<SAbstractConcept>() { public boolean accept(SAbstractConcept it) { return bl.equals(it.getLanguage()); } })); MultiMap<SModel, SAbstractConcept> candidates = findCandidates(models, concepts, processedConsumer, null); for (Map.Entry<SModel, Collection<SAbstractConcept>> e : candidates.entrySet()) { FindUsagesUtil.collectInstances(e.getKey(), e.getValue(), consumer); } } @Override public void findModelUsages(Collection<SModel> scope, Set<SModelReference> modelReferences, Consumer<SModel> consumer, Consumer<SModel> processedConsumer) { modelReferences = SetSequence.fromSetWithValues(new HashSet<SModelReference>(), SetSequence.fromSet(modelReferences).where(new IWhereFilter<SModelReference>() { public boolean accept(SModelReference it) { return SModelStereotype.JAVA_STUB.equals(it.getName().getStereotype()); } })); MultiMap<SModel, SModelReference> candidates = findCandidates(scope, modelReferences, processedConsumer, new Function<SModelReference, String>() { public String apply(SModelReference key) { return key.getModelName(); } }); for (Map.Entry<SModel, Collection<SModelReference>> e : candidates.entrySet()) { if (FindUsagesUtil.hasModelUsages(e.getKey(), e.getValue())) { consumer.consume(e.getKey()); } } } private <T> MultiMap<SModel, T> findCandidates(Collection<SModel> models, Set<T> elems, Consumer<SModel> processedConsumer, @Nullable Function<T, String> id) { MultiMap<SModel, T> result = new SetBasedMultiMap<SModel, T>(); if (elems.isEmpty()) { for (SModel sm : models) { if (sm instanceof JavaClassStubModelDescriptor) { processedConsumer.consume(sm); } } return result; } // get all files in scope final ManyToManyMap<SModel, VirtualFile> scopeFiles = new ManyToManyMap<SModel, VirtualFile>(); Set<FolderSetDataSource> sources = new THashSet<FolderSetDataSource>(); for (final SModel sm : models) { if (!(sm instanceof JavaClassStubModelDescriptor)) { continue; } FolderSetDataSource source = ((JavaClassStubModelDescriptor) sm).getSource(); if (!(sources.add(source))) { continue; } Collection<IFile> files = source.getAffectedFiles(); ArrayList<VirtualFile> vFiles = new ArrayList(); for (IFile path : files) { final VirtualFile vf = VirtualFileUtils.getOrCreateVirtualFile(path); if (vf == null) { if (LOG.isEnabledFor(Level.WARN)) { LOG.warn("File " + path + ", which belows to model source of model " + sm.getReference().toString() + ", was not found in VFS. Assuming no usages in this file."); } continue; } if (vf.isDirectory()) { vFiles.addAll(Arrays.asList(vf.getChildren())); } else { vFiles.add(vf); } } for (VirtualFile vf : vFiles) { // do not enter any directories but one at the top level. Java package (corresponds to model) is just a list of files under single folder, // nested folder corresponds to another package if (vf.isDirectory()) { continue; } scopeFiles.addLink(sm, vf); } if (!(vFiles.isEmpty())) { // for stub models not backed by IDEA's VF, we shall not tell we've processed them. // Let another find participant (e.g. the slowest default that walks model) to look up usages. processedConsumer.consume(sm); } } for (T elem : elems) { String nodeId = (id == null ? elem.toString() : id.apply(elem)); // filter files with usages ConcreteFilesGlobalSearchScope allFiles = new ConcreteFilesGlobalSearchScope(scopeFiles.getSecond()); Collection<VirtualFile> matchingFiles; try { matchingFiles = FileBasedIndex.getInstance().getContainingFiles(IdIndex.NAME, new IdIndexEntry(nodeId, true), allFiles); } catch (ProcessCanceledException ce) { matchingFiles = Collections.emptyList(); } // back-transform for (VirtualFile file : matchingFiles) { for (SModel m : scopeFiles.getBySecond(file)) { result.putValue(m, elem); } } } return result; } }