/* * 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.generator.impl.interpreted; import jetbrains.mps.generator.impl.plan.ModelScanner; import jetbrains.mps.generator.runtime.TemplateMappingPriorityRule; import jetbrains.mps.generator.runtime.TemplateModel; import jetbrains.mps.generator.runtime.TemplateModule; import jetbrains.mps.generator.runtime.TemplateModuleBase; import jetbrains.mps.project.structure.modules.mappingpriorities.MappingConfig_AbstractRef; import jetbrains.mps.project.structure.modules.mappingpriorities.MappingConfig_ExternalRef; import jetbrains.mps.project.structure.modules.mappingpriorities.MappingConfig_RefSet; import jetbrains.mps.project.structure.modules.mappingpriorities.MappingConfig_SimpleRef; import jetbrains.mps.project.structure.modules.mappingpriorities.MappingPriorityRule; import jetbrains.mps.smodel.Generator; import jetbrains.mps.smodel.SNodePointer; import jetbrains.mps.smodel.language.GeneratorRuntime; import jetbrains.mps.smodel.language.LanguageRegistry; import jetbrains.mps.smodel.language.LanguageRuntime; import jetbrains.mps.util.Pair; import org.jetbrains.annotations.NotNull; import org.jetbrains.mps.openapi.language.SLanguage; import org.jetbrains.mps.openapi.model.SModel; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.module.SDependency; import org.jetbrains.mps.openapi.module.SDependencyScope; import org.jetbrains.mps.openapi.module.SModule; import org.jetbrains.mps.openapi.module.SModuleReference; import org.jetbrains.mps.openapi.module.SRepository; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * Lifecycle of the module is not quite well defined now, it's assumed instances are not kept too long * for us to care about model changes (i.e. addition of a new template model or change in used languages) * evgeny, 3/10/11 */ public class TemplateModuleInterpreted extends TemplateModuleBase { private final LanguageRuntime sourceLanguage; private final Generator generator; private Collection<TemplateModel> myModels; private Collection<SLanguage> myTargetLanguages; public TemplateModuleInterpreted(LanguageRuntime sourceLanguage, @NotNull Generator generator) { this.sourceLanguage = sourceLanguage; this.generator = generator; } @NotNull @Override public LanguageRuntime getSourceLanguage() { return sourceLanguage; } @NotNull @Override public SModuleReference getModuleReference() { return generator.getModuleReference(); } @Override public List<TemplateMappingPriorityRule> getPriorities() { return generator.getModuleDescriptor().getPriorityRules().stream().map(this::fixup).collect(Collectors.toList()); } /** * We need to translate 'design' MPR into 'deployed' MPR (we need different behavior in RT and DT, alas, we use the same class). * Here we populate simple reference with actual MC name not to depend from SRepository (see {@link MappingConfig_AbstractRef#asString(SRepository)}. * FIXME I don't really like this approach as we compute values we are unlikely to need. Proper solution is to get rid of TemplateModuleInterpreted altogether. */ private MappingPriorityRule fixup(MappingPriorityRule r) { MappingPriorityRule rv = r.copy(); fixup(rv.getLeft()); fixup(rv.getRight()); return rv; } // somewhat similar to GenerationPartitioner.RuleHelper // Perhaps, I shall introduce a visitor, at last (MPR.acceptLeft(Visitor), Visitor.allOfGenerator(SModuleReference) // Note, it's odd to have MC_ExternalRef as long as SimpleRef bears complete SModelReference private void fixup(MappingConfig_AbstractRef mcRef) { if (mcRef instanceof MappingConfig_SimpleRef) { MappingConfig_SimpleRef r = (MappingConfig_SimpleRef) mcRef; if (!r.isIncomplete() && !r.includesAll()) { SNode mc = new SNodePointer(r.getModelUID(), r.getNodeID()).resolve(generator.getRepository()); if (mc != null) { r.setMapConfigName(mc.getName()); } } } else if (mcRef instanceof MappingConfig_ExternalRef) { fixup(((MappingConfig_ExternalRef) mcRef).getMappingConfig()); } else if (mcRef instanceof MappingConfig_RefSet) { ((MappingConfig_RefSet) mcRef).getMappingConfigs().forEach(this::fixup); } // don't care about others. } @Override public Collection<TemplateModel> getModels() { if (myModels != null) { return myModels; } synchronized (this) { if (myModels != null) { return myModels; } ArrayList<TemplateModelInterpreted> rv = new ArrayList<>(); for (SModel m : generator.getOwnTemplateModels()) { rv.add(new TemplateModelInterpreted(this, m)); } myModels = Arrays.asList(rv.toArray(new TemplateModelInterpreted[rv.size()])); } return myModels; } @Override public String getAlias() { return generator.getAlias(); } @Override public Collection<TemplateModule> getExtendedGenerators() { List<TemplateModule> result = new ArrayList<>(2); for (Pair<SDependencyScope, TemplateModule> p : getReferencedGenerators()) { if (p.o1 == SDependencyScope.EXTENDS) { result.add(p.o2); } } return result; } @Override public Collection<TemplateModule> getEmployedGenerators() { List<TemplateModule> result = new ArrayList<>(4); for (Pair<SDependencyScope, TemplateModule> p : getReferencedGenerators()) { if (p.o1 == SDependencyScope.DEFAULT) { result.add(p.o2); } } return result; } @Override public Set<SLanguage> getQueryLanguages() { return super.getQueryLanguages(); } @Override public Collection<SLanguage> getTargetLanguages() { if (myTargetLanguages == null) { ModelScanner ms = new ModelScanner(); for (SModel m : generator.getOwnTemplateModels()) { ms.scan(m); } // I don't care if I calculate languages twice, hence no synchronization. Most of the time languages are queried // from single thread anyway, and the primary idea for the caching is to improve performance of subsequent query. myTargetLanguages = ms.getTargetLanguages(); } return myTargetLanguages; } @Override public Class<?> loadClass(String qualifiedName) throws ClassNotFoundException { return generator.getOwnClass(qualifiedName); } private Collection<Pair<SDependencyScope, TemplateModule>> getReferencedGenerators() { List<Pair<SDependencyScope, TemplateModule>> result = new ArrayList<>(5); for (SDependency dep : generator.getDeclaredDependencies()) { SModule referencedGenerator = dep.getTarget(); if (referencedGenerator instanceof Generator) { GeneratorRuntime grt = LanguageRegistry.getInstance(generator.getRepository()).getGenerator((Generator) referencedGenerator); if (grt instanceof TemplateModule) { result.add(new Pair<>(dep.getScope(), (TemplateModule) grt)); } } } return result; } }