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    }