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    }