This question will give you practice with quite a range of Java features! You are going to write two functional interfaces: a logger interface, with a method for logging a message at a given severity level, and a string inspector interface, with a method that takes a string and optionally returns a (severity level, message) pair related to the string's content.
You are also going to implement a couple of loggers and string inspectors, using direct implementations and lambdas. The problem will illustrate how we can use interfaces to provide abstract notions of logging and string inspection, and implement an algorithm that can inspect all lines of a file and log the results, and that we can instantiate this algorithm with various different notions of string inspection and various different methods for logging.
-
Immutable pair. [Note: if you tried question 888a you may copy in the
ImmutablePair
class implemented as part of that question.] Write a generic class,ImmutablePair
, with two generic parameters,S
andT
say, that allows the construction of an (S
,T
) pair, and retrieval of its first and second components. OverridetoString()
so that a pair is represented as a string of the form(
string representation of first element,
string representation of second element)
. -
Log level enum. Write an enumeration class,
LogLevel
, specifying the following severity levels, in increasing order of severity:VERBOSE
INFO
WARNING
ERROR
FATAL
-
Logger functional interface. Write a functional interface,
Logger
, specifying one method:void log(LogLevel logLevel, String message);
Use an annotation to indicate that this is a functional interface. Check that, with your annotation, you get a compiler error if you try to add another method to the interface. -
String inspector functional interface. Write a functional interface,
StringInspector
, specifying a single method,inspect
, that takes a string and returns, optionally, anImmutablePair
comprised of aLogLevel
and a string.The idea is that this method will inspect the input string, returning nothing (i.e. an empty optional) if the string is not noteworthy, and a severity level and message if the string is noteworthy. It is up to implementations of this interface to decide what "noteworthy" means in practice.
-
File inspector class. Write a class,
FileInspector
, with a single field of typeLogger
, a constructor that populates this field with a givenLogger
reference, and a single method,inspectFile
, that takes a file name (string) and aStringInspector
.The method should open the file with the given name, and process it line by line. It should invoke the string inspector for each line, and if the string inspector reports that the line is noteworthy, it should invoke the logger with the severity level and message reported by the string inspector.
You can declare the
inspectFile
method should be declared as throwingIOException
, to avoid having to deal with exceptions, but feel free to experiment with using atry...catch
block, e.g. to ignore exceptions, or log a fatal error if an exception occurs. -
File logger implementation. Write a class,
FileLogger
, that implements theLogger
interface. This class should have aBufferedWriter
field, and should be constructed with a file name: the file name should be used to create a buffered reader from a file reader for the given file, throwing anIOException
when something goes wrong.The
log
method should do nothing if the given severity level isINFO
orVERBOSE
. Otherwise it should write a line to the buffered writer of the form "SEVERITY_LEVEL:
message", where SEVERITY_LEVEL is the string representation of the given severity level and message is the message string.If an
IOException
occurs during the execution oflog
, it should be ignored.The class should also have a
close
method that closes the buffered writer, throwing anIOException
if something goes wrong. -
Known words string inspector implementation. Write a class,
KnownWordsStringInspector
, that implements theStringInspector
interface. As fields, this class should have a set of error words and a set of verbose words, each of which should be a set of strings, and the class should be constructed with a reference with which to populate each of these fields.The
inspect
method should split the input string using " ", turning it into an array of strings, and iterate over these strings. Each string that occurs in the set of error words should be added to a list of observed error words. Each string that occurs in the set of verbose words should be added to a list of observed verbose words. If no error or verbose words are observed, the method should return an empty optional. Otherwise, a (severity level, message) pair should be returned (packaged in an optional). The severity level should beERROR
if at least one error word was observed, andVERBOSE
otherwise. The message should indicate which error words and which verbose words were observed. -
Writing a demo, using these classes and using some lambdas. Write a class,
Demo
, with a main method.In the main method, check that exactly three arguments are supplied; these should correspond to an input file and two output files.
Declare a local variable,
stderrLogger
, of typeLogger
, that implements the functional interfaceLogger
via a lambda:final Logger stderrLogger = (logLevel, message) -> { ... };
The body of this lambda should write
message
to standard error, prefixed by***IMPORTANT***\n
if the severity level isWARNING
or higher, and prefixed by*NOTE*\n
otherwise.Similarly, declare a local variable,
lineTooLongInspector
, of typeStringInspector
, that implements the functional interfaceStringInspector
via a lambda. The lambda should return an optional containing a pair of the form (ERROR
, "Line too long: line") if the given string is more than 100 characters in length, and should return an empty optional otherwise.Create three
FileInspector
objects:- A `FileInspector` that takes `stderrLogger` as its logger - Two `FileInspectors` that each take a `FileLogger` as their logger, using `args[1]` and `args[2]` as the output file names
Create a
KnownWordsStringInspector
with some error and verbose words of your choice.Use the following statements to run your file inspectors on the file with name
args[0]
, using your string inspectors:System.err.println("Starting inspection 1:"); inspector1.inspectFile(args[0], lineTooLongInspector); System.err.println("\nStarting inspection 2:"); inspector1.inspectFile(args[0], knownWordsInspector); System.err.println("\nStarting inspection 3 (see " + args[1] + " for details)."); inspector2.inspectFile(args[0], lineTooLongInspector); System.err.println("\nStarting inspection 4 (see " + args[2] + " for details)."); inspector3.inspectFile(args[0], knownWordsInspector);
At the end of
main
, be sure to close the twoFileLogger
instances that you created. -
Running your program. For my "known words" string inspector I used "goto", "finalize" and "static" as the error words, and "continue" and "break" as the verbose words. I populated "input.txt" with the following:
the quick brown fox jumped over the lazy dog a b c d e f g h i j k l m n o p q r s t u v w x y z now I know my abc next time won't you sing with me? Dijkstra said that goto is considered harmful; OS developers disagree I want to goto a warmer place and stop switch ing between tasks so often, all this switch ing makes it hard to finalize anything, and can't continue like this without at least a short break But for now I will continue to continue until that break comes
I then ran this command to execute the program:
java tutorialquestions.questionb33f.Demo input.txt output1.txt output2.txt
which gave the following output (the output your program gives may of course be a bit different from this, depending on exactly how you chose to emit strings):
Starting inspection 1: *** IMPORTANT *** Line too long: a b c d e f g h i j k l m n o p q r s t u v w x y z now I know my abc next time won't you sing with me? *** IMPORTANT *** Line too long: I want to goto a warmer place and stop switch ing between tasks so often, all this switch ing makes it hard to finalize anything, and can't continue like this without at least a short break Starting inspection 2: *** IMPORTANT *** Observed the following error words: [goto] *** IMPORTANT *** Observed the following error words: [goto, finalize] Observed the following words of note: [continue, break] * NOTE * Observed the following words of note: [continue, continue, break] Starting inspection 3 (see output1.txt for details). Starting inspection 4 (see output2.txt for details).
Contents of "output1.txt":
ERROR: Line too long: a b c d e f g h i j k l m n o p q r s t u v w x y z now I know my abc next time won't you sing with me? ERROR: Line too long: I want to goto a warmer place and stop switch ing between tasks so often, all this switch ing makes it hard to finalize anything, and can't continue like this without at least a short break
Contents of "output2.txt":
ERROR: Observed the following error words: [goto] ERROR: Observed the following error words: [goto, finalize] Observed the following words of note: [continue, break]