You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@myfaces.apache.org by Kamran Kashanian <ka...@gmail.com> on 2010/10/12 02:29:41 UTC

[Trinidad - API] RowKeyPropertyModel and RowKeyPropertyTreeModel

Sending this again with the proper heading:

By default, when Java Lists/Arrays are used as models in Trinidad
table/tree/treeTable components, they are wrapped in a SortableModel
instance.

The problem with SortableModel is that it uses row indexes as row keys. This
makes SortableModel immutable and insert/delete operations in the underlying
List/array can cause problems. For example, indexes shift after
insert/delete operations and can cause problems if the component is holding
on to row keys in SelectedRowKey/DisclosedRowKey sets.

The proposal is to add row key based CollectionModel/TreeModels which avoid
using indexes as row keys.   (See below).

One drawback with the implementation of setRowKey API in RowKeyPropertyModel
(below) is that it is inefficient for large Lists and does a linear search
through the model to find the row with the given row key.


1)  Add a RowKeyPropertyModel which extends SortableModel and uses row keys
instead of indexes:

package org.apache.myfaces.trinidad.model;

/**
 * Creates a CollectionModel whose row keys are defined by a unique data
property in the model.
 */
public class RowKeyPropertyModel extends SortableModel
{
  /**
   * Creates a RowKeyPropertyModel.
   *
   * @param model The underlying model. If necessary, this will be converted
into a {@link DataModel}
   * @param rowKeyProperty The property by which the row key can be
accessed. Row key value must be unique
   */
  public RowKeyPropertyModel(Object model, String rowKeyProperty)
  {
    super(model);
    _rowKeyProperty = rowKeyProperty;
  }

  /**
   * No arg constructor for use as a managed-bean.
   * Must call {@link #setWrappedData} and {@link #setRowKeyProperty} before
using this instance.
   */
  public RowKeyPropertyModel()
  {
    super();
  }

  /**
   * Gets the row key for the current row
   * @return row key or null if model is not on any row
   */
  public Object getRowKey()
  {
    if (isRowAvailable())
    {
      Object rowKey = _getRowKey();
      return rowKey;
    }
    else
    {
      return null;
    }
  }

  public void setRowKey(Object key)
  {
    if (key == null)
    {
      setRowIndex(-1);
      return;
    }

    if (getRowKey() != null && getRowKey().equals(key))
      return;

    for (int i = 0; i < getRowCount(); i++)
    {
      setRowIndex(i);
      Object prop = getRowKey();
      if (key.equals(prop))
      {
        return;
      }
    }

    // if we didn't find an element with a matching key,
    // then don't make any rows current
    setRowIndex(-1);
  }

  /**
   * Gets the row key property name for this model
   * @return row key property name
   */
  public String getRowKeyProperty()
  {
    return _rowKeyProperty;
  }

  /**
   * Sets the row key property for this model
   * @param rowKeyProperty row key property to set
   */
  public void setRowKeyProperty(String rowKeyProperty)
  {
    _rowKeyProperty = rowKeyProperty;
  }

  /**
   * gets the row key for the given row by resolving the _rowKeyProperty
   * @param row row to retrieve the row key for
   * @return row key value
   */
  protected Object getRowKey(Object row)
  {
    return __resolveProperty(row, _rowKeyProperty);
  }

  /**
   * gets the row key for current row by resolving the _rowKeyProperty
   * @return
   */
  private Object _getRowKey()
  {
    Object data = getRowData();
    return __resolveProperty(data, _rowKeyProperty);
  }

  private String _rowKeyProperty;

}




2)  Add a RowKeyPropertyTreeModel which extends ChildPropertyTreeModel and
wraps each child model with a RowKeyPropertyModel:

package org.apache.myfaces.trinidad.model;

/**
 * A subclass of {@link ChildPropertyTreeModel} that supports row keys by
creating
 * {@link RowKeyPropertyModel}(s) for its child models.
 *
 * Ooverrides the protected createChildModel method in {@link
ChildPropertyTreeModel} so that it can instantiate
 * RowKeyPropertyModels as it encounters child data.
 */
public class RowKeyPropertyTreeModel
  extends ChildPropertyTreeModel
{

  /**
   * Creates a RowKeyPropertyTreeModel
   *
   * @param model The underlying model. This will be converted into a {@link
DataModel}
   * @param childProperty The property by which the child data can be
accessed.
   * @param rowKeyProperty The property by which the row key can be
accessed.
   */
  public RowKeyPropertyTreeModel(Object model, String childProperty,
                                 String rowKeyProperty)
  {
    super (new RowKeyPropertyModel(model, rowKeyProperty), childProperty);
    _rowKeyProperty = rowKeyProperty;
  }

  /**
   * No-arg constructor for use with managed-beans.
   * Must call the {@link #setChildProperty},
   * {@link #setWrappedData} and {@link #setRowKeyProperty} methods after
constructing this instance.
   */
  public RowKeyPropertyTreeModel()
  {
    super();
  }

  /**
   * Overrides ChildPropertyTreeModel.createChildModel().
   * Converts childData into a RowKeyPropertyModel.
   *
   * @param childData the data to convert. This can be a List or array.
   */
  @Override
  protected CollectionModel createChildModel(Object childData)
  {
    CollectionModel model =
      new RowKeyPropertyModel(childData, _rowKeyProperty);
    model.setRowIndex(-1);
    return model;
  }


  /**
   * Gets the row key property name for this model
   * @return row key property name
   */
  public String getRowKeyProperty()
  {
    return _rowKeyProperty;
  }

  /**
   * Sets the row key property for this model
   * @param rowKeyProperty row key property to set
   */
  public void setRowKeyProperty(String rowKeyProperty)
  {
    _rowKeyProperty = rowKeyProperty;
  }

  private String _rowKeyProperty = null;

}


Thanks
Kamran