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
>