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 >