/*
* Copyright 2003-2014 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.runtime.TemplateMappingConfiguration;
import jetbrains.mps.generator.runtime.TemplateModule;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
/**
* Describes set of mapping configurations to apply at consecutive steps. The phase may constitute one or more generation steps.
* Configurations within single phase have no strict dependency between each other (other than 'strictly together' within individual groups).
* @author Artem Tikhomirov
*/
class GenerationPhase {
private final Collection<Group> myPhaseElements;
GenerationPhase(@NotNull Collection<Group> groups) {
myPhaseElements = groups;
}
public List<TemplateMappingConfiguration> getAllElements() {
return asList(myPhaseElements);
}
public List<Group> getGroups() {
return new ArrayList<Group>(myPhaseElements);
}
List<Group> groupByGenerator() {
// ordering is important as there might be more than 1 group with the same generator, and in addition there might be group
// with this generator as the only one. Ordering ensures this single group is always attached to the same groupWithFewModules
// and that these groups with few generators are in the same order each time generation plan is queried.
LinkedHashMap<TemplateModule, Group> groupByModule = new LinkedHashMap<TemplateModule, Group>();
LinkedHashMap<Group, Set<TemplateModule>> groupsWithFewModules = new LinkedHashMap<Group, Set<TemplateModule>>();
ArrayList<Group> step = new ArrayList<Group>();
Group[] phaseElements = myPhaseElements.toArray(new Group[myPhaseElements.size()]);
Arrays.sort(phaseElements, new GroupComparator());
for (Group g : phaseElements) {
final Set<TemplateModule> involvedGenerators = getInvolvedGenerators(g);
if (involvedGenerators.size() == 1) {
final TemplateModule generator = involvedGenerators.iterator().next();
Group sameModuleGroup = groupByModule.get(generator);
groupByModule.put(generator, sameModuleGroup == null ? g : sameModuleGroup.union(g));
} else {
groupsWithFewModules.put(g, involvedGenerators);
}
}
// add groups with single generator to a group with MC from few generators,
// so that g1 (GenA, GenB, GenC) includes g2(GenB), iow no distinct g1 and g2.
for (Entry<Group, Set<TemplateModule>> e : groupsWithFewModules.entrySet()) {
Group compositeGroup = e.getKey();
for (TemplateModule tm : e.getValue()) {
if (groupByModule.containsKey(tm)) {
compositeGroup = compositeGroup.union(groupByModule.remove(tm));
}
}
step.add(compositeGroup);
}
step.addAll(groupByModule.values()); // add those left not in use by any composite group
return step;
}
private static List<TemplateMappingConfiguration> asList(Collection<Group> groups) {
ArrayList<TemplateMappingConfiguration> stepAsList = new ArrayList<TemplateMappingConfiguration>();
for (Group g : groups) {
stepAsList.addAll(g.getElements());
}
PartitioningSolver.sort(stepAsList);
return stepAsList;
}
private static Set<TemplateModule> getInvolvedGenerators(Group mappings) {
HashSet<TemplateModule> rv = new HashSet<TemplateModule>();
for (TemplateMappingConfiguration tmc : mappings.getElements()) {
rv.add(tmc.getModel().getModule());
}
return rv;
}
private static class GroupComparator implements Comparator<Group> {
@Override
public int compare(Group g1, Group g2) {
HashSet<TemplateMappingConfiguration> g1Unique = new HashSet<TemplateMappingConfiguration>(g1.getElements());
g1Unique.removeAll(g2.getElements());
HashSet<TemplateMappingConfiguration> g2Unique = new HashSet<TemplateMappingConfiguration>(g2.getElements());
g2Unique.removeAll(g1.getElements());
if (g1Unique.size() != g2Unique.size()) {
return g1Unique.size() - g2Unique.size();
}
if (g1Unique.isEmpty()) {
assert g2Unique.isEmpty();
return 0;
}
TemplateMappingConfiguration[] e1 = g1Unique.toArray(new TemplateMappingConfiguration[g1Unique.size()]);
TemplateMappingConfiguration[] e2 = g2Unique.toArray(new TemplateMappingConfiguration[g2Unique.size()]);
final MapCfgComparator comparator = new MapCfgComparator();
Arrays.sort(e1, comparator);
Arrays.sort(e2, comparator);
for (int i = 0; i < e1.length; i++) {
int r = comparator.compare(e1[i], e2[i]);
if (r != 0) {
return r;
}
}
return 0;
}
}
}