Interceptor Pattern for ADF table binding

Interceptor Pattern for TableModel From ADF Table Binding

Written by Don Young , InPowerSoft,  January 2006

Introduction

Using a Swing table to display database data is probably the most natural and prolific presentation in a Swing/ADF based application. Fortunately, ADF Swing has very strong support that allows a user to create a table binding that binds a view object to a JTable. The table binding acts as a controller and takes care of many mundane manipulations of data via the JTable components. For example, UPDATE, DELETE, INSERT and SELECT of data can be performed via the JTable and other UI components such as the tool bar to manipulate a ADF view object's data.These operations if to be wired from first principle without the aid of ADF Swing and ADF will be nontrivial and error prone. ADF Swings code produced a TableModel object to drive the JTable as a result of creating a table binding. However, an application sometimes has needs to intercept what goes into and out of the ADF layer before they interface with a JTable component. For example, if we prefer a different mechanism than what's in ADF to detect dirty cell status within a JTable in comparison to the underlying ADF view object data so that the owning transaction can be marked dirty. We will use the interceptor pattern by introducing an object between ADF's TableModel object and JTable objects. There are many other applications for this Table Model interceptor including converting native oracle.jbo.domain.* data types to your own application Java types.

The JUTableBinding - a Deeper Look

In JUTableBinding class, you will find a function:

public static TableModel createAttributeListBinding(JUPanelBinding formBinding, JTable control, String voInstanceName, String voIterName, /*temporarily taking nulls for this*/ String voIterBindingName, String[] attrNames);

If you use ADF API to create a table binding, you may call this function directly, otherwise if you use UI editor in JDeveloper, this function may be called on your behalf. This function will create a table binding based on the table attributes you passed in that map to the attributes of a view object you want to display in a JTable (see ADF doc for details). The return from this call is a TableModel object that manufactured by ADF.

A Swing TableModel is an interface that is rather simple. Of all the functions on this interface, two deserve more explanation than others.

public Object getValueAt(int row, int column) ;

Whenever JTable needs to paint its content, this function will be called. The ADF TableModel object will get the data from a view object and its cache which in turn get the data from the corresponding entity object and its cache, tracing all the way to the database. In the reverse order,

public void setValueAt(Object newValue, int row, int column) ;

does the opposite of the getValueAt. Notice that the view object will deliver or accept what it expects of a particular java types that maps to ADF domain types. For example, ADF uses oracle.jbo.domain.Number to represent any numeric quantity and that is very different from the traditional usage of Long,Integer,Double,Short,Float and BigDecimal data types. ADF also uses oracle.jbo.domain.Date to represent date time information different from java.util.Date. These underlying oracle domain data types are not type compatible from the traditional java types in terms of derivation hierarchy.

 

Implementing the TableModel

In order to create an interceptor that is compatible with the TableModel, we have to create an implementation of TableModel and catch the instance of TableModel returned by ADF. Notice that every TableModel call has to be delegated to the ADF's TableModel instance we obtained earlier. You now have every opportunity to do processing as deem fit for every call that you intercept. In the example below, we intercept the setValueAt function to detect dirty cell which is slight different than what is in ADF's TableModel implementation (step into the source code for verification).

