Clover Coverage Report - Kuali Student 1.2.1-SNAPSHOT (Aggregated)
Coverage timestamp: Wed Nov 2 2011 04:03:58 EST
../../../../../../../../img/srcFileCovDistChart0.png 42% of files have more coverage
108   412   72   3.27
58   239   0.67   33
33     2.18  
1    
 
  FieldDescriptor       Line # 91 108 0% 72 199 0% 0.0
 
No Tests
 
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    * Constraint text<br>
74    * Watermark text - only if the widget one that accepts text input<br><br>
75    * These are generated by a single key, the additional text is determined by special suffixes on keys within
76    * the messages data - example - you pass in "sampleField" for the message key - it is automatically determined
77    * that if there is a message in the message data named "sampleField-instruct", instructions will be added to the field
78    * in the appropriate location.<br>
79    * List of the appended keys for use in messages data:<br>
80    * "-help" for help text<br>
81    * "-instruct" for instructions<br>
82    * "-constraints" for constraint text<br>
83    * "-watermark" for watermark text<br>
84    *
85    * @author Kuali Student Team
86    * @see FieldElement
87    * @see Section
88    * @see BaseSection
89    * @see Configurer
90    */
 
91    public class FieldDescriptor {
92    protected String fieldKey;
93    protected Metadata metadata;
94    @SuppressWarnings("unchecked")
95    private ModelWidgetBinding modelWidgetBinding;
96    private Callback<Boolean> validationRequestCallback;
97    private boolean dirty = false;
98    private boolean hasHadFocus = false;
99    private final FieldElement fieldElement;
100    private String modelId;
101    private MessageKeyInfo messageKey;
102    private boolean optional = false;
103    private boolean ignoreShowRequired = false;
104   
105    /**
106    * @param fieldKey - key for this field which matches a field in the overall model definition that this
107    * field will be used for
108    * @param messageKey - key object used for determing field labels
109    * @param metadata - metadata used to determine requiredness, validation, and autogenerated widget
110    */
 
111  0 toggle public FieldDescriptor(String fieldKey, MessageKeyInfo messageKey, Metadata metadata) {
112  0 this.fieldKey = fieldKey;
113  0 this.metadata = metadata;
114  0 if(messageKey == null){
115  0 messageKey = new MessageKeyInfo("");
116    }
117  0 setMessageKey(messageKey);
118  0 fieldElement = new FieldElement(fieldKey, messageKey, createFieldWidget(), metadata);
119  0 setupField();
120   
121    //Add mapping from path to field definition
122  0 if((getFieldWidget() instanceof HasDataValue || getFieldWidget() instanceof KSTextBox || getFieldWidget() instanceof HasValue)&&!(this instanceof FieldDescriptorReadOnly)){
123  0 Application.getApplicationContext().putPathToFieldMapping(null, Application.getApplicationContext().getParentPath()+fieldKey, this);
124    }
125   
126    //Add cross constraints
127  0 if(fieldElement.getFieldWidget() instanceof HasCrossConstraints){
128  0 HasCrossConstraints crossConstraintWidget = (HasCrossConstraints) fieldElement.getFieldWidget();
129  0 if(crossConstraintWidget!=null&&crossConstraintWidget.getCrossConstraints()!=null){
130  0 for(String path:crossConstraintWidget.getCrossConstraints()){
131  0 Application.getApplicationContext().putCrossConstraint(null, path, crossConstraintWidget);
132    }
133    }
134    }
135    }
136   
137    /**
138    * @param fieldKey - key for this field which matches a field in the overall model definition that this
139    * field will be used for
140    * @param messageKey - key object used for determing field labels
141    * @param metadata - metadata used to determine requiredness and validation
142    * @param fieldWidget - widget to use instead of an automatically determined one
143    */
 
144  0 toggle public FieldDescriptor(String fieldKey, MessageKeyInfo messageKey, Metadata metadata, Widget fieldWidget){
145  0 this.fieldKey = fieldKey;
146  0 this.metadata = metadata;
147  0 if(messageKey == null){
148  0 messageKey = new MessageKeyInfo("");
149    }
150  0 setMessageKey(messageKey);
151  0 addStyleToWidget(fieldWidget);
152  0 fieldElement = new FieldElement(fieldKey, messageKey, fieldWidget, metadata);
153  0 setupField();
154   
155    //Add mapping from path to field definition if the definition has a data value
156  0 if((fieldWidget instanceof HasDataValue || fieldWidget instanceof KSTextBox) &&!(this instanceof FieldDescriptorReadOnly)){
157  0 Application.getApplicationContext().putPathToFieldMapping(null, Application.getApplicationContext().getParentPath()+fieldKey, this);
158    }
159   
160    //Add cross constraints
161  0 if(fieldElement.getFieldWidget() instanceof HasCrossConstraints){
162  0 HasCrossConstraints crossConstraintWidget = (HasCrossConstraints) fieldElement.getFieldWidget();
163  0 if(crossConstraintWidget!=null&&crossConstraintWidget.getCrossConstraints()!=null){
164  0 for(String path:crossConstraintWidget.getCrossConstraints()){
165  0 Application.getApplicationContext().putCrossConstraint(null, path, crossConstraintWidget);
166    }
167    }
168    }
169   
170    }
171   
 
172  0 toggle protected void addStyleToWidget(Widget w){
173  0 if(fieldKey != null && !fieldKey.isEmpty() && w != null){
174  0 String style = this.fieldKey.replaceAll("/", "-");
175  0 w.addStyleName(style);
176    }
177    }
178   
 
179  0 toggle protected void setupField() {
180  0 if(metadata != null){
181  0 if(MetadataInterrogator.isRequired(metadata)){
182  0 fieldElement.setRequiredString("requiredMarker", "ks-form-module-elements-required");
183    }
184  0 else if(MetadataInterrogator.isRequiredForNextState(metadata)){
185  0 String nextState = MetadataInterrogator.getNextState(metadata);
186  0 if(nextState != null){
187  0 if(nextState.equalsIgnoreCase("SUBMITTED")){
188  0 fieldElement.setRequiredString("requiredOnSubmit", "ks-form-required-for-submit");
189    }
190  0 else if(nextState.equalsIgnoreCase("APPROVED")){
191  0 fieldElement.setRequiredString("reqApproval", "ks-form-required-for-submit");
192    }
193  0 else if(nextState.equalsIgnoreCase("ACTIVE")){
194  0 fieldElement.setRequiredString("reqActivate", "ks-form-required-for-submit");
195    }
196  0 else if(nextState.equalsIgnoreCase("SUSPENDED") ||
197    nextState.equalsIgnoreCase("RETIRED")){
198  0 fieldElement.setRequiredString("reqDeactivate", "ks-form-required-for-submit");
199    }
200    else {
201  0 fieldElement.setRequiredString("requiredOnSubmit", "ks-form-required-for-submit");
202    }
203   
204    }
205    } else{
206  0 fieldElement.clearRequiredText();
207    }
208    }
209    }
210   
211    /**
212    * @see FieldElement#hideLabel()
213    */
 
214  0 toggle public void hideLabel(){
215  0 fieldElement.hideLabel();
216    }
217   
 
218  0 toggle public boolean isLabelShown(){
219  0 return fieldElement.isLabelShown();
220    }
221   
 
222  0 toggle public FieldElement getFieldElement(){
223  0 return fieldElement;
224    }
225   
 
226  0 toggle public String getFieldKey() {
227  0 return fieldKey;
228    }
229   
 
230  0 toggle public void setFieldKey(String fieldKey) {
231  0 this.fieldKey = fieldKey;
232    }
233   
 
234  0 toggle public String getFieldLabel() {
235  0 return fieldElement.getFieldName();
236    }
237   
 
238  0 toggle public Widget getFieldWidget(){
239  0 if (fieldElement.getFieldWidget() == null){
240  0 Widget w = createFieldWidget();
241  0 fieldElement.setWidget(w);
242    }
243  0 return fieldElement.getFieldWidget();
244    }
245   
 
246  0 toggle protected Widget createFieldWidget() {
247  0 if (metadata == null) {
248    // backwards compatibility for old ModelDTO code
249    // for now, default to textbox if not specified
250  0 Widget result = new KSTextBox();
251  0 addStyleToWidget(result);
252  0 return result;
253    } else {
254  0 Widget result = DefaultWidgetFactory.getInstance().getWidget(this);
255  0 addStyleToWidget(result);
256  0 return result;
257    }
258    }
259   
 
260  0 toggle public ModelWidgetBinding<?> getModelWidgetBinding() {
261  0 if(modelWidgetBinding == null){
262  0 if(fieldElement.getFieldWidget() instanceof RichTextEditor){
263  0 modelWidgetBinding = org.kuali.student.common.ui.client.configurable.mvc.binding.RichTextBinding.INSTANCE;
264  0 } else if (fieldElement.getFieldWidget() instanceof KSCheckBox){
265  0 modelWidgetBinding = org.kuali.student.common.ui.client.configurable.mvc.binding.HasValueBinding.INSTANCE;
266  0 } else if(fieldElement.getFieldWidget() instanceof MultiplicityComposite){
267  0 modelWidgetBinding = MultiplicityCompositeBinding.INSTANCE;
268  0 } else if (fieldElement.getFieldWidget()instanceof HasText) {
269  0 modelWidgetBinding = org.kuali.student.common.ui.client.configurable.mvc.binding.HasTextBinding.INSTANCE;
270  0 } else if (fieldElement.getFieldWidget() instanceof KSSelectItemWidgetAbstract){
271  0 modelWidgetBinding = org.kuali.student.common.ui.client.configurable.mvc.binding.SelectItemWidgetBinding.INSTANCE;
272  0 } else if (fieldElement.getFieldWidget() instanceof HasDataValue){
273  0 modelWidgetBinding = org.kuali.student.common.ui.client.configurable.mvc.binding.HasDataValueBinding.INSTANCE;
274  0 } else if (fieldElement.getFieldWidget() instanceof HasValue){
275  0 modelWidgetBinding = org.kuali.student.common.ui.client.configurable.mvc.binding.HasValueBinding.INSTANCE;
276    }
277    }
278  0 return modelWidgetBinding;
279    }
280   
281    /**
282    * Allows additional processing to happen when a validation check is being processed when the input
283    * widget loses focus defined in the callback
284    * @param callback
285    */
 
286  0 toggle public void setValidationCallBack(Callback<Boolean> callback){
287  0 validationRequestCallback = callback;
288    }
289   
 
290  0 toggle public Callback<Boolean> getValidationRequestCallback(){
291  0 return validationRequestCallback;
292    }
293   
294    /**
295    * Returns true if this field is marked as dirty. In KS, dirty is when the field has been changed
296    * in some way - however not completely accurate
297    * @return
298    */
 
299  0 toggle public boolean isDirty() {
300  0 return dirty;
301    }
302   
 
303  0 toggle public void setDirty(boolean dirty) {
304  0 this.dirty = dirty;
305    }
306   
307    /**
308    * Return true if the field has been touched by the user in some fashion, this is set by the field's section
309    * @return
310    */
 
311  0 toggle public boolean hasHadFocus() {
312  0 return hasHadFocus;
313    }
314   
 
315  0 toggle public void setHasHadFocus(boolean hasHadFocus) {
316  0 this.hasHadFocus = hasHadFocus;
317    }
318   
 
319  0 toggle public Metadata getMetadata() {
320  0 return metadata;
321    }
322   
 
323  0 toggle public void setMetadata(Metadata metadata) {
324  0 this.metadata = metadata;
325  0 setupField();
326    }
327   
 
328  0 toggle public void setFieldWidget(Widget fieldWidget) {
329  0 this.fieldElement.setWidget(fieldWidget);
330    }
331   
 
332  0 toggle public String getModelId() {
333  0 return modelId;
334    }
335   
 
336  0 toggle public void setModelId(String modelId) {
337  0 this.modelId = modelId;
338    }
339   
340    /**
341    * Sets the ModelWidgetBinding for this field. Changing this changes the way data from the server and
342    * passed to the server is processed with the widget. Set this when some special processing or handling
343    * has to happen with the data in either phase.
344    * @param widgetBinding
345    */
 
346  0 toggle public void setWidgetBinding(ModelWidgetBinding widgetBinding) {
347  0 this.modelWidgetBinding = widgetBinding;
348    }
349   
 
350  0 toggle public MessageKeyInfo getMessageKey() {
351  0 return messageKey;
352    }
353   
 
354  0 toggle public void setMessageKey(MessageKeyInfo messageKey) {
355  0 this.messageKey = messageKey;
356    }
357   
358    /**
359    * Sets the optional flag
360    * Fields that are optional should not be displayed if there is no data in some cases,
361    * it is up to the section implementation whether or not to honor this flag
362    * @param optional
363    */
 
364  0 toggle public void setOptional(boolean optional){
365  0 this.optional = optional;
366    }
367   
368    /**
369    * Fields that are optional should not be displayed if there is no data in some cases,
370    * it is up to the section implementation whether or not to honor this flag
371    */
 
372  0 toggle public boolean isOptional(){
373  0 return optional;
374    }
375   
376    /**
377    * @return true if this field is visible to the user
378    */
 
379  0 toggle public boolean isVisible() {
380  0 if (metadata != null){
381  0 return metadata.isCanView();
382    } else {
383  0 return true;
384    }
385    }
386   
387    /**
388    * Reset the requiredness of the field descriptor. Note doing this will also dynamically change
389    * the underlying metadata so ui validation for requiredness works as well.
390    *
391    */
 
392  0 toggle public void setRequired(Boolean isRequired){
393  0 fieldElement.setRequiredString("requiredMarker", "ks-form-module-elements-required");
394  0 fieldElement.setRequired(isRequired);
395   
396    //FIXME: This could be problematic if minOccurs should be something other than 1
397  0 if (isRequired){
398  0 getMetadata().getConstraints().get(0).setMinOccurs(1);
399    } else {
400  0 getMetadata().getConstraints().get(0).setMinOccurs(0);
401    }
402    }
403   
 
404  0 toggle public boolean isIgnoreShowRequired() {
405  0 return ignoreShowRequired;
406    }
407   
 
408  0 toggle public void setIgnoreShowRequired(boolean ignoreShowRequired) {
409  0 this.ignoreShowRequired = ignoreShowRequired;
410    }
411   
412    }