/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.submit;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.dspace.app.util.SubmissionInfo;
import org.dspace.authorize.AuthorizeException;
import org.dspace.core.Context;
/**
* Abstract processing class for DSpace Submission Steps. This defines the base
* methods which are required for any Step processing class.
* <P>
* This abstract class defines the base methods which are used by both the
* Manakin XML UI and the JSP UI to perform submission step processing.
* <P>
* This includes the following methods:
* <ul>
* <li>doProcessing() method - called to perform any step processing</li>
* <li>getErrorFields() method - called to determine the fields which errored
* out during processing</li>
* <li>getErrorMessage() method - called to determine any error message
* returned after processing</li>
* </ul>
* <P>
* If you are using the JSP UI (with the SubmissionController servlet) you
* should extend the org.dspace.submit.SubmissionStep class, which defines
* additional methods used to maintain the context of the submission within a
* JSP environment!
*
* @see org.dspace.app.webui.submit.JSPStepManager
* @see org.dspace.app.webui.servlet.SubmissionController
* @see org.dspace.app.util.SubmissionConfig
* @see org.dspace.app.util.SubmissionStepConfig
*
* @author Tim Donohue
* @version $Revision$
*/
public abstract class AbstractProcessingStep
{
/***************************************************************************
* Constant - Name of the "<-Previous" button
**************************************************************************/
public static final String PREVIOUS_BUTTON = "submit_prev";
/***************************************************************************
* Constant - Name of the "Next->" button
**************************************************************************/
public static final String NEXT_BUTTON = "submit_next";
/***************************************************************************
* Constant - Name of the "Select" list
**************************************************************************/
public static final String SELECT_CHANGE = "submit_change";
/***************************************************************************
* Constant - Name of the "Cancel/Save" button
**************************************************************************/
public static final String CANCEL_BUTTON = "submit_cancel";
/***************************************************************************
* Constant - Prefix of all buttons in the Progress Bar
**************************************************************************/
public static final String PROGRESS_BAR_PREFIX = "submit_jump_";
/***************************************************************************
* Flag which specifies that the LAST PAGE of a step has been reached. This
* flag is used when a Workflow Item is rejected (and returned to the
* workspace) to specify that the LAST PAGE of the LAST STEP has already
* been reached
**************************************************************************/
public static final int LAST_PAGE_REACHED = Integer.MAX_VALUE;
/***************************************************************************
* STATUS / ERROR FLAGS (returned by doProcessing() if an error occurs or
* additional user interaction may be required)
**************************************************************************/
public static final int STATUS_COMPLETE = 0;
/** Maps each status/error flag to a textual, human understandable message * */
private Map<Integer, String> errorMessages = null;
private static final String ERROR_FIELDS_ATTRIBUTE = "dspace.submit.error_fields";
/**
* Do any processing of the information input by the user, and/or perform
* step processing (if no user interaction required)
* <P>
* It is this method's job to save any data to the underlying database, as
* necessary, and return error messages (if any) which can then be processed
* by the doPostProcessing() method.
* <P>
* NOTE: If this step is a non-interactive step (i.e. requires no UI), then
* it should perform *all* of its processing in this method!
*
* @param context
* current DSpace context
* @param request
* current servlet request object
* @param response
* current servlet response object
* @param subInfo
* submission info object
* @return Status or error flag which will be processed by
* doPostProcessing() below! (if STATUS_COMPLETE or 0 is returned,
* no errors occurred!)
*/
public abstract int doProcessing(Context context,
HttpServletRequest request, HttpServletResponse response,
SubmissionInfo subInfo) throws ServletException, IOException,
SQLException, AuthorizeException;
/**
* Return a list of all UI fields which had errors that occurred during the
* step processing. This list is for usage in generating the appropriate
* error message(s) in the UI.
* <P>
* The list of fields which had errors should be set by the AbstractProcessingStep's
* doProcessing() method, so that it can be accessed later by whatever UI is
* generated.
*
* @param request
* current servlet request object
* @return List of error fields (as Strings)
*/
public static final List<String> getErrorFields(HttpServletRequest request)
{
return (List<String>) request.getAttribute(ERROR_FIELDS_ATTRIBUTE);
}
/**
* Sets the list of all UI fields which had errors that occurred during the
* step processing. This list is for usage in generating the appropriate
* error message(s) in the UI.
* <P>
* The list of fields which had errors should be set by the AbstractProcessingStep's
* doProcessing() method, so that it can be accessed later by whatever UI is
* generated.
*
* @param request
* current servlet request object
* @param errorFields
* List of all fields (as Strings) which had errors
*/
private static final void setErrorFields(HttpServletRequest request, List<String> errorFields)
{
if(errorFields==null)
{
request.removeAttribute(ERROR_FIELDS_ATTRIBUTE);
}
else
{
request.setAttribute(ERROR_FIELDS_ATTRIBUTE, errorFields);
}
}
/**
* Add a single UI field to the list of all error fields (which can
* later be retrieved using getErrorFields())
* <P>
* The list of fields which had errors should be set by the AbstractProcessingStep's
* doProcessing() method, so that it can be accessed later by whatever UI is
* generated.
*
* @param fieldName
* the name of the field which had an error
*/
protected static final void addErrorField(HttpServletRequest request, String fieldName)
{
//get current list
List<String> errorFields = getErrorFields(request);
if (errorFields == null)
{
errorFields = new ArrayList<String>();
}
//add this field
errorFields.add(fieldName);
//save updated list
setErrorFields(request, errorFields);
}
/**
* Clears the list of all fields that errored out during the previous step's
* processing.
*
* @param request
* current servlet request object
*
*/
protected static final void clearErrorFields(HttpServletRequest request)
{
//get current list
List<String> errorFields = getErrorFields(request);
if (errorFields != null)
{
setErrorFields(request, null);
}
}
/**
* Return the text of an error message based on the passed in error flag.
* These error messages are used for non-interactive steps (so that they can
* log something more specific than just an error flag)
* <P>
* Since each step can define its own error messages and flags, this method
* depends on all the error messages being initialized by using the
* "addErrorMessage()" method within the constructor for the step class!
*
* @param errorFlag
* The error flag defined in this step which represents an error
* message.
* @return String which contains the text of the error message, or null if
* error message not found
*/
public final String getErrorMessage(int errorFlag)
{
if (this.errorMessages == null || this.errorMessages.size() == 0)
{
return null;
}
else
{
return this.errorMessages.get(Integer.valueOf(errorFlag));
}
}
/**
* Add an error message to the internal map for this step.
* <P>
* This method associates a specific error message with an error flag
* defined in this step.
* <P>
* This is extremely useful to define the error message which will be logged
* for a non-interactive step.
*
* @param errorFlag
* the status value indicating the type of error
* @param errorMessage
* text of the message to be added
*/
protected final void addErrorMessage(int errorFlag, String errorMessage)
{
if (this.errorMessages == null)
{
this.errorMessages = new HashMap<Integer, String>();
}
errorMessages.put(Integer.valueOf(errorFlag), errorMessage);
}
/**
* Retrieves the number of pages that this "step" extends over. This method
* is used by the SubmissionController to build the progress bar.
* <P>
* This method may just return 1 for most steps (since most steps consist of
* a single page). But, it should return a number greater than 1 for any
* "step" which spans across a number of HTML pages. For example, the
* configurable "Describe" step (configured using input-forms.xml) overrides
* this method to return the number of pages that are defined by its
* configuration file.
* <P>
* Steps which are non-interactive (i.e. they do not display an interface to
* the user) should return a value of 1, so that they are only processed
* once!
*
* @param request
* The HTTP Request
* @param subInfo
* The current submission information object
*
* @return the number of pages in this step
*/
public abstract int getNumberOfPages(HttpServletRequest request,
SubmissionInfo subInfo) throws ServletException;
/**
* Find out which page a user is currently viewing
*
* @param request
* HTTP request
*
* @return current page
*/
public static final int getCurrentPage(HttpServletRequest request)
{
int pageNum = -1;
// try to retrieve cached page from request attribute
Integer currentPage = (Integer) request.getAttribute("submission.page");
if (currentPage == null)
{
// try and get it as a 'page' parameter
String val = request.getParameter("page");
try
{
pageNum = Integer.parseInt(val.trim());
}
catch (Exception e)
{
// Problem with parameter
pageNum = -1;
}
// if couldn't find page in request parameter
if (pageNum < 0)
{
// default to page #1, since no other optionsc
pageNum = 1;
setCurrentPage(request, pageNum);
}
else
{
// save to request attribute
setCurrentPage(request, pageNum);
}
}
else
{
pageNum = currentPage.intValue();
}
return pageNum;
}
/**
* Set which page a user is currently viewing
*
* @param request
* HTTP request
* @param pageNumber
* new current page
*/
public static final void setCurrentPage(HttpServletRequest request,
int pageNumber)
{
// set info to request
request.setAttribute("submission.page", Integer.valueOf(pageNumber));
}
}