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 }