/* * Copyright 2003-2017 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.smodel; import jetbrains.mps.module.ReloadableModuleBase; import jetbrains.mps.module.SDependencyImpl; import jetbrains.mps.project.ModelsAutoImportsManager; import jetbrains.mps.project.ModelsAutoImportsManager.AutoImportsContributor; import jetbrains.mps.project.ModuleId; import jetbrains.mps.project.structure.modules.GeneratorDescriptor; import jetbrains.mps.project.structure.modules.LanguageDescriptor; import jetbrains.mps.project.structure.modules.ModuleDescriptor; import jetbrains.mps.project.structure.modules.mappingpriorities.MappingConfig_AbstractRef; import jetbrains.mps.project.structure.modules.mappingpriorities.MappingPriorityRule; import jetbrains.mps.util.IterableUtil; import jetbrains.mps.vfs.IFile; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.mps.openapi.language.SLanguage; import org.jetbrains.mps.openapi.model.SModel; import org.jetbrains.mps.openapi.module.SDependency; import org.jetbrains.mps.openapi.module.SDependencyScope; import org.jetbrains.mps.openapi.module.SModuleReference; import org.jetbrains.mps.openapi.module.SRepository; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; public class Generator extends ReloadableModuleBase { public static final Logger LOG = LogManager.getLogger(Generator.class); static { ModelsAutoImportsManager.registerContributor(new GeneratorModelsAutoImports()); } @NotNull private Language mySourceLanguage; private GeneratorDescriptor myGeneratorDescriptor; public Generator(@NotNull Language sourceLanguage, GeneratorDescriptor generatorDescriptor) { super(sourceLanguage.getFileSystem()); mySourceLanguage = sourceLanguage; initGeneratorDescriptor(generatorDescriptor); } //models will be named like xxx.modelName, where xxx is a part of newName before sharp symbol @Override public void rename(@NotNull String newName) { int sharp = newName.indexOf("#"); newName = sharp < 0 ? newName : newName.substring(sharp); renameModels(getSourceLanguage().getModuleName(), newName, false); //see MPS-18743, need to save before setting descriptor getRepository().saveAll(); // MPS-22787 - update generator id String uid = myGeneratorDescriptor.getNamespace(); int sharpIndex = uid.indexOf('#'); myGeneratorDescriptor.setNamespace(newName + "#" + uid.substring(sharpIndex + 1)); // FIXME why there's no fireModuleRenamed() as in super.rename()??? } @Override public boolean isPackaged() { return getSourceLanguage().isPackaged(); } public List<SModel> getOwnTemplateModels() { List<SModel> templateModels = new ArrayList<>(); for (SModel modelDescriptor : getModels()) { if (SModelStereotype.isGeneratorModel(modelDescriptor)) { templateModels.add(modelDescriptor); } } return templateModels; } @NotNull @Override public GeneratorDescriptor getModuleDescriptor() { return myGeneratorDescriptor; } @Override public IFile getModuleSourceDir() { return mySourceLanguage.getModuleSourceDir(); } @Override public IFile getDescriptorFile() { return null; } @Override protected void doSetModuleDescriptor(ModuleDescriptor moduleDescriptor) { assert moduleDescriptor instanceof GeneratorDescriptor; LanguageDescriptor languageDescriptor = getSourceLanguage().getModuleDescriptor(); int index = languageDescriptor.getGenerators().indexOf(getModuleDescriptor()); languageDescriptor.getGenerators().remove(index); languageDescriptor.getGenerators().add(index, (GeneratorDescriptor) moduleDescriptor); getSourceLanguage().setModuleDescriptor(languageDescriptor); } public String getAlias() { String name = myGeneratorDescriptor.getAlias(); return getSourceLanguage().getModuleName() + "/" + (name == null ? "<no name>" : name); } public static String generateGeneratorUID(Language sourceLanguage) { return sourceLanguage.getModuleName() + "#" + jetbrains.mps.smodel.SModel.generateUniqueId(); } public Language getSourceLanguage() { return mySourceLanguage; } /** * @return <code>true</code> if templates for this generator should be generated into Java code instead of being interpreted at runtime */ public boolean generateTemplates() { return myGeneratorDescriptor.isGenerateTemplates(); } public String toString() { return getAlias() + " [generator]"; } /** * fixme why generator saves language?? * generator is contained in language it must be the other way around! */ @Override public void save() { super.save(); mySourceLanguage.save(); } @Override public Iterable<SDependency> getDeclaredDependencies() { HashSet<SDependency> rv = new HashSet<>(IterableUtil.asCollection(super.getDeclaredDependencies())); final SRepository repo = getRepository(); // generator sees its source language rv.add(new SDependencyImpl(mySourceLanguage.getModuleReference(), repo, SDependencyScope.DEFAULT, false)); for (SModuleReference rt : mySourceLanguage.getRuntimeModulesReferences()) { rv.add(new SDependencyImpl(rt, repo, SDependencyScope.RUNTIME, false)); } // generator sees all dependent generators as non-reexport for (SModuleReference refGenerator : getReferencedGeneratorUIDs()) { // XXX not sure it's right to resolve modules through global repository if this module is not attached anywhere // FIXME all referenced generators are of 'extends' dependency at the moment // but this might need a change once we store extended generators as a regular SDependency // instead of hacky getReferencedGeneratorUIDs rv.add(new SDependencyImpl(refGenerator, repo, SDependencyScope.EXTENDS, false)); } return rv; } public List<SModuleReference> getReferencedGeneratorUIDs() { return new ArrayList<>(myGeneratorDescriptor.getDepGenerators()); } public boolean deleteReferenceFromPriorities(org.jetbrains.mps.openapi.model.SModelReference ref) { boolean[] descriptorChanged = new boolean[]{false}; Iterator<MappingPriorityRule> it = myGeneratorDescriptor.getPriorityRules().iterator(); while (it.hasNext()) { MappingPriorityRule rule = it.next(); MappingConfig_AbstractRef right = rule.getRight(); MappingConfig_AbstractRef left = rule.getLeft(); if (right.removeModelReference(ref, descriptorChanged) || left.removeModelReference(ref, descriptorChanged)) { it.remove(); } } return descriptorChanged[0]; } /** * Internal method, used from the process of re-validating generators from the language module. * * We cannot call Generator.setModuleDescriptor() method from there because it is implemented to call * Language.setModuleDescriptor() starting generators re-validation process. * * This method can be removed if we separate generator module persistence from the language module persistence. * */ final void updateGeneratorDescriptor(GeneratorDescriptor generatorDescriptor) { initGeneratorDescriptor(generatorDescriptor); reloadAfterDescriptorChange(); } private void initGeneratorDescriptor(GeneratorDescriptor generatorDescriptor) { myGeneratorDescriptor = generatorDescriptor; String uid = myGeneratorDescriptor.getNamespace(); if (uid == null) { // FIXME is missing namespace/uuid a valid scenario? If yes, why no alias init then? myGeneratorDescriptor.setNamespace(generateGeneratorUID(mySourceLanguage)); } ModuleId uuid = myGeneratorDescriptor.getId(); if (uuid == null) { uuid = ModuleId.regular(); myGeneratorDescriptor.setId(uuid); } setModuleReference(myGeneratorDescriptor.getModuleReference()); } private static class GeneratorModelsAutoImports extends AutoImportsContributor<Generator> { @NotNull @Override public Class<Generator> getApplicableSModuleClass() { return Generator.class; } @Override public Set<SModel> getAutoImportedModels(Generator contextGenerator, SModel model) { // likely, one needs to reference concepts of the source language: if (SModelStereotype.isGeneratorModel(model)) { // FIXME MM, please tell me what to use instead! SModuleOperations.getAspect(SModule, "structure") isn't nice alternative for hand-written code. SModel structureAspect = LanguageAspect.STRUCTURE.get(contextGenerator.getSourceLanguage()); if (structureAspect != null) { // XXX when source language used to be 'used language', we've imported all extended languages as well. Shall we // import structures of extended language modules here as well? return Collections.singleton(structureAspect); } } return Collections.emptySet(); } @NotNull @Override public Collection<SLanguage> getLanguages(Generator contextGenerator, SModel model) { // languages we are going to write templates at are not known at this moment, // generator languages are imported with dedicated templates devkit return Collections.emptySet(); } @Override public Collection<SModuleReference> getDevKits(Generator contextModule, SModel forModel) { return Collections.singleton(BootstrapLanguages.getGeneratorTemplatesDevKit()); } } @Override public ClassLoader getRootClassLoader() { return mySourceLanguage.getRootClassLoader(); } }