package er.extensions.batching;
import com.webobjects.appserver.WOContext;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSMutableArray;
import com.webobjects.foundation.NSMutableDictionary;
import er.extensions.components.ERXStatelessComponent;
import er.extensions.localization.ERXLocalizer;
/**
* BatchNavigationBar that uses a direct action and a "batch" URL parameter to
* switch to a specific batch. You have to do the batching yourself and
* get the information from the request though.
*
* Tip for this:
* ERXEOControlUtilities.objectsInRange is a really big help doing that!
*
* @binding actionName (String) - the name of the directAction to call
* @binding actionClass (String) - the name of the class for the directAction
* call
* @binding additionalUrlParameters (NSDictionary) - parameters that get added
* to navigation URLs
* @binding batchsize (Integer) - the size of the batches
* @binding currentBatchIndex (Integer) - the index of the current page
* @binding numberOfObjects (Integer) - the number of objects to batch
* @binding containerCssClass (String) - the css class to use for the
* surrounding div container
* @binding backString (String) - the string to use for the "back" link (HTML
* enabled); default = « back
* @binding forwardString (String) - the string to use for the "forward" link
* (HTML enabled); default = back »
* @binding showPageNumbers (Boolean) - whether or not to show the page numbers
* (you might want to show only the back and forward links); default =
* true
* @binding showPageString (Boolean) - whether or not to show the "Page:" string
* to the left of the nav (will be made more flexible); default = false
* @binding showBatchNavigationForSinglePage (Boolean) - whether or not to show the
* the batch navigation if we have only a single page; default = true
*
* @author cug - Sep 20, 2007
*/
public class ERXDirectActionBatchNavigationBar extends ERXStatelessComponent {
/**
* Do I need to update serialVersionUID?
* See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
* <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
*/
private static final long serialVersionUID = 1L;
/**
* the batchnumbers to display, cached in this instance, resetted after completion
*/
private NSArray<NSDictionary<String, Object>> batchNumbers;
/**
* The one used in the repetition
*/
public NSDictionary dictInRepetition;
// see the constants for the keys at the end of this file
// *******************************************************************
// implementation
/**
* Standard constructor
*
* @param context the current context
*/
public ERXDirectActionBatchNavigationBar(WOContext context) {
super(context);
}
/**
* Reset the instance variables
*/
@Override
public void reset() {
// reset all ivars
dictInRepetition = null;
batchNumbers = null;
}
/**
* Returns the number of batches, the first batch has value 1.
* If the size of one batch is 0, the number of products will
* be returned, in order to avoid {@link ArithmeticException}.
* If the number of products is 0, it will return 0.
*
* @param productCount number of products
* @param sizeOfOneBatch size of one batch
*
* @author edgar - Nov 23, 2007
* @return number of batches
*/
private int numberOfBatches(int productCount, int sizeOfOneBatch) {
if (sizeOfOneBatch == 0) {
return productCount;
}
// this part is needed since the calculation below would return 1
if (productCount == 0) {
return productCount;
}
int numberOfBatches = productCount / sizeOfOneBatch;
if (productCount % sizeOfOneBatch != 0) {
numberOfBatches++;
}
return numberOfBatches;
}
/**
* Returns "batchNumber" objects which are dictionaries with key-value-pairs for the String to display.
*
* @author cug - Sep 20, 2007
*/
public NSArray<NSDictionary<String, Object>> batchNumbers() {
if (batchNumbers == null) {
NSMutableArray<NSDictionary<String, Object>> tmpArray = new NSMutableArray<NSDictionary<String, Object>>();
// get some stuff into the primitives for easy access
int currentBatchIndex = currentBatchIndex().intValue();
int batchSize = batchSize().intValue();
int numberOfObjects = numberOfObjects().intValue();
int tmp = currentBatchIndex % 9;
if (tmp == 0)
tmp = 9;
int batchIndex = currentBatchIndex - tmp + 1;
int maxPages = numberOfBatches(numberOfObjects, batchSize);
int batchStart = batchIndex;
int batchEnd = 0;
// set the currentBatchIndex in the middle
if (currentBatchIndex > 5 && maxPages > 9) {
// TODO cug: showing 9 page numbers right now as a default, maybe make this configurable
batchIndex = batchStart = currentBatchIndex - 4;
}
// fill the array with dictionaries, one dictionary for each page number to be displayed
while (tmpArray.count() < 9 && batchIndex <= maxPages) {
// TODO cug: showing 9 page numbers right now as a default, maybe make this configurable
NSMutableDictionary<String, Object> entry = new NSMutableDictionary<>();
entry.setObjectForKey(Integer.valueOf(batchIndex), "batchNumber");
entry.setObjectForKey(Integer.valueOf(batchIndex).toString(), "batchString");
entry.setObjectForKey(Boolean.FALSE, "disable");
tmpArray.addObject(entry);
batchEnd = batchIndex;
batchIndex++;
}
if (batchStart >= 2) {
// we add page number 1 at the start and some dots between 1 and the other numbers
NSMutableDictionary<String, Object> entry = new NSMutableDictionary<>();
entry.setObjectForKey(Integer.valueOf(1), "batchNumber");
entry.setObjectForKey(Integer.valueOf(1).toString(), "batchString");
entry.setObjectForKey(Boolean.FALSE, "disabled");
tmpArray.insertObjectAtIndex(entry, 0);
if (batchStart > 2) {
// there are batches hidden, so we add the dots in here
entry = new NSMutableDictionary<>();
entry.setObjectForKey(Integer.valueOf(0), "batchNumber");
entry.setObjectForKey("...", "batchString");
entry.setObjectForKey(Boolean.TRUE, "disabled");
tmpArray.insertObjectAtIndex(entry, 1);
}
else {
// to remove the inconsistency, we remove the batch number 10 from the array
tmpArray.removeObjectAtIndex(9);
}
}
if (batchEnd < maxPages - 1) {
// add dots at the end, because we have more than one batch more on the right side
NSMutableDictionary<String, Object> entry = new NSMutableDictionary<>();
entry = new NSMutableDictionary<>();
entry.setObjectForKey(Integer.valueOf(0), "batchNumber");
entry.setObjectForKey("...", "batchString");
entry.setObjectForKey(Boolean.TRUE, "disabled");
tmpArray.addObject(entry);
}
if (batchEnd < maxPages) {
// add the last batch
NSMutableDictionary<String, Object> entry = new NSMutableDictionary<>();
entry.setObjectForKey(lastBatch(), "batchNumber");
entry.setObjectForKey(lastBatch().toString(), "batchString");
entry.setObjectForKey(Boolean.FALSE, "disabled");
tmpArray.addObject(entry);
}
batchNumbers = tmpArray.immutableClone();
}
return batchNumbers;
}
/**
* Returns whether we have more than one batch and should show the navigation at all
*
* @author cug - Nov 20, 2007
*/
public boolean hasMoreThanOneBatch() {
if (batchNumbers() != null && batchNumbers().count() > 1) {
return true;
}
return false;
}
/**
* Should we show the batch navigation bar? Checks the binding "showBatchNavigationForSinglePage"
*
* @author cug - Nov 20, 2007
* @return true if "showBatchNavigationForSinglePage" is true or there is more than one page
*/
public boolean showNavigationBar() {
return hasMoreThanOneBatch() || showBatchNavigationForSinglePage();
}
/**
* Returns whether the currently generated item from the repetition is the selected one
*
* @return true for the selected batch (page)
*/
public boolean isSelected() {
return dictInRepetition.valueForKey("batchNumber").equals(currentBatchIndex());
}
/**
* Returns the number for the previous batch
*
* @return the previous batch number
*/
public Integer previousBatch() {
return Integer.valueOf(currentBatchIndex().intValue() - 1);
}
/**
* Returns the number for the next batch
*
* @return the number for the next batch
*/
public Integer nextBatch() {
return Integer.valueOf(currentBatchIndex().intValue() + 1);
}
/**
* Returns the number of the last page
*
* @return the number of the last page
*/
public Integer lastBatch() {
return Integer.valueOf(numberOfBatches(numberOfObjects().intValue(), batchSize()));
}
/**
* Returns true if the currently displayed batch is the last one (for hiding the arrows on the right)
*
* @return true for the last batch
*/
public boolean isLastBatch() {
return currentBatchIndex().intValue() == numberOfBatches(numberOfObjects().intValue(), batchSize());
}
/**
* Returns true if the currently displayed batch is the first one (for hiding the arrows on the left)
*
* @return true for the first batch
*/
public boolean isFirstBatch() {
return currentBatchIndex().intValue() == 1;
}
/**
* Convenience method to get the localizer.
*
*/
@Override
public ERXLocalizer localizer() {
if (context().hasSession()) {
return ERXLocalizer.currentLocalizer();
}
return ERXLocalizer.localizerForLanguages(context().request().browserLanguages());
}
// *******************************************************************
// bindings
/**
* Returns the name of the direct action to call
*
* @return a direct action name
*/
public String actionName() {
return stringValueForBinding(ACTION_NAME_KEY);
}
/**
* Sets the name of the direct action to call
*
* @param name - a direct action name
*/
public void setActionName(String name) {
setValueForBinding(name, ACTION_NAME_KEY);
}
/**
* Returns the name of the direct action class, if empty the default class is used (DirectAction)
*
* @return a name for a subclass of WODirectAction
*/
public String actionClass() {
if (stringValueForBinding(ACTION_CLASS_KEY) != null) {
return stringValueForBinding(ACTION_CLASS_KEY);
}
return "DirectAction";
}
/**
* Sets the name of the actionClass to use for the direct action call
*
* @param className - name for the directAction class
*/
public void setActionClass(String className) {
setValueForBinding(className, ACTION_CLASS_KEY);
}
/**
* The current batch size
*
* @return the batch size
*/
public Integer batchSize() {
return Integer.valueOf(intValueForBinding(BATCH_SIZE_KEY, defaultBatchSize));
}
/**
* Set the size of the batches to create the pager
*
* @param size number of items per batch
*/
public void setBatchSize(Integer size) {
setValueForBinding(size, BATCH_SIZE_KEY);
}
/**
* The total number of objects for which we create the batches
*
* @return total number of objects
*/
public Integer numberOfObjects() {
return Integer.valueOf(intValueForBinding(NUMBER_OF_OBJECTS_KEY, 0));
}
/**
* Set total number of objects for which we create the batches
*
* @param n
*/
public void setNumberOfObjects(Integer n) {
takeValueForKey(n, NUMBER_OF_OBJECTS_KEY);
}
/**
* The current batch index
*
* @return the current batch index
*/
public Integer currentBatchIndex() {
return Integer.valueOf(intValueForBinding(CURRENT_BATCH_INDEX_KEY, 0));
}
/**
* Set the current batch index
*
* @param index - the index to set
*/
public void setCurrentBatchIndex(Integer index) {
setValueForBinding(index, CURRENT_BATCH_INDEX_KEY);
}
/**
* The parameters to add to each link
*
* @return dict of parameters
*/
@SuppressWarnings("unchecked")
public NSDictionary<String, Object> additionalUrlParameters() {
return (NSDictionary<String, Object>) valueForBinding(ADDITIONAL_URL_PARAMETERS_KEY);
}
/**
* Set the parameters to add to each link
*
* @param dict parameters
*/
public void setAdditionalUrlParameters(NSDictionary<String, Object> dict) {
setValueForBinding(dict, ADDITIONAL_URL_PARAMETERS_KEY);
}
/**
* The css class to use for the surrounding div
*
* @return css class name
*/
public String containerCssClass() {
return stringValueForBinding(CONTAINER_CSS_CLASS_KEY, "ERXDABatchNav");
}
/**
* Set the class name for the surrounding div
*
* @param cssClass CSS class for container element
*/
public void setContainerCssClass(String cssClass) {
takeValueForKey(cssClass, CONTAINER_CSS_CLASS_KEY);
}
/**
* Returns whether or not to show the string for "Page:"
*
* @return the value for the binding
*/
public Boolean showPageString() {
return Boolean.valueOf(booleanValueForBinding(SHOW_PAGE_STRING_KEY, false));
}
/**
* Set whether to show the string "Page:"
*
* @param flag
*/
public void setShowPageString(Boolean flag) {
setValueForBinding(flag, SHOW_PAGE_STRING_KEY);
}
/**
* The string for the forward link
*
*/
public String forwardString() {
return stringValueForBinding(FORWARD_STRING, "forward »");
}
/**
* Set the string for the "forward" link
*
* @param s
*/
public void setForwardString(String s) {
setValueForBinding(s, FORWARD_STRING);
}
/**
* the string for the "back" link
*/
public String backString() {
return stringValueForBinding(BACK_STRING, "« back");
}
/**
* Set the string for the "back" link
*
* @param s
*/
public void setBackString(String s) {
setValueForBinding(s, BACK_STRING);
}
/**
* Returns whether to show the page numbers
*
* @return true for showing the numbers, defaults to true
*/
public Boolean showPageNumbers() {
return Boolean.valueOf(booleanValueForBinding(SHOW_PAGE_NUMBERS, true));
}
/**
* Set whether to show the page numbers
*
* @param flag
*/
public void setShowPageNumbers(Boolean flag) {
setValueForBinding(flag, SHOW_PAGE_NUMBERS);
}
/**
* Return the value for the showBatchNavigationForSinglePage binding
*
* @author cug - Nov 20, 2007
* @return value for the showBatchNavigationForSinglePage binding
*/
public Boolean showBatchNavigationForSinglePage() {
return booleanValueForBinding(SHOW_BATCH_NAVIGATION_FOR_SINGLE_PAGE, true) ? Boolean.TRUE : Boolean.FALSE;
}
/**
* Set the binding value for showBatchNavigationForSinglePage
*
* @author cug - Nov 20, 2007
* @param flag
*/
public void setShowBatchNavigationForSinglePage(Boolean flag) {
setValueForBinding(flag, SHOW_BATCH_NAVIGATION_FOR_SINGLE_PAGE);
}
// *************************************************************************
// some constants
/**
* key for showBatchNavigationForSinglePage binding
*/
private static final String SHOW_BATCH_NAVIGATION_FOR_SINGLE_PAGE = "showBatchNavigationForSinglePage";
/**
* Key for the actionName binding
*/
private static final String ACTION_NAME_KEY = "actionName";
/**
* key for the actionClass binding
*/
private static final String ACTION_CLASS_KEY = "actionClass";
/**
* key for the batchSize binding
*/
private static final String BATCH_SIZE_KEY = "batchSize";
/**
* key for the numberOfObjects binding
*/
private static final String NUMBER_OF_OBJECTS_KEY = "numberOfObjects";
/**
* key for the currentBatchIndex binding
*/
private static final String CURRENT_BATCH_INDEX_KEY = "currentBatchIndex";
/**
* the default batchSize, defaults to 20
*/
private static int defaultBatchSize = 20;
/**
* the key for the additionalUrlParameters binding
*/
private static final String ADDITIONAL_URL_PARAMETERS_KEY = "additionalUrlParameters";
/**
* the key for the surrounding div container class name
*/
private static final String CONTAINER_CSS_CLASS_KEY = "containerCssClass";
/**
* the key for the showPageString binding
*/
private static final String SHOW_PAGE_STRING_KEY = "showPageString";
/**
* the key for the "backString" binding
*/
private static final String BACK_STRING = "backString";
/**
* the key for the "forwardString" binding
*/
private static final String FORWARD_STRING = "forwardString";
/**
* the key for the "showPageNumbers" bindings
*/
private static final String SHOW_PAGE_NUMBERS = "showPageNumbers";
}