package net.jimblackler.Utils.test; import junit.framework.TestCase; import net.jimblackler.Utils.CollectionAbortedException; import net.jimblackler.Utils.Collector; import net.jimblackler.Utils.ResultHandler; import net.jimblackler.Utils.ThreadedYieldAdapter; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.Set; /** * This demonstrates a function that returns all possible rearrangements of a given word, without * repeats. The implementation uses a ResultHandler<> object, then it is adapted to return the * results through an iterator. */ public class AnagramsDemo extends TestCase { /** * Returns a list of anagrams of a word using a ResultCollector<> */ private static void getAnagrams(String word, ResultHandler handler) throws CollectionAbortedException { // Build frequency table Hashtable frequencies = new Hashtable(); for (Character character : word.toCharArray()) { Integer frequency = frequencies.get(character); if (frequency == null) { frequency = 1; } else { frequency++; } frequencies.put(character, frequency); } getAnagramsRecursive(new StringBuilder(), word.length(), frequencies, handler); } /** * An internal function that returns all possible letter combinations from a table of * frequencies. */ private static void getAnagramsRecursive(StringBuilder word, int remainingCharacters, Hashtable frequencies, ResultHandler handler) throws CollectionAbortedException { final Enumeration chars = frequencies.keys(); while (chars.hasMoreElements()) { final Character character = chars.nextElement(); final Integer frequency = frequencies.get(character); if (frequency > 0) { word.append(character); // append next char of word if (remainingCharacters == 1) { // complete handler.handleResult(word.toString()); } else { frequencies.put(character, frequency - 1); // remove character getAnagramsRecursive(word, remainingCharacters - 1, frequencies, handler); frequencies.put(character, frequency); // reform list for next iteration } word.deleteCharAt(word.length() - 1); // restore word builder } } } /** * The adapter that converts the Collector<> version to an Iterable<> version. */ private static Iterable getAnagrams(final String word) { return new ThreadedYieldAdapter().adapt(new Collector() { public void collect(ResultHandler resultHandler) throws CollectionAbortedException { getAnagrams(word, resultHandler); } }); } private static final String SOURCE_WORD = "hello"; private static final int EXPECTED_COMBINATIONS = 5 * 4 * 3 * 2 / 2; /** * Test that the Collection<> verson works properly. */ public void testUseCollector() throws Exception { // uses takecollector method final Set resultsCheck = new HashSet(); getAnagrams(SOURCE_WORD, new ResultHandler() { public void handleResult(String result) { assertFalse(resultsCheck.contains(result)); // no duplicate results assertEquals(SOURCE_WORD.length(), result.length()); resultsCheck.add(result); System.out.println(resultsCheck.size() + ": " + result); } }); assertEquals(EXPECTED_COMBINATIONS, resultsCheck.size()); } /** * Test that the Iterable<> version works properly. */ public void testAdapted() { final Iterable results = getAnagrams(SOURCE_WORD); final Set resultsCheck = new HashSet(); for (String result : results) { assertFalse(resultsCheck.contains(result)); // no duplicate results assertEquals(SOURCE_WORD.length(), result.length()); resultsCheck.add(result); System.out.println(resultsCheck.size() + ": " + result); } assertEquals(EXPECTED_COMBINATIONS, resultsCheck.size()); } }