/*
* 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.plan;
import jetbrains.mps.generator.ModelGenerationPlan;
import jetbrains.mps.generator.impl.plan.PriorityConflicts.Kind;
import jetbrains.mps.generator.runtime.TemplateMappingConfiguration;
import jetbrains.mps.generator.runtime.TemplateModel;
import jetbrains.mps.generator.runtime.TemplateModule;
import jetbrains.mps.util.NameUtil;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.language.SLanguage;
import org.jetbrains.mps.openapi.model.SModel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* Default/regular/legacy plan to generate a model based solely on a languaes
*
* To get extra information about picked generators, update bin/log.xml like that:
* <pre>
* <category name="jetbrains.mps.generator.impl.plan" additivity="false">
* <priority value="DEBUG"/>
* <appender-ref ref="CONSOLE-DEBUG"/>
* </category>
* </pre>
* @author Evgeny Gryaznov, Jan 18, 2010
* @author Artem Tikhomirov
*/
public class GenerationPlan implements ModelGenerationPlan {
private static final Logger LOG = LogManager.getLogger(GenerationPlan.class);
private final Collection<TemplateModule> myGenerators;
private final Collection<TemplateModel> myTemplateModels;
private final List<List<TemplateMappingConfiguration>> myPlan;
private List<Step> mySteps;
private final PriorityConflicts myConflictingPriorityRules;
public GenerationPlan(@NotNull SModel inputModel) {
this(inputModel, null);
}
public GenerationPlan(@NotNull SModel inputModel, @Nullable Collection<SLanguage> additionalLanguages) {
try {
EngagedGeneratorCollector c = new EngagedGeneratorCollector(inputModel, additionalLanguages);
myGenerators = c.getGenerators();
if (LOG.isDebugEnabled()) {
LOG.debug(">>>");
c.dump(LOG::debug);
LOG.debug("<<<");
}
GenerationPartitioner partitioner = new GenerationPartitioner(c.getGenerators());
myPlan = partitioner.createMappingSets();
if (myPlan.isEmpty()) {
myPlan.add(Collections.<TemplateMappingConfiguration>emptyList());
}
myConflictingPriorityRules = partitioner.getConflictingPriorityRules();
myTemplateModels = new ArrayList<TemplateModel>();
for (TemplateModule module : myGenerators) {
myTemplateModels.addAll(module.getModels());
}
} catch (Throwable t) {
String msg = String.format("Couldn't compute generation steps for model '%s;", NameUtil.getModelLongName(inputModel));
LOG.error(msg, t);
throw new RuntimeException(msg, t);
}
}
@Override
public List<Step> getSteps() {
if (mySteps == null) {
LinkedList<Step> steps = new LinkedList<Step>();
for (List<TemplateMappingConfiguration> p : myPlan) {
steps.add(new Transform(p));
}
// debug
// steps.add(myPlan.size() / 3, new Checkpoint("first"));
// steps.add(myPlan.size() / 3 * 2, new Checkpoint("second"));
mySteps = Arrays.asList(steps.toArray(new Step[steps.size()]));
}
return mySteps;
}
public Collection<TemplateModule> getGenerators() {
return myGenerators;
}
@Override
public boolean coversLanguage(SLanguage language) {
// return myLanguages.contains(language);
//
// disable checking temporarily:
// when generating model jetbrains.mps.baseLanguage.closures.dataFlow,
// type SetType (from collections lang) appears at some moment inside InternalStaticMethodCall node.
// While language 'jetbrains.mps.baseLanguage.collections' wasn't detected when computing generation steps,
// this is harmless for generation (because no text is generated for that node)
// but it sets off the alarms in generator.
//
// todo: postpone the error reporting till text generation phase?
// or
// todo: in a very base text_gen class catch nodes which was not reduced (and has no text_gen) ?
return true;
}
public boolean hasIgnoredPriorityRules() {
return !myConflictingPriorityRules.get(Kind.Invalid).isEmpty();
}
public List<Conflict> getIgnoredPriorityRules() {
return new ArrayList<Conflict>(myConflictingPriorityRules.get(Kind.Invalid));
}
public boolean hasConflictingPriorityRules() {
return myConflictingPriorityRules.hasConflicts(deemedConflict());
}
public List<Conflict> getConflicts() {
return myConflictingPriorityRules.getConflicts(deemedConflict());
}
private static Collection<Kind> deemedConflict() {
ArrayList<Kind> deemedConflict = new ArrayList<Kind>(Arrays.asList(Kind.values()));
deemedConflict.remove(Kind.Invalid);
return deemedConflict;
}
}