001 package org.kuali.student.lum.course.service.assembler;
002
003 import java.util.ArrayList;
004 import java.util.Collections;
005 import java.util.Comparator;
006 import java.util.HashMap;
007 import java.util.HashSet;
008 import java.util.List;
009 import java.util.Map;
010 import java.util.Set;
011 import java.util.Map.Entry;
012
013 import org.kuali.student.common.assembly.BOAssembler;
014 import org.kuali.student.common.assembly.BaseDTOAssemblyNode;
015 import org.kuali.student.common.assembly.BaseDTOAssemblyNode.NodeOperation;
016 import org.kuali.student.common.assembly.data.AssemblyException;
017 import org.kuali.student.common.dto.DtoConstants;
018 import org.kuali.student.common.exceptions.DoesNotExistException;
019 import org.kuali.student.common.exceptions.InvalidParameterException;
020 import org.kuali.student.common.exceptions.MissingParameterException;
021 import org.kuali.student.common.exceptions.OperationFailedException;
022 import org.kuali.student.common.util.UUIDHelper;
023 import org.kuali.student.lum.course.dto.LoDisplayInfo;
024 import org.kuali.student.lum.lo.dto.LoCategoryInfo;
025 import org.kuali.student.lum.lo.dto.LoInfo;
026 import org.kuali.student.lum.lo.dto.LoLoRelationInfo;
027 import org.kuali.student.lum.lo.service.LearningObjectiveService;
028
029
030 public class LoAssembler implements BOAssembler<LoDisplayInfo, LoInfo> {
031
032 private LearningObjectiveService loService;
033
034 @Override
035 public LoDisplayInfo assemble(LoInfo lo, LoDisplayInfo loDisplayInfo,
036 boolean shallowBuild) throws AssemblyException {
037
038 LoDisplayInfo loDisplay = (null != loDisplayInfo) ? loDisplayInfo : new LoDisplayInfo();
039
040 loDisplay.setLoInfo(lo);
041
042 if (!shallowBuild) {
043 String loId = lo.getId();
044 try {
045 List<LoCategoryInfo> loCategories = loService.getLoCategoriesForLo(loId);
046 loDisplay.setLoCategoryInfoList(loCategories);
047 } catch (DoesNotExistException e) {
048 } catch (Exception e) {
049 throw new AssemblyException("Error getting learning objective categories", e);
050 }
051 try {
052 List<LoInfo> childLos = loService.getRelatedLosByLoId(loId,CourseAssemblerConstants.COURSE_LO_RELATION_INCLUDES);
053 for(LoInfo childLo:childLos){
054 LoDisplayInfo childLoDisplay = assemble(childLo, null, shallowBuild);
055 childLoDisplay.setParentLoRelationid(lo.getId());
056 childLoDisplay.setParentRelType(CourseAssemblerConstants.COURSE_LO_RELATION_INCLUDES);
057 loDisplay.getLoDisplayInfoList().add(childLoDisplay);
058 }
059 if(loDisplay.getLoDisplayInfoList().size()>1){
060 Collections.sort(loDisplay.getLoDisplayInfoList(), LoDisplayComparator.getInstance());
061 }
062 } catch (DoesNotExistException e) {
063 } catch (Exception e) {
064 throw new AssemblyException("Error getting learning objective", e);
065 }
066
067 }
068 return loDisplay;
069 }
070
071 @Override
072 //Creation of categories is done in the LoCategoryRpcGwtServlet
073 public BaseDTOAssemblyNode<LoDisplayInfo, LoInfo> disassemble(
074 LoDisplayInfo loDisplay, NodeOperation operation)
075 throws AssemblyException {
076
077 BaseDTOAssemblyNode<LoDisplayInfo, LoInfo> result = new BaseDTOAssemblyNode<LoDisplayInfo, LoInfo>(this);
078
079 //see if this is a new LuInfo
080 if (loDisplay == null || loDisplay.getLoInfo() == null) {
081 throw new AssemblyException("Lo can not be null");
082 }
083 if (NodeOperation.CREATE != operation && null == loDisplay.getLoInfo().getId()) {
084 throw new AssemblyException("Lo id can not be null");
085 }
086
087 //set the id if it's not there already(important for creating relations)
088 loDisplay.getLoInfo().setId(UUIDHelper.genStringUUID(loDisplay.getLoInfo().getId()));
089
090 //Default these values
091 loDisplay.getLoInfo().setType(CourseAssemblerConstants.COURSE_LO_TYPE);
092 loDisplay.getLoInfo().setLoRepositoryKey(CourseAssemblerConstants.COURSE_LO_REPOSITORY_KEY);
093
094
095 //Populate the node
096 result.setBusinessDTORef(loDisplay);
097 result.setNodeData(loDisplay.getLoInfo());
098 result.setOperation(operation);
099
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 }