69 lines
2.2 KiB
Java
69 lines
2.2 KiB
Java
package tc.oc.commons.core.util;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
|
|
import com.google.common.collect.ImmutableList;
|
|
import tc.oc.commons.core.ListUtils;
|
|
|
|
public class C3 {
|
|
private C3() {}
|
|
|
|
/**
|
|
* Merge the given lists into a single list using the C3 linearization algorithm
|
|
* https://en.wikipedia.org/wiki/C3_linearization
|
|
*
|
|
* This algorithm is commonly used to flatten multi-parent inheritance trees into
|
|
* a single ancestral line.
|
|
*/
|
|
public static <T> List<? extends T> merge(Collection<Collection<? extends T>> parents) {
|
|
final List<List<? extends T>> lists = new ArrayList<>();
|
|
for(Collection<? extends T> parent : parents) {
|
|
if(!parent.isEmpty()) {
|
|
lists.add(new ArrayList<>(parent));
|
|
}
|
|
}
|
|
|
|
final ImmutableList.Builder<T> merged = ImmutableList.builder();
|
|
|
|
outerLoop: while(!lists.isEmpty()) {
|
|
for(List<? extends T> list : lists) {
|
|
final T head = list.get(0);
|
|
if(!anyTailsContain(lists, head)) {
|
|
merged.add(head);
|
|
removeHeads(lists, head);
|
|
removeEmpty(lists);
|
|
continue outerLoop;
|
|
}
|
|
}
|
|
throw new IllegalArgumentException("Unable to merge lists due to ordering conflict");
|
|
}
|
|
|
|
return merged.build();
|
|
}
|
|
|
|
private static <T> boolean anyTailsContain(List<List<? extends T>> lists, T element) {
|
|
for(List<? extends T> list : lists) {
|
|
if(ListUtils.contains(list, element, 1)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static <T> void removeHeads(List<List<? extends T>> lists, T element) {
|
|
for(List<? extends T> list : lists) {
|
|
if(Objects.equals(list.get(0), element)) {
|
|
list.remove(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static <T> void removeEmpty(List<List<? extends T>> lists) {
|
|
for(Iterator<List<? extends T>> iterator = lists.iterator(); iterator.hasNext(); ) {
|
|
if(iterator.next().isEmpty()) iterator.remove();
|
|
}
|
|
}
|
|
}
|