Java Tip 64: Get rid of those empty black squares in your text editor

Do you go nuts when your editor shows you unreadable text? The application in this article will help you keep your cool!

Many programmers spend hours analyzing the code written by excellent programmers to improve their own style and learn new programming tricks. With the upcoming release of JDK 1.2, many who look over the supplied source code will encounter a problem on Windows 95/Windows NT 4.0 systems. After unpacking the sources of the Java API, the standard Windows editor shows unreadable text. If you edit the file Applet.java, for example, you'll see a frame that looks like the following snapshot:

Here, the text lines are just lined up using a black square as line separator symbol. When the chain of lines is getting too long, the output is continued in a new line of the frame.

This Java Tip will help you understand and overcome problems with the representation of the line separator on different systems. It introduces a conversion program called EOLConverter (EOL is an abbreviation for "end of line,") which enables you to get a frame that looks like this:

Now the black squares have vanished into thin air and each line of text is printed on its own line. To see this, just compare the colored regions in this frame to the corresponding regions in the last frame.

Converting the line separator

The conversion of the line separator chosen on the system of the text's author to the line separator used on your system is achieved by the application EOLConverter.java.

Copy this file to the directory housing files you want to convert. Compile the source code by executing the command

javac EOLConverter.java

and start the application by invoking the Java interpreter through the command

java EOLConverter

Please note that the application only converts files with the extensions .htm, .html, .java, and .txt, and that the files in the subdirectories of all levels in your current directory are converted, too.

In the section Analyzing classes, you'll learn how the classes BufferedWriter and BufferedReader in the paackage java.io.* manage the line separator.

In the section Understanding EOLConverter, you'll find a detailed discussion of the statements and the execution flow performed in this application.

Analyzing classes

Now we proceed with the discussion of two essential classes we'll use in our application. The source code for these classes is shipped with the JDK 1.2, and is installed automatically if you don't deactivate the corresponding checkbox during the installation process.

Move to the directory jdk1.2 and unjar the file src.jar by executing the command:

jar -xvf src.jar

You should now find the source files of the API 1.2 in the directory jdk1.2/src.

Edit the file BufferedWriter.java, which is stored in the directory jdk1.2/src/java/io.

  1. At the beginning of the class body, the following string object is defined:

    private String lineSeparator;
    
  2. During the execution of the constructor the object lineSeparator is initialized by

    lineSeparator = (String) java.security.AccessController.doPrivileged(

    new sun.security.action.GetPropertyAction("line.separator"));

    Hence the line separator string is defined by the system property line.separator which depends on your system. For example, the value of the line separator is \n on Unix systems and \r\n on Windows systems.

    The character \n represents a line feed (used to roll the paper one line up), and the character \r represents a carriage return (used to move the print head to its initial position). In the past, teletypes (such as normal typewriters -- a combination of a keyboard and a printer) were used to send data directly into the processor of a computer. These characters are an inheritance from the beginning of the computer development and have lost their original meaning today.

  3. To write a line separator to an output stream you use the method newLine, which is defined by:

    public void newLine() throws IOException {

    write(lineSeparator); }

    This method writes the value of the line separator on the current system to the output stream.

Now edit the file BufferedReader.java which is stored in the same directory. The analysis of its method readLine is more complicated and is left to the reader. While studying this method you see that, depending on the occurrence of the characters \n or \r, several cases are studied. In Java, a line is considered terminated by any one of the following: a line feed \n, a carriage return \r, or a carriage return followed immediately by a line feed.

You may convert the line separator used in a file by reading its lines using the method readLine of the class BufferedReader and writing its lines back using the methods write and newLine of the class BufferedWriter.

Understanding EOLConverter

We're now ready to move on to a discussion of the specifics of EOLConverter. But first, a look at the source code:

