Documentation: Javadoc, Doc Comments, and Annotation
AAHC

Click on A to make all fonts on the page smaller.

Click on A to make all fonts on the page larger.

Click on HC to toggle high contrast mode. When you move your mouse over some bold words in high contrast mode, related words are automatically highlighted. Text is shown in black and white.


Documenting Your Work

After all of your hard programmming work, your long hours of planning and toil, most people will generally see just the application or running applet. However, when someone considers purchasing your appplications, or offering you a job writing code, they'll probably want you to show your work. In these cases especially, you want your code and documentation to be clean and readable.

Programmers estimate that about 70% of all programming effort goes toward maintenance. Given this percentage, along with the likelihood that someday your code wil be maintained by others, you want to be sure to document your code adequately. Fortunately, Java makes it easy.

Javadoc and API Pages

Oracle provides good-looking API pages for Java. We can create similar pages for our own code. Java provides a tool called Javadoc, which allows us to create API pages that have the professional and clean look.

But before we create those pages, let's add documentation to the code that we created in earlier lessons. The edits we make first will not affect the running of our application because they'll consist of comments, not new code. The comments will contain the format and documentation tags that will enable us to make beautiful--and accessible--documentation.

NoteSome of our code already contains Javadoc comments we added while creating it.

DatabaseManager

In the java4_Lesson13 project, greenDB package, edit DatabaseManager as shown in blue:

CODE TO EDIT: DatabaseManager
package greenDB;

import java.sql.*;

public class DatabaseManager {
    /** A connection to a database */  
    private Connection conn;                                                      
    /** An executable SQL statement */ 
    private Statement stmt;                                                       
    /** The result of an executed SQL statement */
    private ResultSet rset;                                                       
   
    /* DatabaseManager Constructor */    
    /**
     * This constructor connects to the MySQL database at jdbc:mysql://sql.useractive.com:3306. 
     * It creates instances of Statement and ResultSet that will be used by the other methods 
     * in the class. It also checks to see if the Listings table already exists. If it does, it 
     * simply returns to the caller. Otherwise, it instantiates the method to create a table 
     * called Listings, and then populates the table. 
     * <pre>
     * PRE:  MySQL server is available and account for user has been established.
     *       The MySQL driver is installed on the client workstation and its location
     *       has been defined in CLASSPATH (or for Eclipse, in its Referenced Libraries).
     *       Username and password are not null.
     * POST: A connection is made and the Listings table is either created and initialized on 
     *       jdbc:mysql://sql.useractive.com:3306, or the already existing Listings table is 
     *       identified.
     * </pre>
     */                                   
    public DatabaseManager (String username, String password ) {  // the constructor for the database manager
    
        // Connect to database and execute the SQL commands for creating and initializing the Listings table.
        try {
            Class.forName ("com.mysql.jdbc.Driver");  // Load the MySQL JDBC driver
        }
        catch (ClassNotFoundException e) {
            System.out.println("Failed to load JDBC/ODBC driver.");
            e.printStackTrace();
            return;
        }
        
        try {        // Connect to the database--
                     // give the whole URL as a parameter rather than using a variable
            conn = DriverManager.getConnection ("jdbc:mysql://sql.useractive.com:3306/" + username, username, password);
            stmt = conn.createStatement (ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);   // Create a Statement
            DatabaseMetaData aboutDB = conn.getMetaData ();  // Execute the creation and initialization of table query
            String [] tableType = {"TABLE"};
            ResultSet rs = aboutDB.getTables(null, null, "Listings",  tableType);   
            if (!inspectForTable (rs, "Listings")) {         // First find out if the table is already there
                                                             // there is no table, make it from the initialization listing
                String [] SQL = initListingsTable();         // code for this method is below
                for (int i=0; i < SQL.length; i++) 
                {
                    stmt.execute(SQL[i]);
                }
            }
        }catch (SQLException e) {
            e.printStackTrace();
        }
    }
       
    /* initListingsTable */
    /**
     * Creates the Listings table and initializes it with some records. This method connects 
     * to the MySQL database at jdbc:mysql://sql.useractive.com:3306. It then creates a table
     * called Listings and initializes the table with some arbitrary records.
     * <pre>
     * PRE: True  
     * POST: SQL String is created for the initial population of a table named Listings.
     * </pre>
     */
    private String [] initListingsTable() {
        // Executable SQL commands for creating Listings table and inserting initial names and phone numbers.
        String[]  SQL = {
            "create table Listings ("+
            "LAST_NAME  varchar (16)," +
            "FIRST_NAME varchar (16)," +
            "AREA_CODE  varchar(3)," +
            "PREFIX     varchar(3)," +
            "SUFFIX     varchar(4))",
            "insert into Listings values ('ANDERSON', 'JOHN',      '314', '825', '1695')",
            "insert into Listings values ('CABLES',   'WALLY',     '212', '434', '9685')",
            "insert into Listings values ('FRY',      'EDGAR',     '415', '542', '5885')",
            "insert into Listings values ('MARTIN',   'EDGAR',     '665', '662', '9001')",
            "insert into Listings values ('TUCKER',   'JOHN',      '707', '696', '8541')",
        };
        return SQL;
    }

