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);
}
}
}
}