// File: WordCounter.java
// Program from Section 5.7 to illustrate the use of TreeMaps and Iterators.
// The program opens and reads a file called words.txt. Each line in this
// file should consist of one or more English words separated by spaces.
// The end of each line must not have any extra spaces after the last word.
// The program reads the file and then a table is printed of
// all words and their counts.

import java.util.*;  // Provides TreeMap, Iterator, Scanner
import java.io.*;    // Provides FileReader, FileNotFoundException

public class WordCounter2
{
    public static void main(String[ ] args)  
    {   
        TreeMap<String, Integer> frequencyData = new TreeMap<String, Integer>( );

        
        readWordFile(frequencyData); 

        // Here's the tricky part, we want a sorted List by Value!
        List<Map.Entry<String, Integer>> sortedEntries = makeSortedList(frequencyData);

        
        printAllCounts(sortedEntries);
    }

    public static int getCount
    (String word, TreeMap<String, Integer> frequencyData)
    {
        if (frequencyData.containsKey(word))
        {  // The word has occurred before, so get its count from the map
            return frequencyData.get(word); // Auto-unboxed
        }
        else
        {  // No occurrences of this word
            return 0;
        }
    }

    public static void printAllCounts(List<Map.Entry<String, Integer>> sortedEntries)
    {
        Map.Entry<String, Integer> entry;
        
        System.out.println("-----------------------------------------------");
        System.out.println("    Occurrences    Word");

        
        for(int i = 0; i < sortedEntries.size(); i++)
        {
            entry = sortedEntries.get(i);
            System.out.printf("%15d    %s\n", entry.getValue(), entry.getKey());
        }

        System.out.println("-----------------------------------------------");
    }  

    public static void readWordFile(TreeMap<String, Integer> frequencyData)
    {
        Scanner wordFile;
        String word;     // A word read from the file
        Integer count;   // The number of occurrences of the word

        try
        {
            wordFile = new Scanner(new FileReader("words.txt"));
        }
        catch (FileNotFoundException e)
        {
            System.err.println(e);
            return;
        }

        while (wordFile.hasNext( ))
        {
            // Read the next word and get rid of the end-of-line marker if needed:
            word = wordFile.next( );

            // Get the current count of this word, add one, and then store the new count:
            count = getCount(word, frequencyData) + 1;
            frequencyData.put(word, count);
        }
    } 

    // This code is based on a StackOverflow answer:
    // http://stackoverflow.com/questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java/3420912#3420912
    public static <K, V extends Comparable<? super V>> List<Map.Entry<K, V>> makeSortedList( Map<K, V> map )
    {
        List<Map.Entry<K, V>> list =
            new LinkedList<Map.Entry<K, V>>( map.entrySet() );
        Collections.sort( list, new Comparator<Map.Entry<K, V>>()
            {
                public int compare( Map.Entry<K, V> o1, Map.Entry<K, V> o2 )
                {
                    // the negative sign (-) makes this sort by largest value first
                    return -(o1.getValue()).compareTo( o2.getValue() );
                }
            } );

        return list;
    }

}