    /* inspectForTable */
    /**
     * Determines if a table exists in the db.
     * <pre>
     * PRE:  Connection to database has been established. rs is not null.
     * POST: Table has not been changed, but its presence is verified (or not).
     * </pre>
     * @param rs ResultSet from DatabaseMetaData query about existing Tables
     * @param tableName String identifying the table in question 
     */						    	  
    private boolean inspectForTable (ResultSet rs, String tableName)  throws SQLException {  // exception will be caught when method is used
        int i;
        ResultSetMetaData rsmd = rs.getMetaData ();                                                 // Get the ResultSetMetaData.  This will be used for the column headings
        int numCols = rsmd.getColumnCount ();                                                       // Get the number of columns in the result set
        
        boolean more = rs.next ();
        while (more) {                                                                              // Get each row, fetching until end of the result set
            for (i=1; i<=numCols; i++) { 
                if (rsmd.getColumnLabel(i) == "TABLE_NAME")                                           // Loop through each row, getting the column data looking for Tables
                    if  (rs.getString(i).equals(tableName))                                            // If the column is the TABLE_NAME, is the the one we are looking for?
                    {
                        System.out.println("Found one that equals " + rs.getString(i));
                        return true;
                    }
            }
            System.out.println("");
            more = rs.next ();                                                                        // Fetch the next result set row
        }
        return false;
    }
  
