/* * 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.project.structure.modules.mappingpriorities.MappingPriorityRule; import jetbrains.mps.smodel.SNodePointer; import jetbrains.mps.util.CollectionUtil; import org.jetbrains.annotations.NotNull; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; /** * @author evgeny, 3/10/11 * @author Artem Tikhomirov */ public class PartitioningSolver { private final PriorityGraph myPriorityGraph; /* * Each entry defines a set of mappings, which should be applied together. */ private final List<Group> mySameStepGroups = new ArrayList<Group>(); private final PriorityConflicts myConflicts; private final Set<TemplateMappingConfiguration> myConfigurations = new LinkedHashSet<TemplateMappingConfiguration>(); public PartitioningSolver(@NotNull PriorityConflicts priorityConflicts) { myConflicts = priorityConflicts; myPriorityGraph = new PriorityGraph(); } public void prepare(Collection<TemplateMappingConfiguration> mc) { myConfigurations.addAll(mc); } public Set<TemplateMappingConfiguration> getKnownMapConfigs() { return Collections.unmodifiableSet(myConfigurations); } public void registerCoherent(Collection<TemplateMappingConfiguration> coherentMappings, MappingPriorityRule rule) { ArrayList<Group> groups = new ArrayList<Group>(coherentMappings.size()); for (TemplateMappingConfiguration m : coherentMappings) { groups.add(new Group(m)); } boolean withConflicts = false; for (int i = 1, x = groups.size(); i < x; i++) { final Group prev = groups.get(i - 1); final Group curr = groups.get(i); if (prev.isTopPriority() != curr.isTopPriority()) { myConflicts.registerCoherentPriorityMix(prev, curr, rule); withConflicts = true; } } if (withConflicts) { return; // drop set of conflicting coherent rules } mySameStepGroups.add(new Group(groups)); } // TMC from hiPrio are executed first, then TMC from loPrio are executed public void establishDependency(Collection<TemplateMappingConfiguration> hiPrio, Collection<TemplateMappingConfiguration> loPrio, MappingPriorityRule rule) { // map: lo-pri mapping -> {hi-pri mapping, .... , hi-pri mapping } loPrio = CollectionUtil.subtract(loPrio, hiPrio); for (TemplateMappingConfiguration lesserPriMapping : loPrio) { myPriorityGraph.addEdge(lesserPriMapping, hiPrio, rule); } } List<GenerationPhase> solve() { myPriorityGraph.finalizeEdges(myConfigurations); myPriorityGraph.checkSelfLocking(myConflicts); myPriorityGraph.checkLowPrioLocksTopPrio(myConflicts); final Collection<Group> cycles = myPriorityGraph.removeWeakCycles(); mySameStepGroups.addAll(cycles); List<Group> coherentMappings = joinSameStepMappings(); myPriorityGraph.updateWithCoherent(coherentMappings, myConflicts); myPriorityGraph.replaceWeakEdgesWithStrict(); boolean topPriorityGroup = false; ArrayDeque<Collection<Group>> stack = new ArrayDeque<Collection<Group>>(); while (!myPriorityGraph.isEmpty()) { Collection<Group> step = myPriorityGraph.getGroupsNotInDependency(); if (step.isEmpty()) { // non-empty graph but no independent groups myPriorityGraph.reportEdgesLeft(myConflicts); break; } for (Iterator<Group> it = step.iterator(); it.hasNext(); ) { if (it.next().isTopPriority() != topPriorityGroup) { it.remove(); } } if (step.isEmpty()) { if (topPriorityGroup) { myPriorityGraph.reportEdgesLeft(myConflicts); break; } topPriorityGroup = true; } else { stack.push(step); myPriorityGraph.dropEdgesOf(step); } } ArrayList<GenerationPhase> rv = new ArrayList<GenerationPhase>(stack.size()); while (!stack.isEmpty()) { rv.add(new GenerationPhase(stack.pop())); } return rv; } /** * Process groups of 'run together' to join intersecting into a single group */ private List<Group> joinSameStepMappings() { ArrayList<Group> rv = new ArrayList<Group>(mySameStepGroups.size()); ArrayList<Group> toMerge = new ArrayList<Group>(); LinkedList<Group> queue = new LinkedList<Group>(mySameStepGroups); while (!queue.isEmpty()) { Group head = queue.removeFirst(); for (Iterator<Group> it = queue.iterator(); it.hasNext(); ) { Group g = it.next(); if (head.hasCommonMappings(g)) { // the way mySameStepGroups are checked for same topPri setting at construction ensures single group is consistent // and hence two intersecting groups can't fail this assert head.isTopPriority() == g.isTopPriority(); toMerge.add(g); it.remove(); } } if (toMerge.isEmpty()) { // no groups with common mappings, add current group as is rv.add(head); } else { // get a new group that combines all mappings and rules of the same step toMerge.add(head); // there are chances there are more groups, that didn't intersect with head, but // intersect with one of merged, and we need to check for these again queue.add(new Group(toMerge)); toMerge.clear(); } } return rv; } static void sort(List<TemplateMappingConfiguration> mappingSet) { Collections.sort(mappingSet, new Comparator<TemplateMappingConfiguration>() { @Override public int compare(TemplateMappingConfiguration o1, TemplateMappingConfiguration o2) { return SNodePointer.serialize(o1.getMappingNode()).compareTo((SNodePointer.serialize(o2.getMappingNode()))); } }); } }