01  import java.io.*;
02  
03  public class EOLConverter implements FilenameFilter {
04  
05    public static void main(String[] arguments) {
06      convertDirectory(".");
07    }
08  
09    public boolean accept(File file, String string) {
10      String path = new String(file.toString() + "?" + string).replace('?', File.separatorChar);
11      boolean b_1 = new File(path).isDirectory();
12      boolean b_2 = string.endsWith(".htm");
13      boolean b_3 = string.endsWith(".html");
14      boolean b_4 = string.endsWith(".java");
15      boolean b_5 = string.endsWith(".txt");
16      return (b_1 || b_2 || b_3 || b_4 || b_5);
17    }
18  
19    public static void convertDirectory(String string) {
20      File directory = new File(string);
21      String[] list = directory.list(new EOLConverter());
22      for (int i = 0; i < list.length; i++) {
23  //      System.out.println(list[i]);
24        String path = new String(string + "?" + list[i]).replace('?', File.separatorChar);
25        if (new File(path).isDirectory()) {
26          convertDirectory(path);
27        } else {
28          convertFile(path);
29        }
30      }
31    }
32  
33    public static void convertFile(String string) {
34      File file_txt = new File(string);
35      File file_tmp = new File(string + ".tmp");
36      try {
37        BufferedReader bufferedreader = new BufferedReader(new FileReader(file_txt));
38        BufferedWriter bufferedwriter = new BufferedWriter(new FileWriter(file_tmp));
39        String line;
40        while ((line = bufferedreader.readLine()) != null) {
41          bufferedwriter.write(line);
42          bufferedwriter.newLine();
43        }
44        bufferedreader.close();
45        bufferedwriter.close();
46        if (file_txt.delete()) {
47          file_tmp.renameTo(file_txt);
48        }
49      } catch (FileNotFoundException filenotfoundexception) {
50      } catch (IOException e) {
51      }
52    }
53  
54  }

Now, on to an explanation of the key lines of code:

  • Line 03: The interfacee FilenameFilter is implemented in the class EOLConverter so that only files with the extensions .htm, .html, .java, and .txt are converted. The method accept of this interface is overwritten from lines 09 to 17.

  • Lines 05 to 07: During the execution of the method main, the method convertDirectory is invoked. The string object (".") represents the current directory (in which you have started the EOLConverter program).

  • Lines 09 to 17: The method accept is the only method that needs to be overwritten if you implement a FilenameFilter. In line 10, the complete path of a directory or file is determined. If the path represents a directory, the value of the boolean variable in line 11 is true and the method return in line 16 returns true. During the execution of the statements from lines 12 to 15, we check to see whether the file has one of the above extensions. At this point, you are invited to also accept files with other extensions.

  • Lines 19 to 31: In line 21, the program returns a list of all directories and all files with the above extensions. The method list gets the implemented FilenameFilter via the construction of an object of the current class as its argument. The previously discussed method accept is called automatically for you at this point.

    After that, we examine all list entries to determine whether each is a directory or a file. If the list entry is a directory, we execute the current block again by recalling the method in line 19, but now with our current entry as the new argument. This is the most important statement of the program EOLConverter, because now all the files in the child directories are converted. However, this is also a dangerous statement; you may run into memory problems if you have too many child directories to convert. If the list entry isn't a directory, the method convertFile is invoked to convert the line separator.

    The statement in line 23 is set apart with comment tags because we call a method of the class PrintStream on the object out of the class System. To execute this statement, your operating system must have a standard output device. If this is the case, feel free to uncomment the statement and observe the application doing its job.

  • Lines 33 to 52: The conversion of the line separator is automatically performed when we read a line from the file and print it to a temporary file. This effect was discussed in the section Analyzing classes above. After the closure of the streams, we delete the file with the wrong line separators and rename the temporary file to the previous file.

The execution flow of the application can be summarized as follows: First, create a list of all directories and all files with the specified extensions. Then, determine for each list entry whether it is a directory or a file. If a list entry is a file, the line separator is converted by shipping its text lines into a temporary file, which is renamed after the conversion process has finished. If a list entry is a directory, you must create a new list for this directory and repeat the process.

Conclusion

This tip has shown you how Java is prepared to manage problems concerning the different representation of the line separator. With the discussed application, you don't even need to change your favorite editor when such problems occur. You should now understand why developers of the JDK deliver the whole source code with their API: to help you comprehend the classes and gain insight into what's happening in your applications.

Roland Willms studies mathematics and physics at the University of Siegen in Germany and works on computational methods in numerical mathematics, especially data analysis. He started programming with FORTRAN, C, and C++, but for the last year has used Java as his primary language. Currently, he leads a practical training on Java for the Department of Mathematics.

Learn more about this topic

  • For more information about the package java.io.*, see the following section of the Java Tutorial http://java.sun.com/docs/books/tutorial/essential/io/index.html
  • Browse JDK 1.2 documentation for further functionality of the BufferedReader class at http://java.sun.com/products/jdk/1.2/docs/api/java/io/BufferedReader.html
  • and the BufferedWriter class at http://java.sun.com/products/jdk/1.2/docs/api/java/io/BufferedWriter.html
  • German readers of JavaWorld are invited to consult the chapter "Eingabe und Ausgabe" of my online course about Java http://www.math.uni-siegen.de/willms/java/index.html
Recommended
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more