/* * 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; import jetbrains.mps.generator.plan.CheckpointIdentity; import jetbrains.mps.generator.plan.PlanIdentity; import jetbrains.mps.generator.runtime.TemplateMappingConfiguration; 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.SModule; import org.jetbrains.mps.openapi.module.SModuleReference; import java.util.Collection; /** * PROVISIONAL API * Capture supported statements of a plan declaration, translate them into {@link ModelGenerationPlan} instance suitable to get to * {@link jetbrains.mps.generator.GenerationOptions.OptionsBuilder#customPlan(SModel, ModelGenerationPlan)}. * <p/> * To use, build a plan with appropriate calls and obtain it with finishing {@link #wrapUp(PlanIdentity)}. * <p/> * Reflects present approach with 'interpreted' GPs. I.e. there's a code {@link GenPlanExtractor} that reads model with a plan and uses this * builder to create a plan instance. However, might not be handy once we switch to generated plan representation. We'll need some persisted * presentation of a plan (although could generate a plain Java code that invokes methods of this builder?) * <p/> * It's up to implementation to decide about builder instance re-use (i.e. what happens if new statements are added after {@link #wrapUp(PlanIdentity)}) * * * XXX it's not quite convenient to use varargs from smodel code (more suited for generated). Either introduce alternatives with collections or * consider intermediate step builder to fill one by one. * * FIXME API is inconsistent as we reference languages using deployment identity ({@link SLanguage}), while for generators there are {@link SModule modules}. * There's no {@code SGenerator} counterpart, and use of GeneratorRuntime would imply use of LanguageRuntime instead of SLanguage, so I don't see any * better alternative at the moment. * * @author Artem Tikhomirov * @since 2017.1 */ public interface GenerationPlanBuilder { /** * Apply generators of languages specified to reduce their concepts. * FIXME It's unspecified at the moment what happens with generators that are extended or referenced from those involved (i.e. if they are part of the step). * @param languages languages to reduce */ void transformLanguage(@NotNull SLanguage ... languages); /** * Specified generators (exact set, unlike {@link #applyGeneratorWithExtended(SModule...)} no extended relation between generators is taken into account) * applied as a single transformation step. * FIXME shall decide what happens if a generator references/extends another one, not mentioned. * @param generators generator modules */ void applyGenerator(@NotNull SModule ... generators); /** * Specified generators and those extending them AND visible from scope applied as a single transformation step. * What constitutes this 'scope' is up to plan builder implementation. * * To respect generator priority rules of involved generators to address extensibility scenarios like that of lang.editor, consider * {@link #applyGenerators(Collection, BuilderOption...)} with {@link BuilderOption#WithPriorityRules} and {@link BuilderOption#WithExtendedGenerators}. * @param generators generator modules */ void applyGeneratorWithExtended(@NotNull SModule ... generators); /** * New approach to plan builder. As there's SLanguage for deployed language, there's SModuleReference to identify deployed generator, why * would I need to get a module then? * @param generators deployed generator identities for the step * @param options optional set of options to further specify processing of {@code generators} set * @since 2017.2 */ void applyGenerators(@NotNull Collection<SModuleReference> generators, @NotNull BuilderOption ... options); /** * IMPORTANT: USE OF THIS METHOD IS DISCOURAGED AS IT AFFECTS CONSISTENCY OF PLAN SPECIFICATION (namely, if applyGeneratorWithExtended() shall consider * generators of explicit MCs for extensions). IT'S INTENDED FOR PROTOTYPE AND MIGHT CEASE ONCE PROVISION PHASE IS OVER. USE AT YOUR OWN RISK. * * Specific MCs for a transformation step, applied together. * @param tmc MCs that constitute transformation step */ void apply(@NotNull Collection<TemplateMappingConfiguration> tmc); /** * Tells generator there's a checkpoint identified certain way. * Generally, we don't need to do anything about this when building a plan, as declaration of a CP doesn't affect anything and is merely * an anchor {@linkplain #recordCheckpoint(CheckpointIdentity) record/persist} and {@linkplain #synchronizeWithCheckpoint(CheckpointIdentity) synchronize} * actions could reference. Nevertheless, builder has a chance to react to CP declaration if deemed necessary. * * @param cp checkpoint identity */ default void declareCheckpoint(@NotNull CheckpointIdentity cp) { // no-op by default, CheckpointIdentity here is just an anchor other cp methods could use } /** * Tells generator to record state of transformed model at the given moment and keep it with supplied identity. * Besides, also tells generator to synchronize external references with models of the specified checkpoint. * * @param cp checkpoint identity */ void recordCheckpoint(@NotNull CheckpointIdentity cp); /** * Tells generator to synchronize references with a recorded model state, identified by supplied checkpoint. * State of the actual model being transformed is not recorded. * @param cp checkpoint identity */ void synchronizeWithCheckpoint(@NotNull CheckpointIdentity cp); /** * Completes {@link ModelGenerationPlan} instance with any state information build is aware of (e.g. build extends relation between * generators for {@link #applyGeneratorWithExtended(SModule...) or respect priority rules of generators involved} * * @param planIdentity identity of the plan to build * @return plan instance ready to use */ @NotNull ModelGenerationPlan wrapUp(@NotNull PlanIdentity planIdentity); /** * options of {@link #applyGenerators(Collection, BuilderOption...)} * <p/> * {@link #WithExtendedGenerators} means not only explicitly specified generator shall take part in a transformation process, but other generators that * extend it (transitively) shall take part as well * <p/> * {@link #WithPriorityRules} means priority rules of involved generators (those explicitly specified and extending) are respected. */ enum BuilderOption { None, WithExtendedGenerators, WithPriorityRules; public boolean presentIn(BuilderOption... options) { for (BuilderOption o : options) { if (o == this) { return true; } } return false; } } }