/*
* 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;
import jetbrains.mps.generator.GeneratorTask.Factory;
import jetbrains.mps.smodel.SModelOperations;
import jetbrains.mps.util.GraphUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.model.SModelReference;
import org.jetbrains.mps.openapi.module.SModule;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Produce sequence of {@link GeneratorTask} from collection of {@link SModel} according to rules Generator used to follow
* to date, namely models are groped by module, these groups are processed in 'first noticed' order, models within a group are
* processed according to topological order based on their dependencies.
* @author Artem Tikhomirov
* @since 3.4
*/
public class DefaultTaskBuilder<T extends GeneratorTask> {
private final Map<SModule, List<SModel>> myModuleSequence = new LinkedHashMap<SModule, List<SModel>>();
private final Factory<T> myFactory;
public DefaultTaskBuilder(@NotNull Factory<T> factory) {
myFactory = factory;
}
public List<T> getResult() {
ArrayList<T> rv = new ArrayList<T>();
for (List<SModel> step : myModuleSequence.values()) {
for (SModel m : topoOrder(step)) {
rv.add(myFactory.create(m));
}
}
return rv;
}
public void addAll(Collection<? extends SModel> inputModels) {
for (SModel inputModel : inputModels) {
SModule module = inputModel.getModule();
List<SModel> models = myModuleSequence.get(module);
if (models == null) {
myModuleSequence.put(module, models = new ArrayList<SModel>(5));
}
models.add(inputModel);
}
}
private static List<SModel> topoOrder(List<SModel> inputModels) {
int[][] graph = new int[inputModels.size()][];
HashMap<SModelReference, Integer> vertex2Index = new HashMap<SModelReference, Integer>(graph.length * 2);
HashMap<SModelReference, SModel> vertex2InputModel = new HashMap<SModelReference, SModel>(graph.length * 2);
for (int i = 0; i < graph.length; i++) {
final SModel inputModel = inputModels.get(i);
final SModelReference ref = inputModel.getReference();
vertex2Index.put(ref, i);
vertex2InputModel.put(ref, inputModel);
}
int[] tmp = new int[graph.length];
for (int i = 0, x = inputModels.size(); i < x; i++) {
SModel inputModel = inputModels.get(i);
int j = 0;
for (SModelReference ie : SModelOperations.getImportedModelUIDs(inputModel)) {
if (!vertex2Index.containsKey(ie)) {
continue;
}
tmp[j++] = vertex2Index.get(ie);
}
graph[i] = new int[j];
System.arraycopy(tmp, 0, graph[i], 0, j);
}
final int[][] strongComponents = GraphUtil.tarjan(graph);
List<SModelReference[]> components = new ArrayList<SModelReference[]>(strongComponents.length);
for (int i = 0; i < strongComponents.length; i++) {
SModelReference[] x = new SModelReference[strongComponents[i].length];
for (int j = 0; j < x.length; j++) {
final int vertex = strongComponents[i][j];
x[j] = inputModels.get(vertex).getReference();
}
components.add(x);
}
// flatten components into plain list
ArrayList<SModel> rv = new ArrayList<SModel>(inputModels.size());
for (SModelReference[] mrs : components) {
for (SModelReference mr : mrs) {
assert vertex2InputModel.containsKey(mr);
rv.add(vertex2InputModel.get(mr));
}
}
return rv;
}
}