For my own use I created a new MenuModel implementation that works on
a data list. Each level only has one node. This way you can "fool" the
breadCrumbs and other components that use menus to use a list instead.
Here is my model that seems to be working for breadCrumbs at least. Do
you think there is a good way to include this so components like
tr:breadCrumbs could automatically support list models instead of just
tree models?

package example;

import java.util.ArrayList;
import java.util.List;

import javax.faces.model.DataModel;
import javax.faces.model.DataModelEvent;
import javax.faces.model.DataModelListener;

import org.apache.myfaces.trinidad.model.MenuModel;
import org.apache.myfaces.trinidad.model.ModelUtils;

/**
 *
 * @author Andrew Robinson (andrew)
 */
public class LinearMenuModel
  extends MenuModel
{
  private int rowIndex = -1;
  private DataModel dataModel;

  public LinearMenuModel()
  {
    this(null);
  }

  public LinearMenuModel(Object data)
  {
    setWrappedData(data);
  }

  /**
   * @see org.apache.myfaces.trinidad.model.MenuModel#getFocusRowKey()
   */
  @Override
  public Object getFocusRowKey()
  {
    int num = this.dataModel.getRowCount() - 1;
    return num >= 0 ? num : null;
  }

  /**
   * @see org.apache.myfaces.trinidad.model.TreeModel#enterContainer()
   */
  @Override
  public void enterContainer()
  {
    setRowIndexInternal(this.dataModel.getRowIndex() + 1);
  }

  /**
   * @see org.apache.myfaces.trinidad.model.TreeModel#exitContainer()
   */
  @Override
  public void exitContainer()
  {
    setRowIndexInternal(this.dataModel.getRowIndex() - 1);
  }

  /**
   * @see org.apache.myfaces.trinidad.model.TreeModel#getDepth(java.lang.Object)
   */
  @Override
  public int getDepth(Object rowKey)
  {
    return Math.max(0, asInteger(rowKey));
  }

  /**
   * @see 
org.apache.myfaces.trinidad.model.TreeModel#getAllAncestorContainerRowKeys(java.lang.Object)
   */
  @Override
  public List<Object> getAllAncestorContainerRowKeys(Object childRowKey)
  {
    int i = asInteger(childRowKey);

    List<Object> keys = new ArrayList<Object>(Math.max(0, i - 1));
    for (int num = 0; num < i; num++)
    {
      keys.add(num);
    }

    return keys;
  }

  /**
   * @see 
org.apache.myfaces.trinidad.model.TreeModel#getContainerRowKey(java.lang.Object)
   */
  @Override
  public Object getContainerRowKey(Object childRowKey)
  {
    int i = asInteger(childRowKey);
    return i > 0 ? i - 1 : null;
  }

  /**
   * @see org.apache.myfaces.trinidad.model.TreeModel#isContainer()
   */
  @Override
  public boolean isContainer()
  {
    return dataModel.getRowCount() > dataModel.getRowIndex() + 1;
  }

  /**
   * @see org.apache.myfaces.trinidad.model.CollectionModel#getRowKey()
   */
  @Override
  public Object getRowKey()
  {
    return dataModel.getRowIndex();
  }

  /**
   * @see 
org.apache.myfaces.trinidad.model.CollectionModel#setRowKey(java.lang.Object)
   */
  @Override
  public void setRowKey(Object key)
  {
    setRowIndexInternal(asInteger(key));
  }

  /**
   * @see javax.faces.model.DataModel#getRowCount()
   */
  @Override
  public int getRowCount()
  {
    return dataModel.isRowAvailable() ? 1 : 0;
  }

  /**
   * @see javax.faces.model.DataModel#getRowData()
   */
  @Override
  public Object getRowData()
  {
    return isRowAvailable() ? dataModel.getRowData() : null;
  }

  /**
   * @see javax.faces.model.DataModel#getRowIndex()
   */
  @Override
  public int getRowIndex()
  {
    return this.rowIndex;
  }

  /**
   * @see javax.faces.model.DataModel#getWrappedData()
   */
  @Override
  public Object getWrappedData()
  {
    return dataModel.getWrappedData();
  }

  /**
   * @see javax.faces.model.DataModel#isRowAvailable()
   */
  @Override
  public boolean isRowAvailable()
  {
    return rowIndex == 0 && dataModel.isRowAvailable();
  }

  /**
   * @see javax.faces.model.DataModel#setRowIndex(int)
   */
  @Override
  public void setRowIndex(int rowIndex)
  {
    if (rowIndex < -1)
    {
      throw new IllegalArgumentException("Illegal rowIndex " + rowIndex);
    }

    if (rowIndex != this.rowIndex)
    {
      int oldIndex = this.rowIndex;
      this.rowIndex = rowIndex;
      fireRowSelected();
    }
  }

  /**
   * @see javax.faces.model.DataModel#setWrappedData(java.lang.Object)
   */
  @Override
  public void setWrappedData(Object data)
  {
    this.dataModel = ModelUtils.toDataModel(data);
    int rowIndex = dataModel.getRowCount() == 0 ? -1 : 0;

    setRowIndexInternal(rowIndex);
  }

  private void setRowIndexInternal(int value)
  {
    if (dataModel == null)
    {
      this.rowIndex = -1;
      return;
    }

    dataModel.setRowIndex(value);
    setRowIndex(dataModel.isRowAvailable() ? 0 : -1);
  }

  private int asInteger(Object obj)
  {
    if (obj instanceof Number)
    {
      return ((Number)obj).intValue();
    }
    return -1;
  }

  protected void fireRowSelected()
  {
    Object data = isRowAvailable() ? getRowData() : null;
    DataModelEvent event = new DataModelEvent(this,
      getRowIndex(), data);

    DataModelListener[] listeners = getDataModelListeners();
    for (int i = 0; i < listeners.length; i++)
    {
      listeners[i].rowSelected(event);
    }
  }
}

On 9/27/07, Andrew Robinson <[EMAIL PROTECTED]> wrote:
> I'm having a usage issue with tr:breadCrumbs. The breadCrumbs is build
> as a list of links, and as such I would expect it to be able to have a
> list as a data model. But this is not the case, it is using a
> TreeModel as the only supported object for the value property. This is
> all well and good if you are using the breadCrumbs with the
> XmlMenuModel, but it doesn't really make sense as the only solution.
>
> My use case was to show the user the breadcrumbs of my Seam
> conversations. I could use t:dataList, but wanted the skinning and
> rendering of the tr:breadCrumbs. Before looking into it, I thought I
> could simply have:
>
>       <tr:breadCrumbs
>         value="#{conversationStack}"
>         var="_entry">
>         <f:facet name="nodeStamp">
>           <tr:commandNavigationItem
>             immediate="true"
>             text="#{_entry.description}"
>             action="#{_entry.select}"
>             partialSubmit="true" />
>         </f:facet>
>       </tr:breadCrumbs>
>
> Where "#{conversationStack}" is EL that returns
> "List<ConversationEntry>". The bread crumbs component is expecting a
> MenuModel though instead of supporting CollectionModel or even
> DataModel. Since this component displays a linear list, I don't see
> why it wouldn't support a liner data model (instead of a tree model).
>
> Any chance that we could have this component changed so that it works
> for linear collections as well as tree models?
>
> -Andrew
>

Reply via email to