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;
17  
18  import java.util.HashMap;
19  import java.util.LinkedHashMap;
20  import java.util.List;
21  import java.util.Map;
22  import java.util.Map.Entry;
23  
24  import org.kuali.student.common.ui.client.configurable.mvc.layouts.ViewLayoutController;
25  import org.kuali.student.common.ui.client.configurable.mvc.sections.Section;
26  import org.kuali.student.common.ui.client.configurable.mvc.views.SectionView;
27  import org.kuali.student.common.ui.client.event.ActionEvent;
28  import org.kuali.student.common.ui.client.event.SaveActionEvent;
29  import org.kuali.student.common.ui.client.event.SectionUpdateEvent;
30  import org.kuali.student.common.ui.client.event.SectionUpdateHandler;
31  import org.kuali.student.common.ui.client.event.ValidateRequestEvent;
32  import org.kuali.student.common.ui.client.event.ValidateRequestHandler;
33  import org.kuali.student.common.ui.client.mvc.ActionCompleteCallback;
34  import org.kuali.student.common.ui.client.mvc.Callback;
35  import org.kuali.student.common.ui.client.mvc.Controller;
36  import org.kuali.student.common.ui.client.mvc.DataModel;
37  import org.kuali.student.common.ui.client.mvc.ModelRequestCallback;
38  import org.kuali.student.common.ui.client.mvc.View;
39  import org.kuali.student.common.ui.client.mvc.history.HistoryManager;
40  import org.kuali.student.common.ui.client.widgets.KSButton;
41  import org.kuali.student.common.ui.client.widgets.KSLightBox;
42  import org.kuali.student.common.ui.client.widgets.field.layout.element.FieldElement;
43  import org.kuali.student.core.validation.dto.ValidationResultInfo;
44  import org.kuali.student.core.validation.dto.ValidationResultInfo.ErrorLevel;
45  
46  import com.google.gwt.core.client.GWT;
47  import com.google.gwt.event.dom.client.ClickEvent;
48  import com.google.gwt.event.dom.client.ClickHandler;
49  import com.google.gwt.user.client.ui.FlowPanel;
50  import com.google.gwt.user.client.ui.Widget;
51  
52  public abstract class LayoutController extends Controller implements ViewLayoutController, View {
53  
54  	protected Map<Enum<?>, View> viewMap = new LinkedHashMap<Enum<?>, View>();
55  	protected Map<String, Enum<?>> viewEnumMap = new HashMap<String, Enum<?>>();
56  	protected Enum<?> defaultView;
57  	
58  	protected String name;
59  	protected Enum<?> viewType;
60  
61      protected View startPopupView;
62      protected KSLightBox startViewWindow;
63  	
64      public LayoutController(String controllerId){
65          super(controllerId);
66          //Global section update Event handling
67  		addApplicationEventHandler(SectionUpdateEvent.TYPE, new SectionUpdateHandler(){
68  
69  			@Override
70  			public void onSectionUpdate(final SectionUpdateEvent event) {
71  				LayoutController.this.requestModel(new ModelRequestCallback<DataModel>(){
72  
73  					@Override
74  					public void onRequestFail(Throwable cause) {
75  						GWT.log("Unable to retrieve model for section update", cause);
76  					}
77  
78  					@Override
79  					public void onModelReady(DataModel model) {
80  						event.getSection().updateModel(model);
81  						event.getSection().updateWidgetData(model);
82  						
83  					}
84  				});
85  				
86  			}
87  		});
88  		//Global validation Event handling
89          addApplicationEventHandler(ValidateRequestEvent.TYPE, new ValidateRequestHandler() {
90  
91              @Override
92              public void onValidateRequest(final ValidateRequestEvent event) {
93              	FieldDescriptor originatingField = event.getFieldDescriptor();
94              	String modelId = null;
95              	if (originatingField != null) {
96              		modelId = originatingField.getModelId();
97              	}
98              	if (modelId == null) {
99              		requestModel(new ModelRequestCallback<DataModel>() {
100             			@Override
101             			public void onModelReady(DataModel model) {
102             				validate(model, event);
103             			}
104 
105             			@Override
106             			public void onRequestFail(Throwable cause) {
107             				GWT.log("Unable to retrieve model for validation", cause);
108             			}
109 
110             		});
111             	} else {
112             		requestModel(modelId, new ModelRequestCallback<DataModel>() {
113             			@Override
114             			public void onModelReady(DataModel model) {
115             				validate(model, event);
116             			}
117 
118             			@Override
119             			public void onRequestFail(Throwable cause) {
120             				GWT.log("Unable to retrieve model for validation", cause);
121             			}
122 
123             		});
124             	}
125             }
126 
127         });
128     }
129     
130     private void validate(DataModel model, final ValidateRequestEvent event) {
131     	if(event.validateSingleField()){
132     		model.validateField(event.getFieldDescriptor(), new Callback<List<ValidationResultInfo>>() {
133                 @Override
134                 public void exec(List<ValidationResultInfo> result) {
135                 	if(event.getFieldDescriptor() != null){
136                 		//We dont need to traverse since it is single field, so don't do isValid call here
137                 		//instead add the error messages directly
138                 		FieldElement element = event.getFieldDescriptor().getFieldElement();
139                 		if(element != null){
140 	                		element.clearValidationPanel();
141 	                		for(int i = 0; i < result.size(); i++){
142 	                    		ValidationResultInfo vr = result.get(i);
143 	                    		if(vr.getElement().equals(event.getFieldDescriptor().getFieldKey()) 
144 	                    				&& event.getFieldDescriptor().hasHadFocus()){
145 	    							element.processValidationResult(vr);
146 	                    		}
147 	                    	}
148                 		}
149                 	}
150                 	
151                 }
152     		});
153     	}
154     	else{
155             model.validate(new Callback<List<ValidationResultInfo>>() {
156                 @Override
157                 public void exec(List<ValidationResultInfo> result) {
158                     isValid(result, false, true);
159                 }
160             });
161     	}
162     }
163     
164     public ErrorLevel checkForErrors(List<ValidationResultInfo> list){
165 		ErrorLevel errorLevel = ErrorLevel.OK;
166 		
167 		for(ValidationResultInfo vr: list){
168 			if(vr.getErrorLevel().getLevel() > errorLevel.getLevel()){
169 				errorLevel = vr.getErrorLevel();
170 			}
171 			if(errorLevel.equals(ErrorLevel.ERROR)){
172 				break;
173 			}
174 		}
175     	
176     	return errorLevel;
177     	
178     }
179     
180     public static LayoutController findParentLayout(Widget w){
181         LayoutController result = null;
182         while (true) {
183             if (w == null) {
184                 break;
185             } else if (w instanceof HasLayoutController) {
186             	result = ((HasLayoutController)w).getLayoutController();
187             	if (result != null) {
188             		break;
189             	}
190             } else if (w instanceof LayoutController) {
191                 result = (LayoutController) w;
192                 break;
193             }
194             w = w.getParent();
195             
196         }
197         return result;
198     }
199     
200 	public void addStartViewPopup(final View view){
201 	    startPopupView = view;
202 	    if(startViewWindow == null){
203 	    	startViewWindow = new KSLightBox();
204 	    }
205 
206 	    FlowPanel panel = new FlowPanel();
207 	    panel.add(view.asWidget());
208 	    KSButton save = new KSButton("Save",new ClickHandler(){
209             public void onClick(ClickEvent event) {
210                 view.updateModel();
211                 SaveActionEvent saveActionEvent = new SaveActionEvent(true);
212 
213                 saveActionEvent.setActionCompleteCallback(new ActionCompleteCallback(){
214                     public void onActionComplete(ActionEvent action) {
215                         startViewWindow.hide();
216                     }
217                 });
218                 
219 
220                 fireApplicationEvent(saveActionEvent);
221             }
222 	    });
223 	    startViewWindow.addButton(save);
224 	    
225 	    KSButton cancel = new KSButton("Cancel", new ClickHandler(){
226             public void onClick(ClickEvent event) {
227                 startViewWindow.hide();
228             }
229 	    });
230 	    startViewWindow.addButton(cancel);
231 
232 	    if(view instanceof SectionView){
233 	    	((SectionView) view).setController(this);
234 	    }
235 	    startViewWindow.setWidget(panel);
236 	}
237 	
238     public boolean isStartViewShowing(){
239         if(startViewWindow == null){
240             return false;
241         }
242     	return startViewWindow.isShowing();
243     }
244 
245     public View getStartPopupView(){
246         return startPopupView;
247     }
248     
249     public void showStartPopup(final Callback<Boolean> onReadyCallback){
250         startPopupView.beforeShow(new Callback<Boolean>() {
251 			@Override
252 			public void exec(Boolean result) {
253 				if (result) {
254 					startViewWindow.show();
255 				}
256 				onReadyCallback.exec(result);
257 			}
258         });
259     }
260     
261     public KSLightBox getStartPopup(){
262         return startViewWindow;
263     }
264 
265 
266     /*New methods*/
267 	
268 	public void addView(View view){
269 		viewMap.put(view.getViewEnum(), view);
270 		viewEnumMap.put(view.getViewEnum().toString(), view.getViewEnum());
271 		if(view instanceof SectionView){
272 			((SectionView) view).setController(this);
273 		}
274 		else if(view instanceof ToolView){
275 			((ToolView) view).setController(this);
276 		}
277 	}
278 	
279 	public <V extends Enum<?>> void setDefaultView(V viewType){
280 		this.defaultView = viewType;
281 	}
282 	
283 	public Enum<?> getDefaultView(){
284 		return this.defaultView;
285 	}
286 	
287 	public abstract void updateModel();
288 	
289 	public void updateModelFromView(Enum<?> viewType){
290 		View v = viewMap.get(viewType);
291 		if(v != null){
292 			v.updateModel();
293 		}
294 	}
295 	
296 	public void updateModelFromCurrentView(){
297         if(this.getCurrentView() != null){
298 		    this.getCurrentView().updateModel();
299         }
300 	}
301 
302 
303 	@Override
304 	public <V extends Enum<?>> void getView(V viewType, Callback<View> callback) {
305 		callback.exec(viewMap.get(viewType));
306 	}
307 
308 	@Override
309 	public Enum<?> getViewEnumValue(String enumValue) {
310 		return viewEnumMap.get(enumValue);
311 	}
312 
313 	//TODO remove this method from controller hierarchy?  its not used
314 	@Override
315 	public Class<? extends Enum<?>> getViewsEnum() {
316 		return null;
317 	}
318 
319 	@Override
320 	public void showDefaultView(final Callback<Boolean> onReadyCallback) {
321 		HistoryManager.setLogNavigationHistory(false);
322 		//turn of history support for default showing until view is ready
323 		if(defaultView != null){
324 			showView(defaultView, onReadyCallback);
325 		}
326 		else if(!viewMap.isEmpty()){		
327 			if(defaultView == null){
328 				showView(viewMap.entrySet().iterator().next().getKey(), onReadyCallback);
329 			}	
330 		}
331 		
332 	}
333 	
334 	public void showFirstView(Callback<Boolean> onReadyCallback){
335 		HistoryManager.setLogNavigationHistory(false);
336 		if(!viewMap.isEmpty()){	
337 			showView(viewMap.entrySet().iterator().next().getKey(), onReadyCallback);
338 		}
339 		else{
340 			showDefaultView(onReadyCallback);
341 		}
342 	}
343 	
344 	/**
345  	 * Check to see if current/all section(s) is valid (ie. does not contain any errors)
346  	 *
347 	 * @param validationResults List of validation results for the layouts model.
348 	 * @param checkCurrentSectionOnly true if errors should be checked on current section only, false if all sections should be checked
349 	 * @return true if the specified sections (all or current) has any validation errors
350 	 */
351 	public boolean isValid(List<ValidationResultInfo> validationResults, boolean checkCurrentSectionOnly){
352 		return isValid(validationResults, checkCurrentSectionOnly, true);
353 	}
354 	
355 	public boolean isValid(List<ValidationResultInfo> validationResults, boolean checkCurrentSectionOnly, boolean allFields){
356 		boolean isValid = true;
357 
358 		if (checkCurrentSectionOnly){
359 			//Check for validation errors on the currently displayed section only
360 	    	View v = getCurrentView();
361 	        if(v instanceof Section){
362 	        	isValid = isValid(validationResults, (Section)v, allFields);
363 	    	}
364 	     	if(this.isStartViewShowing()){
365 	     		if(startPopupView instanceof Section){
366 	     			isValid = isValid(validationResults, ((Section) startPopupView), allFields) && isValid;
367 	     		}
368 	     	}
369 		} else {
370 			//Check for validation errors on all sections
371 			String errorSections = "";
372 			StringBuilder errorSectionsbuffer = new StringBuilder();
373 			errorSectionsbuffer.append(errorSections);
374 			for (Entry<Enum<?>, View> entry:viewMap.entrySet()) {
375 				View v = entry.getValue();
376 				if (v instanceof Section){
377 					if (!isValid(validationResults, (Section)v, allFields)){
378 						isValid = false;
379 						errorSectionsbuffer.append(((SectionView)v).getName() + ", ");
380 					}
381 				}
382 			}
383 	     	if(this.isStartViewShowing()){
384 	     		if(startPopupView instanceof Section){
385 	     			isValid = isValid(validationResults, ((Section) startPopupView), allFields) && isValid;
386 	     		}
387 	     	}
388 			errorSections = errorSectionsbuffer.toString();
389 			if (!errorSections.isEmpty()){
390 				errorSections = errorSections.substring(0, errorSections.length()-2);
391 				//container.addMessage("Following section(s) has errors & must be corrected: " + errorSections);
392 			}
393 		}
394 
395 		return isValid;
396 	}
397 
398 	private boolean isValid(List<ValidationResultInfo> validationResults, Section section, boolean allFields){
399 		ErrorLevel status;
400 		if(allFields){
401 			section.setFieldHasHadFocusFlags(true);
402 			status = section.processValidationResults(validationResults);
403 		}
404 		else{
405 			status = section.processValidationResults(validationResults, false);
406 		}
407 
408 		return (status != ErrorLevel.ERROR);
409 	}
410 	
411 	@Override
412 	public void beforeViewChange(Enum<?> viewChangingTo, Callback<Boolean> okToChange) {
413 		if(this.getCurrentView() instanceof Controller){
414 			((Controller)this.getCurrentView()).beforeViewChange(viewChangingTo, okToChange);
415 		}
416 		else{
417 			okToChange.exec(true);
418 		}
419 	}
420 
421 	@Override
422 	public Widget asWidget() {
423 		return this;
424 	}
425 
426 	@Override
427 	public boolean beforeHide() {
428 		return true;
429 	}
430 
431 	@Override
432 	public void beforeShow(Callback<Boolean> onReadyCallback) {
433 		onReadyCallback.exec(true);
434 	}
435 
436 	@Override
437 	public Controller getController() {
438 		return parentController;
439 	}
440 
441 	@Override
442 	public String getName() {
443 		if(name == null && viewType != null){
444 			return viewType.toString();
445 		}
446 		else{
447 			return name;
448 		}
449 	}
450 
451 	@Override
452 	public Enum<?> getViewEnum() {
453 		return viewType;
454 	}
455 	
456 	public void setViewEnum(Enum<?> viewType){
457 		this.viewType= viewType;
458 	}
459 	
460 	public void setName(String name){
461 		this.name = name;
462 	}
463 	
464 	public void setController(Controller controller){
465 		parentController = controller;
466 	}
467 	
468 	@Override
469 	public void collectBreadcrumbNames(List<String> names) {
470 		names.add(this.getName());
471 		if(this.getCurrentView() != null){
472 			this.getCurrentView().collectBreadcrumbNames(names);
473 		}
474 	}
475 	
476 	@Override
477 	public void clear() {
478 		
479 	}
480 	
481 }