Lecture 29 Active Learning: Streams The Logger Application 2 1
Goals Using the framework of the Logger application, we are going to explore three ways to read and write data using Java streams: 1. as text 2. as binary data 3. as serialized objects We're going to learn how to use Java's built in LinkedList class and its helper class ListIterator to maintain the log. 3 Logger Architecture Logger.java: provides main() and GUI generates log data ("Log") as Date objects summarizes and performs minimal validity check ("Summarize"); selects Files for reading and writing using a JFileChooser calls methods in LoggerIO to perform reading and writing of data You are going to write those methods. 4 2
java.util.linkedlist java.util.linkedlist possesses the same methods as our SLinkedList and many others. Logger uses a LinkedList to hold the log data, and it passes the list in calls to the LoggerIO methods you will have to write. For instance: void savetotext( List list, File f ) 5 Iterators An iterator is a helper class designed to be used with a List or other collection class. It has methods to return the collection's members one at a time. Iterators can also implement methods that allow the collection to be modified relative to the current position of the iterator. 6 3
ListIterator Interface public interface ListIterator { public boolean hasnext(); public Object next() throws NoSuchElementException; public boolean hasprevious(); public Object previous() throws NoSuchElementException; public void remove() throws IllegalStateException; public void add( Object o ); public void set( Object o ) throws IllegalStateException.... } 7 Iterator Methods Java iterators return a new element and advance to the next with the same operation, next(). The Java ListIterator also allows you to go backwards ( hasprevious() and previous() ). The most recent element returned by next() is the current element. remove() will delete the current element from the underlying collection. set() will change it. add() will insert a new element after the current element and before the element that would be returned by the following call to next(). After a call to add(), the inserted element becomes the new current. A call to next() will return the element after the inserted one. The first call to next() should return the first element of the list. 8 4
Using an Iterator List mylist = new LinkedList();... ListIterator iter = mylist.listiterator();... while ( iter.hasnext() ) { Object o = iter.next();... } 9 Traditional I/O The traditional approach uses different schemes depending on the type of the source or destination, e.g., keyboard input screen output files interprocess pipes network sockets 10 5
Java I/O Java s preferred approach is to handle I/O using streams (pioneered in C++) Java provides set of abstract stream classes that define the stream interfaces hierarchy of stream implementations Java allows you to treat some I/O distinctively, e.g. RandomAccessFile 11 Input vs Output Streams Streams are unidirectional An input stream controls data coming into the program from some source An output stream controls data leaving the program for some destination If you want to read and write the same destination, you use 2 streams 12 6
Streams and I/O Channels Usually the other end of a stream leads to or arises from a platform-specific media service, for instance, a file system Input Stream Output Stream File System Program File System 13 What Streams Share Java Streams are FIFO queues Streams deliver information in the order it was inserted into the underlying channel Standard Java streams only provide sequential access without rewind, backup, or random access 14 7
Coupling Streams Java streams may be combined by using one stream as a constructor argument to another This is usually done to add functionality and/or convert the data Stream pipelines are constructed from the data source to the program or from the data destination back to the program 15 Stream Pipeline, I File Input Stream Input Stream Reader Buffered Reader Stream Tokenizer 16 8
Stream Pipeline, II A FileInputStream reads bytes from a file An InputStreamReader converts a byte stream to characters A BufferedReader buffers a character stream for efficiency A StreamTokenizer parses a character stream into tokens 17 Constructing the Pipeline FileInputStream f = new FileInputStream( path ); InputStreamReader i = new InputStreamReader( f ); BufferedReader b = new BufferedReader( i ); StreamTokenizer t = new StreamTokenizer( b ); 18 9
Logger Exercise Download the two files Logger.java and LoggerIO.java from the class web site (links from Lecture 29). Save them both to a new directory. Create a new project and mount the directory into which you just saved the two java files. Don't insert package declarations. Leave them in the default package. Compile the project and test it. Try to save to a file in text mode. Did you create a file? Was there anything in it? (Look at it with Notepad). 19 savetotext() We have given you almost the whole savetotext() method; get a sense of the pattern: The try/catch blocks How we construct the stream, a FileWriter in this case How we iterate down the list How we close the stream when we are done (closing the stream saves the file) Find the method in FileWriter to write the date String and use it to output nstring on writer. Test it. Look at what you wrote using Notepad. 20 10
loadfromtext(), 1 Now proceed to the loadfromtext() method. Once again observe the pattern: Construct a BufferedReader from a FileReader; we need a BufferedReader to read lines of text at a time; Look at the while condition; we read the next line and check for EOF in one statement. Many input stream read methods return a special value when they reach the end of file. Understand why we use nested try/catch blocks and multiple catch clauses. Write a line of code to convert the line of text to a Date using Date parse( String s ) throws ParseException 21 loadfromtext(), 2 Write a second line of code to add the Date to the list the method returns. Get rid of the line (its only there to provide a dummy exception throw before you write the real code): if ( false ) throw new ParseException("",0); Test the code. Write data out, clear the logger, and see if you can load it back in in text mode. Use Notepad to hand write a new logger entry using the date pattern of the previous entries. See if you can read in the modified data. Check it with the Summarize command. 22 11
savetodata(), 1 At the heart of the Date object is a private long data field, the number of milliseconds since 12:00 am 1 Jan 1970. savetodata() saves log times as longs instead of Strings. You can retrieve the long using the Date method long gettime(). You will need to construct a DataOutputStream in order to access methods that can write longs. You can't construct one directly from a File. You will need to construct a FileOutputStream first. 23 savetodata(), 2 UsesaveToText() as a model for the rest of the method. Remember to catch exceptions. Now test. Trying writing out the same set of log times as text and data in separate files. Look at both in Notepad. Check the length of both files (you can get an exact byte count by choosing Properties from the right button menu in Explorer). 24 12
loadfromdata(), 1 Write a while loop to read longs from the DataInputStream as long as there are any (check the documentation), to convert each to a Date (there is a Date(long) constructor), and to add the Date to the return list. Pay special attention to what happens when you run out of longs in the input stream. How can you exit the while loop? Compile and test. 25 loadfromdata(), 2 Make sure you can write log data out in Data mode, clear the logger, and read the same data back in. Can you create log data in this format using Notepad? Can you read data written in Data mode back in as text? Can you read Text mode data back in as data? Why? 26 13
savetoobject(), 1 In this method, we will write out the whole List of Dates as a single object using an ObjectOutputStream. Check the documentation on how to create an ObjectOutputStream and how to call the writeobject() method. This single call will write the list and everything on it. We will look at how this works in the next lecture. Compile and test. Write out some log data in Object mode and look at it in Notepad. Check the length of the file. How does it compare to Text and Data mode. 27 loadfromobject() Write the contents of the try/catch block to read the entire log list in using an ObjectInputStream. You will have to construct the stream, call readobject(), and then close the stream. Compile and test. Can you read back in a log list that you previously wrote out in Object mode? Can you read data in Text or Data mode back in using Object mode? What are the comparative advantages of each data mode? 28 14