简体   繁体   中英

Why does <ui:repeat> not work with java.util.Iterator?

Why does JSF2/Facelet's ui:repeat not accept java.util.Iterator's for value? One can hide so much implementation and memory conservation behind an Iterator since length need not be known, it would be so useful to have. But instead I need to convert my Iterators to Lists and throw away all my advantages in order to ui:repeat.

There are probably phase or timing or Serializable reasons, but my skimming of the docs that are available do not give those reasons. Do we not yet have the science to make this impossibility possible?

<ui:repeat> doesn't support java.util.Iterator .
Have look at UIRepeat.getDataModel() source code:

 private DataModel getDataModel() {
    if (this.model == null) {
        Object val = this.getValue();
        if (val == null) {
            this.model = EMPTY_MODEL;
        } else if (val instanceof DataModel) {
            //noinspection unchecked
            this.model = (DataModel<Object>) val;
        } else if (val instanceof List) {
            //noinspection unchecked
            this.model = new ListDataModel<Object>((List<Object>) val);
        } else if (Object[].class.isAssignableFrom(val.getClass())) {
            this.model = new ArrayDataModel<Object>((Object[]) val);
        } else if (val instanceof ResultSet) {
            this.model = new ResultSetDataModel((ResultSet) val);
        } else {
            this.model = new ScalarDataModel<Object>(val);
        }
    }
    return this.model;
}

You can make <ui:repeat> work with an Iterator, but you must wrap the Iterator with an object that extends javax.faces.model.DataModel. Here is my IteratorDataModel class that can make any Iterator accessible to <ui:repeat>. I tested this with JSF 2.1.26:

/**
 * Make a DataModel out of an Iterator that may be used with JSF's ui:repeat tag
 *
 * This DataModel does not support the use of offset or step attributes in the
 * ui:repeat tag, and the size attribute in ui:repeat, when used with this DataModel, 
 * will report an inaccurate value.
 *
 * Copyright (c) 2014 Woldrich, Inc.
 * Licensed under MIT (https://clubcompy.com/wcm/license/mit.txt)
 * 
 * @author Dave Woldrich
 */
public class IteratorDataModel<T> extends DataModel<T> {
    private static final Logger logger = LogManager.getLogger(IteratorDataModel.class);

    private Iterator<?> iterator;
    private int rowIndex; 
    private T item;

    public IteratorDataModel(Iterator<?> iterator) {
        setWrappedData(iterator);      
    }

    @Override
    public int getRowCount() {
        return Integer.MAX_VALUE;
    }

    @Override
    public T getRowData() {
        return this.item;
    }

    @Override
    public int getRowIndex() {
        if(this.rowIndex == -1 && this.iterator.hasNext()) {
            this.setRowIndex(0);
        }

        if(logger.isTraceEnabled()) {
            logger.trace("getRowIndex returns " + this.rowIndex);
        }

        return this.rowIndex;
    }

    @Override
    public Object getWrappedData() {
        return this.iterator;
    }

    @Override
    public boolean isRowAvailable() {
        boolean hasNext = this.item != null || this.iterator.hasNext(); 

        if(logger.isTraceEnabled()) {
            logger.trace("isRowAvailable " + hasNext);
        }

        return hasNext;
    }

    @SuppressWarnings("unchecked")
    @Override
    public void setRowIndex(int newIndex) {
        if(logger.isTraceEnabled()) {
            logger.trace("setRowIndex (" + newIndex + ")");
        }

        if(newIndex == this.rowIndex+1) {
            this.rowIndex = newIndex;

            this.item = (T) (this.iterator.hasNext() ? this.iterator.next() : null);
        } 
        else if(newIndex > -1 || newIndex != this.rowIndex) {
            if(logger.isTraceEnabled()) {
                logger.trace("setRowIndex not incrementing by 1 as expected, ignored");            
            }
        }
    }

    @Override
    public void setWrappedData(Object data) {
        this.iterator = (Iterator<?>) data;
        this.rowIndex = -1;
        this.item = null;
    }    
}

And in your facelets code, for example, you may use:

<ul>
    <ui:repeat value="#{chatRoom.allComments}" var="aComment" varStatus="status">
        <li><h:outputText value="#{aComment.text}" /></li>
    </ui:repeat>
</ul>

where chatRoom.getAllComments() would return a IteratorDataModel<Comment> that is constructed passing an Iterator<Comment> .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM