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    }