All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.myfaces.trinidad.model.CollectionModel Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.myfaces.trinidad.model;

import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import javax.faces.model.DataModel;

/**
 * The data model that is used by the Trinidad Table and Iterator components.
 * This extends the Faces DataModel class and adds on support for
 * rowKeys and sorting.  Ordinary DataModels are still supported,
 * and will automatically be wrapped into CollectionModels, but
 * without the added functionality.
 * 

*

Row key support

*

* In the Faces DataModel, rows are identified entirely by * index. This causes major problems if the underlying data * changes from one request to the next - a user request * to delete one row may delete a different row because a * row got added by another user, etc. To work around * this, CollectionModel is based around row keys instead * of indices. An implementation of CollectionModel must * implement getRowKey() and setRowKey(), and handle * conversion from integer indices to row keys. A trivial * implementation might simply use Integer objects as * the row keys, but a better version could use a unique ID * in the row. *

*/ public abstract class CollectionModel extends DataModel implements RowKeyIndex, LocalRowKeyIndex { /** * Gets the rowKey of the current row. * rowKeys are safer to use than row indices because rowKeys are * unaffected by mutations to this collection. * rowKeys should have efficient implementations of * {@link Object#equals} and {@link Object#hashCode} as they will be used * as keys in hashtables. rowKeys should also be Serializable, so that the * application can run under all JSF state-saving schemes. * @return this key should be Serializable and immutable. * @see #setRowKey */ public abstract Object getRowKey(); /** * Finds the row with the matching key and makes it current * @param key the rowKey, previously obtained from {@link #getRowKey}. */ public abstract void setRowKey(Object key); /** * Checks to see if the row at the given index is available. * This method makes the given row current and calls * {@link #isRowAvailable()}. * Finally, the row that was current before this method was called * is made current again. * @see CollectionModel#isRowAvailable() * @param rowIndex the index of the row to check. * @return true if data for the row exists. */ public boolean isRowAvailable(int rowIndex) { int oldIndex = getRowIndex(); try { setRowIndex(rowIndex); return isRowAvailable(); } finally { setRowIndex(oldIndex); } } /** * Check for an available row by row key. * This method makes the given row current and calls * {@link #isRowAvailable()}. * Finally, the row that was current before this method was called * is made current again. * @see CollectionModel#isRowAvailable() * @param rowKey the row key for the row to check. * @return true if data for the row exists otherwise return false */ public boolean isRowAvailable(Object rowKey) { Object oldKey = getRowKey(); try { setRowKey(rowKey); return isRowAvailable(); } finally { setRowKey(oldKey); } } /** * Gets the rowData at the given index. * This method makes the given row current and calls * {@link #getRowData()}. * Finally, the row that was current before this method was called * is made current again. * @see CollectionModel#getRowData() * @param rowIndex the index of the row to get data from. * @return the data for the given row. */ public Object getRowData(int rowIndex) { int oldIndex = getRowIndex(); try { setRowIndex(rowIndex); return getRowData(); } finally { setRowIndex(oldIndex); } } /** * Returns the rowData for the given rowKey without changing model currency. * Implementations may choose to implement this behavior by saving and restoring the currency. * * @see CollectionModel#getRowData() * @param rowKey the row key of the row to get data from. * @return the data for the given row. */ public Object getRowData(Object rowKey) { Object oldKey = getRowKey(); try { setRowKey(rowKey); return getRowData(); } finally { setRowKey(oldKey); } } /** * Return true if this collection is sortable by the given property. * This implementation always returns false; */ public boolean isSortable(String property) { return false; } /** * Gets the criteria that this collection is sorted by. * This method should never return null. * This implementation always returns an empty List. * @return each element in this List is of type SortCriterion. * An empty list is returned if this collection is not sorted. * @see SortCriterion */ public List getSortCriteria() { return Collections.emptyList(); } /** * Sorts this collection by the given criteria. * @param criteria Each element in this List must be of type SortCriterion. * The empty list may be used to cancel any sort order. null should be treated * the same as an empty list. * @see SortCriterion */ public void setSortCriteria(List criteria) { } /** * Check if a range of rows is available from a starting index. * The current row does not change after this call * @param startIndex the starting index for the range * @param rowsToCheck number of rows to check. If rowsToCheck < 0 set * startIndex = startIndex - abs(rowsToCheck) + 1. This * allows for checking for row availability from the end position. For example * to check for availability of n rows from the end, call * isRangeAvailable(getRowCount()-1, -n) * @return true if rows are available otherwise return false */ public boolean areRowsAvailable(int startIndex, int rowsToCheck) { int oldIndex = getRowIndex(); try { if (rowsToCheck < 0) { rowsToCheck = Math.abs(rowsToCheck); startIndex = startIndex - rowsToCheck + 1; } setRowIndex(startIndex); return areRowsAvailable(rowsToCheck); } finally { setRowIndex(oldIndex); } } /** * Check if a range of rows is available from a starting row key * This method makes the row with the given row key current and calls * {@link #areRowsAvailable(rowsToCheck)}. * The current row does not change after this call * @see CollectionModel#areRowsAvailable(int). * @param startRowKey the starting row key for the range * @param rowsToCheck number of rows to check * @return true if rows are available otherwise return false */ public boolean areRowsAvailable(Object startRowKey, int rowsToCheck) { Object oldKey = getRowKey(); try { setRowKey(startRowKey); return areRowsAvailable(rowsToCheck); } finally { setRowKey(oldKey); } } /** * Check if a range of rows is available starting from the * current row. This implementation checks the start and end rows in the range * for availability. If the number of requested rows is greater than the total * row count, this implementation checks for available rows up to the row count. * The current row does not change after this call * @param rowsToCheck number of rows to check * @return true rows are available otherwise return false */ public boolean areRowsAvailable(int rowsToCheck) { int startIndex = getRowIndex(); if (startIndex < 0 || rowsToCheck <= 0) return false; long count = getRowCount(); if (count != -1) { if (startIndex >= count) return false; if (startIndex + rowsToCheck > count) rowsToCheck = (int)count - startIndex; } int last = startIndex + rowsToCheck - 1; try { // check start index if (!isRowAvailable()) return false; // check end index setRowIndex(last); return isRowAvailable(); } finally { setRowIndex(startIndex); } } /** *

* Adds the listener to the Set of RowKeyChangeListeners on the Collection. *

*

* The same listener instance may be added multiple times, but will only be called once per change. *

*

* Since the Collection may have a lifetime longer than Request, the listener implementation * should take care not to maintain any references to objects only valid in the current Request. * For example, if a UIComponent wishes to listen on a Collection, the UIComponent cannot use a * listener that maintains a Java reference to the UIComponent instance because UIComponent * instances are only valid for the current request (this also precludes the use of a non-static * inner class for the listener). Instead, the UIComponent would need to use an indirect * reference such as {@link ComponentReference} to dynamically find the correct instance to use. * In the case where the Collection has a short lifetime, the code that adds the listener needs to * ensure that it executes every time the Collection instance is reinstantiated. *

* @param listener The listener for RowKeyChangeEvents to add to the Collection * @see #removeRowKeyChangeListener */ public void addRowKeyChangeListener(RowKeyChangeListener listener) { if(!_rowKeyChangeListeners.contains(listener)) _rowKeyChangeListeners.add(listener); } /** *

* Remove an existing listener from the Set of RowKeyChangeListeners on the Collection. *

*

* The same listener instance may be removed multiple times wihtout failure. *

* * @param listener The listener for RowKeyChangeEvents to remove from the Collection */ public void removeRowKeyChangeListener(RowKeyChangeListener listener) { _rowKeyChangeListeners.remove(listener); } /** * Fire an existing RowKeyChangeEvent to any registered listeners. * No event is fired if the given event's old and new row keys are equal and non-null. * @param event The RowKeyChangeEvent object. */ protected void fireRowKeyChange(RowKeyChangeEvent event) { Object oldRowKey = event.getOldRowKey(); Object newRowKey = event.getNewRowKey(); if (oldRowKey != null && newRowKey != null && oldRowKey.equals(newRowKey)) { return; } for (RowKeyChangeListener listener: _rowKeyChangeListeners) { listener.onRowKeyChange(event); } } // // Below is the default implemenation for the LocalRowKeyIndex interface. // /** * Check if a range of rows is locally available starting from a row index. * @see CollectionModel#areRowsAvailable(int, int) * @param startIndex starting row index to check * @param rowsToCheck number of rows to check * @return default implementation returns false */ public boolean areRowsLocallyAvailable(int startIndex, int rowsToCheck) { return false; } /** * Check if a range of rows is locally available starting from a row key. * @see CollectionModel#areRowsAvailable(Object, int) * @param startRowKey starting row key to check * @param rowsToCheck number of rows to check * @return default implementation returns false */ public boolean areRowsLocallyAvailable(Object startRowKey, int rowsToCheck) { return false; } /** * Check if a range of rows is locally available starting from current position. * This implementation checks for a valid current index and delegates to * areRowsLocallyAvailable(startIndex, rowsToCheck) * @param rowsToCheck number of rows to check * @return default implementation returns false * @see areRowsLocallyAvailable(startIndex, rowsToCheck) */ public boolean areRowsLocallyAvailable(int rowsToCheck) { boolean available = false; int startIndex = getRowIndex(); if (startIndex >= 0) { available = areRowsLocallyAvailable(startIndex, rowsToCheck); } return available; } /** * Given a row index, check if the row is locally available. * @param rowIndex row index to check * @return default implementation returns false */ public boolean isRowLocallyAvailable(int rowIndex) { return false; } /** * Given a row key, check if the row is locally available. * @param rowKey row key to check * @return default implementation returns false */ public boolean isRowLocallyAvailable(Object rowKey) { return false; } /** * Convenient API to return a row count estimate. * @see CollectionModel#getRowCount * @return This implementation returns exact row count */ public int getEstimatedRowCount() { return getRowCount(); } /** * Helper API to determine if the row count returned from {@link #getEstimatedRowCount} * is EXACT, or an ESTIMATE. * @see CollectionModel#getRowCount * @return This implementation returns exact row count */ public LocalRowKeyIndex.Confidence getEstimatedRowCountConfidence() { return LocalRowKeyIndex.Confidence.EXACT; } /** * Clears the row with the given index from local cache. * This is a do nothing implementaion. * @see #clearCachedRows(int, int) * @param index row index for the row to remove from cache */ public void clearCachedRow(int index) { clearCachedRows(index, 1); } /** * Clears the row with the given row key from local cache. * This is a do nothing implementaion which delegates to the * correcsponding range based api * @see #clearCachedRows(Object, int) * @param rowKey row key for the row to remove from cache */ public void clearCachedRow(Object rowKey) { clearCachedRows(rowKey, 1); } /** * Clears a range of rows from local cache starting from a row index. * This is a do nothing implemenation. * @see #clearLocalCache * @param startingIndex starting row index to clear the local cache from * @param rowsToClear number of rows to clear */ public void clearCachedRows(int startingIndex, int rowsToClear) { clearLocalCache(); } /** * Clears a range of rows from local cache starting from a row key * This is a do nothing implemenation. * @see #clearLocalCache * @param startingRowKey starting row key to clear the local cache from * @param rowsToClear number of rows to clear */ public void clearCachedRows(Object startingRowKey, int rowsToClear) { clearLocalCache(); } /** * Clears the local cache. * This is a do nothing implementation */ public void clearLocalCache() { // do nothing } /** * Returns the row caching strategy used by this implemenation. Default * implementation indicates no caching supported * @see LocalRowKeyIndex.LocalCachingStrategy * @return caching strategy none */ public LocalRowKeyIndex.LocalCachingStrategy getCachingStrategy() { return LocalRowKeyIndex.LocalCachingStrategy.NONE; } /** * Ensure that the model has at least rowCount number of rows. This is especially * useful for collection model that support paging. The default implementation * is a no-op. * * @param rowCount the number of rows the model should hold. */ public void ensureRowsAvailable(int rowCount) { return; } /** * Gets the row limit of this collection model. Default is to return UNKNOWN_ROW_LIMIT. * Subclasses should override this method if row limit is enforced. * @return the maximum number of rows this collection can hold, possible return values are: * A positive number: the maximum number of rows the collection can hold * UNKNOWN_ROW_LIMIT: row limit is unknown. * UNLIMITED_ROW: there is no limit */ public int getRowLimit() { return UNKNOWN_ROW_LIMIT; } public final static int UNLIMITED_ROW = -2; public final static int UNKNOWN_ROW_LIMIT = -1; private List _rowKeyChangeListeners = new ArrayList(3); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy