/* | |
* 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.empire.jsf2.pageelements; | |
import java.io.Serializable; | |
import java.util.ArrayList; | |
import java.util.HashSet; | |
import java.util.List; | |
import javax.faces.event.ActionEvent; | |
import org.apache.empire.commons.ObjectUtils; | |
import org.apache.empire.exceptions.NotSupportedException; | |
import org.apache.empire.exceptions.ObjectNotValidException; | |
import org.apache.empire.jsf2.pages.Page; | |
import org.apache.empire.jsf2.pages.PageElement; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
public abstract class ListPageElement<T> extends PageElement | |
{ | |
private static final long serialVersionUID = 1L; | |
private static final Logger log = LoggerFactory.getLogger(ListPageElement.class); | |
protected Class<T> beanClass; | |
protected List<T> items = null; | |
protected SelectionSet selectedItems = null; | |
public static class SelectionSet extends HashSet<String> | |
{ | |
private static final long serialVersionUID = 1L; | |
private boolean singleSelection = false; | |
private boolean invertSelection = false; | |
public SelectionSet() | |
{ | |
super(); | |
} | |
public SelectionSet(int size) | |
{ | |
super(size); | |
} | |
public boolean isSingleSelection() | |
{ | |
return singleSelection; | |
} | |
public void setSingleSelection(boolean singleSelection) | |
{ | |
this.singleSelection = singleSelection; | |
clear(); | |
} | |
public boolean isInvertSelection() | |
{ | |
return invertSelection; | |
} | |
public boolean set(String e) | |
{ | |
clear(); | |
return super.add(e); | |
} | |
@Override | |
public boolean add(String e) | |
{ | |
if (singleSelection) | |
return false; | |
return super.add(e); | |
} | |
@Override | |
public boolean remove(Object o) | |
{ | |
if (singleSelection) | |
return false; | |
return super.remove(o); | |
} | |
public void setInvertSelection(boolean invertSelection) | |
{ | |
if (this.invertSelection==invertSelection) | |
return; // no change | |
// invert | |
this.invertSelection = invertSelection; | |
this.clear(); | |
} | |
@Override | |
public boolean contains(Object item) | |
{ | |
boolean exists = super.contains(item); | |
return (invertSelection ? !exists : exists); | |
} | |
} | |
/** | |
* Interface to uniquely identify a list item | |
* This will be used for selection and navigation | |
*/ | |
public static interface ParameterizedItem { | |
public String getIdParam(); | |
public void setIdParam(String idParam); | |
} | |
/** | |
* Abstract superclass to make a list item selectable | |
*/ | |
public static abstract class SelectableItem implements Serializable | |
{ | |
private static final long serialVersionUID = 1L; | |
private SelectionSet selectSet = null; | |
protected void setSelectMap(SelectionSet selectSet) | |
{ | |
this.selectSet = selectSet; | |
} | |
public boolean isSelected() | |
{ | |
String id = getIdParam(); | |
return this.selectSet.contains(id); | |
} | |
public void setSelected(boolean selected) | |
{ | |
String id = getIdParam(); | |
if (selected != this.selectSet.isInvertSelection()) | |
{ | |
this.selectSet.add(id); | |
} | |
else | |
{ | |
this.selectSet.remove(id); | |
} | |
} | |
public abstract String getIdParam(); | |
} | |
/** | |
* This class holds information about the list view to display. | |
* This will be held on the session in order to maintain position and sorting when navigating back and forth. | |
*/ | |
public static class ListTableInfo implements Serializable | |
{ | |
private static final long serialVersionUID = 1L; | |
/** Pagination **/ | |
/** | |
* position, starts with 0 | |
*/ | |
private int itemCount = -1; | |
private boolean valid = false; | |
private boolean modified = false; | |
/** Sorting **/ | |
private String sortColumnName; | |
private boolean sortAscending = true; | |
private boolean sortOrderChanged = false; | |
/** Pagination **/ | |
private int position = 0; | |
private int pageSize = 0; | |
public void init(int itemCount, int pageSize) | |
{ | |
if (pageSize < 0) | |
{ // pageSize must not be negative! | |
throw new ObjectNotValidException(this); | |
} | |
this.itemCount = itemCount; | |
this.pageSize = pageSize; | |
this.position = 0; | |
this.valid = (itemCount >= 0); | |
this.modified = false; | |
} | |
public boolean isValid() | |
{ | |
return this.valid; | |
} | |
public void setValid(boolean valid) | |
{ | |
if (valid && (this.itemCount < 0)) | |
{ // itemCount and position must not be negative! | |
throw new ObjectNotValidException(this); | |
} | |
this.valid = valid; | |
} | |
public boolean isModified() | |
{ | |
return (this.valid && this.modified); | |
} | |
public void setModified(boolean modified) | |
{ | |
this.modified = modified; | |
} | |
public int getItemCount() | |
{ | |
return this.itemCount; | |
} | |
public int getPageSize() | |
{ | |
return this.pageSize; | |
} | |
/*** sorting ***/ | |
public String getSortColumnName() | |
{ | |
return this.sortColumnName; | |
} | |
public void setSortColumnName(String column) | |
{ | |
if (ObjectUtils.compareEqual(column, this.sortColumnName)) | |
{ | |
return; | |
} | |
// change value | |
this.sortColumnName = column; | |
this.position = 0; | |
this.modified = true; | |
this.sortOrderChanged = true; | |
} | |
public boolean getSortAscending() | |
{ | |
return this.sortAscending; | |
} | |
public void setSortAscending(boolean sortAscending) | |
{ | |
if (this.sortAscending == sortAscending) | |
{ | |
return; | |
} | |
// change value | |
this.sortAscending = sortAscending; | |
this.modified = true; | |
this.sortOrderChanged = true; | |
} | |
public boolean isSortOrderChanged() | |
{ | |
return this.sortOrderChanged; | |
} | |
public void setSortOrderChanged(boolean sortOrderChanged) | |
{ | |
this.sortOrderChanged = sortOrderChanged; | |
} | |
/*** pagination ***/ | |
public int getPosition() | |
{ | |
return this.position; | |
} | |
public void setPosition(int position) | |
{ | |
if (this.pageSize == 0) | |
{ | |
throw new NotSupportedException(this, "setPosition"); | |
} | |
if (position < 0) | |
{ | |
position = 0; | |
} | |
if (position >= this.itemCount) | |
{ | |
position = this.itemCount - 1; | |
} | |
if (this.position == position) | |
{ | |
return; | |
} | |
// change value | |
this.position = position; | |
this.modified = true; | |
} | |
public boolean isAllowPagination() | |
{ | |
return (this.pageSize > 0); | |
} | |
public boolean isHasNextPage() | |
{ | |
if (isValid() && isAllowPagination()) | |
{ | |
return ((this.itemCount - (this.position + this.pageSize)) > 0); | |
} | |
return false; | |
} | |
public boolean isHasPrevPage() | |
{ | |
return (isValid() && isAllowPagination() && this.position > 0); | |
} | |
/** | |
* set the next x entries | |
*/ | |
public void nextPage(ActionEvent e) | |
{ | |
if (!isAllowPagination()) | |
{ | |
throw new NotSupportedException(this, "nextPage"); | |
} | |
// Check | |
if ((this.position + this.pageSize) > this.itemCount) | |
{ | |
return; // Already on last page | |
} | |
// One page forward | |
this.position += this.pageSize; | |
this.modified = true; | |
} | |
/** | |
* set the prev x entries | |
*/ | |
public void prevPage(ActionEvent e) | |
{ | |
if (!isAllowPagination()) | |
{ | |
throw new NotSupportedException(this, "prevPage"); | |
} | |
// Check | |
if (this.position == 0) | |
{ | |
return; // Already on first page | |
} | |
// one page back | |
if (this.position > this.pageSize) | |
{ | |
this.position -= this.pageSize; | |
} | |
else | |
{ | |
this.position = 0; | |
} | |
// modified | |
this.modified = true; | |
} | |
public int getPageNumber() | |
{ | |
if (!isAllowPagination()) | |
{ | |
return 1; // always first page | |
} | |
// Calc Page num | |
int pageNumber = new Double(Math.ceil(((double) this.position / this.pageSize))).intValue() + 1; | |
return pageNumber; | |
} | |
public int getPageCount() | |
{ | |
if (!isAllowPagination()) | |
{ | |
return 1; // only one page | |
} | |
// Calc Page count | |
int pageCount = new Double(Math.ceil(((double) this.itemCount / this.pageSize))).intValue(); | |
return pageCount; | |
} | |
} | |
public ListPageElement(Page page, Class<T> beanClass, String propertyName) | |
{ | |
super(page, propertyName); | |
// set bean Class | |
this.beanClass = beanClass; | |
// selectable | |
if (ListPageElement.isSelectableItem(beanClass)) | |
{ | |
this.selectedItems = new SelectionSet(); | |
} | |
} | |
private static <T> boolean isSelectableItem(Class<T> beanClass) | |
{ | |
Class<?> superClass = beanClass.getSuperclass(); | |
while (superClass != null) | |
{ // Check Superclass | |
if (superClass.equals(SelectableItem.class)) | |
{ | |
return true; | |
} | |
superClass = superClass.getSuperclass(); | |
} | |
return false; | |
} | |
/** session scoped properties **/ | |
public abstract ListTableInfo getTableInfo(); | |
public List<T> getItems() | |
{ | |
if (this.items == null) | |
{ | |
ListPageElement.log.warn("Bean List has not been initialized!"); | |
} | |
return this.items; | |
} | |
/** | |
* returns whether the item list has been loaded | |
* @return true if the item list has been loaded even it contains no items for false otherwise | |
*/ | |
public final boolean isValid() | |
{ | |
return (this.items != null); | |
} | |
/** | |
* returns true if the either the item list is not valid or if it contains no items | |
* @return true if the either the item list is not valid or if it contains no items | |
*/ | |
public final boolean isEmpty() | |
{ | |
return (this.items==null || this.items.size()==0); | |
} | |
/** | |
* returns whether the item list is valid and contains at least one item | |
* @return true if the item list contains one or more items or false otherwise | |
*/ | |
public final boolean isNotEmpty() | |
{ | |
return (this.items!=null && this.items.size()>0); | |
} | |
/** | |
* added as "isEmpty" is not accessible from EL. | |
* @return true if the either the item list is not valid or if it contains no items | |
*/ | |
public final boolean isBlank() | |
{ | |
return isEmpty(); | |
} | |
/** | |
* returns the total item count of the entire list (not just the visible part) | |
* @return the total number of items in the list | |
*/ | |
public int getItemCount() | |
{ | |
return getTableInfo().getItemCount(); | |
} | |
public void clearItems() | |
{ | |
clearSelection(); | |
this.items = null; | |
} | |
/*** Selection ***/ | |
public void clearSelection() | |
{ | |
if (this.selectedItems != null) | |
{ | |
this.selectedItems.clear(); | |
} | |
} | |
public boolean isHasSelection() | |
{ | |
if (this.selectedItems==null) | |
return false; | |
// Has selected Items | |
return (selectedItems.size()>0 || selectedItems.isInvertSelection()); | |
} | |
public int getSelectedItemCount() | |
{ | |
if (this.selectedItems==null) | |
return 0; | |
// Item count | |
return this.selectedItems.size(); | |
} | |
public List<T> getSelectedItems() | |
{ | |
if (this.selectedItems==null) | |
throw new NotSupportedException(this, "getSelectedItems"); | |
// find all items | |
List<T> selection = new ArrayList<T>(this.selectedItems.size()); | |
for (T item : getItems()) | |
{ | |
if (((SelectableItem)item).isSelected()) | |
selection.add(item); | |
} | |
return selection; | |
} | |
public boolean isInvertSelection() | |
{ | |
if (this.selectedItems==null) | |
return false; | |
// Invert selection | |
return this.selectedItems.isInvertSelection(); | |
} | |
public void setInvertSelection(boolean invertSelection) | |
{ | |
if (this.selectedItems==null) | |
throw new NotSupportedException(this, "setInvertSelection"); | |
// Invert | |
this.selectedItems.setInvertSelection(invertSelection); | |
} | |
public boolean isSingleSelection() | |
{ | |
if (this.selectedItems==null) | |
return false; | |
// Invert selection | |
return this.selectedItems.isSingleSelection(); | |
} | |
public void setSingleSelection(boolean singleSelection) | |
{ | |
if (this.selectedItems==null) | |
throw new NotSupportedException(this, "setSingleSelection"); | |
// Invert | |
this.selectedItems.setSingleSelection(singleSelection); | |
} | |
public void setSelection(SelectableItem item) | |
{ | |
if (this.selectedItems==null) | |
throw new NotSupportedException(this, "setInvertSelection"); | |
// Invert | |
if (item!=null) | |
this.selectedItems.set(item.getIdParam()); | |
else | |
this.selectedItems.clear(); | |
} | |
public void setSelection(SelectableItem[] items) | |
{ | |
if (this.selectedItems==null) | |
throw new NotSupportedException(this, "setInvertSelection"); | |
// Invert | |
this.selectedItems.clear(); | |
for (SelectableItem item : items) | |
this.selectedItems.add(item.getIdParam()); | |
} | |
protected void assignSelectionMap(List<?> items) | |
{ | |
// Check selectable | |
if (this.selectedItems == null) | |
return; | |
// generate all | |
for (Object item : items) | |
{ | |
if (item instanceof SelectableItem) | |
{ | |
((SelectableItem) item).setSelectMap(this.selectedItems); | |
} | |
} | |
} | |
} |