Coverage Report - org.kuali.student.common.ui.client.configurable.mvc.FieldDescriptor
 
Classes in this File Line Coverage Branch Coverage Complexity
FieldDescriptor
0%
0/134
0%
0/82
2.303
 
 1  
 /**
 2  
  * Copyright 2010 The Kuali Foundation Licensed under the
 3  
  * Educational Community License, Version 2.0 (the "License"); you may
 4  
  * not use this file except in compliance with the License. You may
 5  
  * obtain a copy of the License at
 6  
  *
 7  
  * http://www.osedu.org/licenses/ECL-2.0
 8  
  *
 9  
  * Unless required by applicable law or agreed to in writing,
 10  
  * software distributed under the License is distributed on an "AS IS"
 11  
  * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 12  
  * or implied. See the License for the specific language governing
 13  
  * permissions and limitations under the License.
 14  
  */
 15  
 
 16  
 package org.kuali.student.common.ui.client.configurable.mvc;
 17  
 
 18  
 
 19  
 import org.kuali.student.common.assembly.data.Metadata;
 20  
 import org.kuali.student.common.assembly.data.MetadataInterrogator;
 21  
 import org.kuali.student.common.ui.client.application.Application;
 22  
 import org.kuali.student.common.ui.client.configurable.mvc.binding.ModelWidgetBinding;
 23  
 import org.kuali.student.common.ui.client.configurable.mvc.binding.MultiplicityCompositeBinding;
 24  
 import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityComposite;
 25  
 import org.kuali.student.common.ui.client.mvc.Callback;
 26  
 import org.kuali.student.common.ui.client.mvc.HasCrossConstraints;
 27  
 import org.kuali.student.common.ui.client.mvc.HasDataValue;
 28  
 import org.kuali.student.common.ui.client.widgets.KSCheckBox;
 29  
 import org.kuali.student.common.ui.client.widgets.KSTextBox;
 30  
 import org.kuali.student.common.ui.client.widgets.RichTextEditor;
 31  
 import org.kuali.student.common.ui.client.widgets.field.layout.element.FieldElement;
 32  
 import org.kuali.student.common.ui.client.widgets.field.layout.element.MessageKeyInfo;
 33  
 import org.kuali.student.common.ui.client.widgets.list.KSSelectItemWidgetAbstract;
 34  
 
 35  
 import com.google.gwt.user.client.ui.HasText;
 36  
 import com.google.gwt.user.client.ui.HasValue;
 37  
 import com.google.gwt.user.client.ui.Widget;
 38  
 
 39  
 /**
 40  
  * This is a field descriptor that defines ui fields.
 41  
  * A field descriptor is defined by its fieldKey and its metadata, the field key is the key that is used
 42  
  * to identify it during validation and save processing and maps directly to a field which exists in the
 43  
  * model definition for the model it relates to.  The fieldKey must match something within the model to
 44  
  * be a valid field.  <br>The metadata is its piece of information in the model definition which is used to
 45  
  * generate the field's widget and determine what the validation on this field is (also if this field
 46  
  * is backed by a search it will use the lookup metadata defined in its metadata to generate the appropriate
 47  
  * search widget).  It also determines if a requiredness indicator appears (constraint minOccurs > 1).
 48  
  * <br><br>
 49  
  * A field descriptor can be fully customized to any need, the widget can be chosen to be
 50  
  * defined directly instead of using one that is auto generated by the metadata - if this is done make sure
 51  
  * the data type it is using works with what is in the metadata and that it implements one of these standard 
 52  
  * interfaces:<br>
 53  
  * HasText<br>
 54  
  * KSSelectItemWidgetAbstract<br>
 55  
  * HasDataValue<br>
 56  
  * HasValue<br>
 57  
  * or, you MUST override the ModelWidgetBinding by calling setModelWidgetBinding
 58  
  * <br>
 59  
  * Setting the ModelWidgetBinding allows you to manipulate the widget's data and model data as you see fit
 60  
  * before a save when model data is loaded into the widget.
 61  
  * <br>
 62  
  * <br>
 63  
  * General layout of a field generated by FieldDescriptor, all elements are conditional based on the field's
 64  
  * configuration: <br>
 65  
  * <b>[label][requiredness indicator][help]<br>
 66  
  * [input widget]<br>
 67  
  * [constraint text]<br></b>
 68  
  * <br>
 69  
  * The messageKeyInfo passed in is used to generate the messages needed for a field these include:<br>
 70  
  * Field Label<br>
 71  
  * Help text<br>
 72  
  * Instructions<br>
 73  
  * Example text<br>
 74  
  * Constraint text<br>
 75  
  * Watermark text - only if the widget one that accepts text input<br><br>
 76  
  * These are generated by a single key, the additional text is determined by special suffixes on keys within
 77  
  * the messages data - example - you pass in "sampleField" for the message key - it is automatically determined
 78  
  * that if there is a message in the message data named "sampleField-instruct", instructions will be added to the field
 79  
  * in the appropriate location.<br>
 80  
  * List of the appended keys for use in messages data:<br>
 81  
  * "-help" for help text<br>
 82  
  * "-instruct" for instructions<br>
 83  
  * "-examples" for examples<br>
 84  
  * "-constraints" for constraint text<br>
 85  
  * "-watermark" for watermark text<br>
 86  
  * 
 87  
  * @author Kuali Student Team
 88  
  * @see FieldElement
 89  
  * @see Section
 90  
  * @see BaseSection
 91  
  * @see Configurer
 92  
  */
 93  
 public class FieldDescriptor {
 94  
     protected String fieldKey;
 95  
         protected Metadata metadata;
 96  
     @SuppressWarnings("unchecked")
 97  
         private ModelWidgetBinding modelWidgetBinding;
 98  
     private Callback<Boolean> validationRequestCallback;
 99  0
     private boolean dirty = false;
 100  0
     private boolean hasHadFocus = false;
 101  
     private final FieldElement fieldElement;
 102  
     private String modelId;
 103  
     private MessageKeyInfo messageKey;
 104  0
     private boolean optional = false;
 105  0
     private boolean ignoreShowRequired = false; 
 106  
 
 107  
     /**
 108  
      * @param fieldKey - key for this field which matches a field in the overall model definition that this
 109  
      * field will be used for
 110  
      * @param messageKey - key object used for determing field labels
 111  
      * @param metadata - metadata used to determine requiredness, validation, and autogenerated widget
 112  
      */
 113  0
     public FieldDescriptor(String fieldKey, MessageKeyInfo messageKey, Metadata metadata) {
 114  0
             this.fieldKey = fieldKey;
 115  0
             this.metadata = metadata;
 116  0
             if(messageKey == null){
 117  0
                     messageKey = new MessageKeyInfo("");
 118  
             }
 119  0
             setMessageKey(messageKey);
 120  0
             fieldElement = new FieldElement(fieldKey, messageKey, createFieldWidget(), metadata);
 121  0
             setupField();
 122  
             
 123  
             //Add mapping from path to field definition
 124  0
             if((getFieldWidget() instanceof HasDataValue || getFieldWidget() instanceof KSTextBox || getFieldWidget() instanceof HasValue)&&!(this instanceof FieldDescriptorReadOnly)){
 125  0
                     Application.getApplicationContext().putPathToFieldMapping(null, Application.getApplicationContext().getParentPath()+fieldKey, this);
 126  
                 }
 127  
 
 128  
             //Add cross constraints
 129  0
             if(fieldElement.getFieldWidget() instanceof HasCrossConstraints){
 130  0
                     HasCrossConstraints crossConstraintWidget = (HasCrossConstraints) fieldElement.getFieldWidget();
 131  0
                     if(crossConstraintWidget!=null&&crossConstraintWidget.getCrossConstraints()!=null){
 132  0
                             for(String path:crossConstraintWidget.getCrossConstraints()){
 133  0
                                 Application.getApplicationContext().putCrossConstraint(null, path, crossConstraintWidget);
 134  
                             }
 135  
                     }
 136  
             }
 137  0
     }
 138  
 
 139  
     /**
 140  
      * @param fieldKey - key for this field which matches a field in the overall model definition that this
 141  
      * field will be used for
 142  
      * @param messageKey - key object used for determing field labels
 143  
      * @param metadata - metadata used to determine requiredness and validation
 144  
      * @param fieldWidget - widget to use instead of an automatically determined one
 145  
      */
 146  0
     public FieldDescriptor(String fieldKey, MessageKeyInfo messageKey, Metadata metadata, Widget fieldWidget){
 147  0
             this.fieldKey = fieldKey;
 148  0
             this.metadata = metadata;
 149  0
             if(messageKey == null){
 150  0
                     messageKey = new MessageKeyInfo("");
 151  
             }
 152  0
         setMessageKey(messageKey);
 153  0
             addStyleToWidget(fieldWidget);
 154  0
             fieldElement = new FieldElement(fieldKey, messageKey, fieldWidget, metadata);
 155  0
         setupField();
 156  
             
 157  
             //Add mapping from path to field definition if the definition has a data value
 158  0
             if((fieldWidget instanceof HasDataValue || fieldWidget instanceof KSTextBox) &&!(this instanceof FieldDescriptorReadOnly)){
 159  0
                     Application.getApplicationContext().putPathToFieldMapping(null, Application.getApplicationContext().getParentPath()+fieldKey, this);
 160  
                 }
 161  
             
 162  
             //Add cross constraints
 163  0
             if(fieldElement.getFieldWidget() instanceof HasCrossConstraints){
 164  0
                     HasCrossConstraints crossConstraintWidget = (HasCrossConstraints) fieldElement.getFieldWidget();
 165  0
                     if(crossConstraintWidget!=null&&crossConstraintWidget.getCrossConstraints()!=null){
 166  0
                             for(String path:crossConstraintWidget.getCrossConstraints()){
 167  0
                                 Application.getApplicationContext().putCrossConstraint(null, path, crossConstraintWidget);
 168  
                             }
 169  
                     }
 170  
             }
 171  
             
 172  0
     }
 173  
 
 174  
     protected void addStyleToWidget(Widget w){
 175  0
             if(fieldKey != null && !fieldKey.isEmpty() && w != null){
 176  0
                     String style = this.fieldKey.replaceAll("/", "-");
 177  0
                     w.addStyleName(style);
 178  
             }
 179  0
     }
 180  
 
 181  
     protected void setupField() {
 182  0
             if(metadata != null){
 183  0
                     if(MetadataInterrogator.isRequired(metadata)){
 184  0
                             fieldElement.setRequiredString("requiredMarker", "ks-form-module-elements-required");
 185  
                     }
 186  0
                     else if(MetadataInterrogator.isRequiredForNextState(metadata)){
 187  0
                             String nextState = MetadataInterrogator.getNextState(metadata);
 188  0
                             if(nextState != null){
 189  0
                                     if(nextState.equalsIgnoreCase("SUBMITTED")){
 190  0
                                             fieldElement.setRequiredString("requiredOnSubmit", "ks-form-required-for-submit");
 191  
                                     }
 192  0
                                     else if(nextState.equalsIgnoreCase("APPROVED")){
 193  0
                                             fieldElement.setRequiredString("reqApproval", "ks-form-required-for-submit");
 194  
                                     }
 195  0
                                         else if(nextState.equalsIgnoreCase("ACTIVE")){
 196  0
                                                 fieldElement.setRequiredString("reqActivate", "ks-form-required-for-submit");
 197  
                                     }
 198  0
                                         else if(nextState.equalsIgnoreCase("SUSPENDED")){
 199  0
                                                 fieldElement.setRequiredString("reqDeactivate", "ks-form-required-for-submit");
 200  
                                         }
 201  0
                                         else if(nextState.equalsIgnoreCase("RETIRED")){
 202  0
                                                 fieldElement.setRequiredString("requiredOnSubmit", "ks-form-required-for-submit");
 203  
                                         }
 204  
                                         else {
 205  0
                                                 fieldElement.setRequiredString("requiredOnSubmit", "ks-form-required-for-submit");
 206  
                                         }
 207  
 
 208  
                             }
 209  0
                     } else{
 210  0
                 fieldElement.clearRequiredText();
 211  
             }
 212  
             }
 213  0
     }
 214  
 
 215  
     /**
 216  
      * @see FieldElement#hideLabel()
 217  
      */
 218  
     public void hideLabel(){
 219  0
             fieldElement.hideLabel();
 220  0
     }
 221  
 
 222  
     public boolean isLabelShown(){
 223  0
             return fieldElement.isLabelShown();
 224  
     }
 225  
 
 226  
     public FieldElement getFieldElement(){
 227  0
             return fieldElement;
 228  
     }
 229  
 
 230  
         public String getFieldKey() {
 231  0
         return fieldKey;
 232  
     }
 233  
 
 234  
     public void setFieldKey(String fieldKey) {
 235  0
                 this.fieldKey = fieldKey;
 236  0
         }
 237  
 
 238  
     public String getFieldLabel() {
 239  0
         return fieldElement.getFieldName();
 240  
     }
 241  
 
 242  
     public Widget getFieldWidget(){
 243  0
         if (fieldElement.getFieldWidget() == null){
 244  0
             Widget w = createFieldWidget();
 245  0
             fieldElement.setWidget(w);
 246  
         }
 247  0
         return fieldElement.getFieldWidget();
 248  
     }
 249  
 
 250  
     protected Widget createFieldWidget() {
 251  0
             if (metadata == null) {
 252  
                     // backwards compatibility for old ModelDTO code
 253  
                     // for now, default to textbox if not specified
 254  0
                     Widget result = new KSTextBox();
 255  0
                     addStyleToWidget(result);
 256  0
                     return result;
 257  
             } else {
 258  0
                     Widget result = DefaultWidgetFactory.getInstance().getWidget(this);
 259  0
                     addStyleToWidget(result);
 260  0
                     return result;
 261  
             }
 262  
     }
 263  
 
 264  
     public ModelWidgetBinding<?> getModelWidgetBinding() {
 265  0
         if(modelWidgetBinding == null){
 266  0
             if(fieldElement.getFieldWidget() instanceof RichTextEditor){
 267  0
                     modelWidgetBinding = org.kuali.student.common.ui.client.configurable.mvc.binding.RichTextBinding.INSTANCE;
 268  0
             } else if (fieldElement.getFieldWidget() instanceof KSCheckBox){
 269  0
                     modelWidgetBinding = org.kuali.student.common.ui.client.configurable.mvc.binding.HasValueBinding.INSTANCE;
 270  0
             } else if(fieldElement.getFieldWidget() instanceof MultiplicityComposite){
 271  0
                         modelWidgetBinding = MultiplicityCompositeBinding.INSTANCE;
 272  0
                 } else if (fieldElement.getFieldWidget()instanceof HasText) {
 273  0
                     modelWidgetBinding = org.kuali.student.common.ui.client.configurable.mvc.binding.HasTextBinding.INSTANCE;
 274  0
             } else if (fieldElement.getFieldWidget() instanceof KSSelectItemWidgetAbstract){
 275  0
                 modelWidgetBinding = org.kuali.student.common.ui.client.configurable.mvc.binding.SelectItemWidgetBinding.INSTANCE;
 276  0
             } else if (fieldElement.getFieldWidget() instanceof HasDataValue){
 277  0
                     modelWidgetBinding = org.kuali.student.common.ui.client.configurable.mvc.binding.HasDataValueBinding.INSTANCE;
 278  0
             } else if (fieldElement.getFieldWidget() instanceof HasValue){
 279  0
                     modelWidgetBinding = org.kuali.student.common.ui.client.configurable.mvc.binding.HasValueBinding.INSTANCE;
 280  
             }
 281  
         }
 282  0
         return modelWidgetBinding;
 283  
     }
 284  
 
 285  
     /**
 286  
      * Allows additional processing to happen when a validation check is being processed when the input
 287  
      * widget loses focus defined in the callback
 288  
      * @param callback
 289  
      */
 290  
     public void setValidationCallBack(Callback<Boolean> callback){
 291  0
         validationRequestCallback = callback;
 292  0
     }
 293  
 
 294  
     public Callback<Boolean> getValidationRequestCallback(){
 295  0
         return validationRequestCallback;
 296  
     }
 297  
 
 298  
         /**
 299  
          * Returns true if this field is marked as dirty.  In KS, dirty is when the field has been changed
 300  
          * in some way - however not completely accurate
 301  
          * @return
 302  
          */
 303  
         public boolean isDirty() {
 304  0
                 return dirty;
 305  
         }
 306  
 
 307  
         public void setDirty(boolean dirty) {
 308  0
                 this.dirty = dirty;
 309  0
         }
 310  
 
 311  
         /**
 312  
          * Return true if the field has been touched by the user in some fashion, this is set by the field's section
 313  
          * @return
 314  
          */
 315  
         public boolean hasHadFocus() {
 316  0
                 return hasHadFocus;
 317  
         }
 318  
 
 319  
         public void setHasHadFocus(boolean hasHadFocus) {
 320  0
                 this.hasHadFocus = hasHadFocus;
 321  0
         }
 322  
 
 323  
     public Metadata getMetadata() {
 324  0
                 return metadata;
 325  
         }
 326  
 
 327  
     public void setMetadata(Metadata metadata) {
 328  0
                 this.metadata = metadata;
 329  0
         setupField();
 330  0
         }
 331  
 
 332  
     public void setFieldWidget(Widget fieldWidget) {
 333  0
                 this.fieldElement.setWidget(fieldWidget);
 334  0
         }
 335  
 
 336  
         public String getModelId() {
 337  0
                 return modelId;
 338  
         }
 339  
 
 340  
         public void setModelId(String modelId) {
 341  0
                 this.modelId = modelId;
 342  0
         }
 343  
 
 344  
     /**
 345  
      * Sets the ModelWidgetBinding for this field.  Changing this changes the way data from the server and
 346  
      * passed to the server is processed with the widget.  Set this when some special processing or handling
 347  
      * has to happen with the data in either phase.
 348  
      * @param widgetBinding
 349  
      */
 350  
     public void setWidgetBinding(ModelWidgetBinding widgetBinding) {
 351  0
         this.modelWidgetBinding = widgetBinding;
 352  0
     }
 353  
 
 354  
     public MessageKeyInfo getMessageKey() {
 355  0
         return messageKey;
 356  
     }
 357  
 
 358  
     public void setMessageKey(MessageKeyInfo messageKey) {
 359  0
         this.messageKey = messageKey;
 360  0
     }
 361  
 
 362  
         /**
 363  
          * Sets the optional flag
 364  
          * Fields that are optional should not be displayed if there is no data in some cases,
 365  
          * it is up to the section implementation whether or not to honor this flag
 366  
          * @param optional
 367  
          */
 368  
         public void setOptional(boolean optional){
 369  0
                 this.optional = optional;
 370  0
         }
 371  
 
 372  
         /**
 373  
          * Fields that are optional should not be displayed if there is no data in some cases,
 374  
          * it is up to the section implementation whether or not to honor this flag
 375  
          */
 376  
         public boolean isOptional(){
 377  0
                 return optional;
 378  
         }
 379  
 
 380  
         /**
 381  
          * @return true if this field is visible to the user
 382  
          */
 383  
         public boolean isVisible() {
 384  0
                 if (metadata != null){
 385  0
                         return metadata.isCanView();
 386  
                 } else {
 387  0
                         return true;
 388  
                 }
 389  
         }
 390  
 
 391  
         /**
 392  
          * Reset the requiredness of the field descriptor. Note doing this will also dynamically change
 393  
          * the underlying metadata so ui validation for requiredness works as well.
 394  
          * 
 395  
          */
 396  
         public void setRequired(Boolean isRequired){ 
 397  0
                 fieldElement.setRequiredString("requiredMarker", "ks-form-module-elements-required");
 398  0
                 fieldElement.setRequired(isRequired);
 399  
                 
 400  
                 //FIXME: This could be problematic if minOccurs should be something other than 1
 401  0
                 if (isRequired){
 402  0
                         getMetadata().getConstraints().get(0).setMinOccurs(1);
 403  
                 } else {
 404  0
                         getMetadata().getConstraints().get(0).setMinOccurs(0);
 405  
                 }
 406  0
         }
 407  
 
 408  
     public boolean isIgnoreShowRequired() {
 409  0
         return ignoreShowRequired;
 410  
     }
 411  
 
 412  
     public void setIgnoreShowRequired(boolean ignoreShowRequired) {
 413  0
         this.ignoreShowRequired = ignoreShowRequired;
 414  0
     }
 415  
         
 416  
 }