/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
* <p>
*/
package org.olat.core.gui.components.table;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.AbstractComponent;
import org.olat.core.gui.components.ComponentRenderer;
import org.olat.core.gui.components.choice.ChoiceModel;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.render.StringOutput;
import org.olat.core.gui.render.StringOutputPool;
import org.olat.core.gui.translator.Translator;
import org.olat.core.logging.OLATRuntimeException;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.StringHelper;
import org.olat.core.util.filter.Filter;
import org.olat.core.util.filter.FilterFactory;
/**
* Description: <br>
* a table. 1.) set column descriptors 2.) generate a tabledatamodel with valid
* datas 3.) do table.setTableDataModel(tdm); this inits and sorts the table
*
* @author Felix Jost
*/
public class Table extends AbstractComponent {
private static final int NO_ROW_SELECTED = -1;
private static final int DEFAULT_RESULTS_PER_PAGE = 20;
private static final int INITIAL_COLUMNSIZE = 5;
private static final OLog log = Tracing.createLoggerFor(Table.class);
private static final ComponentRenderer RENDERER = new TableRenderer();
/**
* TableMultiSelectEvent command identifier.
*/
public static final String COMMAND_MULTISELECT = "ms";
// The following two commands will be submitted traditional style via URLBuilder URIS.
/**
* Table row selection.
*/
public static final String COMMANDLINK_ROWACTION_CLICKED = "r";
/**
* Comment for <code>COMMAND_ROWACTION_CLICKED_ROWID</code>
*/
public static final String COMMANDLINK_ROWACTION_ID = "p";
// The following commands will be submitted via hidden form parameters.
// The commands are internal to the table and affect functionality such as sorting and pageing.
// The two ids formCmd and formParam are the hidden fields used by the form to submit the relevant actions.
protected static final String FORM_CMD = "cmd";
protected static final String FORM_PARAM = "param";
/**
* Comment for <code>COMMAND_SORTBYCOLUMN</code>
*/
protected static final String COMMAND_SORTBYCOLUMN = "cid";
/**
* Comment for <code>COMMAND_PAGEACTION</code>
*/
protected static final String COMMAND_PAGEACTION = "pg";
/**
* Comment for <code>COMMAND_PAGEACTION_SHOWALL</code>
*/
protected static final String COMMAND_PAGEACTION_SHOWALL = "a";
/**
* Comment for <code>COMMAND_PAGEACTION_FORWARD</code>
*/
protected static final String COMMAND_PAGEACTION_FORWARD = "f";
/**
* Comment for <code>COMMAND_PAGEACTION_BACKWARD</code>
*/
protected static final String COMMAND_PAGEACTION_BACKWARD = "b";
protected static final String COMMAND_SHOW_PAGES = "s_p";
// order of left-to-right presentation of Columns (visible columndescriptors):
// list of columndescriptors
private List<ColumnDescriptor> columnOrder; // default visibility to improve speed, not private
// all column descriptors whether visible or not
private List<ColumnDescriptor> allCDs; // default visibility to improve speed, not private
private List<ColumnDescriptor> defaultVisibleCDs;
private TableDataModel tableDataModel;
// DO NOT REFERENCE filteredTableDataModel directly, use always getFilteredTableDataModel() because lazy init!
private TableDataModel filteredTableDataModel;
private List<Integer> sorter;
private int sortColumn = 0;
private boolean sortAscending = true;
// config
private boolean multiSelect = false;
private boolean multiSelectDisabled = false;
private boolean selectedRowUnselectable = false;
private boolean sortingEnabled = true;
private boolean displayTableHeader = true;
private boolean displayTableGrid = false; // default
private boolean pageingEnabled = true;
private Integer currentPageId;
private int resultsPerPage;
private boolean isShowAllSelected;
private boolean suppressDirtyFormWarning;
private List<TableMultiSelect> multiSelectActions = new ArrayList<TableMultiSelect>();
private BitSet multiSelectSelectedRows = new BitSet();
private BitSet multiSelectReadonlyRows = new BitSet();
int selectedRowId;
private boolean enableShowAllLinkValue = true;
private String tableSearchString;
/**
* Constructor for a table. The table is preconfigured with the following
* options: downloadOffered=true, sortingEnabled=true,
* columnMovingOffered=true, displayTableHeader=true, displayRowCount=true
*
* @param name
*/
protected Table(String name, Translator translator) {
this(null, name, translator);
}
protected Table(String id, String name, Translator translator) {
super(id, name, translator);
columnOrder = new ArrayList<ColumnDescriptor>(INITIAL_COLUMNSIZE);
allCDs = new ArrayList<ColumnDescriptor>(INITIAL_COLUMNSIZE);
defaultVisibleCDs = new ArrayList<ColumnDescriptor>(INITIAL_COLUMNSIZE);
sorter = new ArrayList<Integer>(DEFAULT_RESULTS_PER_PAGE);
selectedRowId = NO_ROW_SELECTED;
currentPageId = Integer.valueOf(1);
resultsPerPage = DEFAULT_RESULTS_PER_PAGE;
}
/**
* @param column
* @return Column descriptor of given column
*/
protected ColumnDescriptor getColumnDescriptor(final int column) {
return columnOrder.get(column);
}
protected ColumnDescriptor getColumnDescriptorFromAllCDs(final int column) {
return allCDs.get(column);
}
protected int getColumnCountFromAllCDs() {
return allCDs.size();
}
/**
* @return Column descriptor of currently sorted column
*/
protected ColumnDescriptor getCurrentlySortedColumnDescriptor() {
if(columnOrder.size() >= 0 && sortColumn < columnOrder.size()) {
return getColumnDescriptor(sortColumn);
}
return null;
}
/**
*
*/
protected void modelChanged() {
// we got a new TableDataModel, so we need to prepare the sorting
int rows = getRowCount();
selectedRowId = NO_ROW_SELECTED; // no selection anymore
sorter = new ArrayList<Integer>();
for (int i = 0; i < rows; i++) {
sorter.add(Integer.valueOf(i));
}
// notify all ColumnDescriptors so that they get a chance to presort/cache
// sorting before
// the comparator calls start
int cdcnt = getColumnCount();
for (int i = 0; i < cdcnt; i++) {
ColumnDescriptor cd = getColumnDescriptor(i);
cd.modelChanged();
}
// now sort
resort();
updatePageing(getCurrentPageId());
// do not reset pageing to first page when in multiselect - else cannot keep the page selection when item selected (see OLAT-1340)
if (pageingEnabled && !isMultiSelect()) {
currentPageId = Integer.valueOf(1);
}
// Reset multi selected rows
// Best would be to remove the unnecessary bits, but how can we know if
// a row has been added or deleted? Most save action is to clear the selections
// and start fresh. But we will loose the selections this way.
// Any better ideas?
multiSelectSelectedRows = new BitSet();
}
/**
* serves a purpose: it maps from the rowid in the gui (first row = 0, second =
* 1 and so on) to the corresponding row in the tabledatamodel
* getSortedRow(guirow) is used by the columnDescriptors: public String
* getRenderValue(int row).. to determine the row in the model they have to
* return and the tablerenderer
*
* @param originalRow
* @return integer representing the row id after sorting
*/
public int getSortedRow(final int originalRow) {
Integer i = sorter.get(originalRow);
return i.intValue();
}
public int getOriginalIndex(final int sortedRow) {
int count = 0;
for(Integer row:sorter) {
if(row.intValue() == sortedRow) {
return count;
}
count++;
}
return -1;
}
/**
* @return integer representing the number of columns in the table
*/
protected int getColumnCount() {
return columnOrder.size();
}
/**
* @return integer representing the number of rows in the table
*/
protected int getRowCount() {
if (isTableFiltered()) {
return getFilteredTableDataModel().getRowCount();
} else {
return tableDataModel.getRowCount();
}
}
/**
* @param position The position of the column descriptor. Set to -1 to add this CD at the end.
* @param visible
* @param cd
*/
protected void addColumnDescriptor(final ColumnDescriptor cd, final int position, final boolean visible) {
cd.setTable(this);
if (position != NO_ROW_SELECTED){
allCDs.add(position, cd);
} else {
allCDs.add(cd);
}
if (visible) {
if (position != NO_ROW_SELECTED){
columnOrder.add(position, cd);
} else {
columnOrder.add(cd);
}
defaultVisibleCDs.add(cd);
}
}
/**
* @param cd
*/
protected void addColumnDescriptor(final ColumnDescriptor cd) {
addColumnDescriptor(cd, -1, true);
}
/**
* Remove a column descriptor.
*
* @param position
*/
protected void removeColumnDescriptor(final int position) {
allCDs.remove(position);
columnOrder.remove(position);
if (sortColumn >= allCDs.size()){
sortColumn = 0;
}
}
/**
* @see org.olat.core.gui.components.Component#dispatchRequest(org.olat.core.gui.UserRequest)
*/
@Override
protected void doDispatchRequest(final UserRequest ureq) {
String formCmd = ureq.getParameter(FORM_CMD);
String formParam = ureq.getParameter(FORM_PARAM);
String rowAction = ureq.getParameter(COMMANDLINK_ROWACTION_CLICKED);
String multiAction = ureq.getParameter("multi_action_identifier");
if (formCmd != null && formCmd.length() > 0) {
// this is an internal command submitted by a form-submit()
// first update the multiselect state
updateMultiSelectState(ureq);
// then fetch the internal command to be processed
if (formCmd.equals(COMMAND_SORTBYCOLUMN)) {
doSort(ureq, formParam);
} else if (formCmd.equals(COMMAND_PAGEACTION)) {
doAction(ureq, formParam);
}
} else if (rowAction != null) {
// this is a row action clicked by the user. no form is submitted, so we don't evaluate any columns.
String actionId = ureq.getParameter(COMMANDLINK_ROWACTION_ID);
// sanity check
int rowid = Integer.parseInt(rowAction);
int actualrows = getTableDataModel().getRowCount();
if (rowid < 0 || rowid >= actualrows){
setDirty(true);
return;
}
selectedRowId = rowid;
//setDirty(true); commented as timestamp was consumed in AJAX mode: see OLAT-2007
// create and add replay event
fireEvent(ureq, new TableEvent(COMMANDLINK_ROWACTION_CLICKED, selectedRowId, actionId));
} else if(StringHelper.containsNonWhitespace(multiAction)) {
// check for multiselect actions
for (TableMultiSelect action: multiSelectActions) {
String actionIdentifier = action.getAction();
if (multiAction.equals(actionIdentifier)) {
// get the multiselect command
// update multiselect state
updateMultiSelectState(ureq);
setDirty(true);
fireEvent(ureq, new TableMultiSelectEvent(COMMAND_MULTISELECT, actionIdentifier, getMultiSelectSelectedRows()));
break;
}
}
}
}
private void doSort(UserRequest ureq, String value) {
// if sorting command, resort
int oldSortColumn = sortColumn;
sortColumn = Integer.parseInt(value);
if (oldSortColumn == sortColumn) { // click the same column again, change
// sort order
sortAscending = !sortAscending;
} else { // new column, always sort ascending first
sortAscending = true;
}
setDirty(true);
resort();
fireEvent(ureq, new TableEvent(COMMAND_SORTBYCOLUMN, -1, COMMAND_SORTBYCOLUMN));
}
private void doAction(UserRequest ureq, String value) {
if (value.equals(COMMAND_PAGEACTION_SHOWALL)) {
//updatePageing(null); (see OLAT-1340)
setShowAllSelected(true);
fireEvent(ureq, new Event(COMMAND_PAGEACTION_SHOWALL));
setDirty(true);
} else if (value.equals(COMMAND_PAGEACTION_FORWARD)) {
if (currentPageId != null) {
updatePageing(Integer.valueOf(currentPageId.intValue() + 1));
setDirty(true);
}
} else if (value.equals(COMMAND_PAGEACTION_BACKWARD)) {
if (currentPageId != null) {
updatePageing(Integer.valueOf(currentPageId.intValue() - 1));
setDirty(true);
}
} else if (value.equals(COMMAND_SHOW_PAGES)) {
setShowAllSelected(false);
fireEvent(ureq, new Event(COMMAND_SHOW_PAGES));
if (currentPageId != null) {
updatePageing(Integer.valueOf(currentPageId.intValue()));
}else {
updatePageing(Integer.valueOf(1));
}
setDirty(true);
} else {
updatePageing(Integer.valueOf(value));
setDirty(true);
}
}
/**
* Updates the state of multi selects in the table.
* The state is saved in a BitSet (each bit representing a
* column within the tablemodel.
*
*/
private void updateMultiSelectState(final UserRequest ureq) {
String[] sRowIds = ureq.getHttpReq().getParameterValues(TableRenderer.TABLE_MULTISELECT_GROUP);
if (sRowIds == null) {
multiSelectSelectedRows = new BitSet(); //if all deselected create new multiSelectSelectedRows
return;
}
List<Integer> rowIds = new ArrayList<Integer>();
for (int i = 0; i < sRowIds.length; i++) {
String sRowId = sRowIds[i];
try {
rowIds.add(Integer.valueOf(sRowId));
} catch (NumberFormatException nfe) {
throw new OLATRuntimeException("Invalid rowID submitted as table multiselect parameter", nfe);
}
}
int rows = getRowCount();
int startRowId = 0;
int endRowId = rows;
// initalize pageing
if (isPageingEnabled() && currentPageId != null && !isShowAllSelected()) {
startRowId = ((currentPageId.intValue() - 1) * resultsPerPage);
endRowId = startRowId + resultsPerPage;
if (endRowId > rows){
endRowId = rows;
}
} else {
startRowId = 0;
endRowId = rows;
}
// walk through all the rows and see if the row is selected this time.
// we need this because user might have unchecked a row.
for (int i = startRowId; i < endRowId; i++) {
Integer sortedRow = Integer.valueOf(getSortedRow(i));
if (rowIds.contains(sortedRow)) {
rowIds.remove(sortedRow);
multiSelectSelectedRows.set(sortedRow.intValue());
} else {
multiSelectSelectedRows.clear(sortedRow.intValue());
}
}
}
/**
* Sets the tableDataModel. IMPORTANT: Once a tableDataModel is set, it is
* assumed to remain constant in its data & row & colcount. Otherwise a
* modelChanged has to be called
*
* @param tableDataModel The tableDataModel to set
*/
protected void setTableDataModel(final TableDataModel tableDataModel) {
this.tableDataModel = tableDataModel;
this.filteredTableDataModel = null; // lazy init
// modelChanged(); now called from the controller
}
/**
* @return TableDataModel
*/
public TableDataModel getTableDataModel() {
if (isTableFiltered()) {
return getFilteredTableDataModel();
} else {
return tableDataModel;
}
}
/**
* @return filtered TableDataModel
*/
public TableDataModel getUnfilteredTableDataModel() {
return tableDataModel;
}
/**
* @return filtered TableDataModel
*/
public TableDataModel getFilteredTableDataModel() {
if (filteredTableDataModel == null) {
this.filteredTableDataModel = (TableDataModel)tableDataModel.createCopyWithEmptyList();
}
return filteredTableDataModel;
}
protected void resort() {
if (isSortingEnabled() && sortColumn < getColumnCount()) {
ColumnDescriptor currentSortingCd = getColumnDescriptor(sortColumn); // we sort after this
if(currentSortingCd != null && currentSortingCd.isSortingAllowed()) {
// column descriptor
// notify all nonactive ColumnDescriptors about their state
int cdcnt = getColumnCount();
for (int i = 0; i < cdcnt; i++) {
ColumnDescriptor cd = getColumnDescriptor(i);
if (cd != currentSortingCd){
cd.otherColumnDescriptorSorted();
}
}
currentSortingCd.sortingAboutToStart();
Collections.sort(sorter, new TableComparator(currentSortingCd, sortAscending));
} else {
log.error("Sort column not found:" + sortColumn + " in columns: " + columnOrder, null);
}
}
}
public TableComparator getComparator() {
ColumnDescriptor currentSortingCd = getColumnDescriptor(sortColumn);
return new TableComparator(currentSortingCd, sortAscending);
}
/**
* * used by renderer only
*
* @return boolean
*/
public boolean isSortAscending() {
return sortAscending;
}
/**
* Sets the sortColumn.
*
* @param sortColumn The sortColumn to set
* @param isSortAscending true: sorting ascending order
*/
protected void setSortColumn(final int sortColumn, final boolean isSortAscending) {
this.sortColumn = sortColumn;
this.sortAscending = isSortAscending;
}
/**
* @return boolean
*/
protected boolean isSelectedRowUnselectable() {
return selectedRowUnselectable;
}
/**
* Sets the selectedRowUnselectable.
*
* @param selectedRowUnselectable The selectedRowUnselectable to set
*/
protected void setSelectedRowUnselectable(final boolean selectedRowUnselectable) {
this.selectedRowUnselectable = selectedRowUnselectable;
}
/**
* @return int
*/
protected int getSelectedRowId() {
return selectedRowId;
}
/**
* Sets the selectedRowId.
*
* @param selectedRowId The selectedRowId to set
*/
protected void setSelectedRowId(final int selectedRowId) {
this.selectedRowId = selectedRowId;
}
/**
* @return true when data can be and should be sorted, false for data that is
* not sortable
*/
public boolean isSortingEnabled() {
return sortingEnabled;
}
/**
* Set table configuration: should table be sortable and be sorted when adding
* a new table data model?
*
* @param sortingEnabled true: allow table sorting, false: never sort table,
* not even when adding a new table data model.
*/
protected void setSortingEnabled(final boolean sortingEnabled) {
this.sortingEnabled = sortingEnabled;
}
/**
* @return true: table will render header, false: table has no headers
*/
protected boolean isDisplayTableHeader() {
return displayTableHeader;
}
/**
* Set the table header configuration
*
* @param displayTableHeader
*/
protected void setDisplayTableHeader(final boolean displayTableHeader) {
this.displayTableHeader = displayTableHeader;
}
/**
* Option to display a table grid
* @param enabled true: show the table grid; false: don't show table grid (default)
*/
protected void setDisplayTableGrid(boolean enabled) {
this.displayTableGrid = enabled;
}
/**
* @return true: show the table grid; false: don't show table grid (default)
*/
protected boolean isDisplayTableGrid() {
return this.displayTableGrid;
}
/**
* @param cd
* @return true if the columndescriptor is visible
*/
protected boolean isColumnDescriptorVisible(final ColumnDescriptor cd) {
return columnOrder.contains(cd);
}
/**
* @return a tabledatamodel for a choice
*/
protected ChoiceModel createChoiceModel() {
return new ChoiceTableDataModel(isMultiSelect(), allCDs, columnOrder, getTranslator());
}
/**
* Only use this for reset as it reorder the columns, remove the multi-select column
* and so on...
*
* @return The list of index (without multi select) and reorder to begin with 0
*/
protected List<Integer> getDefaultVisibleColumnsToResetColumnsChoice() {
List<Integer> indexList = new ArrayList<>();
for(ColumnDescriptor defaultVisibleCD:defaultVisibleCDs) {
if(defaultVisibleCD instanceof MultiSelectColumnDescriptor) continue;
int index = allCDs.indexOf(defaultVisibleCD);
if(isMultiSelect()) {
if(index > 0) {
indexList.add(index - 1);
}
} else {
indexList.add(index);
}
}
return indexList;
}
/**
* @param selRows
*/
protected void updateConfiguredRows(final List<Integer> selRows) {
setDirty(true);
columnOrder.clear();
for (Iterator<Integer> itSel = selRows.iterator(); itSel.hasNext();) {
int pos = itSel.next().intValue();
// if multiselect, skip the first cd (which is the multiselect CD)
if (isMultiSelect()){
pos += 1;
}
columnOrder.add(allCDs.get(pos));
}
// make sure sorting is smooth in all cases
if (isMultiSelect()) {
// if multiselect, add the multiselect CD at the beginning
MultiSelectColumnDescriptor mscd = new MultiSelectColumnDescriptor();
mscd.setTable(this);
columnOrder.add(0, mscd);
setSortColumn(1, isSortAscending());
} else {
setSortColumn(0, isSortAscending());
}
resort();
}
/**
* @param selRows
* @return true if there is at least one sortable row in the list of all
* columndescriptors (both visible and invisible)
*/
public boolean isSortableColumnIn(final List<Integer> selRows) {
for (Iterator<Integer> itSelRows = selRows.iterator(); itSelRows.hasNext();) {
Integer posI = itSelRows.next();
ColumnDescriptor cd = allCDs.get(posI.intValue());
if (cd.isSortingAllowed()){
return true;
}
}
return false;
}
/**
* @param newPageId new id used as active page
*/
protected void updatePageing(final Integer newPageId) {
if (newPageId == null) {
this.currentPageId = null;
} else {
if (newPageId.intValue() < 1) {
this.currentPageId = Integer.valueOf(1);
} else {
this.currentPageId = newPageId;
if(tableDataModel!=null) {
int maxPageNumber = (tableDataModel.getRowCount()/getResultsPerPage());
if(tableDataModel.getRowCount()%getResultsPerPage() > 0) {
maxPageNumber++;
}
while(currentPageId>maxPageNumber && currentPageId>1) {
currentPageId--;
}
}
}
}
}
/**
* @param enabledFlag
*/
protected void setPageingEnabled(final boolean enabledFlag) {
this.pageingEnabled = enabledFlag;
}
/**
* @return boolean
*/
protected boolean isPageingEnabled() {
return pageingEnabled;
}
/**
* @return Integer current page position
*/
protected Integer getCurrentPageId() {
return currentPageId;
}
/**
* @return int number of results per page
*/
protected int getResultsPerPage() {
return resultsPerPage;
}
/**
* @param resultsPerPage number of results per page
*/
protected void setResultsPerPage(final int resultsPerPage) {
this.resultsPerPage = resultsPerPage;
}
public ComponentRenderer getHTMLRendererSingleton() {
return RENDERER;
}
protected boolean isMultiSelect() {
return multiSelect;
}
protected void setMultiSelect(final boolean multiSelect) {
if (!this.multiSelect && multiSelect) {
// state change: from non-multiselect to multiselect: add extra checkbox column
// add a CD to render the checkboxes
addColumnDescriptor(new MultiSelectColumnDescriptor(), 0, true);
// adjust sort column
setSortColumn(sortColumn + 1, isSortAscending());
} else if (this.multiSelect && !multiSelect) {
// state change: from multiselect to non-multiselect: remove extra checkbox column
// add a CD to render the checkboxes
removeColumnDescriptor(0);
// adjust sort column
setSortColumn(sortColumn - 1, isSortAscending());
}
// only update after state change checks (see above) are through
this.multiSelect = multiSelect;
}
public boolean isMultiSelectAsDisabled() {
return multiSelectDisabled;
}
public void setMultiSelectAsDisabled(boolean disabled) {
multiSelectDisabled = disabled;
}
protected void addMultiSelectAction(String label, String actionKeyi18n, String actionIdentifier) {
multiSelectActions.add(new TableMultiSelect(label, actionKeyi18n, actionIdentifier));
}
protected List<TableMultiSelect> getMultiSelectActions() {
return multiSelectActions;
}
protected BitSet getMultiSelectSelectedRows() {
return multiSelectSelectedRows;
}
public boolean isShowAllSelected() {
return isShowAllSelected;
}
public void setShowAllSelected(final boolean isShowAllSelected) {
this.isShowAllSelected = isShowAllSelected;
}
public void setEnableShowAllLink(final boolean enableShowAllLinkValue) {
this.enableShowAllLinkValue = enableShowAllLinkValue;
}
public boolean isShowAllLinkEnabled() {
return enableShowAllLinkValue;
}
protected void setMultiSelectSelectedAt(final int row, final boolean selected) {
multiSelectSelectedRows.set(row, selected);
}
protected void setMultiSelectReadonlyAt(final int row, final boolean readonly) {
multiSelectReadonlyRows.set(row, readonly);
}
protected BitSet getMultiSelectReadonlyRows() {
return multiSelectReadonlyRows;
}
/**
* @return true: don't check for dirt forms; false: check for dirty forms
* (default)
*/
public boolean isSuppressDirtyFormWarning() {
return suppressDirtyFormWarning;
}
/**
* When pressing an action of the table, the dirty form check is or is not
* check based on the specified setting.
*
* @param suppressDirtyFormWarning true: don't check for dirt forms; false:
* check for dirty forms (default)
*/
public void setSuppressDirtyFormWarning(boolean suppressDirtyFormWarning) {
this.suppressDirtyFormWarning = suppressDirtyFormWarning;
}
protected int getSortColumn() {
return sortColumn;
}
protected boolean getSortAscending() {
return sortAscending;
}
public int getUnfilteredRowCount() {
return tableDataModel.getRowCount();
}
public void setSearchString(final String tableSearchString) {
this.tableSearchString = tableSearchString;
if (isTableFiltered()) {
buildFilteredTableDataModel(tableSearchString);
}
}
private void buildFilteredTableDataModel(final String searchString) {
List<Object> filteredElementList = new ArrayList<Object>();
log.debug("buildFilteredTableDataModel: tableDataModel.getRowCount()=" + tableDataModel.getRowCount());
if (tableDataModel.getRowCount() > 0) {
log.debug("buildFilteredTableDataModel: tableDataModel.getObject(0)=" + tableDataModel.getObject(0));
}
for (int row = 0; row < tableDataModel.getRowCount(); row++) {
if (matchRowWithSearchString(row, searchString)) {
filteredElementList.add(tableDataModel.getObject(row));
}
}
log.debug("buildFilteredTableDataModel: unfiltered-row-count=" + tableDataModel.getRowCount() + " filtered-row-count=" + filteredElementList.size());
getFilteredTableDataModel().setObjects(filteredElementList);
}
/**
* Check if the row-value matches with the search-query.
* @param row
* @param tableSearchString2
* @return
*/
private boolean matchRowWithSearchString(final int row, final String tableSearchString2) {
log.debug("matchRowWithFilter: row=" + row + " tableFilterString=" + tableSearchString);
if ( !isTableFiltered() ) {
return true;
}
// loop over all columns
TableDataModel unfilteredModel = getUnfilteredTableDataModel();
Filter htmlFilter = FilterFactory.getHtmlTagsFilter();
for (int colIndex = getColumnCountFromAllCDs(); colIndex-->0; ) {
ColumnDescriptor cd = getColumnDescriptorFromAllCDs(colIndex);
int dataColumn = cd.getDataColumn();
if (dataColumn >= 0 && isColumnDescriptorVisible(cd)) {
Object value = unfilteredModel.getValueAt(row, dataColumn);
// When a CustomCellRenderer exist, use this to render cell-value to String
if (cd instanceof CustomRenderColumnDescriptor) {
CustomRenderColumnDescriptor cdrd = (CustomRenderColumnDescriptor)cd;
CustomCellRenderer customCellRenderer = cdrd.getCustomCellRenderer();
if (customCellRenderer instanceof CustomCssCellRenderer) {
// For css renderers only use the hover
// text, not the CSS class name and other
// HTLM markup
CustomCssCellRenderer cssRenderer = (CustomCssCellRenderer) customCellRenderer;
value = cssRenderer.getHoverText(value);
if (!StringHelper.containsNonWhitespace((String) value)) {
continue;
}
} else {
StringOutput sb = StringOutputPool.allocStringBuilder(250);
customCellRenderer.render(sb, null, value, cdrd.getLocale(), cd.getAlignment(), null);
value = StringOutputPool.freePop(sb);
}
}
if (value instanceof String) {
String valueAsString = (String)value;
// Remove any HTML markup from the value
valueAsString = htmlFilter.filter(valueAsString);
// Finally compare with search value based on a simple lowercase match
if (valueAsString.toLowerCase().indexOf(tableSearchString2.toLowerCase()) != -1 ) {
return true;
}
}
}
}
return false;
}
public String getSearchString( ) {
return tableSearchString;
}
public boolean isTableFiltered() {
return tableSearchString != null;
}
}
class ChoiceTableDataModel implements ChoiceModel {
private boolean isMultiSelect;
private List<ColumnDescriptor> allCDs;
private List<ColumnDescriptor> columnOrder;
private Translator translator;
protected ChoiceTableDataModel(final boolean isMultiSelect, final List<ColumnDescriptor> allCDs, final List<ColumnDescriptor> columnOrder, final Translator translator) {
this.isMultiSelect = isMultiSelect;
this.allCDs = allCDs;
this.columnOrder = columnOrder;
this.translator = translator;
}
@Override
public int getRowCount() {
// if this is a multiselect table, we do not want the checkboxes of
// the multiselect to be disabled. therefore we simply exclude the entire
// checkbox row (which is at the very beginning of the CD array).
if (isMultiSelect){
return allCDs.size() - 1;
} else {
return allCDs.size();
}
}
@Override
public Boolean isEnabled(int row) {
ColumnDescriptor cd = getObject(row);
return columnOrder.contains(cd) ? Boolean.TRUE : Boolean.FALSE;
}
@Override
public String getLabel(int row) {
ColumnDescriptor cd = getObject(row);
return cd.translateHeaderKey() ? translator.translate(cd.getHeaderKey()) : cd.getHeaderKey();
}
@Override
public boolean isDisabled(int row) {
return false;
}
public ColumnDescriptor getObject(int row) {
return allCDs.get(isMultiSelect? (row + 1): row);
}
}