/*
* Copyright 2003-2015 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.cache;
import jetbrains.mps.extapi.model.GeneratableSModel;
import jetbrains.mps.generator.GenerationCacheContainer;
import jetbrains.mps.generator.GenerationCacheContainer.ModelCacheContainer;
import jetbrains.mps.generator.IncrementalGenerationStrategy;
import jetbrains.mps.generator.impl.GenerationFailureException;
import jetbrains.mps.generator.impl.IncrementalGenerationHandler.IncrementalReporter;
import jetbrains.mps.generator.impl.TemplateGenerator;
import jetbrains.mps.generator.impl.dependencies.DependenciesBuilder;
import jetbrains.mps.generator.impl.dependencies.GenerationDependencies;
import jetbrains.mps.generator.impl.plan.PlanSignature;
import jetbrains.mps.util.DifflibFacade;
import jetbrains.mps.util.performance.IPerformanceTracer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.model.SModelReference;
import java.util.Map;
/**
* Wraps access to optional IncrementalModelCache, facility to store/retrieve cached step models
* @author Artem Tikhomirov
*/
public class IntermediateCacheHelper {
private final IncrementalGenerationStrategy myIncrementalStrategy;
private final PlanSignature myPlanSignature;
private final IPerformanceTracer myPerfTrace;
private IntermediateModelsCache myCache;
public IntermediateCacheHelper(@NotNull IncrementalGenerationStrategy incrementalStrategy, @NotNull PlanSignature genPlanSignature, @NotNull IPerformanceTracer perfTrace) {
myIncrementalStrategy = incrementalStrategy;
myPlanSignature = genPlanSignature;
myPerfTrace = perfTrace;
}
public boolean hasCache() {
return myCache != null;
}
/**
* Initialize new cache container for the model
*/
public void createNew(@NotNull SModel originalInput) {
myCache = null;
GenerationCacheContainer incrementalCacheContainer = myIncrementalStrategy.getContainer();
if (incrementalCacheContainer == null) {
return;
}
final Map<String, String> generationHashes = myIncrementalStrategy.getModelHashes(originalInput, null);
if (generationHashes == null) {
return;
}
String currentHash = generationHashes.get(GeneratableSModel.FILE);
ModelCacheContainer cacheContainer = incrementalCacheContainer.getCache(originalInput, currentHash, true);
if (cacheContainer == null) {
return;
}
myCache = new IntermediateModelsCache(cacheContainer, myPlanSignature.getSignature());
}
/**
* Load cache container for the model
*/
public void loadExisting(@NotNull SModel model, @Nullable IncrementalReporter reporter) {
myCache = null;
GenerationDependencies dependencies = myIncrementalStrategy.getDependencies(model);
if (dependencies == null || !dependencies.isContainsIncrementalInfo()) {
return;
}
GenerationCacheContainer incrementalCacheContainer = myIncrementalStrategy.getContainer();
if (incrementalCacheContainer == null) {
if (reporter != null) {
reporter.report("No container for incremental caches.");
}
return;
}
String oldHash = dependencies.getModelHash();
ModelCacheContainer cacheContainer = incrementalCacheContainer.getCache(model, oldHash, false);
if (cacheContainer == null) {
if (reporter != null) {
reporter.report("No cache for " + model.getReference().toString() + " (" + oldHash + ")");
}
return;
}
IntermediateModelsCache c = IntermediateModelsCache.load(cacheContainer);
if (c != null && myPlanSignature.equals(c.getSignature())) {
myCache = c;
} else if (reporter != null) {
if (c == null) {
reporter.report("Caches are corrupted for " + oldHash);
} else {
reporter.report("Plan differs:");
for (String s : DifflibFacade.getSimpleDiff(c.getSignature(), myPlanSignature.getSignature())) {
reporter.report(s);
}
}
}
}
/**
* Record single step into model's cache container
*/
// XXX TemplateGenerator has DependenciesBuilder, use it from there
public void store(int majorStep, int minorStep, TemplateGenerator tg, DependenciesBuilder dependencyBuilder) throws GenerationFailureException {
if (myCache == null) {
return;
}
myPerfTrace.push("Save intermediate cache", false);
final TransientModelWithMetainfo model = dependencyBuilder.create(tg.getOutputModel(), tg.getMappings());
myCache.store(majorStep, minorStep, model);
myPerfTrace.pop();
}
/**
* Extract step data from model's cache container
*/
@Nullable
public TransientModelWithMetainfo retrieve(int majorStep, int minorStep, @NotNull SModelReference modelReference) {
if (myCache == null) {
return null;
}
// TODO if(myMinorStep >= stepCount) copy from current input model
int stepsCount = myCache.getMinorCount(majorStep);
myPerfTrace.push("Load intermediate cache", false);
final TransientModelWithMetainfo rv = myCache.load(majorStep, minorStep >= stepsCount ? stepsCount - 1 : minorStep, modelReference);
myPerfTrace.pop();
return rv;
}
public boolean isStepCovered(int majorStep, int minorStep) {
return myCache != null && minorStep < myCache.getMinorCount(majorStep);
}
public void commit() {
if (myCache == null) {
return;
}
myPerfTrace.push("Save intermediate cache", false);
myCache.store();
myPerfTrace.pop();
}
public void discard() {
if (myCache != null) {
myCache.remove();
}
}
}