### Text formatting: Get one line of text

• Solving Sub-problem 1: fit as many words as you can on one line (of at most 30 characters)

• Question:

 What would you do if someone ask you to "fit as many words as possible on a line of N characters in length ???

To get a sense of the problem, use a concrete example:

• Here are some words:

 ``` word[0] = "WE" word[1] = "hold" word[2] = "these" word[3] = "Truths" word[4] = "to" word[5] = "be" word[6] = "self-evident," word[7] = "that" word[8] = "all" ```

Here is the line with as many words fitted in:

 ``` 012345678901234567890123456789 >WE hold these Truths to be (next word is: self-evident, and does not fit) ```

• How would you fit as many words as you can on a line of 30 spaces ?

• The common-sense algorithm is as follows:

 ``` Add the first word to the line as long as the next word fits on the line { Add the next word to the line; } ```

• Example:

 ``` String[] words = new String[10000]; int numWords = 35 word[0] = "WE" word[1] = "hold" word[2] = "these" word[3] = "Truths" word[4] = "to" word[5] = "be" word[6] = "self-evident," word[7] = "that" word[8] = "all" word[9] = "Men" word[10] = "are" word[11] = "created" word[12] = "equal," word[13] = "that" word[14] = "they" .... (and so on) ``` ``` Add first word: 012345678901234567890123456789 line = We| Check if next word (hold) fits: 012345678901234567890123456789 line = We| +hold Yes, add next word (hold): 012345678901234567890123456789 line = We hold| Check if next word (these) fits: 012345678901234567890123456789 line = We hold| +these Yes, add next word (these): 012345678901234567890123456789 line = We hold these| ```

• Refining the algorithm

• How do we know which word is the next word that needs to be fitted into the line:

• Let's use a variable currWord which contains the index in the array of words

currWord will point to the next word that will be fitted in the line.

Example:

 ``` String[] words = new String[10000]; int numWords = 35 int currWord; word[0] = "WE" word[1] = "hold" word[2] = "these" word[3] = "Truths" word[4] = "to" word[5] = "be" word[6] = "self-evident," word[7] = "that" word[8] = "all" word[9] = "Men" word[10] = "are" word[11] = "created" word[12] = "equal," word[13] = "that" word[14] = "they" .... (and so on) ``` ``` currWord = 0; Add word[currWord] (word[0] = "WE"); currWord++; (point to next word) 012345678901234567890123456789 line = We| Check if word[currWord] (= word[1]) fits: 012345678901234567890123456789 line = We| +hold Yes, add word[currWord] (= word[1]): currWord++; (point to next word) 012345678901234567890123456789 line = We hold| Check if word[currWord] (= word[2]) fits: 012345678901234567890123456789 line = We hold| +these Yes, add word[currWord] (= word[1]): currWord++; (point to next word) 012345678901234567890123456789 line = We hold these| ```

• Putting the algorithm as a method

• Recall that algorithms in the form of a method is most practical

• To cast the algorithm in the form of a method, we need to determine:

 The inputs used by the algorithm to do its job The output(s) produced by the algorithm

• Inputs and outputs of the readOneLine() method:

• Ideal implementation:

 Pass in all necessary information (as parameters) Have the method perform the task and update parameters

Ideal implementation in method form:

 ``` /* ------------------------------------------------------------ The readOneLine() method must update the currWord parameter We must pass the parameter currWord by-reference ! ------------------------------------------------------------ */ public static String readOneLine( int currWord, String[] w, int maxLineLength ) { String line; // Line with as many words as possible line = w[ currWord ]; // Add current word as first word currWord = currWord + 1; // Get next word while ( currWord < numWords && line.length() + 1 + w[currWord].length() <= maxLineLength ) { // Next word fits; add next word to line line = line + " " + w[ currWord ]; // Add next word to line currWord = currWord + 1; // Get next word } return( line ); } ```

Explanation:

• We pass in the parameters currWord, the array of words as String[] w and the maximum line length as maxLineLength

• We can return the constructed line using return( line )

• HOWEVER, this ideal solution will not work, because:

• Java does not have the pass-by-reference parameter passing mechanism

• Therefore:

 we cannot update the parameter variable currWord

• Solution:

 We define currWord as a class variable (because it can be accessed anywhere in the Java program)

• Java Program:

 ``` import java.io.*; import java.util.Scanner; public class FormatText2 { public static final int NCHARS_IN_LINE = 30; public static String[] words = new String[10000]; // Hold words in input file public static int currWord; // Current word public static int numWords; // Count # words in input public static int readInput( Scanner in, String[] w ) { int nWords = 0; while ( in.hasNext() ) { w[nWords] = in.next(); // Read next string (word) nWords++; // Count # words AND use next // array element for next word } return( nWords ); } /* ================================================================== readOneLine(w, maxLineLength): get one line of text from w[] (<= maxLineLength characters) Starts getting words from w[currLine] =================================================================== */ public static String readOneLine( String[] w, int maxLineLength ) { String line; line = w[ currWord ++ ]; while ( currWord < numWords && line.length() + 1 + w[currWord].length() <= maxLineLength ) { line = line + " " + w[ currWord++ ]; // Add word to line } return(line); } /* ======================================= main(): test the readOneLine() method ======================================= */ public static void main(String[] args) throws IOException { if ( args.length == 0 ) { System.out.println( "Usage: java FormatText2 inputFile"); System.exit(1); } File myFile = new File( args[0] ); // Open file "inp2" Scanner in = new Scanner(myFile); // Make Scanner obj with opened file String nextLine; // Read input from data file numWords = readInput( in, words ); currWord = 0; while ( currWord < numWords ) { nextLine = readOneLine( words, NCHARS_IN_LINE ); System.out.println(" 012345678901234567890123456789"); System.out.println(" >" + nextLine + "\n"); } } } ```

Example:

• Input file:

 ```WE hold these Truths to be self-evident, that all Men are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are Life, Liberty and the Pursuit of Happiness. ```

• Output:

 ``` 012345678901234567890123456789 >WE hold these Truths to be 012345678901234567890123456789 >self-evident, that all Men are 012345678901234567890123456789 >created equal, that they are 012345678901234567890123456789 >endowed by their Creator with 012345678901234567890123456789 >certain unalienable Rights, 012345678901234567890123456789 >that among these are Life, 012345678901234567890123456789 >Liberty and the Pursuit of 012345678901234567890123456789 >Happiness. ```

• Example Program: (Demo above code)

How to run the program:

 Right click on link and save in a scratch directory To compile:   javac FormatText2.java To run:          java FormatText2 inp1 Or:               java FormatText2 inp2

• Programming note

• The following variables store related information:

 ``` public static String[] words = new String[10000]; // words[] hold words from input public static int currWord; // Current word in words[] public static int numWords; // # words in words[] ```

• So it is a good idea to define these variable in the same area in the Java program

• We have determined that:

 The variable currWord must be defined as a class variable in order to be updated by readOneLine() method.

So we also define the other related variables as class variable...

(BTW, words and numWords could have been defined as local variables).