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.lo.service.impl;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  import javax.jws.WebService;
22  
23  import org.kuali.student.common.validator.Validator;
24  import org.kuali.student.common.validator.ValidatorFactory;
25  import org.kuali.student.core.dictionary.dto.ObjectStructureDefinition;
26  import org.kuali.student.core.dictionary.service.DictionaryService;
27  import org.kuali.student.core.dto.StatusInfo;
28  import org.kuali.student.core.exceptions.AlreadyExistsException;
29  import org.kuali.student.core.exceptions.DataValidationErrorException;
30  import org.kuali.student.core.exceptions.DependentObjectsExistException;
31  import org.kuali.student.core.exceptions.DoesNotExistException;
32  import org.kuali.student.core.exceptions.InvalidParameterException;
33  import org.kuali.student.core.exceptions.MissingParameterException;
34  import org.kuali.student.core.exceptions.OperationFailedException;
35  import org.kuali.student.core.exceptions.PermissionDeniedException;
36  import org.kuali.student.core.exceptions.UnsupportedActionException;
37  import org.kuali.student.core.exceptions.VersionMismatchException;
38  import org.kuali.student.core.search.dto.SearchCriteriaTypeInfo;
39  import org.kuali.student.core.search.dto.SearchParam;
40  import org.kuali.student.core.search.dto.SearchRequest;
41  import org.kuali.student.core.search.dto.SearchResult;
42  import org.kuali.student.core.search.dto.SearchResultCell;
43  import org.kuali.student.core.search.dto.SearchResultRow;
44  import org.kuali.student.core.search.dto.SearchResultTypeInfo;
45  import org.kuali.student.core.search.dto.SearchTypeInfo;
46  import org.kuali.student.core.search.service.SearchManager;
47  import org.kuali.student.core.validation.dto.ValidationResultInfo;
48  import org.kuali.student.lum.lo.dao.LoDao;
49  import org.kuali.student.lum.lo.dto.LoCategoryInfo;
50  import org.kuali.student.lum.lo.dto.LoCategoryTypeInfo;
51  import org.kuali.student.lum.lo.dto.LoInfo;
52  import org.kuali.student.lum.lo.dto.LoLoRelationInfo;
53  import org.kuali.student.lum.lo.dto.LoLoRelationTypeInfo;
54  import org.kuali.student.lum.lo.dto.LoRepositoryInfo;
55  import org.kuali.student.lum.lo.dto.LoTypeInfo;
56  import org.kuali.student.lum.lo.entity.Lo;
57  import org.kuali.student.lum.lo.entity.LoCategory;
58  import org.kuali.student.lum.lo.entity.LoCategoryType;
59  import org.kuali.student.lum.lo.entity.LoLoRelation;
60  import org.kuali.student.lum.lo.entity.LoLoRelationType;
61  import org.kuali.student.lum.lo.entity.LoRepository;
62  import org.kuali.student.lum.lo.entity.LoType;
63  import org.kuali.student.lum.lo.service.LearningObjectiveService;
64  import org.springframework.transaction.annotation.Transactional;
65  
66  /*n
67   * @author Kuali Student team
68   *
69   */
70  @WebService(endpointInterface = "org.kuali.student.lum.lo.service.LearningObjectiveService", serviceName = "LearningObjectiveService", portName = "LearningObjectiveService", targetNamespace = "http://student.kuali.org/wsdl/lo")
71  @Transactional(readOnly=true,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
72  public class LearningObjectiveServiceImpl implements LearningObjectiveService {
73      private LoDao loDao;
74  	private SearchManager searchManager;
75      private DictionaryService dictionaryServiceDelegate;
76  	private ValidatorFactory validatorFactory;
77  
78  	public LoDao getLoDao() {
79          return loDao;
80      }
81  
82      public void setLoDao(LoDao dao) {
83          this.loDao = dao;
84      }
85  
86  	public void setSearchManager(SearchManager searchManager) {
87  		this.searchManager = searchManager;
88  	}
89  
90      public void setDictionaryServiceDelegate(DictionaryService dictionaryServiceDelegate) {
91          this.dictionaryServiceDelegate = dictionaryServiceDelegate;
92      }
93  
94  	public ValidatorFactory getValidatorFactory() {
95          return validatorFactory;
96      }
97  
98      public void setValidatorFactory(ValidatorFactory validatorFactory) {
99          this.validatorFactory = validatorFactory;
100     }
101 
102     /*
103      * (non-Javadoc)
104      * @see org.kuali.student.lum.lo.service.LearningObjectiveService#getLoRepositories()
105      */
106 	@Override
107 	public List<LoRepositoryInfo> getLoRepositories()
108 			throws OperationFailedException {
109 	    List<LoRepository> repositories = loDao.find(LoRepository.class);
110 		return LearningObjectiveServiceAssembler.toLoRepositoryInfos(repositories);
111 	}
112 	
113 	/*
114 	 * (non-Javadoc)
115 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#getLoRepository(java.lang.String)
116 	 */
117 	@Override
118 	public LoRepositoryInfo getLoRepository(String loRepositoryKey)
119 			throws DoesNotExistException, InvalidParameterException,
120 			MissingParameterException, OperationFailedException {
121 	    checkForMissingParameter(loRepositoryKey, "loRepositoryKey");
122 		return LearningObjectiveServiceAssembler.toLoRepositoryInfo(loDao.fetch(LoRepository.class, loRepositoryKey));
123 	}
124 
125 	/*
126 	 * (non-Javadoc)
127 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#getLoTypes()
128 	 */
129 	@Override
130 	public List<LoTypeInfo> getLoTypes() throws OperationFailedException {
131 	    List<LoType> find = loDao.find(LoType.class);
132 		return LearningObjectiveServiceAssembler.toLoTypeInfos(find);
133 	}
134 
135 	/*
136 	 * (non-Javadoc)
137 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#getLoType(java.lang.String)
138 	 */
139 	@Override
140 	public LoTypeInfo getLoType(String loTypeKey) throws DoesNotExistException,
141 			InvalidParameterException, MissingParameterException,
142 			OperationFailedException {
143 	    checkForMissingParameter(loTypeKey, "loTypeKey");
144 	    LoType fetch = loDao.fetch(LoType.class, loTypeKey);
145 		return LearningObjectiveServiceAssembler.toLoTypeInfo(fetch);
146 	}
147 
148 	/*
149 	 * (non-Javadoc)
150 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#getLoLoRelationTypes()
151 	 */
152 	@Override
153 	public List<LoLoRelationTypeInfo> getLoLoRelationTypes()
154 			throws OperationFailedException {
155 	    List<LoLoRelationType> fetch = loDao.find(LoLoRelationType.class);
156 		return LearningObjectiveServiceAssembler.toLoLoRelationTypeInfos(fetch);
157 	}
158 
159 	/*
160 	 * (non-Javadoc)
161 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#getLoLoRelationType(java.lang.String)
162 	 */
163 	@Override
164 	public LoLoRelationTypeInfo getLoLoRelationType(String loLoRelationTypeKey)
165 			throws OperationFailedException, MissingParameterException, DoesNotExistException {
166 	    checkForMissingParameter(loLoRelationTypeKey, "loLoRelationTypeKey");
167 		return LearningObjectiveServiceAssembler.toLoLoRelationTypeInfo(loDao.fetch(LoLoRelationType.class, loLoRelationTypeKey));
168 	}
169 
170 	@Override
171 	public List<String> getAllowedLoLoRelationTypesForLoType(String loTypeKey, String relatedLoTypeKey)
172 			throws DoesNotExistException, InvalidParameterException,
173 					MissingParameterException, OperationFailedException {
174 	    checkForMissingParameter(loTypeKey, "loTypeKey");
175 	    checkForMissingParameter(relatedLoTypeKey, "relatedLoTypeKey");
176 	    
177 	    return loDao.getAllowedLoLoRelationTypesForLoType(loTypeKey, relatedLoTypeKey);
178 	}
179 
180     /*
181 	 * (non-Javadoc)
182 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#addLoCategoryToLo(java.lang.String, java.lang.String)
183 	 */
184 	@Override
185 	@Transactional(readOnly=false)
186 	public StatusInfo addLoCategoryToLo(String loCategoryId, String loId)
187 			throws AlreadyExistsException, DoesNotExistException,
188 			InvalidParameterException, MissingParameterException,
189 			OperationFailedException, PermissionDeniedException,
190 			UnsupportedActionException {
191 	    checkForMissingParameter(loCategoryId, "loCategoryId");
192 	    checkForMissingParameter(loId, "loId");
193         StatusInfo statusInfo = new StatusInfo();
194         statusInfo.setSuccess(loDao.addLoCategoryToLo(loCategoryId, loId));
195         return statusInfo;
196 	}
197 
198 	/* (non-Javadoc)
199 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#createLo(java.lang.String, java.lang.String, org.kuali.student.lum.lo.dto.LoInfo)
200 	 */
201 	@Override
202 	@Transactional(readOnly=false)
203 	public LoInfo createLo(String repositoryId, String loType, LoInfo loInfo)
204 			throws DataValidationErrorException, DoesNotExistException,
205 			InvalidParameterException, MissingParameterException,
206 			OperationFailedException, PermissionDeniedException {
207 	    checkForMissingParameter(repositoryId, "repositoryId");
208 	    checkForMissingParameter(loType, "loType");
209 	    checkForMissingParameter(loInfo, "loInfo");
210 	    
211 	    
212 		// Validate LO
213 		List<ValidationResultInfo> val = validateLo("SYSTEM", loInfo);
214 		if(null != val && val.size() > 0) {
215 			for (ValidationResultInfo result : val) {
216 				System.err.println("Validation error. Element: " + result.getElement() + ",  Value: " + result.getMessage());
217 			}
218 			throw new DataValidationErrorException("Validation error!");
219 		}
220 		
221 	    // make sure LoType and LoRepository exist before trying to create
222 	    // if checkForMissingParameter above did its job, we don't have to null-check these id's
223 	    LoType type = null;
224 	    LoRepository repository = null;
225 	    try {
226 		    type = loDao.fetch(LoType.class, loType);
227 		    repository = loDao.fetch(LoRepository.class, repositoryId); 
228 	    } catch (DoesNotExistException dnee) {
229 	    	throw new DoesNotExistException("Specified " + (null == type ? "LoType" : "LoRepository") + " does not exist", dnee);
230 	    }
231 	    
232 	    loInfo.setLoRepositoryKey(repositoryId);
233 	    loInfo.setType(loType);
234 	    
235 	    Lo lo = null;
236 	    try {
237 		    lo = LearningObjectiveServiceAssembler.toLo(false, loInfo, loDao);
238 	    } catch (VersionMismatchException vme) {
239 	    	// should never happen in a create call, but
240 	    	throw new OperationFailedException("VersionMismatchException caught during Learning Objective creation");
241 	    }
242 	    lo.setLoType(type);
243 	    lo.setLoRepository(repository);
244 	    loDao.create(lo);
245 	    
246 		return LearningObjectiveServiceAssembler.toLoInfo(lo);
247 	}
248 
249 	/* (non-Javadoc)
250 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#deleteLo(java.lang.String)
251 	 */
252 	@Override
253 	@Transactional(readOnly=false)
254 	public StatusInfo deleteLo(String loId)
255 			throws DependentObjectsExistException, DoesNotExistException,
256 			InvalidParameterException, MissingParameterException,
257 			OperationFailedException, PermissionDeniedException {
258 	    checkForMissingParameter(loId, "loId");
259 	    
260 	    StatusInfo returnStatus = new StatusInfo();
261 	    returnStatus.setSuccess(loDao.deleteLo(loId));
262 		return returnStatus;
263 	}
264 
265 	/* (non-Javadoc)
266 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#deleteLoCategory(java.lang.String)
267 	 */
268 	@Override
269 	@Transactional(readOnly=false)
270 	public StatusInfo deleteLoCategory(String loCategoryId)
271 			throws DependentObjectsExistException, DoesNotExistException,
272 			InvalidParameterException, MissingParameterException,
273 			OperationFailedException, PermissionDeniedException {
274 	    checkForMissingParameter(loCategoryId, "loCategoryId");
275 	    
276 	    loDao.deleteLoCategory(loCategoryId);
277 	    
278 		return new StatusInfo();
279 	}
280 
281 	/* (non-Javadoc)
282 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#getLo(java.lang.String)
283 	 */
284 	@Override
285 	public LoInfo getLo(String loId) throws DoesNotExistException,
286 			InvalidParameterException, MissingParameterException,
287 			OperationFailedException {
288 	    checkForMissingParameter(loId, "loId");
289 	    
290 		return LearningObjectiveServiceAssembler.toLoInfo(loDao.fetch(Lo.class, loId));
291 	}
292 
293 	/* (non-Javadoc)
294 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#getLoByIdList(java.util.List)
295 	 */
296 	@Override
297 	public List<LoInfo> getLoByIdList(List<String> loIds)
298 			throws InvalidParameterException, MissingParameterException,
299 			OperationFailedException {
300 	    checkForMissingParameter(loIds, "loId");
301 	    checkForEmptyList(loIds, "loId");
302 	    List<Lo> los = loDao.getLoByIdList(loIds);
303 		return LearningObjectiveServiceAssembler.toLoInfos(los);
304 	}
305 
306 	/* (non-Javadoc)
307 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#getLoCategories(java.lang.String)
308 	 */
309 	@Override
310 	public List<LoCategoryInfo> getLoCategories(String loRepositoryKey)
311 			throws DoesNotExistException, InvalidParameterException,
312 			MissingParameterException, OperationFailedException {
313 	    checkForMissingParameter(loRepositoryKey, "loRepositoryKey");
314 	    List<LoCategory> categories = loDao.getLoCategories(loRepositoryKey);
315         return LearningObjectiveServiceAssembler.toLoCategoryInfos(categories);
316 	}
317 
318 	/* (non-Javadoc)
319 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#getLoCategoriesForLo(java.lang.String)
320 	 */
321 	@Override
322 	public List<LoCategoryInfo> getLoCategoriesForLo(String loId)
323 			throws DoesNotExistException, InvalidParameterException,
324 			MissingParameterException, OperationFailedException {
325 	    checkForMissingParameter(loId, "loId");
326 	    List<LoCategory> categories = loDao.getLoCategoriesForLo(loId);
327 		return LearningObjectiveServiceAssembler.toLoCategoryInfos(categories);
328 	}
329 
330 	/* (non-Javadoc)
331 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#getLoCategory(java.lang.String)
332 	 */
333 	@Override
334 	public LoCategoryInfo getLoCategory(String loCategoryId)
335 			throws DoesNotExistException, InvalidParameterException,
336 			MissingParameterException, OperationFailedException {
337 	    checkForMissingParameter(loCategoryId, "loCategoryId");
338 	    
339 		return LearningObjectiveServiceAssembler.toLoCategoryInfo(loDao.fetch(LoCategory.class, loCategoryId));
340 	}
341 
342 	/* (non-Javadoc)
343 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#getLoParents(java.lang.String)
344 	public List<LoInfo> getLoParents(String loId) throws DoesNotExistException,
345 			InvalidParameterException, MissingParameterException,
346 			OperationFailedException {
347 	    checkForMissingParameter(loId, "loId");
348 	    List<Lo> loParents = loDao.getLoParents(loId);
349 		return LearningObjectiveServiceAssembler.toLoInfos(loParents);
350 	}
351 	 */
352 
353 	/* (non-Javadoc)
354 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#getLosByLoCategory(java.lang.String)
355 	 */
356 	@Override
357 	public List<LoInfo> getLosByLoCategory(String loCategoryId)
358 			throws DoesNotExistException, InvalidParameterException,
359 			MissingParameterException, OperationFailedException {
360 	    checkForMissingParameter(loCategoryId, "loCategoryId");
361 	    List<Lo> los = loDao.getLosByLoCategory(loCategoryId);
362 		return LearningObjectiveServiceAssembler.toLoInfos(los);
363 	}
364 
365 	/* (non-Javadoc)
366 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#isDescendant(java.lang.String, java.lang.String)
367 	@Override
368 	public Boolean isDescendant(String loId, String descendantLoId)
369 			throws DoesNotExistException, InvalidParameterException,
370 			MissingParameterException, OperationFailedException {
371 	    checkForMissingParameter(loId, "loId");
372 	    checkForMissingParameter(descendantLoId, "descendantLoId");
373 		return loDao.isDescendant(loId, descendantLoId);
374 	}
375 	 */
376 
377 	/* (non-Javadoc)
378 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#isEquivalent(java.lang.String, java.lang.String)
379 	@Override
380 	public Boolean isEquivalent(String loId, String equivalentLoId)
381 			throws DoesNotExistException, InvalidParameterException,
382 			MissingParameterException, OperationFailedException {
383 	    checkForMissingParameter(loId, "loId");
384 	    checkForMissingParameter(equivalentLoId, "equivalentLoId");
385 		return loDao.isEquivalent(loId, equivalentLoId);
386 	}
387 	 */
388 
389 	/* (non-Javadoc)
390 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#removeChildLoFromLo(java.lang.String, java.lang.String)
391 	@Override
392 	@Transactional(readOnly=false)
393 	public StatusInfo removeChildLoFromLo(String loId, String parentLoId)
394 			throws DependentObjectsExistException, DoesNotExistException,
395 			InvalidParameterException, MissingParameterException,
396 			OperationFailedException, PermissionDeniedException {
397 	    checkForMissingParameter(loId, "loId");
398 	    checkForMissingParameter(parentLoId, "parentLoId");
399 	    
400 	    StatusInfo statusInfo = new StatusInfo();
401 	    statusInfo.setSuccess(loDao.removeChildLoFromLo(loId, parentLoId));
402 		return statusInfo;
403 	}
404 	 */
405 
406 	/* (non-Javadoc)
407 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#removeEquivalentLoFromLo(java.lang.String, java.lang.String)
408 	@Override
409 	@Transactional(readOnly=false)
410 	public StatusInfo removeEquivalentLoFromLo(String loId,
411 			String equivalentLoId) throws DoesNotExistException,
412 			InvalidParameterException, MissingParameterException,
413 			OperationFailedException, PermissionDeniedException {
414 	    checkForMissingParameter(loId, "loId");
415 	    checkForMissingParameter(equivalentLoId, "equivalentLoId");
416 	    
417         StatusInfo statusInfo = new StatusInfo();
418         statusInfo.setSuccess(loDao.removeEquivalentLoFromLo(loId, equivalentLoId));
419         return statusInfo;
420 	}
421 	 */
422 
423 	/* (non-Javadoc)
424 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#removeLoCategoryFromLo(java.lang.String, java.lang.String)
425 	 */
426 	@Override
427 	@Transactional(readOnly=false)
428 	public StatusInfo removeLoCategoryFromLo(String loCategoryId, String loId)
429 			throws DoesNotExistException, InvalidParameterException,
430 			MissingParameterException, OperationFailedException,
431 			PermissionDeniedException, UnsupportedActionException {
432 	    checkForMissingParameter(loCategoryId, "loCategoryId");
433 	    checkForMissingParameter(loId, "loId");
434 	    
435         StatusInfo statusInfo = new StatusInfo();
436         statusInfo.setSuccess(loDao.removeLoCategoryFromLo(loCategoryId, loId));
437         return statusInfo;
438 	}
439 
440 	/* (non-Javadoc)
441 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#updateLo(java.lang.String, org.kuali.student.lum.lo.dto.LoInfo)
442 	 */
443 	@Override
444 	@Transactional(readOnly=false)
445 	public LoInfo updateLo(String loId, LoInfo loInfo)
446 			throws DataValidationErrorException, DoesNotExistException,
447 			InvalidParameterException, MissingParameterException,
448 			OperationFailedException, PermissionDeniedException,
449 			VersionMismatchException {
450 	    checkForMissingParameter(loId, "loId");
451 	    checkForMissingParameter(loInfo, "loInfo");
452 
453 		// Validate LO
454 		List<ValidationResultInfo> val = validateLo("SYSTEM", loInfo);
455 		if(null != val && val.size() > 0) {
456 			for (ValidationResultInfo result : val) {
457 				System.err.println("Validation error. Element: " + result.getElement() + ",  Value: " + result.getMessage());
458 			}
459 			throw new DataValidationErrorException("Validation error!");
460 		}
461 		
462 	    Lo lo = loDao.fetch(Lo.class, loId);
463         
464         if (!String.valueOf(lo.getVersionNumber()).equals(loInfo.getMetaInfo().getVersionInd())){
465             throw new VersionMismatchException("LO to be updated is not the current version");
466         }
467         
468         lo = LearningObjectiveServiceAssembler.toLo(true, lo, loInfo, loDao);
469         loDao.update(lo);
470         return LearningObjectiveServiceAssembler.toLoInfo(lo);
471 	}
472 
473 	/* (non-Javadoc)
474 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#updateLoCategory(java.lang.String, org.kuali.student.lum.lo.dto.LoCategoryInfo)
475 	 */
476 	@Override
477 	@Transactional(readOnly=false)
478 	public LoCategoryInfo updateLoCategory(String loCategoryId,
479 			LoCategoryInfo loCategoryInfo) throws DataValidationErrorException,
480 			DoesNotExistException, InvalidParameterException,
481 			MissingParameterException, OperationFailedException,
482 			PermissionDeniedException, VersionMismatchException {
483 	    checkForMissingParameter(loCategoryId, "loCategoryId");
484 	    checkForMissingParameter(loCategoryInfo, "loCategoryInfo");
485 	    
486 		// Validate LoCategory
487 		List<ValidationResultInfo> val = validateLoCategory("SYSTEM", loCategoryInfo);
488 
489 		//kslum-136 - don't allow dups w/ same name (case insensitive), type, state & repository
490         if (doesLoCategoryExist(loCategoryInfo.getLoRepository(), loCategoryInfo, loCategoryId)) {
491             ValidationResultInfo vr = new ValidationResultInfo();
492             vr.setElement("LO Category Name");
493             vr.setError("LO Category already exists");
494             val.add(vr);
495         }
496         if(null != val && val.size() > 0) {
497             for (ValidationResultInfo result : val) {
498                 System.err.println("Validation error. Element: " + result.getElement() + ",  Value: " + result.getMessage());
499             }
500             throw new DataValidationErrorException("Validation error!", val);
501         }
502 	    LoCategory loCategory = loDao.fetch(LoCategory.class, loCategoryId);
503         
504         if (!String.valueOf(loCategory.getVersionNumber()).equals(loCategoryInfo.getMetaInfo().getVersionInd())){
505             throw new VersionMismatchException("LoCategory to be updated is not the current version");
506         }
507         
508         // if state is changing from "active"
509         if (loCategory.getState().equals("active") && ( ! loCategoryInfo.getState().equals("active") )) {
510     		// N.B. - ability to 'retire' LoCategory's that are still associated w/ active
511     		// LO's is configured and enforced on the client
512         	List<LoInfo> loInfos = getLosByLoCategory(loCategoryId);
513     		if (null != loInfos) {
514 				// remove associations of this LoCategory from active LO's
515     			for (LoInfo info : loInfos) {
516     				if (info.getState().equals("active"))  {
517 	    				try {
518 							removeLoCategoryFromLo(loCategoryId, info.getId());
519 						} catch (UnsupportedActionException uaee) {
520 				    		throw new OperationFailedException("Unable to update LoCategory: could not remove association with active LearningObjective", uaee);
521 						}
522     				}
523     			}
524     		}
525         }
526         	
527         // if type is changing
528         if ( ! loCategory.getLoCategoryType().getId().equals(loCategoryInfo.getType()) ) {
529         	loCategory = cloneLoCategory(loCategory, loCategoryInfo);
530         } else {
531 	        loCategory = LearningObjectiveServiceAssembler.toLoCategory(loCategory, loCategoryInfo, loDao);
532 	        loDao.update(loCategory);
533         }
534         return LearningObjectiveServiceAssembler.toLoCategoryInfo(loCategory);
535 	}
536 
537     // inactivate current LoCategory & clone it w/ its relationships,
538 	// used when changing immutable type of LoCategory
539 	// https://test.kuali.org/confluence/display/KULSTG/DS+-+LO+Centrally+Maintain+Categories
540 	private LoCategory cloneLoCategory(LoCategory loCategory, LoCategoryInfo loCategoryInfo) throws DoesNotExistException, InvalidParameterException, OperationFailedException {
541     	LoCategoryType catType = null;
542     	
543     	try {
544         	catType = loDao.fetch(LoCategoryType.class, loCategoryInfo.getType());
545     	} catch (DoesNotExistException dnee) {
546     		throw new DoesNotExistException("Attempt to set LoCategory's type to nonexistent LoCategoryType", dnee);
547     	}
548         	
549     	// clone the existing LO
550     	LoCategoryInfo newLoCategoryInfo = LearningObjectiveServiceAssembler.toLoCategoryInfo(loCategory);
551     	newLoCategoryInfo.setType(catType.getId());
552     	newLoCategoryInfo.setName(loCategoryInfo.getName());
553     	LoCategory newLoCategory = loDao.create(LearningObjectiveServiceAssembler.toLoCategory(newLoCategoryInfo, loDao));
554         	
555     	// clone Lo-LoCategory relations
556     	List<Lo> catsLos = loDao.getLosByLoCategory(loCategory.getId());         	
557     	for (Lo lo : catsLos) {
558     		try {
559     			// create the new one
560 				loDao.addLoCategoryToLo(newLoCategory.getId(), lo.getId());
561 				// remove the old one
562 				loDao.removeLoCategoryFromLo(loCategory.getId(), lo.getId());
563 			} catch (UnsupportedActionException uae) {
564 				throw new OperationFailedException(uae.getMessage(), uae);
565 			}
566     	}
567         	
568     	// inactivate old LoCategory
569     	loCategory.setState("inactive");
570     	loDao.update(loCategory);
571         	
572     	return newLoCategory;
573 	}
574 
575 	/* (non-Javadoc)
576 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#validateLo(java.lang.String, org.kuali.student.lum.lo.dto.LoInfo)
577 	 */
578 	@Override
579 	public List<ValidationResultInfo> validateLo(String validationType,
580 			LoInfo loInfo) throws DoesNotExistException,
581 			InvalidParameterException, MissingParameterException,
582 			OperationFailedException {
583 	    checkForMissingParameter(validationType, "validationType");
584 	    checkForMissingParameter(loInfo, "loInfo");
585 	    
586 	    try{
587 	    	String loDesc = loInfo.getDesc().getPlain(); 
588 	    	checkForEmptyString(loDesc, "loInfo.Desc");
589 	    } catch (NullPointerException e){
590 			//do not checkForEmptyString
591 		}
592 	    
593 	    ObjectStructureDefinition objStructure = this.getObjectStructure(LoInfo.class.getName());
594 	    Validator validator = validatorFactory.getValidator();
595 	    return validator.validateObject(loInfo, objStructure);
596 	}
597 
598 	/* (non-Javadoc)
599 	 * @see org.kuali.student.lum.lo.service.LearningObjectiveService#validateLoCategory(java.lang.String, org.kuali.student.lum.lo.dto.LoCategoryInfo)
600 	 */
601 	@Override
602 	public List<ValidationResultInfo> validateLoCategory(String validationType,
603 			LoCategoryInfo loCategoryInfo) throws DoesNotExistException,
604 			InvalidParameterException, MissingParameterException,
605 			OperationFailedException {
606 	    checkForMissingParameter(validationType, "validationType");
607 	    checkForMissingParameter(loCategoryInfo, "loCategoryInfo");
608 	    
609 	    try{
610 	    	String catDesc = loCategoryInfo.getDesc().getPlain(); 
611 	    	checkForEmptyString(catDesc, "loCategoryInfo.Desc");
612 	    } catch (NullPointerException e){
613 			//do not checkForEmptyString
614 		}
615 
616         ObjectStructureDefinition objStructure = this.getObjectStructure(LoCategoryInfo.class.getName());
617         Validator validator = validatorFactory.getValidator();
618         return validator.validateObject(loCategoryInfo, objStructure);
619 
620 	}
621 
622 	@Override
623 	public List<ValidationResultInfo> validateLoLoRelation(
624 			String validationType, LoLoRelationInfo loLoRelationInfo)
625 			throws DoesNotExistException, InvalidParameterException,
626 			MissingParameterException, OperationFailedException {
627 
628         ObjectStructureDefinition objStructure = this.getObjectStructure(LoLoRelationInfo.class.getName());
629         Validator validator = validatorFactory.getValidator();
630         return validator.validateObject(loLoRelationInfo, objStructure);
631 	}
632 
633     /**
634      * Check for missing parameter and throw localized exception if missing
635      *
636      * @param param
637      * @param parameter name
638      * @throws MissingParameterException
639      */
640     private void checkForMissingParameter(Object param, String paramName)
641             throws MissingParameterException {
642         if (param == null) {
643             throw new MissingParameterException(paramName + " can not be null");
644         }
645     }
646 
647     /**
648      * @param param
649      * @param paramName
650      * @throws MissingParameterException
651      */
652     private void checkForEmptyList(Object param, String paramName)
653             throws MissingParameterException {
654         if (param != null && param instanceof List && ((List<?>)param).size() == 0) {
655             throw new MissingParameterException(paramName + " can not be an empty list");
656         }
657     }
658 
659     /**
660      * @param param
661      * @param paramName
662      * @throws MissingParameterException
663      */
664     private void checkForEmptyString(String param, String paramName)
665             throws MissingParameterException {
666         if (param != null && "".equals(param.trim())) {
667             throw new MissingParameterException(paramName + " can not be empty");
668         }
669     }
670     
671     /**
672      * @param loRepositoryKey
673      * @param loCategoryInfo
674      * @param loCategoryId
675      * @throws MissingParameterException,OperationFailedException
676      */
677     private boolean doesLoCategoryExist(String loRepositoryKey, LoCategoryInfo loCategoryInfo, String loCategoryId)
678             throws MissingParameterException, DataValidationErrorException {
679 
680         boolean exists = false;
681 	    SearchRequest request = new SearchRequest();
682 	    request.setSearchKey("lo.search.loCategoriesByNameRepoTypeState");
683 	    
684  		List<SearchParam> searchParams = new ArrayList<SearchParam>();
685 		SearchParam qpv1 = new SearchParam();
686 		qpv1.setKey("lo.queryParam.loCategoryName");
687 		qpv1.setValue(loCategoryInfo.getName().toLowerCase());
688 		searchParams.add(qpv1);
689 		SearchParam qpv2 = new SearchParam();
690 		qpv2.setKey("lo.queryParam.loCategoryRepo");
691 		qpv2.setValue(loRepositoryKey);
692 		searchParams.add(qpv2);
693 		SearchParam qpv3 = new SearchParam();
694 		qpv3.setKey("lo.queryParam.loCategoryType");
695 		qpv3.setValue(loCategoryInfo.getType());
696 		searchParams.add(qpv3);
697 		SearchParam qpv4 = new SearchParam();
698 		qpv4.setKey("lo.queryParam.loCategoryState");
699 		qpv4.setValue(loCategoryInfo.getState());
700 		searchParams.add(qpv4);
701 		
702 		request.setParams(searchParams);
703 		
704 		SearchResult result = search(request);
705 		
706 		if(loCategoryId != null && !loCategoryId.trim().equals("")){
707 			if (result.getRows().size() > 0) {
708 				for(SearchResultRow srrow : result.getRows()){
709 					List<SearchResultCell> srCells = srrow.getCells();
710 					if(srCells != null && srCells.size() > 0){
711 						for(SearchResultCell srcell : srCells){
712 							if(!srcell.getValue().equals(loCategoryId)) {
713                                 exists = true;
714                             }
715 						}
716 					}
717 				}
718 			}
719 		}
720 		else{
721 			if (result.getRows().size() > 0) {
722                 exists = true;
723 			}
724 		}
725         return exists;
726     }
727     
728     @Override
729     public ObjectStructureDefinition getObjectStructure(String objectTypeKey) {
730         return dictionaryServiceDelegate.getObjectStructure(objectTypeKey);
731     }
732 
733     @Override
734     public List<String> getObjectTypes() {
735         return dictionaryServiceDelegate.getObjectTypes();
736     }
737 
738 	/* (non-Javadoc)
739 	 * @see org.kuali.student.core.search.service.SearchService#getSearchCriteriaType(java.lang.String)
740 	 */
741     @Override
742     public SearchCriteriaTypeInfo getSearchCriteriaType(
743             String searchCriteriaTypeKey) throws DoesNotExistException,
744             InvalidParameterException, MissingParameterException,
745             OperationFailedException {
746 
747         return searchManager.getSearchCriteriaType(searchCriteriaTypeKey);
748     }
749 
750 	/* (non-Javadoc)
751 	 * @see org.kuali.student.core.search.service.SearchService#getSearchCriteriaTypes()
752 	 */
753     @Override
754     public List<SearchCriteriaTypeInfo> getSearchCriteriaTypes()
755     throws OperationFailedException {
756         return searchManager.getSearchCriteriaTypes();
757     }
758 
759 	/* (non-Javadoc)
760 	 * @see org.kuali.student.core.search.service.SearchService#getSearchResultType(java.lang.String)
761 	 */
762     @Override
763     public SearchResultTypeInfo getSearchResultType(String searchResultTypeKey)
764     throws DoesNotExistException, InvalidParameterException,
765     MissingParameterException, OperationFailedException {
766         checkForMissingParameter(searchResultTypeKey, "searchResultTypeKey");
767         return searchManager.getSearchResultType(searchResultTypeKey);
768     }
769 
770 	/* (non-Javadoc)
771 	 * @see org.kuali.student.core.search.service.SearchService#getSearchResultTypes()
772 	 */
773     @Override
774     public List<SearchResultTypeInfo> getSearchResultTypes()
775     throws OperationFailedException {
776         return searchManager.getSearchResultTypes();
777     }
778 
779 	/* (non-Javadoc)
780 	 * @see org.kuali.student.core.search.service.SearchService#getSearchType(java.lang.String)
781 	 */
782     @Override
783     public SearchTypeInfo getSearchType(String searchTypeKey)
784     throws DoesNotExistException, InvalidParameterException,
785     MissingParameterException, OperationFailedException {
786         checkForMissingParameter(searchTypeKey, "searchTypeKey");
787         return searchManager.getSearchType(searchTypeKey);
788     }
789 
790 	/* (non-Javadoc)
791 	 * @see org.kuali.student.core.search.service.SearchService#getSearchTypes()
792 	 */
793     @Override
794     public List<SearchTypeInfo> getSearchTypes()
795     throws OperationFailedException {
796         return searchManager.getSearchTypes();
797     }
798 
799 	/* (non-Javadoc)
800 	 * @see org.kuali.student.core.search.service.SearchService#getSearchTypesByCriteria(java.lang.String)
801 	 */
802     @Override
803     public List<SearchTypeInfo> getSearchTypesByCriteria(
804             String searchCriteriaTypeKey) throws DoesNotExistException,
805             InvalidParameterException, MissingParameterException,
806             OperationFailedException {
807         checkForMissingParameter(searchCriteriaTypeKey, "searchCriteriaTypeKey");
808         return searchManager.getSearchTypesByCriteria(searchCriteriaTypeKey);
809     }
810 
811 	/* (non-Javadoc)
812 	 * @see org.kuali.student.core.search.service.SearchService#getSearchTypesByResult(java.lang.String)
813 	 */
814     @Override
815     public List<SearchTypeInfo> getSearchTypesByResult(
816             String searchResultTypeKey) throws DoesNotExistException,
817             InvalidParameterException, MissingParameterException,
818             OperationFailedException {
819         checkForMissingParameter(searchResultTypeKey, "searchResultTypeKey");
820         return searchManager.getSearchTypesByResult(searchResultTypeKey);
821     }
822 
823 	@Override
824 	@Transactional(readOnly=false)
825 	public LoLoRelationInfo createLoLoRelation(String loId, String relatedLoId,
826 			String loLoRelationType, LoLoRelationInfo loLoRelationInfo)
827 			throws AlreadyExistsException, 
828 			DataValidationErrorException, DoesNotExistException,
829 			InvalidParameterException, MissingParameterException,
830 			OperationFailedException, PermissionDeniedException {
831 	    checkForMissingParameter(loId, "loId");
832 	    checkForMissingParameter(relatedLoId, "relatedLoId");
833 	    checkForMissingParameter(loLoRelationType, "loLoRelationType");
834 	    checkForMissingParameter(loLoRelationInfo, "loLoRelationInfo");
835 	    
836 		// Validate LoLoRelation
837 		List<ValidationResultInfo> val = validateLoLoRelation("SYSTEM", loLoRelationInfo);
838 		if(null != val && val.size() > 0) {
839 			for (ValidationResultInfo result : val) {
840 				System.err.println("Validation error. Element: " + result.getElement() + ",  Value: " + result.getMessage());
841 			}
842 			throw new DataValidationErrorException("Validation error!");
843 		}
844 	    
845 	    if (null == loLoRelationInfo.getState()) {
846 	    	loLoRelationInfo.setState("draft"); // TODO - enum of allowed states? retrieve allowed states from dictionary?
847 	    }
848 	    Lo lo = loDao.fetch(Lo.class, loId);
849 	    Lo relatedLo = loDao.fetch(Lo.class, relatedLoId);
850 	    LoLoRelationType type = loDao.fetch(LoLoRelationType.class, loLoRelationType);
851 	    loLoRelationInfo.setLoId(loId);
852 	    loLoRelationInfo.setRelatedLoId(relatedLoId);
853 	    loLoRelationInfo.setType(loLoRelationType);
854 	    
855 	    LoLoRelation relation = null;
856 	    try {
857 		    relation = LearningObjectiveServiceAssembler.toLoLoRelation(false, loLoRelationInfo, loDao);
858 	    } catch (VersionMismatchException vme) {
859 	    	// should never happen in a create call, but
860 	    	throw new OperationFailedException("VersionMismatchException caught during LoLoRelation creation");
861 	    }
862 	    relation.setLo(lo);
863 	    relation.setRelatedLo(relatedLo);
864 	    relation.setLoLoRelationType(type);
865 	    
866 	    relation = loDao.create(relation);
867 	    
868 		return LearningObjectiveServiceAssembler.toLoLoRelationInfo(relation);
869 	}
870 
871 	@Override
872 	@Transactional(readOnly=false)
873 	public StatusInfo deleteLoLoRelation(String loLoRelationId)
874 			throws DoesNotExistException, InvalidParameterException,
875 			MissingParameterException, OperationFailedException,
876 			PermissionDeniedException {
877 	    checkForMissingParameter(loLoRelationId, "loLoRelationId");
878 	    
879 	    loDao.deleteLoLoRelation(loLoRelationId);
880 	    
881 		return new StatusInfo();
882 	}
883 
884 	@Override
885 	public LoLoRelationInfo getLoLoRelation(String loLoRelationId)
886 			throws DoesNotExistException, InvalidParameterException,
887 			MissingParameterException, OperationFailedException {
888 	    checkForMissingParameter(loLoRelationId, "loLoRelationId");
889 		return LearningObjectiveServiceAssembler.toLoLoRelationInfo(loDao.fetch(LoLoRelation.class, loLoRelationId));
890 	}
891 
892 	@Override
893 	public List<LoLoRelationInfo> getLoLoRelationsByLoId(String loId)
894 			throws DoesNotExistException, InvalidParameterException,
895 			MissingParameterException, OperationFailedException {
896 		List<LoLoRelation> llRelations = loDao.getLoLoRelationsByLoId(loId);
897 		return LearningObjectiveServiceAssembler.toLoLoRelationInfos(llRelations);
898 	}
899 
900 	@Override
901 	public List<LoInfo> getLosByRelatedLoId(String relatedLoId,
902 			String loLoRelationType) throws DoesNotExistException,
903 			InvalidParameterException, MissingParameterException,
904 			OperationFailedException {
905 		List<Lo> relatedLos = loDao.getLosByRelatedLoId(relatedLoId, loLoRelationType);
906 		return LearningObjectiveServiceAssembler.toLoInfos(relatedLos);
907 	}
908 
909 	@Override
910 	public List<LoInfo> getRelatedLosByLoId(String loId, String loLoRelationTypeKey)
911 			throws DoesNotExistException, InvalidParameterException,
912 			MissingParameterException, OperationFailedException {
913 	    checkForMissingParameter(loId, "loId");
914 	    checkForMissingParameter(loLoRelationTypeKey, "loLoRelationTypeKey");
915 	    List<Lo> relatedLos = loDao.getRelatedLosByLoId(loId, loLoRelationTypeKey);
916 		return LearningObjectiveServiceAssembler.toLoInfos(relatedLos);
917 	}
918 
919 	@Override
920 	@Transactional(readOnly=false)
921 	public LoLoRelationInfo updateLoLoRelation(String loLoRelationId,
922 			LoLoRelationInfo loLoRelationInfo)
923 			throws DataValidationErrorException, DoesNotExistException,
924 			InvalidParameterException, MissingParameterException,
925 			OperationFailedException, PermissionDeniedException,
926 			VersionMismatchException {
927 		
928 		// TODO - this is never called; why does it exist?
929 		/*
930 		// Validate LoLoRelation
931 		List<ValidationResultInfo> val = validateLoLoRelation("SYSTEM", loLoRelationInfo);
932 		if(null != val && val.size() > 0) {
933 			throw new DataValidationErrorException("Validation error!");
934 		}
935 		*/
936 	    
937 		return null;
938 	}
939 
940 	@Override
941 	@Transactional(readOnly=false)
942 	public LoCategoryInfo createLoCategory(String loRepositoryKey,
943 			String loCategoryTypeKey, LoCategoryInfo loCategoryInfo)
944 			throws DataValidationErrorException, DoesNotExistException,
945 			InvalidParameterException, MissingParameterException,
946 			OperationFailedException, PermissionDeniedException {
947 	    checkForMissingParameter(loRepositoryKey, "loRepositoryKey");
948 	    checkForMissingParameter(loCategoryTypeKey, "loCategoryTypeKey");
949 	    checkForMissingParameter(loCategoryInfo, "loCategoryInfo");
950 	    
951 		// Validate LoCategory
952 		List<ValidationResultInfo> val = validateLoCategory("SYSTEM", loCategoryInfo);
953 
954         //kslum-136 - don't allow dups w/ same name (case insensitive), type, state & repository       
955         if (doesLoCategoryExist(loRepositoryKey, loCategoryInfo, null)) {
956             ValidationResultInfo vr = new ValidationResultInfo();
957             vr.setElement("LO Category Name");
958             vr.setError("LO Category already exists");
959             val.add(vr);
960         }
961         if(null != val && val.size() > 0) {
962 			for (ValidationResultInfo result : val) {
963 				System.err.println("Validation error. Element: " + result.getElement() + ",  Value: " + result.getMessage());
964 			}
965 			throw new DataValidationErrorException("Validation error!", val);
966 		}
967 
968 	    LoCategory category = LearningObjectiveServiceAssembler.toLoCategory(loCategoryInfo, loDao);
969 	    LoCategoryType loCatType = loDao.fetch(LoCategoryType.class, loCategoryTypeKey);
970 	    category.setLoCategoryType(loCatType);
971 	    LoRepository loRepository = loDao.fetch(LoRepository.class, loRepositoryKey);
972 	    category.setLoRepository(loRepository);
973 	    loDao.create(category);
974 		return LearningObjectiveServiceAssembler.toLoCategoryInfo(category);
975 	}
976 
977 	@Override
978 	public LoCategoryTypeInfo getLoCategoryType(String loCategoryTypeKey)
979 			throws DoesNotExistException, InvalidParameterException,
980 			MissingParameterException, OperationFailedException {
981 	    checkForMissingParameter(loCategoryTypeKey, "loCategoryTypeKey");
982 	    LoCategoryType loCatType = loDao.fetch(LoCategoryType.class, loCategoryTypeKey);
983 	    return LearningObjectiveServiceAssembler.toLoCategoryTypeInfo(loCatType);
984 	}
985 
986 	@Override
987 	public List<LoCategoryTypeInfo> getLoCategoryTypes()
988 			throws OperationFailedException {
989 		List<LoCategoryType> categoryTypes = loDao.find(LoCategoryType.class);
990 		return LearningObjectiveServiceAssembler.toLoCategoryTypeInfos(categoryTypes);
991 	}
992 
993 	@Override
994 	public List<LoInfo> getLosByRepository(String loRepositoryKey,
995 			String loTypeKey, String loStateKey)
996 			throws InvalidParameterException, MissingParameterException,
997 			OperationFailedException {
998 	    checkForMissingParameter(loRepositoryKey, "loRepositoryKey");
999 	    List<Lo> los = loDao.getLosByRepository(loRepositoryKey);
1000 		return LearningObjectiveServiceAssembler.toLoInfos(los);
1001 	}
1002 
1003 	@Override
1004 	public SearchResult search(SearchRequest searchRequest) throws MissingParameterException {
1005         checkForMissingParameter(searchRequest, "searchRequest");
1006         return searchManager.search(searchRequest, loDao);
1007 	}
1008 
1009 }