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 }