/* * Copyright 2003-2016 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.dependencies; import jetbrains.mps.extapi.model.GeneratableSModel; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.model.SNodeReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Evgeny Gryaznov, May 14, 2010 */ public class GenerationDependencies { private static final int DEPENDENCIES_VERSION = 2; private static final String ROOT_ELEMENT = "dependencies"; private static final String NODE_ROOT = "source"; private static final String NODE_COMMON = "common"; private static final String ATTR_MODEL_HASH = "modelHash"; private static final String ATTR_PARAMS_HASH = "parametersHash"; private static final String ATTR_VERSION = "version"; private static final String NODE_MODEL = "dep"; private static final String ATTR_MODEL_ID = "model"; private static final String ATTR_HASH = "hash"; private final boolean containsIncrementalInfo; private final List<GenerationRootDependencies> myRootDependencies; private final Map<String, GenerationRootDependencies> myRootDependenciesMap; private final String myModelHash; private final String myParametersHash; private final Map<String, String> myUsedModelsHashes; private /* transient */ Map<String, String> myDependenciesTraces; private /* transient */ final List<GenerationRootDependencies> myUnchanged; private /* transient */ final int mySkippedCount; private /* transient */ final int myFromCacheCount; /*package*/ GenerationDependencies(String modelHash, String parametersHash) { this.containsIncrementalInfo = false; this.myModelHash = modelHash; this.myParametersHash = parametersHash; this.myRootDependencies = Collections.emptyList(); this.myRootDependenciesMap = Collections.emptyMap(); this.myUsedModelsHashes = Collections.emptyMap(); this.myUnchanged = Collections.emptyList(); this.mySkippedCount = 0; this.myFromCacheCount = 0; } /*package*/ GenerationDependencies(List<GenerationRootDependencies> data, String modelHash, String parametersHash, Map<String, String> externalHashes, List<GenerationRootDependencies> unchanged, int skippedCount, int fromCacheCount, Map<String, String> dependenciesTraces) { this.containsIncrementalInfo = true; this.myRootDependencies = data; this.mySkippedCount = skippedCount; this.myFromCacheCount = fromCacheCount; this.myRootDependenciesMap = new HashMap<String, GenerationRootDependencies>(data.size()); this.myModelHash = modelHash; this.myParametersHash = parametersHash; this.myUnchanged = unchanged; this.myUsedModelsHashes = externalHashes.isEmpty() ? Collections.<String, String>emptyMap() : externalHashes; for (GenerationRootDependencies rd : data) { String id = rd.getRootId(); myRootDependenciesMap.put(id == null ? GeneratableSModel.HEADER : id, rd); } this.myDependenciesTraces = dependenciesTraces; } public String getModelHash() { return myModelHash; } public String getParametersHash() { return myParametersHash; } public int getSkippedCount() { return mySkippedCount; } public int getFromCacheCount() { return myFromCacheCount; } public boolean isContainsIncrementalInfo() { return containsIncrementalInfo; } public GenerationRootDependencies getDependenciesFor(String rootId) { return myRootDependenciesMap.get(rootId); } public Map<String, String> getExternalHashes() { return Collections.unmodifiableMap(myUsedModelsHashes); } public List<GenerationRootDependencies> getRootDependencies() { return Collections.unmodifiableList(myRootDependencies); } public List<GenerationRootDependencies> getUnchangedDependencies() { return Collections.unmodifiableList(myUnchanged); } public String extractDependenciesTraces() { if (myDependenciesTraces == null) return null; StringBuilder sb = new StringBuilder(); String[] keys = myDependenciesTraces.keySet().toArray(new String[myDependenciesTraces.keySet().size()]); Arrays.sort(keys); for (String key : keys) { sb.append("-------------------------\n"); sb.append(key).append("\n"); String val = myDependenciesTraces.get(key); for (String s : val.split("\n")) { sb.append("\t\t").append(s).append("\n"); } sb.append("\n"); } myDependenciesTraces = null; return sb.toString(); } public void update(@Nullable SNodeReference root, @NotNull String fileName) { final GenerationRootDependencies rd; if (root == null) { rd = getDependenciesFor(GeneratableSModel.HEADER); } else { rd = getDependenciesFor(root.getNodeId().toString()); } if (rd != null) { rd.addGeneratedFile(fileName); } } public Element toXml() { Element root = new Element(ROOT_ELEMENT); root.setAttribute(ATTR_VERSION, Integer.toString(DEPENDENCIES_VERSION)); if (myModelHash != null) { root.setAttribute(ATTR_MODEL_HASH, myModelHash); } if (myParametersHash != null) { root.setAttribute(ATTR_PARAMS_HASH, myParametersHash); } String[] models = myUsedModelsHashes.keySet().toArray(new String[myUsedModelsHashes.size()]); Arrays.sort(models); for (String model : models) { Element e = new Element(NODE_MODEL); e.setAttribute(ATTR_MODEL_ID, model); String hash = myUsedModelsHashes.get(model); if (hash != null) { e.setAttribute(ATTR_HASH, hash); } root.addContent(e); } for (GenerationRootDependencies data : myRootDependencies) { Element e = new Element(data.getRootId() != null ? NODE_ROOT : NODE_COMMON); data.saveTo(e); root.addContent(e); } return root; } public static GenerationDependencies fromXml(Element root) { String version = GenerationRootDependencies.getValue(root, ATTR_VERSION); if (version == null || !version.equals(Integer.toString(DEPENDENCIES_VERSION))) { /* regenerate all */ return null; } // XXX Might be worth sharing intern with other reads/files, but this would take another refactoring round Intern intern = new Intern(); Map<String, String> externalHashes = new HashMap<String, String>(); for (Element e : root.getChildren(NODE_MODEL)) { String modelReference = GenerationRootDependencies.getValue(e, ATTR_MODEL_ID); String rootHash = GenerationRootDependencies.getValue(e, ATTR_HASH); if (modelReference != null) { externalHashes.put(intern.value(modelReference), rootHash); } } List<GenerationRootDependencies> data = new ArrayList<GenerationRootDependencies>(); for (Element e : root.getChildren(NODE_COMMON)) { data.add(GenerationRootDependencies.fromXml(e, true, intern)); } for (Element e : root.getChildren(NODE_ROOT)) { data.add(GenerationRootDependencies.fromXml(e, false, intern)); } String modelHash = GenerationRootDependencies.getValue(root, ATTR_MODEL_HASH); String paramsHash = GenerationRootDependencies.getValue(root, ATTR_PARAMS_HASH); if (externalHashes.isEmpty() && data.isEmpty()) { return new GenerationDependencies(modelHash, paramsHash); } return new GenerationDependencies(data, modelHash, paramsHash, externalHashes, Collections.<GenerationRootDependencies>emptyList(), 0, 0, null); } }