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.lum.common.client.lo;
17  
18  import java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.List;
21  import java.util.Map;
22  
23  import org.kuali.student.r1.common.assembly.data.Metadata;
24  import org.kuali.student.r2.common.dto.ValidationResultInfo;
25  import org.kuali.student.r2.common.infc.ValidationResult.ErrorLevel;
26  import org.kuali.student.common.ui.client.application.Application;
27  import org.kuali.student.common.ui.client.application.ApplicationContext;
28  import org.kuali.student.common.ui.client.configurable.mvc.CanProcessValidationResults;
29  import org.kuali.student.common.ui.client.configurable.mvc.FieldDescriptor;
30  import org.kuali.student.common.ui.client.configurable.mvc.sections.Section;
31  import org.kuali.student.common.ui.client.configurable.mvc.sections.VerticalSection;
32  import org.kuali.student.common.ui.client.event.ValidateRequestEvent;
33  import org.kuali.student.common.ui.client.widgets.KSButton;
34  import org.kuali.student.common.ui.client.widgets.KSLabel;
35  import org.kuali.student.common.ui.client.widgets.KSButtonAbstract.ButtonStyle;
36  import org.kuali.student.common.ui.client.widgets.field.layout.element.FieldElement;
37  import org.kuali.student.common.ui.client.widgets.list.SelectionChangeEvent;
38  import org.kuali.student.common.ui.client.widgets.list.SelectionChangeHandler;
39  import org.kuali.student.common.ui.client.widgets.search.KSPicker;
40  import org.kuali.student.lum.common.client.lu.LUUIConstants;
41  import org.kuali.student.r2.lum.lo.dto.LoCategoryInfo;
42  
43  import com.google.gwt.core.client.GWT;
44  import com.google.gwt.event.dom.client.ChangeEvent;
45  import com.google.gwt.event.dom.client.ChangeHandler;
46  import com.google.gwt.event.dom.client.ClickEvent;
47  import com.google.gwt.event.dom.client.ClickHandler;
48  import com.google.gwt.event.logical.shared.ValueChangeEvent;
49  import com.google.gwt.event.logical.shared.ValueChangeHandler;
50  import com.google.gwt.event.shared.HandlerRegistration;
51  import com.google.gwt.user.client.ui.HasValue;
52  import com.google.gwt.user.client.ui.HorizontalPanel;
53  
54  /**
55   * This class manages the users interactions when building/updating Learning
56   * Objectives within the context of managing CLUs. It allows the user to type in
57   * LO text directly or execute a search and select one or more of the returned
58   * LOs.
59   * 
60   * Users can then re-organize LOs on the screen including altering the sequence
61   * and creating sub LOs
62   * 
63   * @author Kuali Student Team
64   * 
65   */
66  public class LOBuilder extends VerticalSection implements HasValue<List<OutlineNode<LOPicker>>>, CanProcessValidationResults {
67  
68  	private static String type;
69  	private static String state;
70  	private static String repoKey;
71  	private static String messageGroup;
72      private static String startOfPath;
73      private static String endOfPath = "loInfo/descr/plain";
74      private static String middleOfPath = "loDisplayInfoList";
75  	HorizontalPanel searchMainPanel = new HorizontalPanel();
76  	KSPicker searchWindow;
77  
78  	LearningObjectiveList loList;
79  	KSLabel instructions;
80  	
81  	private static int loListDescLength;
82  	
83  	private boolean onTheFlyValidation = true;
84  
85  	protected LOBuilder() {
86  	}
87  
88      public LOBuilder(String luType, String luState, String luGroup, String loRepoKey, String queryPathStart, final Metadata metadata) {
89  		super();
90  		
91  		loListDescLength = metadata.getProperties().get("plain").getConstraints().get(0).getMaxLength();
92  		
93  		type = luType;
94  		state = luState;
95  		repoKey = loRepoKey;
96  		messageGroup = luGroup;
97          startOfPath = queryPathStart;       
98  
99          if (metadata.getInitialLookup() != null) {
100             searchWindow = GWT.create(KSPicker.class);
101             searchWindow.init(metadata.getInitialLookup(), metadata.getAdditionalLookups());
102             searchWindow.addValuesChangeHandler(new ValueChangeHandler<List<String>>() {
103                 public void onValueChange(ValueChangeEvent<List<String>> event) {
104                     List<String> selection = event.getValue();
105                     loList.addSelectedLOs(selection);
106                 }
107             });
108             searchMainPanel.add(searchWindow);
109         }
110         
111         Metadata descMeta = new Metadata();
112         descMeta = metadata.getProperties().get("plain");
113 
114 		instructions = new KSLabel(getLabel(LUUIConstants.LO_INSTRUCTIONS_KEY, descMeta));
115 
116         loList = GWT.create(LearningObjectiveList.class);
117         loList.setLoInfoMaxLength(loListDescLength);
118 
119 		searchMainPanel.addStyleName("KS-LOBuilder-Search-Panel");
120 
121         loList.addStyleName(LUUIConstants.STYLE_SECTION);
122         loList.addStyleName(LUUIConstants.STYLE_SECTION_DIVIDER);
123 
124 		instructions.addStyleName("KS-LOBuilder-Instructions");
125 
126         this.addWidget(searchMainPanel);
127         this.addWidget(instructions);
128         this.addSection(loList);
129         
130 	}
131 
132 	/**
133 	 * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object,
134 	 *      boolean)
135 	 */
136 	@Override
137 	public void setValue(List<OutlineNode<LOPicker>> value, boolean fireEvents) {
138 		setValue(value);
139 	}
140 
141 	/**
142 	 * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object)
143 	 */
144 	@Override
145 	public void setValue(List<OutlineNode<LOPicker>> data) {
146 		loList.setValue(data);
147 	}
148 
149 	/**
150 	 * @see com.google.gwt.user.client.ui.HasValue#getValue()
151 	 */
152 	@Override
153 	public List<OutlineNode<LOPicker>> getValue() {
154 		return loList.getValue();
155 	}
156 
157 	/**
158 	 * @see com.google.gwt.event.logical.shared.HasValueChangeHandlers#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler)
159 	 */
160 	@Override
161 	public HandlerRegistration addValueChangeHandler(ValueChangeHandler<List<OutlineNode<LOPicker>>> handler) {
162 		return loList.addValueChangeHandler(handler);
163 	}
164 	
165     private static String getLabel(String labelKey) {
166         return Application.getApplicationContext().getUILabel(messageGroup, type, state, labelKey);
167     }
168 
169     private static String getLabel(String labelKey, Metadata metadata) {
170         return Application.getApplicationContext().getUILabel(messageGroup, type, state, labelKey, metadata);
171     }
172 
173 	/**
174 	 * @return the type
175 	 */
176 	public static String getType() {
177 		return type;
178 	}
179 
180 	/**
181 	 * @return the state
182 	 */
183 	public static String getState() {
184 		return state;
185 	}
186 
187 	public static String getRepoKey() {
188 		return repoKey;
189 	}
190 
191 	/**
192 	 * @return the messageGroup
193 	 */
194 	public static String getMessageGroup() {
195 		return messageGroup;
196 	}
197 
198     public static class LearningObjectiveList extends VerticalSection implements HasValue<List<OutlineNode<LOPicker>>> {
199 		OutlineNodeModel<LOPicker> outlineModel = new OutlineNodeModel<LOPicker>();
200         protected KSButton addNew;
201         OutlineManager outlineComposite;
202         
203         private int loInfoMaxLength = 0;
204 		
205 		SelectionChangeHandler loPickerChangeHandler = new SelectionChangeHandler(){
206 			public void onSelectionChange(SelectionChangeEvent event) {
207 				fireChangeEvent();
208 			}			
209 		};
210 		
211         public LearningObjectiveList() {
212             addNew = new KSButton(getLabel(LUUIConstants.LEARNING_OBJECTIVE_ADD_LABEL_KEY), ButtonStyle.SECONDARY,
213                     new ClickHandler() {
214                         public void onClick(ClickEvent event) {
215                             setValue(getValue());
216                             appendLO("");
217                             reDraw();
218                         }
219                     });
220 			
221             addNew.addStyleName("KS-LOBuilder-New");
222 
223 			outlineModel.addChangeHandler(new ChangeHandler() {
224 				public void onChange(ChangeEvent event) {
225                     reDraw();
226 					fireChangeEvent();
227 				}
228 			});
229 
230             initEmptyLoList();
231 //			reDraw();
232 		}
233 
234 		protected void initEmptyLoList(){
235 			List<String> list = new ArrayList<String>();
236 			list.add("");
237 			list.add("");
238 			list.add("");
239 			list.add("");
240 			list.add("");
241 			addSelectedLOs(list);			
242 		}
243 		
244 		protected void fireChangeEvent(){
245 			ValueChangeEvent.fire(this, outlineModel.getOutlineNodes());
246 		}
247 		
248 		public List<OutlineNode<LOPicker>> getValue() {
249 			return outlineModel.getOutlineNodes();
250 		}
251 
252 		public void setValue(List<OutlineNode<LOPicker>> value) {
253 			outlineModel.clearNodes();
254 			outlineModel.getOutlineNodes().addAll(value);
255 
256 			if (value == null || value.isEmpty()){
257 				initEmptyLoList();
258 			} else {
259 				//Add selection change handler to LOPickers if not set
260 				for (OutlineNode<LOPicker> node:value){
261 					LOPicker picker = node.getUserObject();
262 					if (!picker.hasChangeHandler()){
263 						picker.addSelectionChangeHandler(loPickerChangeHandler);
264 					}
265 				}
266 				
267 				reDraw();
268 			}
269 		}
270 		
271         public void setLoInfoMaxLength(int loInfoMaxLength) {
272             this.loInfoMaxLength = loInfoMaxLength;
273         }
274 
275         private void appendLO(String loValue) {
276 			OutlineNode<LOPicker> aNode = new OutlineNode<LOPicker>();
277 			LOPicker newPicker = new LOPicker(messageGroup, type, state, repoKey, this.loInfoMaxLength);
278 
279 			newPicker.addSelectionChangeHandler(loPickerChangeHandler);
280 			newPicker.setLOText(loValue);
281 			aNode.setUserObject(newPicker);
282 			aNode.setModel(outlineModel);
283 
284 			outlineModel.addOutlineNode(aNode);
285 		}
286 
287 		// add one or more description by going through existing LO box and
288 		// populating the empty ones
289 		// if not enough empty LO boxes then add new ones
290 		public void addSelectedLOs(List<String> loDescription) {
291 
292 			List<OutlineNode<LOPicker>> existingLOs = outlineModel.getOutlineNodes();
293 
294 			int ix = existingLOs.size();
295 			for (String strValue : loDescription) {
296 
297 				boolean foundEmptyBox = false;
298 				
299 				for(int i=0;i<ix;i++)
300 				{
301 					if (existingLOs.get(i).getUserObject().getLOText().trim()
302 							.length() == 0) {
303 						existingLOs.get(i).getUserObject().setLOText(strValue);
304 						foundEmptyBox = true;
305 						i=ix;
306 					}
307 				}
308 
309 				// we didn't find empty LO box so add a new one
310 				if (foundEmptyBox == false) {
311 					appendLO(strValue);
312 				}
313 			}
314 			reDraw();
315 		}
316 
317 		private void reDraw() {
318             if (null != outlineComposite) {
319                 this.removeSection(outlineComposite);
320             }
321             this.removeWidget(addNew); // no error if it's not currently there
322             outlineComposite = new OutlineManager(startOfPath, middleOfPath, endOfPath);
323             outlineComposite.setValue(outlineModel);
324             this.addSection(outlineComposite);
325             this.addWidget(addNew);
326 			outlineComposite.render();
327 		}
328 
329 		public HandlerRegistration addValueChangeHandler(ValueChangeHandler<List<OutlineNode<LOPicker>>> handler) {
330 			return addHandler(handler, ValueChangeEvent.getType());
331 		}
332 		
333 		public SelectionChangeHandler getChangeHandlerForLOPicker(){
334 			return loPickerChangeHandler;
335 		}
336 
337 		@Override
338 		public void setValue(List<OutlineNode<LOPicker>> value,
339 				boolean fireEvents) {
340 			setValue(value);
341 		}
342 	}
343 
344     @Override
345     public ErrorLevel processValidationResults(FieldDescriptor fd, List<ValidationResultInfo> results) {
346         return processValidationResults(fd, results, true);
347     }
348 
349     @Override
350     public ErrorLevel processValidationResults(FieldDescriptor fd, List<ValidationResultInfo> results, boolean clearErrors) {
351         
352         ErrorLevel status = ErrorLevel.OK;
353         
354         for (Section section : getSections()) {
355             ErrorLevel level = section.processValidationResults(results, clearErrors);
356             if (level.getLevel() > status.getLevel()) {
357                 status = level;
358             }
359         }
360         return status;
361     }
362     
363     public static int getLoListDescLength() {
364         return loListDescLength; 
365         
366     }
367 
368     @Override
369     public boolean doesOnTheFlyValidation() {      
370         return onTheFlyValidation;
371     }
372 
373     @Override
374     public void Validate(ValidateRequestEvent event, List<ValidationResultInfo> result) {
375 
376         if (event.getFieldDescriptor().hasHadFocus()) {
377             Map<String, FieldElement> loFieldModelMapping = doLOFieldModelMapping();
378 
379             for (int i = 0; i < result.size(); i++) {
380                 ValidationResultInfo vr = result.get(i);
381                 FieldElement element = loFieldModelMapping.get(vr.getElement());
382 
383                 if (element != null) {
384                     element.clearValidationErrors();
385                     element.processValidationResult(vr);
386                 }
387             }
388         }
389 
390     }
391     
392     private Map<String, FieldElement> doLOFieldModelMapping() {
393         Map<String, FieldElement> loFieldModelMapping = new HashMap<String, FieldElement>();
394 
395         int z = 0;
396 
397         for (int i = 0; i < this.getValue().size(); i++) {
398             String startPath = startOfPath + "/";
399             String endPathFormatted = "/" + "loInfo/desc/formatted";
400             String endPathPlain = "/" + "loInfo/desc/plain";
401 
402             this.getFields().get(i).getFieldElement().clearValidationErrors();
403             
404             String desc = this.getValue().get(i).getUserObject().getLOText();
405             int indentLevel = this.getValue().get(i).getIndentLevel();
406             List<LoCategoryInfo> categories = this.getValue().get(i).getUserObject().getLoCategories();
407 
408             if (desc != null && desc.trim().length() > 0 || indentLevel > 0 || categories != null && !categories.isEmpty()) {
409                 loFieldModelMapping.put(startPath + z + endPathFormatted, this.getFields().get(i).getFieldElement());
410                 loFieldModelMapping.put(startPath + z++ + endPathPlain, this.getFields().get(i).getFieldElement());
411             }
412 
413         }        
414         return loFieldModelMapping;
415     }   
416 
417 }