001    package hirondelle.web4jtools.codegenerator.feature;
002    
003    import hirondelle.web4j.model.ModelCtorException;
004    import hirondelle.web4j.model.ModelUtil;
005    import hirondelle.web4jtools.util.Check;
006    import hirondelle.web4j.util.Util;
007    import static hirondelle.web4j.util.Consts.FAILS;
008    import hirondelle.web4jtools.codegenerator.codes.ApplyOperation;
009    import hirondelle.web4jtools.codegenerator.codes.ShowOperation;
010    import hirondelle.web4jtools.codegenerator.codes.UiStyle;
011    
012    /**
013    * Model Object for your feature's basic information. 
014    * 
015    * <P>When generating code for a new feature, you start by entering this data. 
016    */
017    public final class Feature {
018    
019      /**
020      * Constructor.
021      * 
022      * <P>The feature name is used to generate names for many different items that form 
023      * part of the implementation. For example, the base name of "Jet Engine" (with the space)
024      * will be used to generate names such as :
025      * <ul>
026      * <li><tt>JetEngineAction.java</tt> - the Action class
027      * <li><tt>JetEngine.java</tt> - the Model Object
028      * <li><tt>fJetEngine</tt> - a field reference in the Action class
029      * <li>and so on...
030      * </ul>
031      * 
032      * <P>The formatting of the name depends on the context in which it is used, and is mostly 
033      * controlled by the {@link hirondelle.web4jtools.util.Functions} utility class. You can implement 
034      * custom coding conventions by altering the implementation of <tt>Functions</tt>.
035      * 
036      * <P>Warning : the text items in this class are modeled as <tt>String</tt>, not 
037      * {@link hirondelle.web4j.security.SafeText}. If rendering this data in markup, it is 
038      * strongly recommended that the text be escaped for special characters using <tt>w:safe()</tt>.
039      * 
040      * @param aName for the feature, 2..50 characters, required
041      * @param aUiStyle denotes the style of user interaction, required
042      * @param aPackageName name of the package for generated items, 2..200 characters, valid package name, required
043      * @param aShowOperation either <tt>List</tt> or <tt>Fetch</tt>, optional. Used only with <tt>ActionTemplateShowAndApply</tt>.
044      * @param aApplyOperation one of <tt>Add</tt>, <tt>Change</tt>, or <tt>Delete</tt>, optional. Used only with <tt>ActionTemplateShowAndApply</tt>.
045      */
046      public Feature(String aName, String aUiStyle, String aPackageName, String aShowOperation, String aApplyOperation) throws ModelCtorException {
047        fName =  aName;
048        fUiStyle = Util.textHasContent(aUiStyle) ? UiStyle.valueOf(aUiStyle) : null;
049        fPackageName = aPackageName;
050        fShowOperation = Util.textHasContent(aShowOperation) ? ShowOperation.valueOf(aShowOperation) : null;
051        fApplyOperation = Util.textHasContent(aApplyOperation) ? ApplyOperation.valueOf(aApplyOperation) : null;
052        validateState();
053      }
054    
055      public String getName() { return fName; }
056      public UiStyle getUiStyle() { return fUiStyle; }
057      public String getPackageName() { return fPackageName; }
058      public ShowOperation getShowOperation() { return fShowOperation; }
059      public ApplyOperation getApplyOperation() { return fApplyOperation; }
060      
061      /** Intended for debugging only.  */
062      @Override public String toString() {
063        return ModelUtil.toStringFor(this);
064      }
065    
066      @Override public boolean equals( Object aThat ) {
067        Boolean result = ModelUtil.quickEquals(this, aThat);
068        if ( result == null ){
069          Feature that = (Feature) aThat;
070          result = ModelUtil.equalsFor(this.getSignificantFields(), that.getSignificantFields());
071        }
072        return result;    
073      }
074    
075      @Override public int hashCode() {
076        if ( fHashCode == 0 ) {
077          fHashCode = ModelUtil.hashCodeFor(getSignificantFields());
078        }
079        return fHashCode;
080      }
081      
082      // PRIVATE //
083      private final String fName;
084      private final UiStyle fUiStyle;
085      private final String fPackageName;
086      private final ShowOperation fShowOperation;
087      private final ApplyOperation fApplyOperation;
088      private int fHashCode;
089      
090      private void validateState() throws ModelCtorException {
091        ModelCtorException ex = new ModelCtorException();
092        if ( FAILS == Check.required(fName, Check.range(2,50))) {
093          ex.add("Feature Name is required, 2..50 chars.");
094        }
095        if ( FAILS == Check.required(fUiStyle) ) {
096          ex.add("UI Style is required.");
097        }
098        if ( FAILS == Check.required(fPackageName, Check.range(2,200))) {
099          ex.add("Package name is required, 2..200 characters.");
100        }
101        
102        if ( fUiStyle == UiStyle.ListAndEdit ) {
103          if ( fShowOperation != null ) {
104            ex.add("Show Operation applies only if UI Style is Show and Apply");
105          }
106          if ( fApplyOperation != null ) {
107            ex.add("Apply Operation applies only if UI Style is Show and Apply");
108          }
109        }
110        
111        if ( fUiStyle == UiStyle.ShowAndApply ){
112          if ( fShowOperation == null ){
113            ex.add("Please select a Show Operation.");
114          }
115          if ( fApplyOperation == null ){
116            ex.add("Please select an Apply Operation.");
117          }
118        }
119        
120        if ( ! ex.isEmpty() ) throw ex;
121      }
122      
123      private Object[] getSignificantFields(){
124        return new Object[]{fName, fUiStyle, fPackageName, fShowOperation, fApplyOperation};
125      }
126    }