<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">/**
* The WordCount program implements a word counting functionality for class demo.
*
* @author  		Lucy Lu
* @since   		2020-08-02
* @version    	CS 4414 Fall 2020
*/

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.TreeMap;
import java.util.List;
import java.util.Map;

public class WordCount {

	public static void main(String[] args) {
		try {
			String path = args[0]; // takes in one argument that specifies directory path
			List&lt;File&gt; files = new ArrayList&lt;&gt;();
			System.out.println("start getting files");
			getFiles(path, files);
			System.out.println("got " + files.size() + " files");

			TreeMap&lt;String, Integer&gt; countTree = new TreeMap&lt;&gt;();
			System.out.println("start processing files");
			int count = 0;
			for (File file : files) {
				read(file, countTree);
				if (count % 1000 == 0)
					System.out.println("processed " + count + " files");
				count++;
			}
			System.out.println("finish processing files");

			sort(countTree);

		} catch (FileNotFoundException e) {
			System.out.println("Input file " + " not found");
			System.exit(1);
		} catch (IOException e) {
			System.out.println("IOException: " + e.getMessage());
			System.exit(1);
		}
	}

	/*
	 * Return a list of all the files that ends with .c or .h in the directory
	 * specified by path and all its sub-directories.
	 */
	private static void getFiles(String path, List&lt;File&gt; wantedList) {
		File folder = new File(path);
		File[] fList = folder.listFiles();

		if (fList != null)
			for (File file : fList) {
				if (file.isFile() &amp;&amp; (file.getName().endsWith(".c") || file.getName().endsWith(".h"))) {
					wantedList.add(file);
				} else if (file.isDirectory()) {
					getFiles(file.getAbsolutePath(), wantedList);
				}
			}
	}

	/*
	 * Read lines in a file and process each line by adding entries to the counting
	 * tree.
	 */
	private static void read(File file, TreeMap&lt;String, Integer&gt; countTree) throws IOException {
		BufferedReader br = null;

		br = new BufferedReader(new FileReader(file));
		String line;
		while ((line = br.readLine()) != null) {
			processLine(line, countTree);
		}
		br.close();
	}

	/*
	 * A helper method that processes each line by replacing all unwanted characters
	 * with spaces and add words in the line to the counting tree.
	 */
	private static void processLine(String line, TreeMap&lt;String, Integer&gt; countTree) {
		line = line.replaceAll("[^A-Za-z0-9_ ]", " ");
		int index = line.indexOf(" ");
		int prev = 0;
		// allegedly substring-indexOf combo is faster than split
		while (index != -1) {
			String word = line.substring(prev, index);
			if (word.length() != 0)
				countTree.put(word, countTree.getOrDefault(word, 0) + 1);
			prev = index + 1;
			index = line.indexOf(" ", prev);
		}
	}

	/*
	 * Sort the counting tree by descending value (count) and ascending key (word).
	 */
	private static void sort(TreeMap&lt;String, Integer&gt; countTree) {
		countTree.entrySet().parallelStream()
				.sorted((Map.Entry&lt;String, Integer&gt; entry1, Map.Entry&lt;String, Integer&gt; entry2) -&gt; {
					if (entry1.getValue() == entry2.getValue()) {
						return entry1.getKey().compareTo(entry2.getKey());
					}
					return entry2.getValue() - entry1.getValue();
				}).forEachOrdered(e -&gt; System.out.println(e.getKey() + " " + e.getValue()));
	}
}
</pre></body></html>