001 package hirondelle.web4j.action; 002 003 import hirondelle.web4j.util.Util; 004 import java.util.logging.*; 005 006 /** 007 Type-safe enumeration for common operations. 008 009 <P>This <a href="http://www.javapractices.com/Topic1.cjp">type-safe enumeration</a> 010 is somewhat unusual, since its elements are not meant to form a strictly distinct 011 set. For example, one might choose to identify the same operation using 012 either {@link #Save} or {@link #Add}, simply according to taste. Elements may be used as 013 desired, but please note that {@link #isDatastoreEdit} and {@link #hasSideEffects()} return 014 <tt>true</tt> only for specified items. 015 016 <P>Many {@link Action} implementations can benefit from using a request parameter named 017 <tt>'Operation'</tt>, whose value corresponds to a specific <em>subset</em> of the members of this enumeration. 018 For example, {@link hirondelle.web4j.action.ActionTemplateSearch} expects only {@link #Show} and {@link #Search} 019 operations. See {@link hirondelle.web4j.action.ActionImpl#getOperation()} as well. 020 021 <P>Occasionally, you may need to define just such a subset of operations for your own actions. 022 Typically, your code would use the following code snippets. 023 <P> 024 Define an <tt>Operation</tt> {@link hirondelle.web4j.request.RequestParameter} in the {@link Action} using : 025 <PRE> 026 {@code 027 public static final RequestParameter SUPPORTED_OP = RequestParameter.withRegexCheck( 028 "Operation", 029 Pattern.compile("(" + Operation.Show + "|" + Operation.Search + ")") 030 ); 031 } 032 </PRE> 033 Set the value of a corresponding <tt>Operation</tt> field in the {@link Action} constructor using : 034 <PRE> 035 {@code 036 fOperation = Operation.valueOf(aRequestParser.toString(SUPPORTED_OP)); 037 } 038 </PRE> 039 040 <P>Your {@link Action#execute} method will then branch according to the value of 041 the <tt>fOperation</tt> field. 042 043 <P><em>Note regarding forms submitted by hitting the Enter key.</em><br> 044 One must exercise care for the possible submission of forms by hitting the Enter key. 045 Browser behavior is not specified exactly by HTML 4, and various browsers exhibit 046 different behaviors. A common workaround is to place the <tt>Operation</tt> in a 047 <tt>HIDDEN</tt> form item, to ensure that it is always submitted, regardless of 048 the submission mechanism. 049 050 <P>See as well this 051 <a href="http://www.javapractices.com/Topic203.cjp">discussion</a> of <tt>Submit</tt> 052 buttons in multilingual applications. 053 */ 054 public enum Operation { 055 056 /** Add an item to the datastore. */ 057 Add, 058 059 /** Post a change to an item to the datastore. */ 060 Change, 061 062 /** Delete an item from the datastore. */ 063 Delete, 064 065 /** Delete all items from the datastore. */ 066 DeleteAll, 067 068 /** Save an edit to the datastore. */ 069 Save, 070 071 /** Apply an edit to the datastore. */ 072 Apply, 073 074 /** 075 Activate an item. 076 077 <P>The reverse of {@link #Inactivate} 078 */ 079 Activate, 080 081 /** 082 Inactivate an item. 083 084 <P>Often used to implement an <em>abstract</em> <tt>delete</tt> operation, where 085 an item is nominally removed (perhaps by setting the value of a certain column), 086 but no physical deletion of records occurs. 087 */ 088 Inactivate, 089 090 /** 091 Start some process. 092 093 <P>The reverse of {@link #Stop}. 094 */ 095 Start, 096 097 /** 098 Stop some process. 099 100 <P>The reverse of {@link #Start}. 101 */ 102 Stop, 103 104 /** Retrieve a single item, for read-only display. */ 105 Fetch, 106 107 /** Fetch a single item, in preparation for editing the item. */ 108 FetchForChange, 109 110 /** 111 Fetch items needed before adding a new item. 112 An example use case is adding a new item which has a parent of some sort. 113 The parent will need to be fetched before showing the form for adding the child. 114 */ 115 FetchForAdd, 116 117 /** Retrieve what is usually a list of many items, for read-only display. */ 118 List, 119 120 /** Retrieve what is usually a list of many items, in preparation for editing the items. */ 121 ListForChange, 122 123 /** Show an item. */ 124 Show, 125 126 /** Generate a result. */ 127 Generate, 128 129 /** Render a result. */ 130 Render, 131 132 /** Display a result. */ 133 Display, 134 135 /** Search for one or more items. */ 136 Search, 137 138 /** Fetch the next item in a list. */ 139 Next, 140 141 /** Fetch the previous item in a list. */ 142 Previous, 143 144 /** Fetch the first item in a list. */ 145 First, 146 147 /** Fetch the last item in a list. */ 148 Last, 149 150 /** Generic operation meant as a catch-all. */ 151 Do; 152 153 /** 154 Return <tt>true</tt> only if this <tt>Operation</tt> represents an action which 155 has edited the datastore : {@link #Add}, {@link #Change}, {@link #Delete}, 156 {@link #DeleteAll}, {@link #Save}, {@link #Apply}, {@link #Inactivate}, or {@link #Activate}. 157 158 <P>Intended to identify actions which very likely require 159 <a href="http://www.javapractices.com/Topic181.cjp">a redirect instead of a forward</a>. 160 */ 161 public boolean isDatastoreEdit(){ 162 return ( 163 this == Add || this == Change || this == Delete || this == DeleteAll || 164 this == Save || this == Apply || this == Inactivate || this == Activate 165 ); 166 } 167 168 /** 169 Returns <tt>true</tt> only if this <tt>Operation</tt> <tt>isDataStoreEdit()</tt>, 170 or is {@link #Start} or {@link #Stop}. 171 172 <P>Intended to identify cases that need a <tt>POST</tt> request. 173 */ 174 public boolean hasSideEffects(){ 175 return isDatastoreEdit() || this == Start || this == Stop; 176 } 177 178 /** 179 Parse a parameter value into an <tt>Operation</tt> (not case-sensitive). 180 181 <P>Similar to {@link #valueOf}, but not case-sensitive, and has alternate behavior when a problem is found. 182 If <tt>aOperation</tt> has no content, or has an unknown value, then a message 183 is logged at <tt>SEVERE</tt> level, and <tt>null</tt> is returned. 184 */ 185 public static Operation valueFor(String aOperation) { 186 Operation result = null; 187 if ( Util.textHasContent(aOperation) ) { 188 for (Operation operation : Operation.values()) { 189 if ( operation.name().equalsIgnoreCase(aOperation)) { 190 result = operation; 191 break; 192 } 193 } 194 if( result == null ) { 195 fLogger.severe("'Operation' has an unknown value: " + Util.quote(aOperation)); 196 } 197 } 198 else { 199 String message = "'Operation' has an empty or null value."; 200 fLogger.severe(message); 201 } 202 return result; 203 } 204 205 // PRIVATE // 206 private static final Logger fLogger = Util.getLogger(Operation.class); 207 }