/*
Copyright (C) 2011 Stephan Schiffel <stephan.schiffel@gmx.de>
This file is part of GameController.
GameController is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GameController is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GameController. If not, see <http://www.gnu.org/licenses/>.
*/
package tud.auxiliary;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.Map;
/**
* CrossProductMap computes a cross product of a HashMap of collections on the fly.
* It contains a list of HashMaps with each combination of elements of the given lists, e.g.,
* if you put in {a -> [1, 2], b -> [3, 4]} you will get [{a->1, b->3}, {a->2, b->3}, {a->1, b->4}, {a->2, b->4}].
*
* @author "Stephan Schiffel" <stephan.schiffel@gmx.de>
*
* @param <E> the type of the elements of the tuples, so far all elements must be of the same type
*/
public class CrossProductMap<K, V> extends AbstractCollection<Map<K, V>> implements Collection<Map<K, V>> {
private Map<K, Collection<? extends V>> collections;
public CrossProductMap(Map<K, Collection<? extends V>> collections) {
super();
this.collections = collections;
}
@Override
public Iterator<Map<K, V>> iterator() {
return new CrossProductMapIterator();
}
@Override
public int size() {
int size = 1;
for (Collection<? extends V> collection : collections.values()) {
size*=collection.size();
}
return size;
}
private final class CrossProductMapIterator implements
Iterator<Map<K, V>> {
private Map<K, Iterator<? extends V>> iterators;
private Map<K, V> nextTuple;
private List<K> keys;
public CrossProductMapIterator() {
super();
iterators = new HashMap<K, Iterator<? extends V>>();
nextTuple = new HashMap<K, V>();
keys = new Vector<K>(collections.keySet());
for (K key:keys) {
Iterator<? extends V> iterator = collections.get(key).iterator();
iterators.put(key, iterator);
if(nextTuple!=null && iterator.hasNext()) {
nextTuple.put(key, iterator.next());
} else {
nextTuple=null;
}
}
}
@Override
public boolean hasNext() {
return nextTuple!=null;
}
@Override
public Map<K,V> next() {
Map<K, V> currentTuple = nextTuple;
if(currentTuple!=null) {
nextTuple = new HashMap<K,V>(currentTuple); // copy the tuple
Iterator<K> keyIterator = keys.iterator();
K key = null;
while(keyIterator.hasNext() && (key = keyIterator.next()) != null && !iterators.get(key).hasNext()) {
// reset this iterator to the start and put first element in currentTuple
Iterator<? extends V> iterator = collections.get(key).iterator();
iterators.put(key, iterator);
nextTuple.put(key, iterator.next());
key = null;
}
if(key != null){
nextTuple.put(key, iterators.get(key).next());
} else {
nextTuple=null;
}
}
return currentTuple;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}