    /* doGetQuery */
    /**
     * Executes the select query specified.
     * <pre>
     * PRE:  Connection to database has been established. Query is assigned and is a simple
     *       select statement against the Listings table.
     * POST: The query is executed.
     * </pre>
     * @param query a simple select query against the Listings table
     */				
    public void doGetQuery(String query) {                                                          // rather than the "getEntries" of the previous example
        try {
            rset = stmt.executeQuery(query);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
  
    /* doInsertQuery */
    /**
     * Executes an insert statement, specified by query.
     * <pre>
     * PRE:  Connection to database has been established. Query is assigned and is a simple
     *       insert statement into the Listings table.
     * POST: The query is executed.
     * </pre>
     * @param query a simple insert query into the Listings table
     */
    public void doInsertQuery(String query) {   // rather than the hard-coded "addEntry" of the previous example
        try {  
            stmt.executeUpdate(query);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
  
    /* getResultSet */
    /**
     * Returns the current value of the ResultSet instance
     * <pre>
     * PRE:  True
     * POST: ResultSet instance value is returned, its value remains the same as upon entry.
     * </pre>
     */		
    public ResultSet getResultSet() {  // a new method that will let the GUI get the resultSet to manipulate it                      
        return rset;
    } 
  
    /* close */
    /**
     * Closes opened Statements and the Connection.
     * <pre>
     * PRE:  Connection to database has been established. Statement has been created. Listings is a table in the db
     * POST: If remove is true, table Listings is dropped, otherwise it is preserved.  Open Connection and Statement are closed
     * </pre>
     * @param remove boolean to specify if the table Listings should be dropped or not.
     */
    public void close(boolean remove){                                                              // closes all open connections                                             
        try {
            if (remove) 
                stmt.execute ("drop table Listings;");                                 
            stmt.close();
            conn.close();
        }
        catch ( SQLException e ) {
            System.out.println ("\n*** SQLException caught ***\n");
            e.printStackTrace();
        }
    }
}

Save it.

Creating Javadocs

In the top Eclipse Menu Bar, select Project | Generate Javadoc...:

In the Generate Javadoc window, use the Configure... button to get to the C:\jdk\bin\ directory. Choose javadoc.exe to retrieve the javadoc executable code. The default should remain Private; this allows us to view all members (private, package, protected and public). Keep the Destination as it is, so that the resulting Javadoc will be located with the Project. Click Finish:

In the Update Javadoc Location dialog box that appears, you're given the option to choose Javadoc location as the destination folder. Click Yes:

The console displays the actions taking place and then displays a new doc folder in the java4_Lesson13 Project:

Open the doc folder and its greenDB subfolder, then double-click on DatabaseManager.html. Scroll through this API page of the application and observe the way the comments were printed. There is more documentation within the Constructor Summary and Method Summary, because we used more comments in those areas than in others:

When the Javadoc executable runs, it looks for the special comments that begin with /** and end with */. When they're written for class members (instance and class methods and variables), these comments should be entered just before the field's definition or declaration in your code.

Documenting and Tagging the Application

Java provides some tags by default, but you can create your own as well. In this section, we go over each class with additional comments. The code is the same; the only differences are the added comments.

Note"@" is used to denote tags that will be created within the API pages.

Some methods include the words PRE and POST. These stand for preconditions and postconditions. Such specifications for methods are useful for the design, implementation, and use of code:

A PREcondition is an assertion that must be true in order for the operation to run properly. A preconditions of true (or empty) means that there are no stipulations on running the particular method.

A POSTcondition is an assertion that prevails upon completion of the operation. Postconditions describe the results of the actions performed by the operation. They must be accurately and precisely stated.

We will edit and save each class in our database application, and then run the Javadoc executable on the entire package when the classes all have Javadoc comments.

SimplePhoneBook

In java4_Lesson13, edit SimplePhoneBook as shown in blue below:

CODE TO EDIT: SimplePhoneBook
package greenDB;

import javax.swing.JFrame;

/* class SimplePhoneBook */
/**
 * This is the entry point of the SimplePhoneBook application. SimplePhoneBook is a Java application
 * that uses JDBC to store, retrieve, add, and delete phone number listings in a MySQL
 * database. The SimplePhoneBook class instantiates the application window frame and displays it
 * on screen.
 *
 * @author David Green
 * @version 1.0
 */
public class SimplePhoneBook {
    /* main */
    /**
     * The entry point for the SimplePhoneBook application. The main method instantiates the
     * application's frame window and displays it.
     * <pre>
     * PRE:
     * POST: SimplePhoneBook started.
     * </pre>
     * @param args    not used.
     */
    public static void main(String args[]) {
    // Instantiate the phone book frame window and display it.
	 
        PhoneBookFrame pbFrame = new PhoneBookFrame();
        pbFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pbFrame.setVisible(true);
    }
}// End SimplePhoneBook class

Save it.

PhoneBookFrame

In java4_Lesson13, edit PhoneBookFrame as shown in blue below:

CODE TO EDIT: PhoneBookFrame
package greenDB;

import java.awt.*;
import java.awt.event.*;
import java.sql.*;
import javax.swing.*;

//* class PhoneBookFrame */
/*
 * This class represents the SimplePhoneBook user interface. PhoneBookFrame includes the application
 * window as well as the components for retrieving, adding, displaying, and deleting phone number listings
 * for the user.
 *
 * @author David Green
 * @version 1.0
 */
class PhoneBookFrame extends JFrame {
    /** The initial user interface width, in pixels */
    private static final int WIDTH  = 577;
    /** The initial user interface height, in pixels */
    private static final int HEIGHT = 466;
    /** Provides methods for displaying a SQL result set in a JTable */
    private ListingsTableModel tblModel;
    /** Used to display the SQL result set in a cell format */
    private JTable table;
    /** A scrollable view for the SQL result set */
    private JScrollPane scrollPane;
    /** A text field for entering the phone listing's last name */
    private JTextField lNameField    = new JTextField(10);
    /** A text field for entering the phone listing's first name */
    private JTextField fNameField    = new JTextField(10);
    /** A text field for entering the phone listing's area code */
    private JTextField areaCodeField = new JTextField(2);
    /** A text field for entering the phone listing's prefix */
    private JTextField prefixField   = new JTextField(2);
    /** A text field for entering the phone listing's extension */
    private JTextField suffixField   = new JTextField(3);
    /** Database Operations */
    private DatabaseManager myDB;

    /* PhoneBookFrame */
    /**
     * The PhoneBookFrame constructor.
     */
    public PhoneBookFrame() {
        String [] info = PasswordDialog.login(this);  // static login so can call from class
        // create and initialize the listings table
        myDB = new DatabaseManager(info[0], info[1]);
        // Should have access so make GUI   
        
        JButton getButton = new JButton("Get");     // get the listing
        JButton add       = new JButton("+");       // add a listing
        JButton rem       = new JButton("-");       // remove a listing
        JLabel  space     = new JLabel(" ");
        
        // set the window size and title
        setTitle("Simple Phone Book");
        setSize(WIDTH, HEIGHT);
        
        // if user presses Enter, get button pressed
        getRootPane().setDefaultButton(getButton);
        
        // create the panel for looking up listing
        JPanel south = new JPanel();
        south.setLayout(new FlowLayout(FlowLayout.LEFT));
        
        south.add(new JLabel("Last:"));
        south.add(lNameField);
        south.add(new JLabel(" First:"));
        south.add(fNameField);
        south.add(new JLabel("  Phone:  ("));
        south.add(areaCodeField);
        south.add(new JLabel(") "));
        south.add(prefixField);
        south.add(new JLabel("-"));
        south.add(suffixField);
        south.add(new JLabel("   "));
        south.add(getButton);
        
        // create the panel for adding and deleting listings
        JPanel  east           = new JPanel();
        GridBagLayout gb       = new GridBagLayout();
        GridBagConstraints gbc = new GridBagConstraints();
        east.setLayout(gb);
        add.setFont(new Font("SansSerif", Font.BOLD, 12));
        rem.setFont(new Font("SansSerif", Font.BOLD, 12));
        
        gbc.fill = GridBagConstraints.BOTH;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        gb.setConstraints(add, gbc);
        gb.setConstraints(space, gbc);
        gb.setConstraints(rem, gbc);
        east.setLayout(gb);
        east.add(add);
        east.add(space);
        east.add(rem);
        
        // add the panels
        Container contentPane = getContentPane();
        contentPane.add(south, BorderLayout.SOUTH);
        contentPane.add(east, BorderLayout.EAST);
        
        // Add listeners
        getButton.addActionListener(new GetListener());
        
        areaCodeField.addFocusListener(new PhoneFocusListener());
        areaCodeField.getDocument().addDocumentListener(new PhoneDocumentListener(areaCodeField, 3));
        
        prefixField.addFocusListener(new PhoneFocusListener());
        prefixField.getDocument().addDocumentListener(new PhoneDocumentListener(prefixField, 3));
        
        suffixField.addFocusListener(new PhoneFocusListener());
        suffixField.getDocument().addDocumentListener(new PhoneDocumentListener(suffixField, 4));
        
        add.addActionListener(new AddListingListener(this));
        
        // delete the highlighted listing from the result set and database
        rem.addActionListener(
            new ActionListener() {
                public void actionPerformed(ActionEvent aEvent) {
                    try {
                        int selected = table.getSelectedRow();
                        ResultSet rset  = myDB.getResultSet();
                        if(selected != -1 && selected < tblModel.getRowCount()) {
                            rset.absolute(table.getSelectedRow() + 1);
                            rset.deleteRow();
                            table.repaint();
                            table.clearSelection();
                        }
                    } catch (SQLException e) {
                    e.printStackTrace();
                    }
                }
        });
        
        // When the application closes, drop the Listings table and close the connection to MySQL
        addWindowListener(
            new WindowAdapter() {
                public void windowClosing(WindowEvent wEvent) {      
        	        myDB.close(false);
                }
        });
        
        // when the ui first displays do an empty lookup so the center panel doesn't look funny
        getButton.doClick();
        lNameField.requestFocus();    // set focus to last name field (most common lookup)
    }

    public DatabaseManager getDBManager(){
	   return myDB;
    }
 
    /* inner class GetListener */
    /**
     * An inner class for handling the event when the user clicks the "Get" button.
     *
     * @author David Green
     * @version 1.0
     */
    class GetListener implements ActionListener {

    /* actionPerformed */
    /**
     * This method builds a select Query and executes it against the Listings table to retrieve 
     * records. This method creates a select string based on what the user has entered in the 
     * fields for Last Name, First Name, Area Code, Prefix, and Extension. The user may look up a 
     * record in the Listings table based on any combination of data entered in the text fields. 
     * The actionPerformed method builds the query string based on the user's input, executes the 
     * query, and displays it in a scrollable cell format. All data entered in the text fields 
     * is converted to upper case and any single quote character is replaced with a space 
     * character before the query is executed.
     * <pre>
     * PRE:  A connection to the database has been established. All text fields can be empty.
     * POST: A select string is created based on what was entered, the query is executed and the
     *      results are displayed.
     * </pre>
     * @param aEvent an event generated as a result of the "Get" button being clicked
     */
        public void actionPerformed(ActionEvent aEvent) {
            // Get whatever the user entered, trim any white space and change to upper case
            String last  = lNameField.getText().trim().toUpperCase();
            String first = fNameField.getText().trim().toUpperCase();
            String ac    = areaCodeField.getText().trim().toUpperCase();
            String pre   = prefixField.getText().trim().toUpperCase();
            String sfx   = suffixField.getText().trim().toUpperCase();
            
            // Replace any single quote chars w/ space char or SQL will think the ' is the end of the string
            last  = last.replace('\'', ' ');
            first = first.replace('\'', ' ');
            ac    = ac.replace('\'', ' ');
            pre   = pre.replace('\'', ' ');
            sfx   = sfx.replace('\'', ' ');
            
            // Get rid of the last result displayed if there is one
            if(scrollPane != null)
            getContentPane().remove(scrollPane);
            
            // Only execute the query if one or more fields have data, else just display an empty table
            if(last.length()  > 0 ||
             first.length() > 0 ||
             ac.length()    > 0 ||
             pre.length()   > 0 ||
             sfx.length()   > 0) {
             
                // build the query and execute it. Provide the results to the table model
                myDB.doGetQuery(buildQuery(last, first, ac, pre ,sfx));
                ResultSet rset = myDB.getResultSet();
                tblModel = new ListingsTableModel(rset);
                table = new JTable(tblModel);
                
            } else {
                table = new JTable();
            }
            
            // Allows the user to only delete on record at a time
            table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            
            // Add the table with the results to the contentPane and display it.
            scrollPane = new JScrollPane(table);
            getContentPane().add(scrollPane, BorderLayout.CENTER);
            pack();
            doLayout();
        }

    /* buildQuery */
    /**
     * This method builds a simple select statement for retrieving records from the Listings table.
     * The select statement is returned as a string. The select statement includes the last, first, ac,
     * pre, and sfx parameters as the search strings in the where clause of the select statement.
     * If more than one parameter has data, buildQuery will combine them with an "AND" in the where
     * clause.
     * <pre>
     * PRE:  One or more parameters has length > 0.
     * POST: A SQL select statement is returned that selects records from the Listings table.
     * </pre>
     * @param last  create a SQL query that searches Listings where LAST_NAME = last.
     * @param first create a SQL query that searches Listings where FIRST_NAME = first.
     * @param ac    create a SQL query that searches Listings where AREA_CODE = ac.
     * @param pre   create a SQL query that searches Listings where PREFIX = pre.
     * @param sfx   create a SQL query that searches Listings where SUFFIX = sfx.
     * @return a SQL select statement that selects records from the Listings table
     */
        public String buildQuery(String last, String first, String ac, String pre, String sfx) {
            String whereClause = " where";
            // Build the where clause
            if(last.length() > 0)
                whereClause += (" LAST_NAME = '" + last + "'");

            if(first.length() > 0) {
                if(whereClause.length() > 6)
                whereClause += " AND";
                whereClause += (" FIRST_NAME = '" + first + "'");
            }

            if(ac.length() > 0) {
                if(whereClause.length() > 6)
                    whereClause += " AND";
                whereClause += (" AREA_CODE = '" + ac + "'");
            }
            
            if(pre.length() > 0) {
                if(whereClause.length() > 6)
                    whereClause += " AND";
                whereClause += (" PREFIX = '" + pre + "'");
            }
            
            if(sfx.length() > 0) {
                if(whereClause.length() > 6)
                    whereClause += " AND";
                whereClause += (" SUFFIX = '" + sfx + "'");
            }
            
            return "select LAST_NAME, FIRST_NAME, AREA_CODE, PREFIX, SUFFIX from Listings" + whereClause;
        }
    }// End GetListener inner class
}// End PhoneBookFrame class

Save it.

NoteWe have an Inner class defined in this class. Remember to check it out in the Javadoc page!

ListingsTableModel

In java4_Lesson13, edit ListingsTableModel as shown in blue:

CODE TO EDIT: ListingsTableModel
package greenDB;

import java.sql.ResultSet;
import java.sql.SQLException;

import javax.swing.table.AbstractTableModel;

/* class ListingsTableModel */
/**
 * This class provides methods for displaying the results returned from the Listings
 * table. The methods are used by a JTable object so the results may displayed in a cell format.
 *
 * @author David Green
 * @version 1.0
 */
public class ListingsTableModel extends AbstractTableModel {
    /** The result set from the Listings table to be displayed */
    private ResultSet rs;

    /* ListingsTableModel */
    /**
     * The ListingsTableModel constructor.
     * @param rs the result set from the Listings table to be displayed.
     */
    public ListingsTableModel(ResultSet rs) {
        this.rs = rs;
    }

    /* getRowCount */
    /**
     * Returns the number of rows in the result set.
     * <pre>
     * PRE: True
     * POST: The number of rows in the result set is returned.
     * </pre>
     * @return the number of rows in the result set.
     */
    public int getRowCount() {
        try {
            rs.last();
            return rs.getRow();
        } catch (SQLException e) {
            e.printStackTrace();
            return 0;
        }
    }

    /* getColumnCount */
    /**
     * Returns the number of columns to be displayed for the result set. Note that
     * this does not return the number of columns IN the result set. The three phone
     * number fields (area code, prefix, and extension) are combined together to form
     * a single column for output. This method always returns 3 for Last Name, First
     * Name, and Phone Number.
     * <pre>
     * PRE: True
     * POST: The number 3 is returned.
     * </pre>
     * @return the number 3, for the three output columns Last Name, First Name, and Phone Number.
     */
    public int getColumnCount() {
        return 3;
    }

    /* getColumnName */
    /**
     * Returns the name of the column specified by the index.
     * <pre>
     * PRE:  Column is assigned and 0 >= column <= 2.
     * POST: A column name is returned.
     * </pre>
     * @param column the index of the column name to be returned.
     * @return the column name specified.
     */
    public String getColumnName(int column) {
        try {
            String colName = rs.getMetaData().getColumnName(column + 1);
            // Return column names that look better than the database column names.
            // Since getColumnCount always returns 3 we only look for first 3 columns in
            // the result set.
            if(colName.equals("LAST_NAME"))
                return "Last Name";
            else if(colName.equals("FIRST_NAME"))
                return "First Name";
            else if(colName.equals("AREA_CODE"))
                return "Phone Number";
            else return colName;                  // Should never get here.

        } catch (SQLException e) {
            e.printStackTrace();
            return "";
        }
    }

    /* getValueAt */
    /**
     * Returns the value in the result set at the location specified by row and column. If column
     * is equal to 2 (the AREA_CODE), combine the AREA_CODE, PREFIX, and SUFFIX for that row and
     * return the combined string.
     * <pre>
     * PRE:  row and column are assigned and 0 >= column <= 2 and row is within range.
     * POST: The value in the result set at row and column is returned, or the combined
     *       phone number is returned if column = 2.
     * </pre>
     * @param row the row of the result set whose value is to be returned.
     * @param column the column of the result set whose value is to be returned.
     * @return the value in the result set at row and column is returned, or the combined
     * phone number is returned if column = 2.
     */
    public Object getValueAt(int row, int column) {
        try {
            rs.absolute(row + 1);
            // for the 3rd column in the results, combine all of the phone number fields for output
            if(column == 2)
                return "(" + rs.getObject(column + 1) + ") " + rs.getObject(column + 2) + "-" + rs.getObject(column + 3);
            else
                return rs.getObject(column + 1);
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }

}// End ListingsTableModel class

Save it.

phew! This is a lot of work. It's worth doing though to make sure that everyone who encounters our code understands it, appreciates its elegance and efficiency, and can use and modify it as needed. That's the reason we document. OK, now get back to work!

AddListingListener

In java4_Lesson13, edit AddListingListener as shown in blue:

CODE TO EDIT: AddListingListener
package greenDB;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/* class AddListingListener */
/**
 * A listener for when the add button is clicked. The add button looks like a plus ("+") sign. The
 * AddListingListener creates and displays an AddListingDialog box when actionPerformed is called.
 *
 * @author David Green
 * @version 1.0
 */
class AddListingListener implements ActionListener {
    /** The SimplePhoneBook application frame */
    PhoneBookFrame pbf;

    /* AddListingListener */
    /**
     * The AddListingListener constructor.
     * @param pbFrame the SimplePhoneBook application frame object.
     */
    public AddListingListener(PhoneBookFrame pbFrame) {
        pbf = pbFrame;
    }

    /* actionPerformed */
    /**
     * Instantiates and displays the Add Listings Dialog Box. This method is  
     * called when the "+" button is clicked.
     * <pre>
     * PRE:
     * POST: The Add Listings Dialog Box is displayed on screen.
     * </pre>
     * @param aEvent an event generated as a result of the "+" button being clicked.
     */
    public void actionPerformed(ActionEvent aEvent) {
        AddListingDialog addDialog = new AddListingDialog(pbf);
        addDialog.setVisible(true);
    }
}// End AddListingListener class  

Save it.

AddListingDialog

In java4_Lesson13, edit AddListingDialog as shown in blue below:

CODE TO EDIT: AddListingDialog
package greenDB;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

/* class AddListingDialog */
/**
 * A dialog box for adding a new listing to the Listings table. The AddListingDialog has text
 * fields for gathering the new listing's last name, first name, and phone number. All of the text
 * fields are assigned an InputListener, which is responsible for enabling the "Add" button once 
 * all fields contain data. This dialog box is displayed when the user clicks the "+" button on 
 * the application frame window.
 *
 * @author David Green
 * @version 1.0
 */
public class AddListingDialog extends JDialog {
    /** A text field for entering the new phone listing's last name */
    private JTextField lNameField    = new JTextField(16);
    /** A text field for entering the new phone listing's first name */
    private JTextField fNameField    = new JTextField(16);
    /** A text field for entering the new phone listing's area code */
    private JTextField areaCodeField = new JTextField(2);
    /** A text field for entering the new phone listing's prefix */
    private JTextField prefixField   = new JTextField(2);
    /** A text field for entering the new phone listing's extension */
    private JTextField suffixField   = new JTextField(3);
    /** A button which, when clicked, will add the new listing to the Listings table */
    private JButton addButton;
    
    /* AddListingDialog */
    /**
    * The AddListingDialog constructor. Creates a dialog box for adding a new listing to the
    * Listings table.
    * @param owner the Frame from which the dialog is displayed.
    */
    public AddListingDialog(final JFrame owner) {
        // set the dialog title and size
        super(owner, "Add Listing", true);
        setSize(280, 150);
        
        // Create the center panel which contains the fields for entering the new listing
        JPanel center = new JPanel();
        center.setLayout(new GridLayout(3, 2));
        center.add(new JLabel(" Last Name:"));
        center.add(lNameField);
        center.add(new JLabel(" First Name:"));
        center.add(fNameField);
        
        // Here we create a panel for the phone number fields and add it to the center panel.
        JPanel pnPanel = new JPanel();
        pnPanel.add(new JLabel("("));
        pnPanel.add(areaCodeField);
        pnPanel.add(new JLabel(") "));
        pnPanel.add(prefixField);
        pnPanel.add(new JLabel("-"));
        pnPanel.add(suffixField);
        center.add(new JLabel(" Phone Number:"));
        center.add(pnPanel);
        
        // Create the south panel which contains the buttons
        JPanel south = new JPanel();
        addButton    = new JButton("Add");
        JButton cancelButton = new JButton("Cancel");
        addButton.setEnabled(false);
        south.add(addButton);
        south.add(cancelButton);
        
        // Add listeners to the fields and buttons
        lNameField.getDocument().addDocumentListener(new InputListener());
        fNameField.getDocument().addDocumentListener(new InputListener());
        areaCodeField.getDocument().addDocumentListener(new InputListener());
        prefixField.getDocument().addDocumentListener(new InputListener());
        suffixField.getDocument().addDocumentListener(new InputListener());
        
        areaCodeField.getDocument().addDocumentListener(new PhoneDocumentListener(areaCodeField, 3));
        prefixField.getDocument().addDocumentListener(new PhoneDocumentListener(prefixField, 3));
        suffixField.getDocument().addDocumentListener(new PhoneDocumentListener(suffixField, 4));
        
        areaCodeField.addFocusListener(new PhoneFocusListener());
        prefixField.addFocusListener(new PhoneFocusListener());
        suffixField.addFocusListener(new PhoneFocusListener());
        
        // listeners to close the window
        addButton.addActionListener(
            new ActionListener() {
                public void actionPerformed(ActionEvent aEvent) {
                    // ((PhoneBookFrame)owner).doInsertQuery(buildQuery());
                    DatabaseManager ownersDB = ((PhoneBookFrame)owner).getDBManager();
                    ownersDB.doInsertQuery(buildQuery());
                    dispose();
                }
        });

        cancelButton.addActionListener(
            new ActionListener() {
                public void actionPerformed(ActionEvent aEvent) {
                    dispose();
            }
        });
        
        // Add the panels to the dialog window
        Container contentPane = getContentPane();
        contentPane.add(center, BorderLayout.CENTER);
        contentPane.add(south,  BorderLayout.SOUTH);
    }

    /* buildQuery */
    /**
     * This method builds an insert statement for inserting a new record into the Listings table.
     * The insert statement is returned as a string. The insert statement will include the last name,
     * first name, area code, prefix, and extension that the user entered in the add listing dialog
     * box.
     * <pre>
     * PRE:  All of the fields in the Add Listing Dialog box contain data.
     * POST: A SQL insert statement is returned that inserts a new listing into the Listings table.
     * </pre>
     * @return a SQL insert statement that will insert a new listing in the Listings table.
     */
    public String buildQuery() {
        // Get the data entered by the user, trim the white space and change to upper case
        String query = "";
        String last  = lNameField.getText().trim().toUpperCase();
        String first = fNameField.getText().trim().toUpperCase();
        String ac    = areaCodeField.getText().trim().toUpperCase();
        String pre   = prefixField.getText().trim().toUpperCase();
        String sfx   = suffixField.getText().trim().toUpperCase();
        
        // Replace any single quote chars with a space char so the string will not get truncated by SQL
        last  = last.replace('\'', ' ');
        first = first.replace('\'', ' ');
        ac    = ac.replace('\'', ' ');
        pre   = pre.replace('\'', ' ');
        sfx   = sfx.replace('\'', ' ');
        
        // build  and return the insert statement
        return new String("insert into Listings values ('" + last + "', '" +
                                                         first + "', '" +
                                                         ac + "', '" +
                                                         pre + "', '" +
                                                         sfx + "')");
    }

    /* inner class InputListener */
    /**
     * This inner class is a Listener for the text fields of the Add Listing Dialog Box.
     * The listener keeps track of whether all fields (last name, first name, area code,
     * prefix, and extension) have data entered in them. If all fields contain data, the
     * "Add" button of the Add Listing Dialog box is enabled for the user. If any one of
     * the fields is empty or if the phone number fields contain fewer characters than
     * required, the "Add" button is unavailable.
     *
     * @author David Green
     * @version 1.0
     */
    class InputListener implements DocumentListener {

    /* insertUpdate */
    /**
     * This method is called when data is put in the text field, either by typing or by a paste operation.
     * This method tracks the number of characters entered in the field. If all fields, last name,
     * first name, area code, prefix, and extension have data and the phone number fields contain the correct
     * number of characters (that is, 3 characters for area code and prefix and 4 characters for extension),
     * then the Add Listing Dialog box "Add" button is enabled.
     * <pre>
     * PRE:
     * POST: Add Listing Dialog Box "Add" button is enabled if all fields have the required number of characters
     * entered.
     * </pre>
     * @param dEvent the document event
     */

        public void insertUpdate(DocumentEvent dEvent) {
            // If first name, last name have data and phone number is complete
            // enable the add button, give it focus and make it clickable if
            // user hits <enter>.
            if(lNameField.getDocument().getLength()     > 0 &&
                fNameField.getDocument().getLength()     > 0 &&
                areaCodeField.getDocument().getLength() == 3 &&
                prefixField.getDocument().getLength()   == 3 &&
                suffixField.getDocument().getLength()   == 4) {
                
                addButton.setEnabled(true);
                if(dEvent.getDocument() == suffixField.getDocument()) {
                    addButton.requestFocus();
                    getRootPane().setDefaultButton(addButton);
                }
            }
        }


    /* removeUpdate */
    /**
     * This method is called when data is removed from the text field, either by backspacing or highlighting and
     * deleting. This method will track the number of characters removed from the field. If any of the fields
     * last name, first name, area code, prefix, and extension contain less than the required number of characters
     * the "Add" button of the Add Listing Dialog box is disabled.
     * <pre>
     * PRE:
     * POST: Add Listing Dialog Box "Add" button is disabled if any of the fields contain less than the required
     *       number of characters.
     * </pre>
     * @param dEvent the document event
     */
        public void removeUpdate(DocumentEvent dEvent) {
            // If last name or first name don't have data or phone number
            // is  not complete, disable the Add button.
            if(lNameField.getDocument().getLength()   == 0 ||
             fNameField.getDocument().getLength()   == 0 ||
             areaCodeField.getDocument().getLength() < 3 ||
             prefixField.getDocument().getLength()   < 3 ||
             suffixField.getDocument().getLength()   < 4 )
             
            addButton.setEnabled(false);
        }

    /** Not implemented */
        public void changedUpdate(DocumentEvent dEvent) {}

    }// End InputListener inner class
}// End AddListingDialog class

Save it.

NoteAnother Inner class--remember to look for this one on the Javadoc page too.

PhoneDocumentListener

In java4_Lesson13, edit PhoneDocumentListener as shown in blue below:

CODE TO EDIT: PhoneDocumentListener
package greenDB;

import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

/* class PhoneDocumentListener */
/**
 * A listener that is applied to any of the telephone number fields (area code, prefix,
 * and extension). The purpose of this listener is to prevent more than the expected number
 * of characters from being entered in the telephone number fields. That is, the area code and
 * prefix fields might only be allowed to contain 3 characters each while the extension field
 * might only be allowed to contain four characters. The PhoneDocumentListener class accomplishes
 * this by accepting a 'numbers allowed' parameter during construction. Every time a character is
 * entered in the phone number field to which this listener applies, the insertUpdate method is
 * called. The insertUpdate method checks the number of characters in the field and if the number
 * is equal to 'numbers allowed', focus is transferred to the next component.
 *
 * @author David Green
 * @version 1.0
 */

class PhoneDocumentListener implements DocumentListener {
    /** The phone number text field to which this listener applies */
    private JTextField txtField;
    /** The number of characters that will cause focus to be transferred */ 
    private int numsAllowed;

    /* PhoneDocumentListener */
    /**
     * The PhoneDocumentListener constructor.
     * @param tf The phone number text field to which this listener applies.
     * @param numsAllowed The number of characters that can be entered in this field.
     */
    public PhoneDocumentListener(JTextField tf, int numsAllowed) {
        txtField = tf;
        this.numsAllowed = numsAllowed;
    }

    /* insertUpdate */
    /**
     * Called when a character is typed in the field to which this listener is applied.
     * The field is examined for number of characters and if the number is equal to the
     * numbers allowed, as specified during construction, focus is transferred to the next
     * component.
     * <pre>
     * PRE:
     * POST: Focus is transferred if field length equals numsAllowed; else nothing happens.
     * </pre>
     * @param dEvent An event generated as a result of a character being entered in the
     * telephone number field to which this listener is applied.
     */
    public void insertUpdate(DocumentEvent dEvent) {
        if(dEvent.getDocument().getLength() == numsAllowed)
            txtField.transferFocus();
    }

    /** Empty implementation. Method necessary for implementation of DocumentListener */
    public void removeUpdate(DocumentEvent dEvent) {}
   
    /** Empty implementation. Method necessary for implementation of DocumentListener */
    public void changedUpdate(DocumentEvent dEvent) {}

}// End PhoneDocumentListener class

Save it.

PhoneFocusListener

In java4_Lesson13, edit PhoneFocusListener as shown in blue below:

CODE TO EDIT: PhoneFocusListener
package greenDB;

import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.JTextField;

/* class PhoneFocusListener */
/**
 * A listener that will delete any data currently in the telephone number fields (area code, prefix,
 * and extension) whenever focus is detected for those fields. This listener is used in conjunction with
 * the PhoneDocumentListener to prevent more than the expected number of characters from being entered
 * in the telephone number fields. That is, the area code and prefix fields will only be allowed to
 * contain three characters each while the extension field will only be allowed to contain four characters.
 *
 * @author David Green
 * @version 1.0
 */
class PhoneFocusListener implements FocusListener {

    /* focusGained */
    /**
     * Called when the field to which this listener applies gains focus. This method will delete
     * any data currently contained in the field.
     * <pre>
     * PRE:  True.
     * POST: Any data in the telephone number field to which this listener applies is deleted.
     * </pre>
     * @param fEvent An event generated as a result of focus being gained on this telephone number field.
     */
    public void focusGained(FocusEvent fEvent) {
        JTextField tf = (JTextField)fEvent.getSource();
        tf.setText("");
    }

    /** Not implemented */
    public void focusLost(FocusEvent fEvent){}

}// End PhoneFocusListener class


Save it.

The Package Documentation

All classes should be fine and error-free. Remember that the class to start it all was SimplePhoneBook. Select SimplePhoneBook and run it to verify that all is well. Everything should work the same as before; all that we changed was documentation.

Select the java4_Lesson13 Project so that it is highlighted. In the top Eclipse Menu Bar, select Project | Generate Javadoc.... In the Generate Javadoc window that opens, click the Configure... button to get to the directory C:\java\bin. Select javadoc.exe to get the javadoc executable code. Keep the default of Private as it is. This will let us see all members (private, package, protected, and public). Keep the Destination as it is so the documentation will be located with the Project. Click Finish.

It's the same process we performed earlier--we are simply now making the API html pages for all of the classes. This time we want to see a listing with links to all of the classes, so open doc | index.html.

On your generated docs index page, click on the links as you would any html page to see your class documentation. In fact, look at these pages until you see all that is offered, and how it all relates to the comments we wrote. For example, check out PhoneBookFrame.html and AddListingDialog.html and look for a Nested Class Summary to see their inner classes. Good documentation makes your work more refined and finished.

Additional Resources

We gone over many JDBC elements, but lots of additional techniques are available. For example, you might want to:

Here are additional links to assist you with your database applications:

SQL

Or just go to Google and do a search on SQL tutorial. Our last search got 149,000 hits--there are LOTS of tutorials on SQL out there! And, of course, for those who like something in their hands, there are lots of books on SQL!

Javadoc

Annotations

Annotations look similar to Javadoc tags (use of @) but they are actually a form of metadata that can be added to Java source code. Annotations complement Javadoc tags. In general, if the markup is intended to affect or produce documentation, it should probably be a Javadoc tag; otherwise, it should be an annotation.

The use of the "@" symbol in both Javadoc comments and in annotations is not coincidental-—they are related conceptually. But note that the Javadoc deprecated tag starts with a lowercase d and the annotation starts with an uppercase D. We will see them more when we look at enterprise applications. For now, here are a few resources for you:

What's Next?

In the next course we will continue our work with applications, but delve into distributed computing as well. Everything you learned in this course--GUIs, Exceptions, Threads, Database Connectivity, Documentation--you'll use again. You're cultivating some great skills!