001 package hirondelle.web4j.action;
002
003 import java.util.logging.*;
004 import java.util.regex.Pattern;
005 import hirondelle.web4j.request.RequestParameter;
006 import hirondelle.web4j.request.RequestParser;
007 import hirondelle.web4j.util.Util;
008 import hirondelle.web4j.model.AppException;
009
010 /**
011 <b>Template</b> for "first show, then validate and apply" groups of operations.
012
013 <P>A good example is a page allowing the user to change their preferences : from the
014 point of view of each user, there is only one set of user preferences. Often, a single form can be used
015 to allow editing of preferences. Here, there is no listing of multiple items.
016
017 <P>There are two operations in such cases :
018 <ul>
019 <li>show me the form with the current data (if any)
020 <li>allow me to post my (validated) changes
021 </ul>
022 */
023 public abstract class ActionTemplateShowAndApply extends ActionImpl {
024
025 /**
026 Constructor.
027
028 @param aForward used for {@link Operation#Show} operations, and also for <em>failed</em>
029 {@link Operation#Apply} operations. This is the default response.
030 @param aRedirect used for <em>successful</em> {@link Operation#Apply} operations.
031 @param aRequestParser passed to the superclass constructor.
032 */
033 protected ActionTemplateShowAndApply(ResponsePage aForward, ResponsePage aRedirect, RequestParser aRequestParser){
034 super(aForward, aRequestParser);
035 fRedirect = aRedirect;
036 }
037
038 /**
039 The operations supported by this template.
040
041 <P>The supported operations are :
042 <ul>
043 <li> {@link Operation#Show}
044 <li> {@link Operation#Apply}
045 </ul>
046
047 The source of the <tt>Operation</tt> is described by {@link ActionImpl#getOperation()}.
048 */
049 public static final RequestParameter SUPPORTED_OPERATION = RequestParameter.withRegexCheck(
050 "Operation", Pattern.compile("(" + Operation.Show + "|" + Operation.Apply + ")")
051 );
052
053 /**
054 <b>Template</b> method.
055
056 <P>In order to clearly understand the operation of this method, here is the
057 core of its implementation, with all abstract methods in <em>italics</em> :
058 <PRE>
059 if ( Operation.Show == getOperation() ){
060 <em>show();</em>
061 }
062 else if ( Operation.Apply == getOperation() ){
063 <em>validateUserInput();</em>
064 if( ! hasErrors() ){
065 <em>apply();</em>
066 if ( ! hasErrors() ){
067 setResponsePage(fRedirect);
068 }
069 }
070 }
071 </PRE>
072 */
073 @Override public ResponsePage execute() throws AppException {
074 if ( Operation.Show == getOperation() ){
075 fLogger.fine("'Show' Operation.");
076 show();
077 }
078 else if ( Operation.Apply == getOperation() ){
079 fLogger.fine("'Apply' Operation.");
080 validateUserInput();
081 if( ! hasErrors() ){
082 fLogger.fine("Passes validation.");
083 apply();
084 if ( ! hasErrors() ){
085 fLogger.fine("Applied successfully.");
086 setResponsePage(fRedirect);
087 }
088 }
089 }
090 else {
091 throw new AssertionError("Unexpected value for Operation : " + getOperation());
092 }
093
094 return getResponsePage();
095 }
096
097 /**
098 Show the input form.
099
100 <P>The form may or may not be populated.
101 */
102 protected abstract void show() throws AppException;
103
104 /**
105 Validate items input by the user into a form.
106
107 <P>Applied to {@link Operation#Apply}.
108 If an error occurs, then <tt>addError</tt> must be called.
109 */
110 protected abstract void validateUserInput() throws AppException;
111
112 /**
113 Validate the user input, and then apply an edit to the database.
114 If an error occurs, then <tt>addError</tt> must be called.
115 */
116 protected abstract void apply() throws AppException;
117
118 // PRIVATE //
119 private ResponsePage fRedirect;
120 private static final Logger fLogger = Util.getLogger(ActionTemplateShowAndApply.class);
121 }