import java.io.*;
import java.util.*;

public class tf_10 {
    /*
     * The main function
     */
    public static void main(String[] args) throws IOException {
        new WordFrequencyController(args[0]).run();
    }
}

/*
 * The classes
 */

abstract class TFExercise {
    public String getInfo() {
        return this.getClass().getName();
    }
}

class WordFrequencyController extends TFExercise {
    private DataStorageManager storageManager;
    private StopWordManager stopWordManager;
    private WordFrequencyManager wordFreqManager;
    
    public WordFrequencyController(String pathToFile) throws IOException {
        this.storageManager = new DataStorageManager(pathToFile);
        this.stopWordManager = new StopWordManager();
        this.wordFreqManager = new WordFrequencyManager();
    }
    
    public void run() {
        for (String word : this.storageManager.calculateWords()) {
            if (!this.stopWordManager.isStopWord(word)) {
                this.wordFreqManager.incrementCount(word);
            }
        }
        
        int numWordsPrinted = 0;
        for (WordFrequencyPair pair : this.wordFreqManager.sorted()) {
            System.out.println(pair.getWord() + " - " + pair.getFrequency());
            
            numWordsPrinted++;
            if (numWordsPrinted >= 25) {
                break;
            }
        }
    }
}

/** Models the contents of the file. */
class DataStorageManager extends TFExercise {
    private String data;
    
    public DataStorageManager(String pathToFile) throws IOException {
        byte[] dataBytes;
        RandomAccessFile f = new RandomAccessFile(new File(pathToFile), "r");
        try {
            dataBytes = new byte[(int) f.length()];
            f.readFully(dataBytes);
        } finally {
            f.close();
        }
        
        this.data = new String(dataBytes, "UTF-8").replaceAll("[\\W_]+", " ").toLowerCase();
    }
    
    public String[] calculateWords() {
        return this.data.split("\\s+");
    }
    
    public String getInfo() {
        return super.getInfo() + ": My major data structure is a " + this.data.getClass().getName();
    }
}

/** Models the stop word filter. */
class StopWordManager extends TFExercise {
    private Set<String> stopWords;
    
    public StopWordManager() throws IOException {
        this.stopWords = new HashSet<String>();
        
        Scanner f = new Scanner(new File("../stop_words.txt"), "UTF-8");
        try {
            f.useDelimiter(",");
            while (f.hasNext()) {
                this.stopWords.add(f.next());
            }
        } finally {
            f.close();
        }
        
        // Add single-letter words
        for (char c = 'a'; c <= 'z'; c++) {
            this.stopWords.add("" + c);
        }
    }
    
    public boolean isStopWord(String word) {
        return this.stopWords.contains(word);
    }
    
    public String getInfo() {
        return super.getInfo() + ": My major data structure is a " + this.stopWords.getClass().getName();
    }
}

/** Keeps the word frequency data. */
class WordFrequencyManager extends TFExercise {
    private Map<String, MutableInteger> wordFreqs;
    
    public WordFrequencyManager() {
        this.wordFreqs = new HashMap<String, MutableInteger>();
    }
    
    public void incrementCount(String word) {
        MutableInteger count = this.wordFreqs.get(word);
        if (count == null) {
            this.wordFreqs.put(word, new MutableInteger(1));
        } else {
            count.setValue(count.getValue() + 1);
        }
    }
    
    public List<WordFrequencyPair> sorted() {
        List<WordFrequencyPair> pairs = new ArrayList<WordFrequencyPair>();
        for (Map.Entry<String, MutableInteger> entry : wordFreqs.entrySet()) {
            pairs.add(new WordFrequencyPair(entry.getKey(), entry.getValue().getValue()));
        }
        Collections.sort(pairs);
        Collections.reverse(pairs);
        return pairs;
    }
    
    public String getInfo() {
        return super.getInfo() + ": My major data structure is a " + this.wordFreqs.getClass().getName();
    }
}

class MutableInteger {
    private int value;
    
    public MutableInteger(int value) {
        this.value = value;
    }
    
    public int getValue() {
        return value;
    }
    
    public void setValue(int value) {
        this.value = value;
    }
}

class WordFrequencyPair implements Comparable<WordFrequencyPair> {
    private String word;
    private int frequency;
    
    public WordFrequencyPair(String word, int frequency) {
        this.word = word;
        this.frequency = frequency;
    }
    
    public String getWord() {
        return word;
    }
    
    public int getFrequency() {
        return frequency;
    }
    
    public int compareTo(WordFrequencyPair other) {
        return this.frequency - other.frequency;
    }
}
@@ -884,25 +884,19 @@
 Manager.
-calculate
+get
 Words())
@@ -1473,19 +1473,26 @@
 ate 
+List%3C
 String
- data
+%3E words
 ;%0A  
@@ -1576,75 +1576,86 @@
     
-byte%5B%5D dataBytes;%0A        RandomAccessFile f = new RandomAccessFile
+this.words = new ArrayList%3CString%3E();%0A        %0A        Scanner f = new Scanner
 (new
@@ -1674,17 +1674,21 @@
 File), %22
-r
+UTF-8
 %22);%0A    
@@ -1713,127 +1713,129 @@
     
-dataBytes = new byte%5B(int) f.length()%5D;%0A            f.readFully(dataBytes);%0A        %7D finally %7B%0A            f.clo
+f.useDelimiter(%22%5B%5C%5CW_%5D+%22);%0A            while (f.hasNext()) %7B%0A                this.words.add(f.next().toLowerCa
 se()
+)
 ;%0A
+    
     
@@ -1852,108 +1852,59 @@
     
-%0A        this.data = new String(dataBytes, %22UTF-8%22).replaceAll(%22%5B%5C%5CW_%5D+%22, %22 %22).toLowerCa
+%7D finally %7B%0A            f.clo
 se();%0A
+    
     %7D%0A
+    %7D%0A
     
@@ -1919,26 +1919,24 @@
 lic 
+List%3C
 String
-%5B%5D calculate
+%3E get
 Word
@@ -1965,26 +1965,13 @@
 his.
-data.split(%22%5C%5Cs+%22)
+words
 ;%0A  
@@ -2083,20 +2083,21 @@
  + this.
-data
+words
 .getClas

Refactor DataStorageManager to be more idiomatic.