package org.unicode.cldr.util; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; public class MergeLists { Collection> source = new ArrayList>(); Set orderedWorkingSet; public MergeLists() { this(new LinkedHashSet()); } public MergeLists(Set orderedWorkingSet) { this.orderedWorkingSet = orderedWorkingSet; } public MergeLists add(Collection orderedItems) { if (orderedItems.size() == 0) { // skip empties return this; } final LinkedHashSet linkedHashSet = new LinkedHashSet(orderedItems); if (linkedHashSet.size() != orderedItems.size()) { throw new IllegalArgumentException("Multiple items in ordering!"); } source.add(linkedHashSet); return this; } @SuppressWarnings("unchecked") public MergeLists add(T... stuff) { return add(Arrays.asList(stuff)); } public MergeLists addAll(Collection> collectionsOfOrderedItems) { for (Collection orderedItems : collectionsOfOrderedItems) { add(orderedItems); } return this; } public List merge() { List result = new ArrayList(); for (Collection sublist : source) { orderedWorkingSet.addAll(sublist); } // now that we have things ordered, we take the first one that is only at the front of a list // this is slower, but puts things into as much of the order specified as possible // could be optimized further, but we don't care that much Set first = new LinkedHashSet(); while (orderedWorkingSet.size() != 0) { getFirsts(first); if (first.size() == 0) { Map> reasons = new LinkedHashMap>(); getFirsts(first, reasons); throw new IllegalArgumentException( "Inconsistent requested ordering: cannot merge if we have [...A...B...] and [...B...A...]: " + reasons); } // now get first item that is in first T best = extractFirstOk(orderedWorkingSet, first); // removes from working set // remaining items now contains no non-first items removeFromSource(best); result.add(best); } return result; } public static boolean hasConsistentOrder(Collection a, Collection b) { LinkedHashSet remainder = new LinkedHashSet(a); remainder.retainAll(b); if (remainder.size() == 0) { return true; } // remainder is now in a's order, and contains only the items that are in both Iterator bi = b.iterator(); T current = bi.next(); for (T item : remainder) { if (item.equals(current)) { if (!bi.hasNext()) { return true; } current = bi.next(); } } return !bi.hasNext(); // if we have any left over, we failed } public static Collection hasConsistentOrderWithEachOf(Collection a, Collection> bs) { for (Collection b : bs) { if (!hasConsistentOrder(a, b)) { return b; } } return null; } // could be optimized since we know the item will only occur at the head of a list private void removeFromSource(T item) { for (Iterator> iterator = source.iterator(); iterator.hasNext();) { Collection sublist = iterator.next(); sublist.remove(item); if (sublist.size() == 0) { iterator.remove(); } } } /** * Get the first item that is also in the ok set. */ private T extractFirstOk(Collection remainingItems, Set ok) { for (Iterator it = remainingItems.iterator(); it.hasNext();) { T item = it.next(); if (ok.contains(item)) { it.remove(); return item; } } throw new IllegalArgumentException("Internal Error"); } public void getFirsts(Set result) { getFirsts(result, null); } /** * Get first of each sets. Guaranteed non-empty */ public void getFirsts(Set result, Map> reasons) { result.clear(); result.addAll(orderedWorkingSet); for (Collection sublist : source) { // get all the first items final Iterator iterator = sublist.iterator(); iterator.next(); // skip first while (iterator.hasNext()) { final T nextItem = iterator.next(); boolean changed = result.remove(nextItem); if (changed && reasons != null) { reasons.put(nextItem, sublist); } } } } }