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 /** Retrieve what is usually a list of many items, for read-only display. */
111 List,
112
113 /** Retrieve what is usually a list of many items, in preparation for editing the items. */
114 ListForChange,
115
116 /** Show an item. */
117 Show,
118
119 /** Generate a result. */
120 Generate,
121
122 /** Render a result. */
123 Render,
124
125 /** Display a result. */
126 Display,
127
128 /** Search for one or more items. */
129 Search,
130
131 /** Fetch the next item in a list. */
132 Next,
133
134 /** Fetch the previous item in a list. */
135 Previous,
136
137 /** Fetch the first item in a list. */
138 First,
139
140 /** Fetch the last item in a list. */
141 Last,
142
143 /** Generic operation meant as a catch-all. */
144 Do;
145
146 /**
147 Return <tt>true</tt> only if this <tt>Operation</tt> represents an action which
148 has edited the datastore : {@link #Add}, {@link #Change}, {@link #Delete},
149 {@link #DeleteAll}, {@link #Save}, {@link #Apply}, {@link #Inactivate}, or {@link #Activate}.
150
151 <P>Intended to identify actions which very likely require
152 <a href="http://www.javapractices.com/Topic181.cjp">a redirect instead of a forward</a>.
153 */
154 public boolean isDatastoreEdit(){
155 return (
156 this == Add || this == Change || this == Delete || this == DeleteAll ||
157 this == Save || this == Apply || this == Inactivate || this == Activate
158 );
159 }
160
161 /**
162 Returns <tt>true</tt> only if this <tt>Operation</tt> <tt>isDataStoreEdit()</tt>,
163 or is {@link #Start} or {@link #Stop}.
164
165 <P>Intended to identify cases that need a <tt>POST</tt> request.
166 */
167 public boolean hasSideEffects(){
168 return isDatastoreEdit() || this == Start || this == Stop;
169 }
170
171 /**
172 Parse a parameter value into an <tt>Operation</tt> (not case-sensitive).
173
174 <P>Similar to {@link #valueOf}, but not case-sensitive, and has alternate behavior when a problem is found.
175 If <tt>aOperation</tt> has no content, or has an unknown value, then a message
176 is logged at <tt>SEVERE</tt> level, and <tt>null</tt> is returned.
177 */
178 public static Operation valueFor(String aOperation) {
179 Operation result = null;
180 if ( Util.textHasContent(aOperation) ) {
181 for (Operation operation : Operation.values()) {
182 if ( operation.name().equalsIgnoreCase(aOperation)) {
183 result = operation;
184 break;
185 }
186 }
187 if( result == null ) {
188 fLogger.severe("'Operation' has an unknown value: " + Util.quote(aOperation));
189 }
190 }
191 else {
192 String message = "'Operation' has an empty or null value.";
193 fLogger.severe(message);
194 }
195 return result;
196 }
197
198 // PRIVATE //
199 private static final Logger fLogger = Util.getLogger(Operation.class);
200 }