///////////////////////////////////////////////////////////////////////////////
//                                                                             
// JTOpen (IBM Toolbox for Java - OSS version)                              
//                                                                             
// Filename: SQLResultSetTablePane.java
//                                                                             
// The source code contained herein is licensed under the IBM Public License   
// Version 1.0, which has been approved by the Open Source Initiative.         
// Copyright (C) 1997-2001 International Business Machines Corporation and     
// others. All rights reserved.                                                
//                                                                             
///////////////////////////////////////////////////////////////////////////////

package com.ibm.as400.vaccess;

import com.ibm.as400.access.Trace;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.FocusEvent;                       // @C2A
import java.awt.event.FocusListener;                    // @C2A
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;                              // @C0A
import java.sql.SQLWarning;
import java.sql.Statement;
import java.sql.Types;
import java.util.Enumeration;

import java.awt.datatransfer.StringSelection; //@D6A
import javax.swing.JTextField; //@D6A
import javax.swing.ActionMap; //@D6A
import javax.swing.InputMap; //@D6A
import javax.swing.text.DefaultEditorKit; //@D6A
import javax.swing.KeyStroke; //@D6A
import javax.swing.Action; //@D6A
import java.awt.event.ActionEvent; //@D6A
import javax.swing.AbstractAction; //@D6A
import java.awt.Toolkit; //@D6A


/**
The SQLResultSetTablePane class represents a graphical user
interface that presents the contents of a JDBC result set
generated by an SQL query in a JTable.

<p>You must specify an <a href="SQLConnection.html">
SQLConnection</a> object and SQL query string to use for generating
the data.  Alternately, you can specify a ResultSet object directly.
If you specify a ResultSet object, it will override any SQLConnection
or SQL query previously set.  In addition, if you specify a ResultSet,
this class will use memory more efficiently if you create the ResultSet as scrollable.

<p>You must explicitly call <a href="#load()">load()</a> to load the information
from the result set.  The table will be empty until load() is called.
If the query or result set includes updatable columns, then the respective
columns will be editable.

<p>This class assumes that the necessary JDBC driver(s) are already registered.

<p>Call <a href="#close()">close()</a> to ensure that the result set
is closed when this table is no longer needed.

<p>Most errors are reported as <a href="ErrorEvent.html">ErrorEvent</a>s
rather than throwing exceptions.  Listen for ErrorEvents in order to diagnose and recover
from error conditions.

<p>SQLResultSetTablePane objects generate the following events:
<ul>
  <li>ErrorEvent
  <li>PropertyChangeEvent
  <li>TableModelEvent
  <li>WorkingEvent
</ul>

<p>This example creates an SQLResultSetTablePane using an SQLConnection
and query and displays it using a JTable:

<pre>
// Register the IBM Toolbox for Java JDBC driver.
DriverManager.registerDriver(new com.ibm.as400.access.AS400JDBCDriver());
<br>
// Create the SQLResultSetTablePane object.
SQLConnection connection = new SQLConnection("jdbc:as400://MySystem", "Userid", "Password");
String query = "SELECT * FROM MYLIB.MYTABLE";
SQLResultSetTablePane table = new SQLResultSetTablePane(connection, query);
<br>
// Put the table in a JFrame.
JFrame frame = new JFrame("My Window");
frame.getContentPane().add(table);
<br>
// Set up the error dialog adapter.
table.addErrorListener(new ErrorDialogAdapter(frame));
<br>
// Display the JFrame.
frame.pack();
frame.show();
<br>
// Load the contents of the table.
table.load();
</pre>

<p>This example creates an SQLResultSetTablePane using a ResultSet
and displays it using a JTable:

<pre>
// Register the IBM Toolbox for Java JDBC driver.
DriverManager.registerDriver(new com.ibm.as400.access.AS400JDBCDriver());
<br>
// Use JDBC to execute the SQL query directly.
Connection connection = DriverManager.getConnection("jdbc:as400://MySystem", "Userid", "Password");
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ResultSet rs = statement.executeQuery("SELECT * FROM MYLIB.MYTABLE");
<br>
// Create the SQLResultSetTablePane object.
SQLResultSetTablePane table = new SQLResultSetTablePane(rs);
<br>
// Put the table in a JFrame.
JFrame frame = new JFrame("My Window");
frame.getContentPane().add(table);
<br>
// Set up the error dialog adapter.
table.addErrorListener(new ErrorDialogAdapter(frame));
<br>
// Display the JFrame.
frame.pack();
frame.show();
<br>
// Load the contents of the table.
table.load();
</pre>
@deprecated Use Java Swing instead, along with the classes in package <tt>com.ibm.as400.access</tt>
**/
public class SQLResultSetTablePane
    extends JComponent
    implements Serializable
{
  private static final String copyright = "Copyright (C) 1997-2001 International Business Machines Corporation and others.";

  // The variables and methods which have private commented out
  // had to be made package scope since some JVMs (IE and AS400)
  // does not allow inner class to access private items in their
  // containing class.


  // The table contained in this panel.
  /*private*/
  transient JTable table_; //@B0C - made transient
  /*private*/transient JScrollPane tablePane_; //@B0C - made transient
  // The data model for the table.
  /*private*/SQLResultSetTableModel model_; //@B0C - made transient

  //@B0 - need to save the table's state since it's transient now.
  private Color tableColor_ = null; //@B0A
  private boolean tableShowHorizontalLines_ = true; //@B0A
  private boolean tableShowVerticalLines_ = true; //@B0A

  // Event support.
  transient private PropertyChangeSupport changeListeners_
      = new PropertyChangeSupport(this);
  transient private VetoableChangeSupport vetoListeners_
      = new VetoableChangeSupport(this);
  transient private ErrorEventSupport errorListeners_
      = new ErrorEventSupport(this);
  transient private ListSelectionEventSupport selectionListeners_
      = new ListSelectionEventSupport(this);

  // Adapter for listening for working events and enabling working cursor.
  transient private WorkingCursorAdapter worker_
      = new WorkingCursorAdapter(this); 

  // Renderers for the different types of data, columns use these.
  /*private*/DBCellRenderer rightCell_ = new DBCellRenderer(SwingConstants.RIGHT); 
  /*private*/DBCellRenderer leftCell_ = new DBCellRenderer(SwingConstants.LEFT);
  // @C1D /*private*/ DBDateCellRenderer dateCell_ = new DBDateCellRenderer(DBDateCellRenderer.FORMAT_DATE);
  // @C1D /*private*/ DBDateCellRenderer timeCell_ = new DBDateCellRenderer(DBDateCellRenderer.FORMAT_TIME);
  // @C1D /*private*/ DBDateCellRenderer timestampCell_ = new DBDateCellRenderer(DBDateCellRenderer.FORMAT_TIMESTAMP);

  DBCellSelector cellSelector_ = new DBCellSelector(); //@D6A
  DBCellEditor cellEditor_ = new DBCellEditor(); //@D6A

  // General types of data in columns.  Used to determine column renderer.
  private static final int TYPE_CHAR = 1;
  private static final int TYPE_TIME = 2;
  private static final int TYPE_TIMESTAMP = 3;
  private static final int TYPE_DATE = 4;
  private static final int TYPE_BIT = 5;
  private static final int TYPE_NUMBER = 6;

  /**
  Constructs a SQLResultSetTablePane object.
  **/
  public SQLResultSetTablePane()
  {
    super();

    // Create table and model to hold data.
    model_ = new SQLResultSetTableModel();

    /* @B0M - moved to initializeTransient()
    
        table_ = new JTable();
        table_.setAutoCreateColumnsFromModel(false);
        table_.setModel(model_);
        table_.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    
        // Listen for events, pass them on to our listeners.
        model_.addPropertyChangeListener(changeListeners_);
        model_.addVetoableChangeListener(vetoListeners_);
        model_.addErrorListener(errorListeners_);
        model_.addWorkingListener(worker_);
        table_.getSelectionModel().addListSelectionListener(selectionListeners_);
    
        // Build GUI
             setLayout(new BorderLayout());
        tablePane_ = new JScrollPane(table_); // @A1C
        add("Center",tablePane_);
    
    */

    initializeTransient(); //@B0A
  }


  /**
  Constructs a SQLResultSetTablePane object.
  
  @param       connection   The SQL connection.
  @param       query        The SQL query.
  **/
  public SQLResultSetTablePane(SQLConnection connection, String query)
  {
    super();

    // Create table and model to hold data.
    // note: model validates parms
    model_ = new SQLResultSetTableModel(connection, query);

    /* @B0M - moved to initializeTransient()
    
        table_ = new JTable();
        table_.setAutoCreateColumnsFromModel(false);
        table_.setModel(model_);
        table_.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    
        // Listen for events, pass them on to our listeners.
        model_.addPropertyChangeListener(changeListeners_);
        model_.addVetoableChangeListener(vetoListeners_);
        model_.addErrorListener(errorListeners_);
        model_.addWorkingListener(worker_);
        table_.getSelectionModel().addListSelectionListener(selectionListeners_);
    
        // Build GUI
        setLayout(new BorderLayout());
        tablePane_ = new JScrollPane(table_); // @A1C
        add("Center",tablePane_);
    
    */

    initializeTransient(); //@B0A
  }


  // @C0A
  /**
  Constructs a SQLResultSetTablePane object.
  
  @param resultSet  The SQL result set.
  @param cacheAll   true to cache the entire result set when <a href="#load()">load()</a>
                    is called, false to cache parts of the result set as they are
                    needed.  Passing true may result in slow initial presentation of
                    the data.  However, it may be necessary to pass true if the result
                    set is expected to close when the model is still needed.
  **/
  public SQLResultSetTablePane(ResultSet resultSet, boolean cacheAll)
  {
    super();

    // Create table and model to hold data.
    // note: model validates parms
    model_ = new SQLResultSetTableModel(resultSet, cacheAll);
    initializeTransient();
  }


  /**
  Adds a listener to be notified when an error occurs.
  
  @param  listener  The listener.
  **/
  public void addErrorListener(ErrorListener listener)
  {
    errorListeners_.addErrorListener(listener);
  }


  /**
  Adds a listener to be notified when the selection changes.
  
  @param  listener  The listener.
  **/
  public void addListSelectionListener(ListSelectionListener listener)
  {
    selectionListeners_.addListSelectionListener(listener);
  }


  /**
  Adds a listener to be notified when the value of any bound
  property is changed.
  
  @param  listener  The listener.
  **/
  public void addPropertyChangeListener(PropertyChangeListener listener)
  {
    changeListeners_.addPropertyChangeListener(listener);
    super.addPropertyChangeListener(listener);
  }


  /**
  Adds a listener to be notified when the value of any constrained
  property is changed.
  
  @param  listener  The listener.
  **/
  public void addVetoableChangeListener(VetoableChangeListener listener)
  {
    vetoListeners_.addVetoableChangeListener(listener);
    super.addVetoableChangeListener(listener);
  }



  /**
  Clears all SQL warnings.
  **/
  public void clearWarnings()
  {
    model_.clearWarnings();
  }


  /**
  Closes the SQL result set this table represents.
  **/
  public void close()
  {
    model_.close();
  }



  /**
  Returns the column model for this table.
  
  @return  Model for this table's columns.
  **/
  public TableColumnModel getColumnModel()
  {
    return table_.getColumnModel();
  }



  /**
  Returns the title of a column.  This is used for the table column heading.
  If an error occurs, null is returned.
  
  @param columnIndex The index of the column.  Indices start at 0.
  
  @return  The title of the column.
  **/
  public String getColumnTitle(int columnIndex)
  {
    try
    {
      return(String)(table_.getColumnModel().getColumn(columnIndex).getHeaderValue());
    }
    catch(Exception e)
    {
      Trace.log(Trace.WARNING, "getColumnTitle() error:" + e);
      return null;
    }
  }



  /**
  Returns the width of a column.
  If an error occurs, 0 is returned.
  
  @param columnIndex The index of the column.  Indices start at 0.
  
  @return  The width of the column.
  **/
  public int getColumnWidth(int columnIndex)
  {
    try
    {
      return table_.getColumnModel().getColumn(columnIndex).getPreferredWidth(); //@B1C
    }
    catch(Exception e)
    {
      Trace.log(Trace.WARNING, "getColumnWidth() error:" + e);
      return 0;
    }
  }



  /**
  Returns the SQL connection with which to access data.
  
  @return The SQL connection.
  **/
  public SQLConnection getConnection()
  {
    return model_.getConnection();
  }



  /**
  Returns the data model for the table.
  
  @return  The data model for the table.
  **/
  public SQLResultSetTableModel getDataModel()
  {
    return model_;
  }



  /**
  Returns the color used to draw grid lines.
  
  @return The color used to draw grid lines.
  **/
  public Color getGridColor()
  {
    //@B0D    return table_.getGridColor();
    return tableColor_; //@B0A
  }



  /**
  Returns the SQL query used to generate the table data.
  
  @return The SQL query.
  **/
  public String getQuery()
  {
    return model_.getQuery();
  }



  // @C0A
  /**
  Returns the SQL result set.
  
  @return The SQL result set.
  **/
  public ResultSet getResultSet()
  {
    return model_.getResultSet();
  }



  /**
  Returns the ListSelectionModel that is used to maintain row selection state.
  
  @return  The model that provides row selection state.
  **/
  public ListSelectionModel getSelectionModel()
  {
    return table_.getSelectionModel();
  }



  /**
  Returns whether horizontal lines are drawn between rows.
  @return true if horizontal lines are to be drawn; false otherwise.
  **/
  public boolean getShowHorizontalLines()
  {
    //@B0D    return table_.getShowHorizontalLines();
    return tableShowHorizontalLines_; //@B0A
  }



  /**
  Returns whether vertical lines are drawn between columns.
  @return true if vertical lines are to be drawn; false otherwise.
  **/
  public boolean getShowVerticalLines()
  {
    //@B0D    return table_.getShowVerticalLines();
    return tableShowVerticalLines_; //@B0A
  }



  /**
  Returns the string value at the specifed row and column.
  Indices start at 0.
  If an error occurs, null is returned.
  
  @param  rowIndex            The row index.
  @param  columnIndex         The column index.
  
  @return The value at the specified row and column as a string.
  **/
  // Note that this method is dependent on the cell renderer of a column
  // being a JLabel.
  public String getStringValueAt(int rowIndex, int columnIndex)
  {
    // Try to catch row index out of range.
    if(rowIndex >= model_.getRowCount())
    {
      Trace.log(Trace.WARNING, "getStringValueAt() column out of range");
      return null;
    }

    try
    {
      TableColumnModel cmodel = getColumnModel();
      Component cellComp = cmodel.getColumn(columnIndex)
                           .getCellRenderer()
                           .getTableCellRendererComponent(table_,
                                                          getValueAt(rowIndex,columnIndex),
                                                          false,
                                                          false,
                                                          rowIndex,
                                                          columnIndex);
      if(cellComp instanceof JLabel)
        return((JLabel)cellComp).getText();
      else
        return null;
    }
    catch(Exception e)
    {
      Trace.log(Trace.WARNING, "getStringValueAt() error:" + e);
      return null;
    }
  }



  /**
  Returns the value at the specifed row and column.
  Indices start at 0.
  If an error occurs, null is returned.
  
  @param  rowIndex            The row index.
  @param  columnIndex         The column index.
  
  @return The value at the specified row and column.
  **/
  public Object getValueAt(int rowIndex, int columnIndex)
  {
    try
    {
      // must change the table column index to the
      // model index
      return model_.getValueAt(rowIndex,
                               getColumnModel().getColumn(columnIndex).getModelIndex());
    }
    catch(Exception e)
    {
      Trace.log(Trace.WARNING, "getStringValueAt() error:" + e);
      return null;
    }
  }



  /**
  Returns the warnings generated by the JDBC connection, statement, and
  result set.
  The warnings from the result set will be
  linked to the end of any statement warnings, which in turn are linked
  to the end of any connection warnings.
  Warnings are cleared when <i>load()</i> or <i>clearWarnings()</i>
  is called.
  
  @return The warnings generated by the connection, statement, and
  result set, or null if none.
  **/
  public SQLWarning getWarnings()
  {
    return model_.getWarnings();
  }



  void handleFocus(FocusEvent event)                                                    // @C2A
  {                                                                                     // @C2A
    // Can not call this directly from inner                                             @C2A
    // class, since it is protected.                                                     @C2A
    processFocusEvent(new FocusEvent(this, event.getID(), event.isTemporary()));      // @C2A
  }                                                                                     // @C2A


  /**
   * Initializes transient data.
  **/
  private void initializeTransient()
  {
    //@B0M - this code moved out of ctor

    table_ = new JTable();
    table_.setAutoCreateColumnsFromModel(false);
    table_.setModel(model_);
    model_.setTable(table_);   /* Let the model know about the table so that the selected cell can be preserved @B6A*/ 
    table_.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);

    table_.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); //@D6A
    table_.setRowSelectionAllowed(true); //@D6A
    table_.setColumnSelectionAllowed(true); //@D6A
    table_.setCellSelectionEnabled(true); //@D6A

    //@D6A - See if we can use the new JDK 1.3 function
    boolean useNewBehavior;
    try
    {
      Class.forName("javax.swing.ActionMap");
      useNewBehavior = true;
    }
    catch(Throwable t)
    {
      Trace.log(Trace.WARNING, "Unable to load JDK 1.3 classes. Will use old behavior for SQLResultSetTablePane.");
      useNewBehavior = false;
    }

    if (useNewBehavior) //@D6A
    {
      //@D6A - JTextField has a copy action already associated with it
      // that was put there by the Swing implementation for this platform.
      // We look that up and get the correct key mappings for this platform's
      // copy function. We then add those mappings to the JTable.
      Object keyToLookFor = null;
      JTextField dummyField = new JTextField();
      ActionMap dummyActions = dummyField.getActionMap();
      Object[] allKeys = dummyActions.allKeys();
      for(int i=0; i<allKeys.length && keyToLookFor == null; ++i)
      {
        if(dummyActions.get(allKeys[i]) instanceof DefaultEditorKit.CopyAction)
        {
          keyToLookFor = allKeys[i];
        }
      }
      if(keyToLookFor != null)
      {
        InputMap dummyInputs = dummyField.getInputMap();
        KeyStroke[] allPossibleKeys = dummyInputs.allKeys();
        for(int i=0; i<allPossibleKeys.length; ++i)
        {
          Object actionForKey = dummyInputs.get(allPossibleKeys[i]);
          if(actionForKey.equals(keyToLookFor))
          {
            table_.getInputMap().put(allPossibleKeys[i], keyToLookFor); // Add all the key bindings for this action
          }
        }
        if(table_.getInputMap().size() > 0)
        {
          Action copyAction = new AbstractAction("copy")
          {
            public void actionPerformed(ActionEvent e)
            {
              if(e.getSource() == table_)
              {
                StringBuffer textToCopy = new StringBuffer();
                int[] rows = table_.getSelectedRows();
                int[] columns = table_.getSelectedColumns();
                for (int i=0; i<rows.length; ++i)
                {
                  for (int j=0; j<columns.length; ++j)
                  {
                    if (table_.isCellSelected(rows[i], columns[j]))
                    {
                      textToCopy.append(table_.getValueAt(rows[i], columns[j]));
                      textToCopy.append('\t');
                    }
                  }
                  textToCopy.append('\n');
                }
                StringSelection dataToTransfer = new StringSelection(textToCopy.toString());
                Toolkit.getDefaultToolkit().getSystemClipboard().setContents(dataToTransfer, dataToTransfer);
              }
              else
              {
                Trace.log(Trace.WARNING, "Copy action received from unknown source: "+e.getSource());
              }
            }
          };
          table_.getActionMap().put(keyToLookFor, copyAction);
        }
        else
        {
          Trace.log(Trace.WARNING, "Table copy function not enabled. No key bindings found.");
        }
      }
      else
      {
        Trace.log(Trace.WARNING, "Table copy function not enabled. No copy actions found.");
      }
    }


    if(tableColor_ == null) tableColor_ = table_.getGridColor(); //@B0A

    // Listen for events, pass them on to our listeners.
    model_.addPropertyChangeListener(changeListeners_);
    model_.addVetoableChangeListener(vetoListeners_);
    model_.addErrorListener(errorListeners_);
    model_.addWorkingListener(worker_);
    table_.getSelectionModel().addListSelectionListener(selectionListeners_);

    // Build GUI
    setLayout(new BorderLayout());
    tablePane_ = new JScrollPane(table_); // @A1C
    add("Center",tablePane_);

    // Scrolling performance improvement.                                                  @C3A
    table_.setDoubleBuffered(true);                                                     // @C3A
    tablePane_.getViewport().setBackingStoreEnabled(true);                              // @C3A
    tablePane_.setDoubleBuffered(true);                                                 // @C3A
    tablePane_.getViewport().setDoubleBuffered(true);                                   // @C3A

    if (!useNewBehavior) //@D6A - don't need it if we are running JDK 1.3
    {
      // This option taken from Swing performance hints in JViewport javadoc                 @C3A
      // and at http://java.sun.com/products/jfc/tsc/articles/performance/                   @C3A
      tablePane_.getViewport().putClientProperty("EnableWindowBlit", Boolean.TRUE);       // @C3A
    }

    // Pass on focus events from the table to our callers.                                 @C2A
    table_.addFocusListener(new FocusListener()
                            {                                       // @C2A
                              public void focusGained(FocusEvent event)
                              {
                                handleFocus(event);
                              }               // @C2A
                              public void focusLost(FocusEvent event)
                              {
                                handleFocus(event);
                              }                 // @C2A
                            });                                                                             // @C2A

    //@B0A
    // We add a fake FocusListener whose real purpose is to uninstall
    // the UI early so the JTable that is part of our UI does not try
    // to get serialized.
    // See also: source code for javax.swing.JComponent in JDK 1.2.
    addFocusListener(new SerializationListener(this)); //@B0A
    addFocusListener(new SerializationListener(model_, table_)); //@B0A

  }



  /**
  Loads the table based on the state of the system.  This causes the
  query to be run.
  The <i>query</i> and <i>connection</i> properties
  must be set before this method is called.
  The table heading is reconstructed to ensure it matches
  the data, so any column customization will be lost.
  **/
  public void load()
  {
    // refresh the result set data
    // note: model handles error conditions
    model_.load();
    refreshHeadings();
  }


  /**
  Restore the state of this object from an object input stream.
  It is used when deserializing an object.
  @param in The input stream of the object being deserialized.
  @exception IOException
  @exception ClassNotFoundException
  **/
  private void readObject(java.io.ObjectInputStream in)
  throws IOException, ClassNotFoundException
  {
    // Restore the non-static and non-transient fields.
    in.defaultReadObject();

    // Initialize the transient fields.
    changeListeners_ = new PropertyChangeSupport(this);
    model_.addPropertyChangeListener(changeListeners_);
    vetoListeners_ = new VetoableChangeSupport(this);
    model_.addVetoableChangeListener(vetoListeners_);
    errorListeners_ = new ErrorEventSupport(this);
    model_.addErrorListener(errorListeners_);
    worker_ = new WorkingCursorAdapter(this);
    model_.addWorkingListener(worker_);
    selectionListeners_ = new ListSelectionEventSupport(this);

    initializeTransient(); //@B0A
    //@B0D table_.getSelectionModel().addListSelectionListener(selectionListeners_);

    //@B0 - table_ is now transient, so we need to reset its attributes.
    table_.setGridColor(tableColor_); //@B0A
    table_.setShowHorizontalLines(tableShowHorizontalLines_); //@B0A
    table_.setShowVerticalLines(tableShowVerticalLines_); //@B0A

  }



  /**
  Updates the table header to match the result set data.
  Any column customization will be lost.
  **/
  public void refreshHeadings()
  {
    Runnable refreshHeading = new Runnable()
    {
      public void run()
      {

        // Remove all columns.
        // First copy enumereration, then delete each column.
        TableColumnModel model = table_.getColumnModel();
        int oldColumnCount = model.getColumnCount();
        TableColumn oldColumns[] = new TableColumn[oldColumnCount];
        Enumeration e = model.getColumns();
        for(int i=0; e.hasMoreElements() ; ++i)
        {
          oldColumns[i] = (TableColumn)e.nextElement();
        }
        for(int i=0; i<oldColumnCount; ++i)
        {
          model.removeColumn(oldColumns[i]);
        }

        // set up columns to match data
        int numColumns = model_.getColumnCount();

        // Type of data in column.
        int type, sqltype;

        // Get size of font.  Note if this method is called too early,
        // table_.getFont()==null, and column widths won't be adjusted.
        int size = 0;
        if(table_.getFont() != null)
          // Note: 'M' is just used to get a reasonable width of a
          // large character in this font.
          size = table_.getFontMetrics(table_.getFont()).charWidth('M');
        int colSize, colDataSize, colTitleSize;

        // set up columns
        for(int i=0; i<numColumns; ++i)
        {
          TableColumn col = new TableColumn(i);
          col.setIdentifier(model_.getColumnID(i));

          sqltype = model_.getColumnType(i);
          switch(sqltype)
          {
            case Types.BIGINT:
            case Types.BIT:
            case Types.DECIMAL:
            case Types.DOUBLE:
            case Types.FLOAT:
            case Types.INTEGER:
            case Types.NUMERIC:
            case Types.REAL:
            case Types.SMALLINT:
            case Types.TINYINT:
              type = TYPE_NUMBER;
              break;
            case Types.DATE:
              type = TYPE_DATE;
              break;
            case Types.TIME:
              type = TYPE_TIME;
              break;
            case Types.TIMESTAMP:
              type = TYPE_TIMESTAMP;
              break;
            case Types.BINARY:
            case Types.LONGVARBINARY:
            case Types.VARBINARY:
              type = TYPE_BIT;
              break;
            default:  // CHAR, LONGVARCHAR, NULL, OTHER, VARCHAR
              type = TYPE_CHAR;
          }

          String title = model_.getColumnName(i);

          if(type == TYPE_CHAR ||
             type == TYPE_BIT ||
             type == TYPE_DATE ||
             type == TYPE_TIME ||
             type == TYPE_TIMESTAMP)
          {
            col.setCellRenderer(leftCell_);
            col.setHeaderRenderer(new VObjectHeaderRenderer(title, SwingConstants.LEFT));
          }
/*          else if(type == TYPE_DATE)
          {
            col.setCellRenderer(leftCell_);     // @C1C
            col.setHeaderRenderer(new VObjectHeaderRenderer(
                                                           title,SwingConstants.LEFT));
          }
          else if(type == TYPE_TIME)
          {
            col.setCellRenderer(leftCell_);     // @C1C
            col.setHeaderRenderer(new VObjectHeaderRenderer(
                                                           title,SwingConstants.LEFT));
          }
          else if(type == TYPE_TIMESTAMP)
          {
            col.setCellRenderer(leftCell_);     // @C1C
            col.setHeaderRenderer(new VObjectHeaderRenderer(
                                                           title,SwingConstants.LEFT));
          }
*/          
          else    // numeric
          {
            col.setCellRenderer(rightCell_);
            col.setHeaderRenderer(new VObjectHeaderRenderer(title, SwingConstants.RIGHT));
          }

          // Adjust width if font size is available.
          if(size != 0)
          {
            colDataSize = model_.getColumnWidth(i);
            if(type == TYPE_BIT)
              // bit data displayed in hex, need more room
              colDataSize = colDataSize*2;
            colTitleSize = title.length();
            colSize = colDataSize>colTitleSize?colDataSize:colTitleSize;
            // add 10 to account for the empty border in the cells
            col.setPreferredWidth(colSize*size+10); //@B1C
          }

          if(!model_.isUpdatable()) //@D6A
          {
            col.setCellEditor(cellSelector_); //@D6A
          }
          else if (type == TYPE_CHAR) //@D6C
          {
            col.setCellEditor(cellEditor_); //@D6A
          }
          // otherwise, just use the table's default editor.


          table_.addColumn(col);
        }

        // Redo the panel.  This is needed in the case where there was
        // no data previously, since no header would have been created
        // for the table.  Only done if no previous columns, and now
        // there are columns.
        if(oldColumnCount == 0 && numColumns > 0)
        {
          if(tablePane_ != null)
            remove(tablePane_);
          tablePane_ = new JScrollPane(table_); // @A1C
          add("Center",tablePane_);
        }

        // Refresh the pane.
        validate();

      }
    };

    // Try and refresh the heading in the event dispatcher thread.
    // This is done because doing it inline seems to cause hangs,
    // and Swing documentation seems to suggest doing all GUI
    // work in the event dispatching thread.
    try
    {
      SwingUtilities.invokeAndWait(refreshHeading);
    }
    catch(Error e)
    {
      // Error received.  Assume that the error was because we are
      // already in the event dispatching thread.  Do work in the
      // current thread.
      Trace.log(Trace.DIAGNOSTIC, "invokeAndWait error:" + e);
      refreshHeading.run();
    }
    catch(Exception e)
    {
      Trace.log(Trace.ERROR, "invokeAndWait exception:" + e);
    }
  }



  /**
  Removes a listener from being notified when an error occurs.
  
  @param  listener  The listener.
  **/
  public void removeErrorListener(ErrorListener listener)
  {
    errorListeners_.removeErrorListener(listener);
  }



  /**
  Removes a listener from being notified when the selection changes.
  
  @param  listener  The listener.
  **/
  public void removeListSelectionListener(ListSelectionListener listener)
  {
    selectionListeners_.removeListSelectionListener(listener);
  }



  /**
  Removes a listener from being notified when the value of any bound
  property is changed.
  
  @param  listener  The listener.
  **/
  public void removePropertyChangeListener(PropertyChangeListener listener)
  {
    changeListeners_.removePropertyChangeListener(listener);
    super.removePropertyChangeListener(listener);
  }



  /**
  Removes a listener from being notified when the value of any constrained
  property is changed.
  
  @param  listener  The listener.
  **/
  public void removeVetoableChangeListener(VetoableChangeListener listener)
  {
    vetoListeners_.removeVetoableChangeListener(listener);
    super.removeVetoableChangeListener(listener);
  }



  /**
  Sets the title of a column.  This is used for the table column heading.
  
  @param columnIndex The index of column.  Indices start at 0.
  @param title       The title for the column.
  **/
  public void setColumnTitle(int columnIndex,
                             String title)
  {
    // Catch errors if the index is out of range.
    try
    {
      table_.getColumnModel().getColumn(columnIndex).setHeaderValue(title);
    }
    catch(Exception e)
    {
      Trace.log(Trace.WARNING, "setColumnTitle() error:" + e);
    }
    // Swing doesn't repaint without a little prodding.
    validate();
    repaint(); // @B2A
  }



  /**
  Sets the width of a column.
  
  @param columnIndex The index of column.  Indices start at 0.
  @param width       The column width.
  **/
  public void setColumnWidth(int columnIndex,
                             int width)
  {
    // Catch errors if the index being out of range.
    try
    {
      table_.getColumnModel().getColumn(columnIndex).setPreferredWidth(width); //@B1C
      table_.getColumnModel().getColumn(columnIndex).setWidth(width); // @B2A
    }
    catch(Exception e)
    {
      Trace.log(Trace.WARNING, "setColumnWidth() error:" + e);
    }
    validate(); // @B2A
    repaint(); // @B2A
  }



  /**
  Sets the SQL connection with which to access data.
  This property is bound and constrained.
  Note that the data in the table will not change until a
  <i>load()</i> is done.
  
  @param       connection              The SQL connection.
  @exception  PropertyVetoException   If the change is vetoed.
  **/
  public void setConnection(SQLConnection connection)
  throws PropertyVetoException
  {
    // Note: the model handles the binding and constraining.
    // note: model validates parms
    model_.setConnection(connection);
  }



  /**
  Sets the color used to draw grid lines.
  
  @param color The color used to draw the grid lines.
  **/
  public void setGridColor(Color color)
  {
    table_.setGridColor(color);
    tableColor_ = color; //@B0A
  }



  /**
  Sets the SQL query used to build the table.
  This property is bound and constrained.
  Note that the data in the table will not change until a
  <i>load()</i> is done.
  
  @param       query                   The SQL query.
  @exception  PropertyVetoException   If the change is vetoed.
  **/
  public void setQuery(String query)
  throws PropertyVetoException
  {
    // Note: the model handles the binding and constraining.
    // note: model validates parms
    model_.setQuery(query);
  }



  // @C0A
  /**
  Sets the SQL result set used to build the table.
  If this is set, it is used instead of the SQL connection
  and SQL query.
  Note that the data in the table will not change until a
  <i>load()</i> is done.
  
  @param resultSet        The SQL result set.
  **/
  public void setResultSet(ResultSet resultSet)
  {
    model_.setResultSet(resultSet);
  }



  /**
  Sets the ListSelectionModel that is used to maintain row selection state.
  
  @param  model The model that provides the row selection state.
  **/
  public void setSelectionModel(ListSelectionModel model)
  {
    // cleanup old listener
    table_.getSelectionModel().removeListSelectionListener(selectionListeners_);
    // make change
    table_.setSelectionModel(model);
    // listen to new model
    table_.getSelectionModel().addListSelectionListener(selectionListeners_);
  }



  /**
  Sets whether horizontal lines are drawn between rows.
  @param show true if horizontal lines are to be drawn; false otherwise.
  **/
  public void setShowHorizontalLines(boolean show)
  {
    table_.setShowHorizontalLines(show);
    tableShowHorizontalLines_ = show; //@B0A
  }



  /**
  Sets whether vertical lines are drawn between columns.
  @param show true if vertical lines are to be drawn; false otherwise.
  **/
  public void setShowVerticalLines(boolean show)
  {
    table_.setShowVerticalLines(show);
    tableShowVerticalLines_ = show; //@B0A
  }

  //
  // Temporary testcase code.  -- Leave here if we need to test @B6A
  // 
  public static void main(String args[]) {
	    BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); 

	  try {
		  String line = "X"; 
		  boolean loop=true; 
		  while (line != null && !"exit".equalsIgnoreCase(line)) { 
	// Register the IBM Toolbox for Java JDBC driver.
		  
	  Class.forName("com.ibm.as400.access.AS400JDBCDriver");
	// Use JDBC to execute the SQL query directly.
	Connection connection = DriverManager.getConnection("jdbc:as400://"+args[0], args[1], args[2]);
	Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
	ResultSet rs = statement.executeQuery("SELECT * FROM QSYS2.SYSCOLUMNS");
	// Create the SQLResultSetTablePane object.
	SQLResultSetTablePane table = new SQLResultSetTablePane(rs, false);
	// Put the table in a JFrame.
	JFrame frame = new JFrame("My Window");
	frame.getContentPane().add(table);
	// Set up the error dialog adapter.
	table.addErrorListener(new ErrorDialogAdapter(frame));
	// Display the JFrame.
	frame.pack();
	frame.show();
	// Load the contents of the table.
	table.load();
	
	System.out.println("Table loaded Sleeping for 10 minutes");
	try {
		Thread.sleep(600000); 
	} catch (Exception e) {
		e.printStackTrace(); 
	}
	line = input.readLine(); 
	
		  }
	  } catch (Exception e) { 
		  e.printStackTrace(); 
	  }
  }

}


