/*
* 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.make.dependencies;
import jetbrains.mps.make.ModuleMaker;
import jetbrains.mps.make.dependencies.graph.Graph;
import jetbrains.mps.make.dependencies.graph.Graphs;
import jetbrains.mps.make.dependencies.graph.IVertex;
import jetbrains.mps.project.dependency.GlobalModuleDependenciesManager;
import jetbrains.mps.project.dependency.GlobalModuleDependenciesManager.Deptype;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.module.SModule;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class StronglyConnectedModules<M extends SModule> {
private final Set<M> myModules;
public StronglyConnectedModules(Set<M> modules) {
myModules = modules;
}
public List<Set<M>> getStronglyConnectedComponents() {
return getStronglyConnectedComponents(myModules, new DefaultModuleDecoratorBuilder<M>());
}
private <D extends SModuleDecorator<M>> List<Set<M>> getStronglyConnectedComponents(Set<M> modules, SModuleDecoratorBuilder<M, D> decoratorBuilder) {
Graph<SModuleDecorator<M>> graph = new Graph<SModuleDecorator<M>>();
Map<SModule, SModuleDecorator<M>> moduleToDecorator = new LinkedHashMap<SModule, SModuleDecorator<M>>();
for (M module : modules) {
SModuleDecorator<M> decorator = decoratorBuilder.decorate(module);
moduleToDecorator.put(module, decorator);
graph.add(decorator);
}
for (SModuleDecorator<M> decorator : graph.getData()) {
decorator.fill(moduleToDecorator);
}
List<List<SModuleDecorator<M>>> cycles = Graphs.findStronglyConnectedComponents(graph);
List<Set<M>> result = new LinkedList<Set<M>>();
for (List<SModuleDecorator<M>> cycle : cycles) {
Set<M> mset = new LinkedHashSet<M>();
result.add(mset);
for (SModuleDecorator<M> decorator : cycle) {
mset.add(decorator.getModule());
}
}
Collections.reverse(result);
return result;
}
interface SModuleDecorator<M extends SModule> extends IVertex, Comparable<SModuleDecorator<M>> {
M getModule();
void fill(Map<SModule, SModuleDecorator<M>> map);
}
interface SModuleDecoratorBuilder<M extends SModule, D extends SModuleDecorator<M>> {
D decorate(M module);
}
private static class DefaultModuleDecoratorBuilder<M extends SModule> implements SModuleDecoratorBuilder<M, DefaultModuleDecorator<M>> {
@Override
public DefaultModuleDecorator<M> decorate(M module) {
return new DefaultModuleDecorator<M>(module);
}
}
private static class DefaultModuleDecorator<M extends SModule> implements SModuleDecorator<M> {
private final M myModule;
private final Set<SModuleDecorator<M>> myNext = new LinkedHashSet<SModuleDecorator<M>>();
public DefaultModuleDecorator(M module) {
myModule = module;
}
@Override
public void fill(Map<SModule, SModuleDecorator<M>> map) {
List<SModule> dependency = new ArrayList<SModule>(new GlobalModuleDependenciesManager(myModule).getModules(Deptype.COMPILE));
Collections.sort(dependency, ModuleMaker.MODULE_BY_NAME_COMPARATOR);
for (SModule module : dependency) {
SModuleDecorator<M> next = map.get(module);
if (next != null) {
assert next.getModule() == module;
myNext.add(next);
}
}
}
@Override
public M getModule() {
return myModule;
}
@Override
public Set<? extends IVertex> getNexts() {
return Collections.unmodifiableSet(myNext);
}
@Override
public int compareTo(@NotNull SModuleDecorator<M> decorator) {
return ModuleMaker.MODULE_BY_NAME_COMPARATOR.compare(myModule, decorator.getModule());
}
public String toString() {
return "DefaultModuleDecorator for the " + myModule;
}
}
}