package jetbrains.mps.project.structure;
/*Generated by MPS */
import jetbrains.mps.project.AbstractModule;
import jetbrains.mps.components.CoreComponent;
import java.util.Map;
import org.jetbrains.mps.openapi.model.SModelId;
import java.util.concurrent.ConcurrentHashMap;
import jetbrains.mps.smodel.MPSModuleOwner;
import jetbrains.mps.smodel.BaseMPSModuleOwner;
import jetbrains.mps.extapi.module.SRepositoryExt;
import org.jetbrains.mps.openapi.module.SModuleListener;
import org.jetbrains.mps.openapi.module.SModuleListenerBase;
import org.jetbrains.mps.openapi.module.SModule;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.model.SModelReference;
import org.jetbrains.mps.openapi.module.SRepositoryListener;
import org.jetbrains.mps.openapi.module.SRepositoryListenerBase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.module.SRepository;
import jetbrains.mps.util.annotation.ToRemove;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.persistence.PersistenceFacade;
import jetbrains.mps.project.Solution;
import jetbrains.mps.smodel.Language;
import jetbrains.mps.project.DevKit;
import java.util.List;
import java.util.Set;
import org.jetbrains.mps.openapi.language.SLanguage;
import java.util.Collections;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import jetbrains.mps.extapi.model.SModelBase;
import org.jetbrains.mps.openapi.module.SModuleId;
import jetbrains.mps.smodel.SModelStereotype;
import java.util.ArrayList;
import jetbrains.mps.smodel.RegularModelDescriptor;
import org.jetbrains.mps.openapi.persistence.NullDataSource;
import jetbrains.mps.smodel.ModelLoadResult;
import jetbrains.mps.smodel.SnapshotModelData;
import jetbrains.mps.smodel.nodeidmap.ForeignNodeIdMap;
import jetbrains.mps.project.structure.modules.ModuleDescriptor;
import jetbrains.mps.vfs.IFile;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.project.structure.stub.ProjectStructureBuilder;
import jetbrains.mps.smodel.Generator;
import jetbrains.mps.util.SModuleNameComparator;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SLinkOperations;
import jetbrains.mps.smodel.loading.ModelLoadingState;
public class ProjectStructureModule extends AbstractModule implements CoreComponent {
private static final String MODULE_REF = "642f71f8-327a-425b-84f9-44ad58786d27(jetbrains.mps.lang.project.modules)";
private Map<SModelId, ProjectStructureModule.ProjectStructureSModelDescriptor> myModels = new ConcurrentHashMap<SModelId, ProjectStructureModule.ProjectStructureSModelDescriptor>();
private static ProjectStructureModule INSTANCE;
private final MPSModuleOwner myOwner = new BaseMPSModuleOwner();
private final SRepositoryExt myRepository;
private final SModuleListener myModuleListener = new SModuleListenerBase() {
@Override
public void modelAdded(SModule module, SModel model) {
refreshModule(module, false);
}
@Override
public void modelRemoved(SModule module, SModelReference reference) {
refreshModule(module, false);
}
@Override
public void moduleChanged(SModule module) {
refreshModule(module, false);
}
};
private final SRepositoryListener myListener = new SRepositoryListenerBase() {
@Override
public void moduleAdded(@NotNull SModule module) {
refreshModule(module, false);
module.addModuleListener(myModuleListener);
}
@Override
public void beforeModuleRemoved(@NotNull SModule module) {
module.removeModuleListener(myModuleListener);
refreshModule(module, true);
}
};
/**
*
* @deprecated use {@link jetbrains.mps.project.structure.ProjectStructureModule#getInstance(SRepository) } instead
*/
@Deprecated
@ToRemove(version = 3.3)
public static ProjectStructureModule getInstance() {
return INSTANCE;
}
/**
* There's single ProjectStructureModule per project, thus if you use Project.getRepository(), you are guaranteed to get an instance.
*/
@Nullable
public static ProjectStructureModule getInstance(@NotNull SRepository repo) {
// FIXME likely, shall do it with myModuleRef.resolve(mpsProject.getRepository)
// Generally, I'd prefer plain SModule as return value, however exact instance of the class are needed to access #getModelByModule.
// The only reason to have a helper access method here is to hide module reference we use (so that client code shall not keep MODULE_REF).
return getInstance();
}
public ProjectStructureModule(@NotNull SRepositoryExt repository, @NotNull PersistenceFacade persistenceFacade) {
myRepository = repository;
setModuleReference(persistenceFacade.createModuleReference(MODULE_REF));
}
private void refreshModule(SModule module, boolean isDeleted) {
assertCanChange();
if (!((module instanceof Solution || module instanceof Language || module instanceof DevKit))) {
return;
}
SModelReference ref = getSModelReference(module);
if (isDeleted) {
ProjectStructureModule.ProjectStructureSModelDescriptor descriptor = myModels.get(ref.getModelId());
if (descriptor != null) {
removeModel(descriptor);
}
} else
if (myModels.containsKey(ref.getModelId())) {
ProjectStructureModule.ProjectStructureSModelDescriptor descriptor = myModels.get(ref.getModelId());
descriptor.originalModuleChanged();
} else {
createModel(module);
}
}
public SModel getModelByModule(SModule module) {
myRepository.getModelAccess().checkReadAccess();
if (module == null) {
return null;
}
SModelReference ref = getSModelReference(module);
ProjectStructureModule.ProjectStructureSModelDescriptor descriptor = myModels.get(ref.getModelId());
return (descriptor == null ? null : descriptor);
}
@Override
public void init() {
if (INSTANCE != null) {
throw new IllegalStateException("double initialization");
}
INSTANCE = this;
myRepository.addRepositoryListener(myListener);
myRepository.getModelAccess().runWriteAction(new Runnable() {
@Override
public void run() {
myRepository.registerModule(ProjectStructureModule.this, myOwner);
}
});
}
@Override
public void dispose() {
// it is disposed as CoreComponent
if (INSTANCE == null) {
return;
}
INSTANCE = null;
clearAll();
myRepository.getModelAccess().runWriteAction(new Runnable() {
@Override
public void run() {
myRepository.unregisterModule(ProjectStructureModule.this, myOwner);
}
});
myRepository.removeRepositoryListener(myListener);
}
public void clearAll() {
myRepository.getModelAccess().runWriteAction(new Runnable() {
@Override
public void run() {
removeAll();
dependenciesChanged();
myModels.clear();
}
});
}
private void removeAll() {
List<SModel> models = getProjectStructureModels();
for (SModel model : models) {
removeModel(model);
}
}
@Override
public Set<SLanguage> getUsedLanguages() {
return Collections.singleton(MetaAdapterFactory.getLanguage(0x86ef829012bb4ca7L, 0x947f093788f263a9L, "jetbrains.mps.lang.project"));
}
private void removeModel(SModel md) {
if (myModels.remove(md.getReference().getModelId()) != null) {
md.unload();
unregisterModel((SModelBase) md);
}
}
public ProjectStructureModule.ProjectStructureSModelDescriptor createModel(SModule module) {
ProjectStructureModule.ProjectStructureSModelDescriptor result = new ProjectStructureModule.ProjectStructureSModelDescriptor(getSModelReference(module), module);
myModels.put(getSModelReference(module).getModelId(), result);
registerModel(result);
return result;
}
private SModelReference getSModelReference(SModule module) {
SModuleId moduleId = module.getModuleReference().getModuleId();
SModelId id = (moduleId != null ? jetbrains.mps.smodel.SModelId.foreign("project", moduleId.toString()) : null);
return new jetbrains.mps.smodel.SModelReference(this.getModuleReference(), id, "module." + module.getModuleName() + "@" + SModelStereotype.getStubStereotypeForId("project"));
}
public String toString() {
return getModuleName();
}
private List<SModel> getProjectStructureModels() {
return new ArrayList<SModel>(myModels.values());
}
@Override
protected void collectMandatoryFacetTypes(Set<String> set) {
// none
}
public class ProjectStructureSModelDescriptor extends RegularModelDescriptor {
private final SModule myModule;
private ProjectStructureSModelDescriptor(SModelReference ref, SModule module) {
super(ref, new NullDataSource());
myModule = module;
}
@Override
@NotNull
protected ModelLoadResult<jetbrains.mps.smodel.SModel> createModel() {
final SnapshotModelData modelData = new SnapshotModelData(getReference(), new ForeignNodeIdMap());
final ModuleDescriptor moduleDescriptor = ((AbstractModule) myModule).getModuleDescriptor();
final IFile file = ((AbstractModule) myModule).getDescriptorFile();
if (file != null && moduleDescriptor != null) {
if (myModule instanceof Language) {
SNode langNode = new ProjectStructureBuilder((AbstractModule) myModule, this).convertLanguage();
ArrayList<Generator> generators = new ArrayList<Generator>(((Language) myModule).getGenerators());
// I'd like to have predictable order in project model iteration, as well as generated code, that's why I sort here, not in templates.
Collections.sort(generators, new SModuleNameComparator());
for (Generator g : generators) {
ListSequence.fromList(SLinkOperations.getChildren(langNode, MetaAdapterFactory.getContainmentLink(0x86ef829012bb4ca7L, 0x947f093788f263a9L, 0x5869770da61dfe1fL, 0x5869770da61dfe37L, "generator"))).addElement(new ProjectStructureBuilder(g, this).convertGenerator());
}
modelData.addRootNode(langNode);
} else {
SNode root = new ProjectStructureBuilder((AbstractModule) myModule, this).convert();
modelData.addRootNode(root);
}
}
return new ModelLoadResult<jetbrains.mps.smodel.SModel>(modelData, ModelLoadingState.FULLY_LOADED);
}
/*package*/ void originalModuleChanged() {
jetbrains.mps.smodel.SModel oldModel = getCurrentModelInternal();
if (oldModel == null) {
return;
}
// since we know the module is still there (just has been changed), tell those not caring about unload
// that the content of the model is new (instead of a MLResult with null, could pass createModel() but see no reason
// to read module file unless needed)
replace(new ModelLoadResult<jetbrains.mps.smodel.SModel>(null, ModelLoadingState.NOT_LOADED));
}
}
}