package jetbrains.mps.idea.java.psiStubs; /*Generated by MPS */ import jetbrains.mps.smodel.RegularModelDescriptor; import org.apache.log4j.Logger; import java.util.Map; import java.util.Set; import org.jetbrains.mps.openapi.model.SNodeId; import jetbrains.mps.internal.collections.runtime.MapSequence; import java.util.HashMap; import com.intellij.psi.PsiElement; import org.jetbrains.mps.openapi.model.SModelReference; import org.jetbrains.annotations.NotNull; import org.jetbrains.mps.openapi.module.SRepository; import java.util.Collection; import org.jetbrains.mps.openapi.language.SLanguage; import java.util.Arrays; import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory; import java.util.List; import org.jetbrains.mps.openapi.module.SModuleReference; import java.util.Collections; import jetbrains.mps.smodel.ModelLoadResult; import jetbrains.mps.smodel.SModel; import jetbrains.mps.smodel.loading.ModelLoadingState; import com.intellij.openapi.progress.NonCancelableSection; import com.intellij.openapi.progress.ProgressIndicatorProvider; import com.intellij.psi.PsiJavaFile; import org.jetbrains.mps.openapi.model.SNode; import jetbrains.mps.internal.collections.runtime.SetSequence; import java.util.HashSet; import com.intellij.psi.PsiClass; import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations; import jetbrains.mps.lang.smodel.generator.smodelAdapter.AttributeOperations; import jetbrains.mps.lang.smodel.generator.smodelAdapter.IAttributeDescriptor; import org.jetbrains.mps.openapi.persistence.DataSource; import jetbrains.mps.smodel.CopyUtil; import jetbrains.mps.idea.java.psi.JavaPsiListener; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiImportStatementBase; import jetbrains.mps.lang.smodel.generator.smodelAdapter.SConceptOperations; import com.intellij.psi.PsiJavaCodeReferenceElement; import jetbrains.mps.lang.smodel.generator.smodelAdapter.SPropertyOperations; import com.intellij.psi.PsiImportStaticStatement; import jetbrains.mps.internal.collections.runtime.ListSequence; import jetbrains.mps.lang.smodel.generator.smodelAdapter.SLinkOperations; import org.jetbrains.mps.openapi.util.Consumer; import jetbrains.mps.util.Pair; import jetbrains.mps.internal.collections.runtime.IVisitor; public class PsiJavaStubModelDescriptor extends RegularModelDescriptor implements PsiJavaStubListener { /*package*/ Logger LOG = Logger.getLogger(PsiJavaStubModelDescriptor.class); private Map<String, Set<SNodeId>> myRootsPerFile = MapSequence.fromMap(new HashMap<String, Set<SNodeId>>()); private Map<String, Set<SNodeId>> myAllNodesPerFile = MapSequence.fromMap(new HashMap<String, Set<SNodeId>>()); private Map<SNodeId, PsiElement> myGlobalMps2PsiMapping = MapSequence.fromMap(new HashMap<SNodeId, PsiElement>()); private PsiJavaStubModelDescriptor.MyMps2PsiMapper myMps2PsiMapper = new PsiJavaStubModelDescriptor.MyMps2PsiMapper(); public PsiJavaStubModelDescriptor(SModelReference modelRef, JavaFilesHolder dataSource) { super(modelRef, dataSource); } @Override @NotNull public JavaFilesHolder getSource() { return (JavaFilesHolder) super.getSource(); } @Override public void attach(SRepository repository) { super.attach(repository); getSource().addListener(this); } @Override public void detach() { super.detach(); getSource().removeListener(this); } @Override public Collection<SLanguage> importedLanguageIds() { return Arrays.asList(new SLanguage[]{MetaAdapterFactory.getLanguage(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, "jetbrains.mps.baseLanguage"), MetaAdapterFactory.getLanguage(0xf280165065d5424eL, 0xbb1b463a8781b786L, "jetbrains.mps.baseLanguage.javadoc")}); } @Override public List<SModuleReference> importedDevkits() { return Collections.emptyList(); } @NotNull @Override protected ModelLoadResult<SModel> createModel() { SModel m = new SModel(getReference()); loadContents(m); return new ModelLoadResult<SModel>(m, ModelLoadingState.FULLY_LOADED); } private void loadContents(SModel into) { // todo think why it's needed (otherwise we get ProcessCancelException) // in idea ce it's used only twice: and one case is switch from stubs to AST // maybe when traversing PSI we trigger parse (which would be very wrong) NonCancelableSection section = ProgressIndicatorProvider.startNonCancelableSectionIfSupported(); try { for (PsiJavaFile jf : getSource().getJavaFiles()) { SNode javaImports = getImports(jf.getImportList().getAllImportStatements()); ASTConverter converter = new ASTConverter(myMps2PsiMapper); Set<SNodeId> roots = SetSequence.fromSet(new HashSet<SNodeId>()); for (PsiClass cls : jf.getClasses()) { SNode node = converter.convertClass(cls); if (SNodeOperations.isInstanceOf(node, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101d9d3ca30L, "jetbrains.mps.baseLanguage.structure.Classifier"))) { AttributeOperations.setAttribute(SNodeOperations.cast(node, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101d9d3ca30L, "jetbrains.mps.baseLanguage.structure.Classifier")), new IAttributeDescriptor.NodeAttribute(MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x53f7c33f069862f2L, "jetbrains.mps.baseLanguage.structure.JavaImports")), javaImports); } // TODO check for duplicate ids (in java sources there may be 2 classes with the same name // which is an error but none the less) into.addRootNode(node); SetSequence.fromSet(roots).addElement(node.getNodeId()); } if (SetSequence.fromSet(roots).isNotEmpty()) { MapSequence.fromMap(myRootsPerFile).put(jf.getName(), roots); } } } catch (Exception e) { LOG.error("could not build stub model for code PSI: you may not be able to reference java code from MPS", e); } finally { section.done(); } } @Override public void changed(DataSource source) { // ignore, we should never receive this one // always the more detailed changed(source, psiEvent) } @Override public synchronized void changed(DataSource source, final PsiJavaStubEvent event) { // locking could possibly be made more fine-grained // already attached, but not createModel'd yet? final SModel actualModel = getCurrentModelInternal(); if (actualModel == null) { return; } final SModel modelCopy = CopyUtil.copyModel(actualModel); for (PsiJavaFile file : event.removed()) { myMps2PsiMapper.clearFile(modelCopy, file.getName()); } for (JavaPsiListener.FSRename rename : event.renamed()) { String oldName = rename.oldName; myMps2PsiMapper.clearFile(modelCopy, oldName); } for (PsiJavaFile file : event.needReparse()) { if (!(file.isValid())) { // going upwards and trying to find the valid file with this filename... // it should probably be removed, looks like a hack String name = file.getName(); for (PsiFile f : file.getParent().getFiles()) { if (name.equals(f.getName()) && f instanceof PsiJavaFile) { file = (PsiJavaFile) f; break; } } } // it's still not valid if (!(file.isValid())) { continue; } myMps2PsiMapper.clearFile(modelCopy, file.getName()); SNode javaImports = getImports(file.getImportList().getAllImportStatements()); ASTConverter converter = new ASTConverter(myMps2PsiMapper); Set<SNodeId> roots = SetSequence.fromSet(new HashSet<SNodeId>()); for (PsiClass cls : file.getClasses()) { SNode node = converter.convertClass(cls); if (SNodeOperations.isInstanceOf(node, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101d9d3ca30L, "jetbrains.mps.baseLanguage.structure.Classifier"))) { AttributeOperations.setAttribute(SNodeOperations.cast(node, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101d9d3ca30L, "jetbrains.mps.baseLanguage.structure.Classifier")), new IAttributeDescriptor.NodeAttribute(MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x53f7c33f069862f2L, "jetbrains.mps.baseLanguage.structure.JavaImports")), javaImports); } modelCopy.addRootNode(node); SetSequence.fromSet(roots).addElement(node.getNodeId()); } if (SetSequence.fromSet(roots).isNotEmpty()) { MapSequence.fromMap(myRootsPerFile).put(file.getName(), roots); } } replace(new ModelLoadResult<SModel>(modelCopy, ModelLoadingState.FULLY_LOADED)); } private SNode getImports(PsiImportStatementBase[] imports) { SNode javaImports = SConceptOperations.createNewNode(MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x53f7c33f069862f2L, "jetbrains.mps.baseLanguage.structure.JavaImports")); for (PsiImportStatementBase imp : imports) { PsiJavaCodeReferenceElement ref = imp.getImportReference(); if (ref == null) { continue; } SNode javaImport = SConceptOperations.createNewNode(MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x64c0181e603bcfL, "jetbrains.mps.baseLanguage.structure.JavaImport")); SPropertyOperations.set(javaImport, MetaAdapterFactory.getProperty(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x64c0181e603bcfL, 0x64c0181e603bd0L, "onDemand"), "" + (imp.isOnDemand())); SPropertyOperations.set(javaImport, MetaAdapterFactory.getProperty(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x64c0181e603bcfL, 0x4d5c30eb30af1572L, "static"), "" + (imp instanceof PsiImportStaticStatement)); String qName = ref.getQualifiedName(); SPropertyOperations.set(javaImport, MetaAdapterFactory.getProperty(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x5a98df4004080866L, 0x1996ec29712bdd92L, "tokens"), qName); ListSequence.fromList(SLinkOperations.getChildren(javaImports, MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x53f7c33f069862f2L, 0x64c0181e6020a7L, "entries"))).addElement(javaImport); } return javaImports; } public PsiElement getPsiSource(SNode node) { return MapSequence.fromMap(myGlobalMps2PsiMapping).get(node.getNodeId()); } private class MyMps2PsiMapper implements Consumer<Pair<SNode, PsiElement>> { @Override public void consume(Pair<SNode, PsiElement> pair) { SNode node = pair.o1; PsiElement element = pair.o2; MapSequence.fromMap(myGlobalMps2PsiMapping).put(node.getNodeId(), element); PsiFile file = element.getContainingFile(); Set<SNodeId> mapForFile = MapSequence.fromMap(myAllNodesPerFile).get(file.getName()); if (mapForFile == null) { mapForFile = SetSequence.fromSet(new HashSet<SNodeId>()); MapSequence.fromMap(myAllNodesPerFile).put(file.getName(), mapForFile); } SetSequence.fromSet(mapForFile).addElement(node.getNodeId()); } /*package*/ void clearFile(final SModel model, String fileName) { if (MapSequence.fromMap(myRootsPerFile).get(fileName) != null) { SetSequence.fromSet(MapSequence.fromMap(myRootsPerFile).get(fileName)).visitAll(new IVisitor<SNodeId>() { public void visit(SNodeId it) { SNode node = model.getNode(it); model.removeRootNode(node); } }); MapSequence.fromMap(myRootsPerFile).removeKey(fileName); } Set<SNodeId> mapForFile = MapSequence.fromMap(myAllNodesPerFile).get(fileName); if (mapForFile == null) { return; } MapSequence.fromMap(myAllNodesPerFile).removeKey(fileName); for (SNodeId node : mapForFile) { MapSequence.fromMap(myGlobalMps2PsiMapping).removeKey(node); } } } }