View Javadoc

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.sections;
17  
18  import java.util.ArrayList;
19  import java.util.HashSet;
20  import java.util.List;
21  
22  import org.kuali.student.common.ui.client.application.Application;
23  import org.kuali.student.common.ui.client.configurable.mvc.CanProcessValidationResults;
24  import org.kuali.student.common.ui.client.configurable.mvc.FieldDescriptor;
25  import org.kuali.student.common.ui.client.configurable.mvc.LayoutController;
26  import org.kuali.student.common.ui.client.configurable.mvc.ValidationEventBinding;
27  import org.kuali.student.common.ui.client.configurable.mvc.ValidationEventBindingImpl;
28  import org.kuali.student.common.ui.client.configurable.mvc.binding.ModelWidgetBinding;
29  import org.kuali.student.common.ui.client.configurable.mvc.binding.ModelWidgetBindingSupport;
30  import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityComposite;
31  import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityGroup;
32  import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityGroupItem;
33  import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityItem;
34  import org.kuali.student.common.ui.client.event.ContentDirtyEvent;
35  import org.kuali.student.common.ui.client.event.ValidateRequestEvent;
36  import org.kuali.student.common.ui.client.mvc.Callback;
37  import org.kuali.student.common.ui.client.mvc.Controller;
38  import org.kuali.student.common.ui.client.mvc.DataModel;
39  import org.kuali.student.common.ui.client.mvc.HasCrossConstraints;
40  import org.kuali.student.common.ui.client.mvc.ModelRequestCallback;
41  import org.kuali.student.common.ui.client.mvc.View;
42  import org.kuali.student.common.ui.client.widgets.KSDropDown;
43  import org.kuali.student.common.ui.client.widgets.field.layout.element.AbbrPanel;
44  import org.kuali.student.common.ui.client.widgets.field.layout.element.FieldElement;
45  import org.kuali.student.common.ui.client.widgets.field.layout.element.SpanPanel;
46  import org.kuali.student.common.ui.client.widgets.field.layout.layouts.FieldLayout;
47  import org.kuali.student.common.ui.client.widgets.search.KSPicker;
48  import org.kuali.student.r1.common.assembly.data.Data;
49  import org.kuali.student.r1.common.assembly.data.Data.Key;
50  import org.kuali.student.r1.common.assembly.data.QueryPath;
51  import org.kuali.student.r2.common.dto.ValidationResultInfo;
52  import org.kuali.student.r2.common.infc.ValidationResult.ErrorLevel;
53  
54  import com.google.gwt.core.client.GWT;
55  import com.google.gwt.user.client.ui.TextBoxBase;
56  import com.google.gwt.user.client.ui.Widget;
57  
58  /**
59   * The base section is the base implementation of the section interface.
60   * @author Kuali Student
61   *
62   */
63  public abstract class BaseSection extends SpanPanel implements Section{
64  
65  	protected FieldLayout layout;
66  
67  	protected ArrayList<Section> sections = new ArrayList<Section>();
68  	protected ArrayList<FieldDescriptor> fields = new ArrayList<FieldDescriptor>();
69  	protected LayoutController layoutController = null;
70  	protected boolean isValidationEnabled = true;
71  	protected boolean isDirty = false;
72  
73  	/**
74  	 * Adds a field to this section.  Adds the field to this section's layout.
75  	 * Attaches an event handler for lost focus events on the field widget
76  	 * to validate against the metadata defined in its FieldDescriptor as well as firing events for dirty
77  	 * field handling.
78  	 * 
79  	 * Note if a field has been marked as hidden in the dictionary, this method will not add this field to the layout.
80  	 * If it is not visible the key returned by making call to addField is null.
81  	 * 
82  	 * @param field
83  	 * @return key/path of this field
84  	 */
85  	@Override
86  	public String addField(final FieldDescriptor fieldDescriptor) {
87  		String key = null;
88  		
89  		if (fieldDescriptor.isVisible()){
90  			fields.add(fieldDescriptor);
91  	        key = layout.addField(fieldDescriptor.getFieldElement());
92  	
93  	        ValidationEventBinding binding = new ValidationEventBindingImpl();
94  	        if(fieldDescriptor.getValidationRequestCallback()== null){
95  	            fieldDescriptor.setValidationCallBack(new Callback<Boolean>() {
96  	                @Override
97  	                public void exec(Boolean result) {
98  	            	    final ModelWidgetBinding mwb = fieldDescriptor.getModelWidgetBinding();
99  	            	    if (mwb != null) {
100 	            	        final Widget w = fieldDescriptor.getFieldWidget();
101 	            	        final String modelId = fieldDescriptor.getModelId();
102 	            	        final Controller parent;
103 	                        Controller findResult = LayoutController.findParentLayout(w);
104 	                        if(BaseSection.this instanceof View){
105 	                        	findResult = ((View)BaseSection.this).getController();
106 	                        }
107 	                        parent = findResult;
108 	                        if(parent != null){
109 	                        	if (modelId == null) {
110 	                        		parent.requestModel(new ModelRequestCallback<DataModel>() {
111 	
112 	                        			@Override
113 	                        			public void onModelReady(DataModel model) {
114 	                        				validateField(fieldDescriptor, model, parent);
115 	                                        
116 	                        				//Cross referenced field update:
117 	                        				HashSet<HasCrossConstraints> fds = Application.getApplicationContext().getCrossConstraint(null, Application.getApplicationContext().getParentPath()+fieldDescriptor.getFieldKey());
118 	                        				if(fds!=null){
119 	                        					for(HasCrossConstraints fd:fds){
120                         							fd.reprocessWithUpdatedConstraints();
121 	                        					}
122 	                        				}
123 	                        			}
124 	
125 	                        			@Override
126 	                        			public void onRequestFail(Throwable cause) {
127 	                        				GWT.log("Unable to retrieve model to validate " + fieldDescriptor.getFieldKey(), null);
128 	                        			}
129 	
130 	                        		});
131 	                        	} else {
132 	                        		parent.requestModel(modelId, new ModelRequestCallback<DataModel>() {
133 	
134 	                        			@Override
135 	                        			public void onModelReady(DataModel model) {
136 	                        				validateField(fieldDescriptor, model, parent);
137 	                        			}
138 	
139 	                        			@Override
140 	                        			public void onRequestFail(Throwable cause) {
141 	                        				GWT.log("Unable to retrieve model to validate " + fieldDescriptor.getFieldKey(), null);
142 	                        			}
143 	
144 	                        		});                        	}
145 	            	    	}
146 	                    } else {
147 	                        GWT.log(fieldDescriptor.getFieldKey() + " has no widget binding.", null);
148 	                    }
149 	                }
150 	            });
151 	        }
152 	        binding.bind(fieldDescriptor);
153 		}	       
154 	        
155         return key;
156 	}
157 
158 	private void validateField(
159 			final FieldDescriptor fieldDescriptor, final DataModel model, Controller controller) {
160 		Widget w = fieldDescriptor.getFieldWidget();
161 		ModelWidgetBinding mwb = fieldDescriptor.getModelWidgetBinding();
162 		if(fieldDescriptor.getFieldKey() != null){
163 			mwb.setModelValue(w, model, fieldDescriptor.getFieldKey());
164 			dirtyCheckField(fieldDescriptor, model);
165 			ValidateRequestEvent e = new ValidateRequestEvent();
166 			e.setFieldDescriptor(fieldDescriptor);
167 			e.setValidateSingleField(true);
168 			controller.fireApplicationEvent(e);
169 		}
170 	}
171 
172 	private void dirtyCheckField(FieldDescriptor fieldDescriptor, DataModel model){
173         QueryPath fieldPath = QueryPath.parse(fieldDescriptor.getFieldKey());
174 		QueryPath qPathDirty = fieldPath.subPath(0, fieldPath.size() - 1);
175 	    qPathDirty.add(ModelWidgetBindingSupport.RUNTIME_ROOT);
176 	    qPathDirty.add(ModelWidgetBindingSupport.DIRTY_PATH);
177 	    qPathDirty.add(fieldPath.get(fieldPath.size()-1));
178 	    Boolean dirty = false;
179 
180 	    if(ensureDirtyFlagPath(model.getRoot(), qPathDirty)){
181 	    	dirty = model.get(qPathDirty);
182 	    }
183 	    if(dirty){
184 	    	setIsDirty(true);
185 	    	fieldDescriptor.setDirty(true);
186 	    }
187 	}
188 
189     protected boolean ensureDirtyFlagPath(Data root, QueryPath path) {
190         Data current = root;
191         boolean containsFlag = false;
192         for (int i = 0; i < path.size(); i++) {
193             Key key = path.get(i);
194             if(i == path.size() - 1){
195             	containsFlag = current.containsKey(key);
196             	break;
197             }
198             Data d = current.get(key);
199             if (d == null) {
200                 containsFlag = false;
201                 break;
202             }
203             current = d;
204         }
205         return containsFlag;
206     }
207 
208 	/** 
209 	 * Adds a section to this section's layout.
210 	 * 
211 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#addSection(org.kuali.student.common.ui.client.configurable.mvc.sections.Section)
212 	 */
213 	@Override
214 	public String addSection(Section section) {
215 
216         section.setLayoutController(layoutController);
217 		sections.add(section);
218         String key = layout.addLayout(section.getLayout());
219         return key;
220 	}
221 
222 	/**
223 	 * Removes a section from this section's layout.
224 	 * 
225 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#removeSection(org.kuali.student.common.ui.client.configurable.mvc.sections.Section)
226 	 */
227 	@Override
228 	public void removeSection(Section section){
229 		sections.remove(section);
230 		layout.removeLayoutElement(section.getLayout());
231 	}
232 
233 	/**
234 	 * Clear all validation errors from the layout (removes all red highlight and error text shown on the
235 	 * screen)
236 	 */
237 	protected void clearValidationErrors() {
238 		layout.clearValidationErrors();
239 	}
240 
241 	/**
242 	 * Clear all validation warnings from the layout (removes all yellow highlight and warning text shown on the
243 	 * screen)
244 	 */
245 	public void clearValidationWarnings() {
246 		layout.clearValidationWarnings();
247 	}
248 	
249 	/**
250 	 * Gets all the fields in a section and its subsections.
251 	 * 
252 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#getFields()
253 	 */
254 	@Override
255 	public List<FieldDescriptor> getFields() {
256         List<FieldDescriptor> allFields = new ArrayList<FieldDescriptor>();
257         allFields.addAll(fields);
258 
259         for(Section ns: sections){
260             allFields.addAll(ns.getFields());
261         }
262         return allFields;
263 	}
264 
265 	/**
266 	 * Gets all the fields in this section, but does not include fields in its nested sections.
267 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#getUnnestedFields()
268 	 */
269 	@Override
270 	public List<FieldDescriptor> getUnnestedFields() {
271         return fields;
272 	}
273 
274 	/**
275 	 * Gets all nested sections contained in this section
276 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#getSections()
277 	 */
278 	@Override
279 	public List<Section> getSections() {
280 		return sections;
281 	}
282 
283 	/**
284 	 * Processes the validation results passed in and displays the appropriate message on the screen next
285 	 * to the corresponding field or section.  If clearAllValidation is true, it will clear all validation before
286 	 * displaying the errors (otherwise will append these errors to the ones already visible on the section).
287 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#processValidationResults(java.util.List, boolean)
288 	 */
289 	@Override
290 	public ErrorLevel processValidationResults(List<ValidationResultInfo> results, boolean clearErrors){
291 		if(clearErrors){
292 			this.clearValidationErrors();
293 		}
294 		ErrorLevel status = ErrorLevel.OK;
295 
296 		if (isValidationEnabled){
297 
298 			for(FieldDescriptor f: this.fields){
299 
300 				if(f.hasHadFocus()){
301 					for(ValidationResultInfo vr: results){
302                         String vrElement = vr.getElement();
303                         if(vrElement.startsWith("/")){
304                             vrElement = vrElement.substring(1);
305                         }
306                         //Strip out end indexes for collections of primitives to be handled by the element
307                         vrElement = vrElement.replaceFirst("^(\\S+)/[0-9]+$", "$1");
308                         
309                         //Check the parent path (mostly for mapping validation to specializations)
310                         String parentPath = Application.getApplicationContext().getParentPath();
311                         parentPath = parentPath==null?"":parentPath;
312                         
313 						if((parentPath+vrElement).equals(parentPath+f.getFieldKey())){
314 							FieldElement element = f.getFieldElement();
315 							if (element != null){
316 								ErrorLevel fieldStatus = element.processValidationResult(vr);
317 								if(fieldStatus.getLevel() > status.getLevel()){
318 									status = fieldStatus;
319 								}
320 							}
321 						}
322 					}
323 				}
324 
325 				if(f.getFieldWidget() instanceof MultiplicityComposite ){
326 					MultiplicityComposite mc = (MultiplicityComposite) f.getFieldWidget();
327 
328 					//possibly return error state from processValidationResults to give composite title bar a separate color
329 	            	for(MultiplicityItem item: mc.getItems()){
330 	            		if(item.getItemWidget() instanceof Section && !item.isDeleted()){
331 	            			ErrorLevel fieldStatus = ((Section)item.getItemWidget()).processValidationResults(results, clearErrors);
332 							if(fieldStatus.getLevel() > status.getLevel()){
333 								status = fieldStatus;
334 							}
335 	            		}
336 	            	}
337 				}
338 				// TODO Can we do this without checking for instanceof  MG??
339 				if(f.getFieldWidget() instanceof MultiplicityGroup ){
340 					MultiplicityGroup mg = (MultiplicityGroup) f.getFieldWidget();
341 
342 					//possibly return error state from processValidationResults to give composite title bar a separate color
343 	            	for(MultiplicityGroupItem item: mg.getItems()){
344 	            		if(item.getItemWidget() instanceof Section && !item.isDeleted()){
345 	            			ErrorLevel fieldStatus = ((Section)item.getItemWidget()).processValidationResults(results, clearErrors);
346 							if(fieldStatus.getLevel() > status.getLevel()){
347 								status = fieldStatus;
348 							}
349 	            		}
350 	            	}
351 				}
352                 if (f.getFieldWidget() instanceof CanProcessValidationResults) {
353                     ErrorLevel fieldStatus = ((CanProcessValidationResults) f.getFieldWidget()).processValidationResults(f, results, clearErrors);
354                     if (fieldStatus.getLevel() > status.getLevel()) {
355                         status = fieldStatus;
356                     }
357                 }
358 			}
359 
360 	        for(Section s: sections){
361 	            ErrorLevel subsectionStatus = s.processValidationResults(results,clearErrors);
362 	            if(subsectionStatus.getLevel() > status.getLevel()){
363 	            	status = subsectionStatus;
364 	            }
365 	        }
366 		}
367 
368         return status;
369 	}
370 
371 	/**
372 	 * Same as processValidationResults(results, true)
373 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#processValidationResults(java.util.List)
374 	 */
375 	@Override
376 	public ErrorLevel processValidationResults(List<ValidationResultInfo> results) {
377 		return processValidationResults(results, true);
378 	}
379 
380 	/**
381 	 * Gets the defined controller for this section if one exists.
382 	 * 
383 	 * @see org.kuali.student.common.ui.client.configurable.mvc.HasLayoutController#getLayoutController()
384 	 */
385 	@Override
386 	public LayoutController getLayoutController() {
387 		return this.layoutController;
388 	}
389 
390 	@Override
391 	public void setLayoutController(LayoutController controller) {
392 		this.layoutController = controller;
393 	}
394 
395 
396 	/**
397 	 * Use this to add widgets to a sections layout.  DO NOT use the add(Widget widget) call.
398 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#addWidget(com.google.gwt.user.client.ui.Widget)
399 	 */
400 	@Override
401 	public String addWidget(Widget w) {
402 		return layout.addWidget(w);
403 	}
404 
405     /**
406      * Sets the fields has had focus flag.  This flag is used for determining if the user has interacted with
407      * any fields on the page or if the fields need to be assumed to have been interacted with.  Used for determining
408      * when validation is necessary on a particular section.
409      * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#setFieldHasHadFocusFlags(boolean)
410      */
411     public void setFieldHasHadFocusFlags(boolean hadFocus) {
412         for(FieldDescriptor f: fields){
413             f.setHasHadFocus(hadFocus);
414             if(f.getFieldWidget() instanceof MultiplicityComposite){
415 				MultiplicityComposite mc = (MultiplicityComposite) f.getFieldWidget();
416 
417             	for(MultiplicityItem item: mc.getItems()){
418             		if(item.getItemWidget() instanceof Section && !item.isDeleted()){
419             			((Section) item.getItemWidget()).setFieldHasHadFocusFlags(hadFocus);
420             		}
421             	}
422             }else if(f.getFieldWidget() instanceof MultiplicityGroup){
423             	MultiplicityGroup mg = (MultiplicityGroup) f.getFieldWidget();
424 
425             	for(MultiplicityGroupItem item: mg.getItems()){
426             		if(item.getItemWidget() instanceof Section && !item.isDeleted()){
427             			((Section) item.getItemWidget()).setFieldHasHadFocusFlags(hadFocus);
428             		}
429             	}
430             }
431         }
432 
433         for(Section s: sections){
434             s.setFieldHasHadFocusFlags(hadFocus);
435         }
436 
437     }
438 
439     /**
440      * A section can turn off all validation by setting this flag
441      * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#enableValidation(boolean)
442      */
443     @Override
444     public void enableValidation(boolean enableValidation) {
445     	this.isValidationEnabled = enableValidation;
446     }
447 
448     @Override
449     public boolean isValidationEnabled() {
450 		return isValidationEnabled;
451 	}
452 
453 	/**
454 	 * Update the model passed in with data from the fields on this section.  This method will use the 
455 	 * modelWidgetBinding defined in each of this sections fields to determine how to add this data to the
456 	 * model.
457 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#updateModel(org.kuali.student.common.ui.client.mvc.DataModel)
458 	 */
459 	@Override
460     public void updateModel(DataModel model){
461         SectionBinding.INSTANCE.setModelValue(this, model, "");
462     }
463 
464     /**
465      * Updates the section's fields with data from the model passed in.  This effects all the data input and
466      * display widgets on the particular section.  This method will use the 
467 	 * modelWidgetBinding defined in each of this sections fields to determine how to interpret data from the
468 	 * model and display it on the fields corresponding widget.
469 	 * 
470      * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#updateWidgetData(org.kuali.student.common.ui.client.mvc.DataModel)
471      */
472     @Override
473     public void updateWidgetData(DataModel model) {
474         SectionBinding.INSTANCE.setWidgetValue(this, model, "");
475     }
476 
477     /**
478      * Resets all the dirty and focus flags on fields.
479      * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#resetFieldInteractionFlags()
480      */
481     @Override
482     public void resetFieldInteractionFlags() {
483     	this.isDirty = false;
484         for(FieldDescriptor f: fields){
485             f.setDirty(false);
486             f.setHasHadFocus(false);
487         }
488 
489         for(Section s: sections){
490             s.resetFieldInteractionFlags();
491         }
492 
493     }
494 
495     @Override
496     public void resetDirtyFlags() {
497     	this.isDirty = false;
498         for(FieldDescriptor f: fields){
499             f.setDirty(false);
500         }
501 
502         for(Section s: sections){
503             s.resetDirtyFlags();
504         }
505     }
506 
507 
508 	/**
509 	 * Same as addSection except with an option user defined key (for retrieval of the section if necessary).
510 	 * 
511 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#addSection(java.lang.String, org.kuali.student.common.ui.client.configurable.mvc.sections.Section)
512 	 */
513 	@Override
514 	public String addSection(String key, Section section) {
515         sections.add(section);
516         section.getLayout().setKey(key);
517         String rkey = layout.addLayout(section.getLayout());
518         return rkey;
519 	}
520 
521 	@Override
522 	public FieldDescriptor getField(String fieldKey) {
523 		for(FieldDescriptor f: fields){
524 			if(f.getFieldKey().equals(fieldKey)){
525 				return f;
526 			}
527 		}
528 		return null;
529 	}
530 
531 	/**
532 	 * Gets this sections fieldLayout.  The fieldLayout is used to determine how sections will layout the
533 	 * ui and contains things such as the title and validation panels.
534 	 * 
535 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#getLayout()
536 	 */
537 	@Override
538 	public FieldLayout getLayout() {
539 		return layout;
540 	}
541 
542 	@Override
543 	public Section getSection(String sectionKey) {
544 		for(Section s: sections){
545 			if(s.getLayout().getKey().equals(sectionKey)){
546 				return s;
547 			}
548 		}
549 		return null;
550 	}
551 
552 	@Override
553 	public void removeField(String fieldKey) {
554 		int index = 0;
555 		boolean found = false;
556 		for(FieldDescriptor f: fields){
557 			if(f.getFieldKey().equals(fieldKey)){
558 				index = fields.indexOf(f);
559 				found = true;
560 				break;
561 			}
562 		}
563 		if(found){
564 			fields.remove(index);
565 		}
566 		layout.removeLayoutElement(fieldKey);
567 
568 	}
569 
570 	@Override
571 	public void removeField(FieldDescriptor field) {
572 
573 		fields.remove(field);
574 		layout.removeLayoutElement(field.getFieldKey());
575 
576 	}
577 
578 	@Override
579 	public void removeSection(String sectionKey) {
580 		int index = 0;
581 		boolean found = false;
582 		for(Section s: sections){
583 			if(s.getLayout().getKey().equals(sectionKey)){
584 				index = sections.indexOf(s);
585 				found = true;
586 				break;
587 			}
588 		}
589 		if(found){
590 			sections.remove(index);
591 		}
592 		layout.removeLayoutElement(sectionKey);
593 
594 	}
595 
596 	@Override
597 	public void removeWidget(Widget widget) {
598 		layout.removeLayoutElement(widget);
599 
600 	}
601 
602 	@Override
603 	public void removeWidget(String key) {
604 		layout.removeLayoutElement(key);
605 
606 	}
607 
608 	/**
609 	 * Returns true if this this section is considered dirty (the user may have entered data into this
610 	 * section)
611 	 * 
612 	 * @see org.kuali.student.common.ui.client.configurable.mvc.sections.Section#isDirty()
613 	 */
614 	public boolean isDirty(){
615 		if(!this.isDirty){
616 			//Check child sections for dirtyness
617 			for(Section s: sections){
618 				if(s.isDirty()){
619 					setIsDirty(true);
620 					break;
621 				}
622 			}
623 		}
624 		return isDirty;
625 	}
626 
627     public void setIsDirty(boolean state) {
628 		//Should this trust layoutController to be already set?
629     	if (layoutController == null){
630     		layoutController = LayoutController.findParentLayout(layout);
631     	}
632     	if (isDirty != state){
633         	isDirty = state;
634 	    	if (layoutController != null && isDirty){
635 	    		layoutController.fireApplicationEvent(new ContentDirtyEvent());
636 	    	}
637     	}
638     }
639 
640 	/**
641 	 * DO NOT use this method for adding sections, fields, or widgets to sections
642 	 */
643 	@Override
644 	public void add(Widget w) {
645 		super.add(w);
646 	}
647 
648 	/**
649 	 * Adds a style to this section's underlying layout.
650 	 * @see com.google.gwt.user.client.ui.UIObject#addStyleName(java.lang.String)
651 	 */
652 	@Override
653 	public void addStyleName(String style) {
654 		layout.addStyleName(style);
655 	}
656 
657 	@Override
658 	public void setStyleName(String style) {
659 		layout.setStyleName(style);
660 	}
661 
662 	/**
663 	 * Sets instructions for this entire section (appears next to section title)
664 	 * @param html
665 	 */
666 	public void setInstructions(String html){
667 		layout.setInstructions(html);
668 	}
669 
670 	/**
671 	 * Sets help for this entire section (appears next to section title)
672 	 * @param html
673 	 */
674 	public void setHelp(String html){
675 		layout.setHelp(html);
676 	}
677 	
678 	/**
679 	 * Sets required for this entire section (appears next to section title)
680 	 * @param required
681 	 */
682 	public void setRequired(AbbrPanel required){
683 		layout.setRequired(required);
684 	}
685 	
686 	public void setSectionId(String id){
687 		((Widget)layout).getElement().setId(id);		
688 	}
689 
690 
691 	/* METHODS TO ENABLE/DISABLE FIELD */
692 	
693 	/**
694      * This will progressively enable/disable a set of fields
695      * 
696      * @param isEnabled if the fields should be enabled or disabled
697      * @param fieldDescriptor List of field descriptors to enable/disable
698      */
699 	public static void progressiveEnableFields(Boolean isEnabled, FieldDescriptor ... fieldDescriptors){
700 		
701 		for (FieldDescriptor fd : fieldDescriptors){
702 			enableField(isEnabled, fd);
703 		} 
704 	}
705 	
706 	/** 
707 	 * This will progressively enable/disable and require/unrequire a set of fields
708 	 * @param isEnabled
709 	 * @param fieldDescriptors
710 	 */
711 	public static void progressiveEnableAndRequireFields(Boolean isRequiredAndEnabled, FieldDescriptor ... fieldDescriptors){
712 
713 		for (FieldDescriptor fd : fieldDescriptors){
714 			fd.setRequired(isRequiredAndEnabled);
715 			enableField(isRequiredAndEnabled, fd);
716 		} 
717 	}
718 	
719 	 /** 
720      * This will progressively require/unrequire a set of fields
721      * @param isRequired
722      * @param fieldDescriptors
723      */
724     public static void progressiveRequireFields(Boolean isRequired, FieldDescriptor ... fieldDescriptors){
725 
726         for (FieldDescriptor fd : fieldDescriptors){
727             fd.setRequired(isRequired);
728         } 
729     }	
730     
731 	/**
732 	 * Used to enable,disable widget defined in field descriptor 
733 	 * 
734 	 * @param isEnabled
735 	 * @param fd
736 	 */
737 	protected static void enableField(Boolean isEnabled, FieldDescriptor fd){
738 		//TODO: May want to use different styles for field label if not enabled
739 		Widget widget = fd.getFieldWidget();
740 		
741 		if (!isEnabled){
742 			fd.getFieldElement().clearValidationErrors();
743 			fd.getFieldElement().clearValidationWarnings();
744 		}
745 
746 		//TODO: Make sure this works with all (most?) types of widgets
747 		if (widget instanceof KSPicker && ((KSPicker)widget).getInputWidget() instanceof KSDropDown){
748 			((KSDropDown)((KSPicker)widget).getInputWidget()).setEnabled(isEnabled);				
749 		} else if (widget instanceof TextBoxBase){
750 			((TextBoxBase)widget).setReadOnly(!isEnabled);
751 		}
752 	}
753 	
754 	
755 }