View Javadoc

1   package org.kuali.student.lum.course.service.assembler;
2   
3   import java.util.ArrayList;
4   import java.util.Collections;
5   import java.util.Comparator;
6   import java.util.HashMap;
7   import java.util.HashSet;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Set;
11  import java.util.Map.Entry;
12  
13  import org.kuali.student.common.assembly.BOAssembler;
14  import org.kuali.student.common.assembly.BaseDTOAssemblyNode;
15  import org.kuali.student.common.assembly.BaseDTOAssemblyNode.NodeOperation;
16  import org.kuali.student.common.assembly.data.AssemblyException;
17  import org.kuali.student.common.dto.DtoConstants;
18  import org.kuali.student.common.exceptions.DoesNotExistException;
19  import org.kuali.student.common.exceptions.InvalidParameterException;
20  import org.kuali.student.common.exceptions.MissingParameterException;
21  import org.kuali.student.common.exceptions.OperationFailedException;
22  import org.kuali.student.common.util.UUIDHelper;
23  import org.kuali.student.lum.course.dto.LoDisplayInfo;
24  import org.kuali.student.lum.lo.dto.LoCategoryInfo;
25  import org.kuali.student.lum.lo.dto.LoInfo;
26  import org.kuali.student.lum.lo.dto.LoLoRelationInfo;
27  import org.kuali.student.lum.lo.service.LearningObjectiveService;
28  
29  
30  public class LoAssembler implements BOAssembler<LoDisplayInfo, LoInfo> {
31  
32  	private LearningObjectiveService loService;
33  	
34  	@Override
35  	public LoDisplayInfo assemble(LoInfo lo, LoDisplayInfo loDisplayInfo,
36  			boolean shallowBuild) throws AssemblyException {
37  		
38  		LoDisplayInfo loDisplay = (null != loDisplayInfo) ? loDisplayInfo : new LoDisplayInfo();
39  		
40  		loDisplay.setLoInfo(lo);
41  
42  		if (!shallowBuild) {
43  			String loId = lo.getId();
44  			try {
45  				List<LoCategoryInfo> loCategories = loService.getLoCategoriesForLo(loId);
46  				loDisplay.setLoCategoryInfoList(loCategories);
47  			} catch (DoesNotExistException e) {
48  			} catch (Exception e) {
49  				throw new AssemblyException("Error getting learning objective categories", e);
50  			}
51  			try {
52  				List<LoInfo> childLos = loService.getRelatedLosByLoId(loId,CourseAssemblerConstants.COURSE_LO_RELATION_INCLUDES);
53  				for(LoInfo childLo:childLos){
54  					LoDisplayInfo childLoDisplay = assemble(childLo, null, shallowBuild);
55  					childLoDisplay.setParentLoRelationid(lo.getId());
56  					childLoDisplay.setParentRelType(CourseAssemblerConstants.COURSE_LO_RELATION_INCLUDES);
57  					loDisplay.getLoDisplayInfoList().add(childLoDisplay);
58  				}
59  				if(loDisplay.getLoDisplayInfoList().size()>1){
60  					Collections.sort(loDisplay.getLoDisplayInfoList(), LoDisplayComparator.getInstance());
61  				}
62  			} catch (DoesNotExistException e) {
63  			} catch (Exception e) {
64  				throw new AssemblyException("Error getting learning objective", e);
65  			}
66  
67  		}
68  		return loDisplay;
69  	}
70  
71  	@Override
72  	//Creation of categories is done in the LoCategoryRpcGwtServlet
73  	public BaseDTOAssemblyNode<LoDisplayInfo, LoInfo> disassemble(
74  			LoDisplayInfo loDisplay, NodeOperation operation)
75  			throws AssemblyException {
76  		
77  		BaseDTOAssemblyNode<LoDisplayInfo, LoInfo> result = new BaseDTOAssemblyNode<LoDisplayInfo, LoInfo>(this);
78  		
79  		//see if this is a new LuInfo
80  		if (loDisplay == null || loDisplay.getLoInfo() == null) {
81  			throw new AssemblyException("Lo can not be null");
82  		}
83  		if (NodeOperation.CREATE != operation && null == loDisplay.getLoInfo().getId()) {
84  			throw new AssemblyException("Lo id can not be null");
85  		}
86  		
87  		//set the id if it's not there already(important for creating relations)
88  		loDisplay.getLoInfo().setId(UUIDHelper.genStringUUID(loDisplay.getLoInfo().getId()));
89  		
90  		//Default these values
91  		loDisplay.getLoInfo().setType(CourseAssemblerConstants.COURSE_LO_TYPE);
92  		loDisplay.getLoInfo().setLoRepositoryKey(CourseAssemblerConstants.COURSE_LO_REPOSITORY_KEY);
93  		
94  		
95  		//Populate the node
96  		result.setBusinessDTORef(loDisplay);
97  		result.setNodeData(loDisplay.getLoInfo());
98  		result.setOperation(operation);
99  		
100 		//Process the child los
101 		try {
102 			List<BaseDTOAssemblyNode<?, ?>> childLoNodes = disassembleChildLos(loDisplay, operation);
103 			result.getChildNodes().addAll(childLoNodes);
104 		} catch (DoesNotExistException e) {
105 		} catch (Exception e) {
106 			throw new AssemblyException("Error disassembling child los", e);
107 		} 
108 
109 		//Process the categories
110 		try {
111 			List<BaseDTOAssemblyNode<?, ?>> categoryNodes = disassembleCategories(loDisplay, operation);
112 			result.getChildNodes().addAll(categoryNodes);
113 		} catch (Exception e) {
114 			throw new AssemblyException("Error disassembling categories", e);
115 		} 
116 		
117 		return result;
118 	}
119 
120 	private List<BaseDTOAssemblyNode<?, ?>> disassembleCategories(
121 			LoDisplayInfo loDisplay, NodeOperation operation) throws AssemblyException {
122 		
123 		List<BaseDTOAssemblyNode<?, ?>> results = new ArrayList<BaseDTOAssemblyNode<?, ?>>();
124 		
125 		//Category relations
126 		Set<String> currentCategoryIds = new HashSet<String>();
127 		//Get current relations
128 		if (!NodeOperation.CREATE.equals(operation)) {
129 			try {
130 				List<LoCategoryInfo> categories = loService.getLoCategoriesForLo(loDisplay.getLoInfo().getId());
131 				for (LoCategoryInfo category : categories) {
132 					currentCategoryIds.add(category.getId());
133 				}
134 			} catch (DoesNotExistException e) {
135 			} catch (Exception e) {
136 				throw new AssemblyException("Error getting categories",	e);
137 			}
138 		}
139 		//Update
140 		for (LoCategoryInfo category : loDisplay.getLoCategoryInfoList()) {
141 
142 			// If this is a format create/new activity update then all activities will be created
143 		    if (NodeOperation.CREATE == operation
144 		            || (NodeOperation.UPDATE == operation &&  !currentCategoryIds.contains(category.getId()))) {
145 		    	
146 		    	LoCategoryRelationInfo loCategoryRelation = new LoCategoryRelationInfo();
147 		    	loCategoryRelation.setCategoryId(category.getId());
148 		    	loCategoryRelation.setLoId(loDisplay.getLoInfo().getId());
149 		    	
150                 BaseDTOAssemblyNode<LoDisplayInfo, LoCategoryRelationInfo> relationToAddNode = new BaseDTOAssemblyNode<LoDisplayInfo, LoCategoryRelationInfo>(null);
151                 relationToAddNode.setNodeData(loCategoryRelation);
152                 relationToAddNode.setOperation(NodeOperation.CREATE);
153                 results.add(relationToAddNode);
154 
155                 currentCategoryIds.remove(category.getId());
156 		    } else if (NodeOperation.UPDATE == operation
157 					&& currentCategoryIds.contains(category.getId())) {
158                 currentCategoryIds.remove(category.getId());
159 			} 
160 		}
161 		//Delete leftovers
162 		for(String categoryId:currentCategoryIds){
163 	    	LoCategoryRelationInfo loCategoryRelation = new LoCategoryRelationInfo();
164 	    	loCategoryRelation.setCategoryId(categoryId);
165 	    	loCategoryRelation.setLoId(loDisplay.getLoInfo().getId());
166 	    	
167             BaseDTOAssemblyNode<LoDisplayInfo, LoCategoryRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<LoDisplayInfo, LoCategoryRelationInfo>(null);
168             relationToDeleteNode.setNodeData(loCategoryRelation);
169             relationToDeleteNode.setOperation(NodeOperation.DELETE);
170             results.add(relationToDeleteNode);
171 		}
172 		
173 		return results;
174 	}
175 
176 	private List<BaseDTOAssemblyNode<?, ?>> disassembleChildLos(LoDisplayInfo loDisplay, NodeOperation operation) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, AssemblyException{
177 		List<BaseDTOAssemblyNode<?, ?>> results = new ArrayList<BaseDTOAssemblyNode<?, ?>>();
178 		Map<String,LoLoRelationInfo> currentLoRelations = new HashMap<String,LoLoRelationInfo>();
179 		//Make lu lu relations
180 		if (!NodeOperation.CREATE.equals(operation)) {
181 			try {
182 				List<LoLoRelationInfo> loRelations = loService.getLoLoRelationsByLoId(loDisplay.getLoInfo().getId());
183 				for (LoLoRelationInfo loRelation : loRelations) {
184 					//getLoLoRelationsByLoId returns if the lo is related or if it is the owner(this seems wrong)
185 					if(CourseAssemblerConstants.COURSE_LO_RELATION_INCLUDES.equals(loRelation.getType())&&
186 							!loDisplay.getLoInfo().getId().equals(loRelation.getRelatedLoId())){
187 						currentLoRelations.put(loRelation.getRelatedLoId(), loRelation);
188 					}
189 				}
190 			} catch (DoesNotExistException e) {
191 			} catch (Exception e) {
192 				throw new AssemblyException("Error getting categories",	e);
193 			}
194 		}
195 		
196 		// Loop through all the activities in this format
197 		for (LoDisplayInfo childDisplay : loDisplay.getLoDisplayInfoList()) {
198 		    
199 		    // Set the state of the child LO to match the state of the parent
200 		    // LO. This end up propagating the program state to all of the LOs,
201 		    // since we set parent LO state to program state earlier in the code
202 		    childDisplay.getLoInfo().setState(loDisplay.getLoInfo().getState());
203 		    
204 			// If this is a format create/new activity update then all activities will be created
205 		    if (NodeOperation.CREATE == operation
206 		            || (NodeOperation.UPDATE == operation &&  !currentLoRelations.containsKey(childDisplay.getLoInfo().getId()))) {
207 		        		    
208                 // the lo does not exist, so create
209                 // Assemble and add the lo
210 		    	childDisplay.getLoInfo().setId(null);
211                 BaseDTOAssemblyNode<LoDisplayInfo, LoInfo> loNode = this
212                         .disassemble(childDisplay, NodeOperation.CREATE);
213                 results.add(loNode);
214 
215                 // Create the relationship and add it as well
216                 LoLoRelationInfo relation = new LoLoRelationInfo();
217                 relation.setLoId(loDisplay.getLoInfo().getId());
218                 relation.setRelatedLoId(loNode.getNodeData().getId());
219                 relation.setType(CourseAssemblerConstants.COURSE_LO_RELATION_INCLUDES);
220                 
221                 // Relations can only have states of Active or SUSPENDED
222                 // DO NOT use states like Approve, Draft, etc on relations
223                 // Will default to Active
224                 relation.setState(DtoConstants.STATE_ACTIVE);
225                 
226 
227                 BaseDTOAssemblyNode<LoDisplayInfo, LoLoRelationInfo> relationNode = new BaseDTOAssemblyNode<LoDisplayInfo, LoLoRelationInfo>(
228                         null);
229                 relationNode.setNodeData(relation);
230                 relationNode.setOperation(NodeOperation.CREATE);
231 
232                 results.add(relationNode);
233             } else if (NodeOperation.UPDATE == operation
234 					&& currentLoRelations.containsKey(childDisplay.getLoInfo().getId())) {
235 				// If the lo already has this child lo, then just update the
236 				// child lo
237 				BaseDTOAssemblyNode<LoDisplayInfo, LoInfo> loNode = this
238 						.disassemble(childDisplay, NodeOperation.UPDATE);
239 				results.add(loNode);
240 
241 				// remove this entry from the map so we can tell what needs to
242 				// be deleted at the end
243 				currentLoRelations.remove(childDisplay.getLoInfo().getId());
244 			} else if (NodeOperation.DELETE == operation
245                     && currentLoRelations.containsKey(childDisplay.getLoInfo().getId())) {
246 			    
247                 // Delete the Format and its relation
248 				LoLoRelationInfo relationToDelete = currentLoRelations.get(childDisplay.getLoInfo().getId());
249                 BaseDTOAssemblyNode<LoDisplayInfo, LoLoRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<LoDisplayInfo, LoLoRelationInfo>(
250                         null);
251                 relationToDeleteNode.setNodeData(relationToDelete);
252                 relationToDeleteNode.setOperation(NodeOperation.DELETE);
253                 results.add(relationToDeleteNode);
254             
255                 BaseDTOAssemblyNode<LoDisplayInfo, LoInfo> loNode = this
256                 .disassemble(childDisplay, NodeOperation.DELETE);
257                 results.add(loNode);                                
258 
259                 // remove this entry from the map so we can tell what needs to
260                 // be deleted at the end
261                 currentLoRelations.remove(childDisplay.getLoInfo().getId());			    
262 			}
263 		}         
264 
265         // Now any leftover lo ids are no longer needed, so delete
266         // los and relations
267         for (Entry<String, LoLoRelationInfo> entry : currentLoRelations.entrySet()) {
268             // Create a new relation with the id of the relation we want to
269             // delete
270         	LoLoRelationInfo relationToDelete = entry.getValue();
271             BaseDTOAssemblyNode<LoDisplayInfo, LoLoRelationInfo> relationToDeleteNode = new BaseDTOAssemblyNode<LoDisplayInfo, LoLoRelationInfo>(
272                     null);
273             relationToDeleteNode.setNodeData(relationToDelete);
274             relationToDeleteNode.setOperation(NodeOperation.DELETE);
275             results.add(relationToDeleteNode);
276 
277             LoInfo loToDelete = loService.getLo(entry.getKey());
278             LoDisplayInfo loDisplayToDelete = this.assemble(loToDelete, null, false);
279             BaseDTOAssemblyNode<LoDisplayInfo, LoInfo> loNode = this.disassemble(loDisplayToDelete, NodeOperation.DELETE);
280             results.add(loNode);                                            
281         }
282 		return results;
283 		
284 	}
285 
286 	public void setLoService(LearningObjectiveService loService) {
287 		this.loService = loService;
288 	}
289 
290 	public static class LoDisplayComparator implements Comparator<LoDisplayInfo>{
291 		private static LoDisplayComparator instance = new LoDisplayComparator();
292 		@Override
293 		public int compare(LoDisplayInfo o1, LoDisplayInfo o2) {
294 			String o1Sequence = o1.getLoInfo().getAttributes().get(CourseAssemblerConstants.COURSE_LO_SEQUENCE);
295 			String o2Sequence = o1.getLoInfo().getAttributes().get(CourseAssemblerConstants.COURSE_LO_SEQUENCE);
296 			if(o1Sequence!=null){
297 				return o1Sequence.compareTo(o2Sequence);
298 			}else if(o2Sequence!=null){
299 				return -1;
300 			}
301 			return 0;
302 		}
303 		public static LoDisplayComparator getInstance() {
304 			return instance;
305 		}
306 	}
307 }