/* SimpleListModel.java
Purpose:
Description:
History:
Thu Aug 18 15:40:14 2005, Created by tomyeh
Copyright (C) 2005 Potix Corporation. All Rights Reserved.
{{IS_RIGHT
This program is distributed under LGPL Version 2.1 in the hope that
it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.zul;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import org.zkoss.lang.Objects;
import org.zkoss.util.ArraysX;
import org.zkoss.zul.event.ListDataEvent;
import org.zkoss.zul.ext.Sortable;
/**
* A simple implementation of {@link ListModel}.
* <p>Note: It assumes the content is immutable. If not, use {@link ListModelList}
* or {@link ListModelArray} instead.
* In additions, it stores the data in the array format, so if the original
* data is not an array. It is better not to use this class.
*
* <p>Also notice that {@link SimpleListModel} also implements
* {@link ListSubModel}. It means when it is used with {@link Combobox},
* only the data that matches what the user typed will be shown.
*
* @author tomyeh
* @see ListModelArray
* @see ListModelSet
* @see ListModelList
* @see ListModelMap
* @see ListSubModel (since 3.0.2)
*/
public class SimpleListModel<E> extends AbstractListModel<E>
implements Sortable<E>, ListSubModel<E>, java.io.Serializable {
private static final long serialVersionUID = 20060707L;
private Object[] _data;
private Comparator<E> _sorting;
private boolean _sortDir;
/** Constructor.
*
* @param data the array to represent
* @param live whether to have a 'live' {@link ListModel} on top of
* the specified list.
* If false, the content of the specified list is copied.
* If true, this object is a 'facade' of the specified list,
* i.e., when you add or remove items from this ListModelList,
* the inner "live" list would be changed accordingly.
*
* However, it is not a good idea to modify <code>data</code>
* once it is passed to this method with live is true,
* since {@link Listbox} is not smart enough to handle it.
* @since 2.4.1
*/
public SimpleListModel(E[] data, boolean live) {
_data = live ? data : ArraysX.duplicate(data);
}
/** Constructor.
* It made a copy of the specified array (<code>data</code>).
*
* <p>Notice that if the data is static or not shared, it is better to
* use <code>SimpleListModelMap(data, true)</code> instead, since
* making a copy is slower.
*/
public SimpleListModel(E[] data) {
this(data, false);
}
/** Constructor.
* Notice the data will be converted to an array, so the performance
* is not good if the data is huge. Use {@link ListModelList} instead
* if the data is huge.
* @since 2.4.1
*/
public SimpleListModel(List<? extends E> data) {
_data = data.toArray();
}
//-- ListModel --//
public int getSize() {
return _data.length;
}
@SuppressWarnings("unchecked")
public E getElementAt(int j) {
return (E) _data[j];
}
//-- Sortable --//
/** Sorts the data.
*
* @param cmpr the comparator.
* @param ascending whether to sort in the ascending order.
* It is ignored since this implementation uses cmpr to compare.
*/
@SuppressWarnings("unchecked")
public void sort(Comparator<E> cmpr, final boolean ascending) {
_sorting = cmpr;
_sortDir = ascending;
Arrays.sort(_data, (Comparator) cmpr);
fireEvent(ListDataEvent.STRUCTURE_CHANGED, -1, -1);
}
public String getSortDirection(Comparator<E> cmpr) {
if (Objects.equals(_sorting, cmpr))
return _sortDir ? "ascending" : "descending";
return "natural";
}
/**
* Returns the subset of the list model data that matches
* the specified value.
* It is usually used for implementation of auto-complete.
*
* <p>The implementation uses {@link #inSubModel} to check if
* the returned object of {@link #getElementAt} shall be in
* the sub model.
*
* <p>Notice the maximal allowed number of items is decided by
* {@link #getMaxNumberInSubModel}, which, by default, returns 15
* if nRows is negative.
*
* @param value the value to retrieve the subset of the list model.
* It is the key argument when invoking {@link #inSubModel}.
* this string.
* @param nRows the maximal allowed number of matched items.
* If negative, it means the caller allows any number, but the implementation
* usually limits to a certain number (for better performance).
* @see #inSubModel
* @see #getMaxNumberInSubModel
* @since 3.0.2
*/
@SuppressWarnings("unchecked")
public ListModel<E> getSubModel(Object value, int nRows) {
final List<E> data = new LinkedList<E>();
nRows = getMaxNumberInSubModel(nRows);
for (int i = 0; i < _data.length; i++) {
if (inSubModel(value, _data[i])) {
data.add((E) _data[i]);
if (--nRows <= 0)
break; //done
}
}
return new SimpleListModel<E>(data);
}
/** Returns the maximal allowed number of matched items in the sub-model
* returned by {@link #getSubModel}.
* <p>Default: <code>nRows < 0 ? 15: nRows</code>.
* @since 5.0.4
*/
protected int getMaxNumberInSubModel(int nRows) {
return nRows < 0 ? 15 : nRows;
}
/** Compares if the given value shall belong to the submodel represented
* by the key.
* <p>Default: converts both key and value to String objects and
* then return true if the String object of value starts with
* the String object
* @param key the key representing the submodel. In autocomplete,
* it is the value entered by user.
* @param value the value in this model.
* @see #getSubModel
* @since 5.0.4
*/
protected boolean inSubModel(Object key, Object value) {
String idx = objectToString(key);
return idx.length() > 0 && objectToString(value).startsWith(idx);
}
/**
* @deprecated As of release 5.0.4, replaced with {@link #inSubModel}.
*/
protected String objectToString(Object value) {
final String s = value != null ? value.toString() : "";
return s != null ? s : "";
}
@SuppressWarnings("unchecked")
public Object clone() {
SimpleListModel clone = (SimpleListModel) super.clone();
if (_data != null)
clone._data = ArraysX.duplicate(_data);
return clone;
}
protected void fireSelectionEvent(E e) {
int index = -1;
for (int j = 0; j < _data.length; ++j) {
index++;
if (Objects.equals(e, _data[j])) {
break;
}
}
fireEvent(ListDataEvent.SELECTION_CHANGED, index, -1);
}
//For Backward Compatibility//
/** @deprecated As of release 6.0.0, replaced with {@link #addToSelection}.
*/
public void addSelection(E obj) {
addToSelection(obj);
}
/** @deprecated As of release 6.0.0, replaced with {@link #removeFromSelection}.
*/
public void removeSelection(Object obj) {
removeFromSelection(obj);
}
}