public class IpsTableModel implements TableModel 
{ 
  protected TableModel juTableBindingTableModel; 
  public IpsTableModel(TableModel juTableBindingTableModel) 
  { 
    this.juTableBindingTableModel = juTableBindingTableModel; 
  }
  public int getRowCount() 
  { 
    return juTableBindingTableModel.getRowCount(); 
  }
  public int getColumnCount() 
  { 
    return juTableBindingTableModel.getColumnCount(); 
  }
  public String getColumnName(int column) 
  { 
    return juTableBindingTableModel.getColumnName(column); 
  }
  public Class getColumnClass(int column) 
  { 
    return juTableBindingTableModel.getColumnClass(column); 
  }
  public boolean isCellEditable(int row, int column) 
  { 
    return juTableBindingTableModel.isCellEditable(row, column); 
  }
  public Object getValueAt(int row, int column) 
  { 
    return juTableBindingTableModel.getValueAt(row, column); 
  }
  public void setValueAt(Object newValue, int row, int column) 
  { 
    Object oldValue = getValueAt(row, column); 
 
    boolean save = false; 
    if(oldValue != null && !oldValue.equals(newValue)) 
      save = true; 
    else if(newValue != null && !newValue.equals(oldValue)) 
      save = true; 
 
    if(save) 
      juTableBindingTableModel.setValueAt(newValue, row, column); 
  }
  public void addTableModelListener(TableModelListener l) 
  { 
    juTableBindingTableModel.addTableModelListener(l); 
  }
  public void removeTableModelListener(TableModelListener l) 
  { 
    juTableBindingTableModel.removeTableModelListener(l); 
  }
} 

Intercepting Values

If you choose to convert ADF data type to your Java data type, you can do it within the setValueAt and getValueAt functions. For example, we like to convert oracle.jbo.domain.Date to java.util.Date, we can use the following code:

  public Object getValueAt(int row, int column) 
  { 
    Object valueAt = juTableBindingTableModel.getValueAt(row, column); 
 
    if(valueAt instanceof oracle.jbo.domain.Date) 
    { 
      oracle.jbo.domain.Date date = ((oracle.jbo.domain.Date) valueAt); 
      return Converter.oracleDateToJavaDate(date); 
    } 
 
    return juTableBindingTableModel.getValueAt(row, column); 
  }

  public void setValueAt(Object newValue, int row, int column) 
  { 
    if(newValue instanceof java.util.Date) 
      newValue = Converter.javaDateToOracleDate((Date) newValue); 
 
    Object oldValue = getValueAt(row, column); 
 
    boolean save = false; 
    if(oldValue != null && !oldValue.equals(newValue)) 
      save = true; 
    else if(newValue != null && !newValue.equals(oldValue)) 
      save = true; 
 
    if(save) 
      juTableBindingTableModel.setValueAt(newValue, row, column); 
  }

Here we assume to have a utility class Converter that knows how to convert from a java.util.Date to oracle.jbo.domain.Date and vice versa. These type conversion may be necessary if you buy a date picker component that would not understand the oracle data types and only work with traditional java types. All editor components in our InPowerForms - a framework that extends Oracle's ADF Swing, however, works with native ADF java data types and will require no conversion in the process. Needless to say, if you do your type conversion, you will also need to intercept public Class getColumnClass(int column); such that it returns the right type to the JTable. Do not intercept public int getRowCount() and public int getColumnCount() or you would confuse JTable and make it inconsistent with the ADF view object. ADF Swing does some smart on-demand scrolling of the table if it has a large number of rows. Unless you want to use a different resource bundle than ADF's for internationalization, do not change public String getColumnName(int column) as well.

Instantiate the TableModel

Last, we will create a static function to instantiate a TableModel. During the instantiation process, we will insert our own version of the table model and capture the original version from ADF for delegation purpose.

 public static TableModel myCreateAttributeListBinding(final JUPanelBinding formBinding, 
                                                        JTable control, String voInstanceName, 
							String voIterName, String voIterBindingName, 
                                                        String[] attrNames) 
  { 
 
 
    TableModel innerModel = JUTableBinding.createAttributeListBinding(formBinding, control, 
							voInstanceName, voIterName, voIterBindingName, attrNames); 
    return new IpsTableModel(innerModel,null); 
  }

 

Summary

The TableModel interceptor pattern offers an invaluble tricks to allow further customization of data interface between ADF's view object and JTable.

 

E-mail this page
Printer View Printer View
Oracle Is The Information Company About Oracle | Oracle RSS Feeds | Careers | Contact Us | Site Maps | Legal Notices | Terms of Use | Privacy