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 }