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  package org.kuali.student.r2.lum.lu.service.impl;
16  
17  import org.apache.log4j.Logger;
18  import org.kuali.rice.core.api.criteria.QueryByCriteria;
19  import org.kuali.student.r1.common.dictionary.dto.ObjectStructureDefinition;
20  import org.kuali.student.r1.common.dictionary.service.DictionaryService;
21  import org.kuali.student.r1.common.entity.Amount;
22  import org.kuali.student.r1.common.entity.TimeAmount;
23  import org.kuali.student.r1.common.entity.Version;
24  import org.kuali.student.r1.common.entity.VersionEntity;
25  import org.kuali.student.r2.common.dto.ContextInfo;
26  import org.kuali.student.r2.common.dto.DtoConstants;
27  import org.kuali.student.r2.common.dto.StatusInfo;
28  import org.kuali.student.r2.common.dto.ValidationResultInfo;
29  import org.kuali.student.r2.common.exceptions.*;
30  import org.kuali.student.r2.core.search.dto.*;
31  import org.kuali.student.r2.core.search.service.SearchManager;
32  import org.kuali.student.r2.core.search.service.SearchService;
33  import org.kuali.student.r2.common.validator.Validator;
34  import org.kuali.student.r2.common.validator.ValidatorFactory;
35  import org.kuali.student.r2.core.class1.type.dto.TypeInfo;
36  import org.kuali.student.r2.core.versionmanagement.dto.VersionDisplayInfo;
37  import org.kuali.student.r2.lum.clu.dto.*;
38  import org.kuali.student.r2.lum.clu.service.CluService;
39  import org.kuali.student.r2.lum.lu.dao.LuDao;
40  import org.kuali.student.r2.lum.lu.entity.*;
41  import org.kuali.student.r2.lum.util.constants.CluServiceConstants;
42  import org.springframework.beans.BeanUtils;
43  import org.springframework.transaction.annotation.Transactional;
44  
45  import javax.jws.WebParam;
46  import javax.jws.WebService;
47  import javax.persistence.NoResultException;
48  import java.util.*;
49  import java.util.Map.Entry;
50  
51  @WebService(endpointInterface = "org.kuali.student.r2.lum.clu.service.CluService", serviceName = "CluService", portName = "CluService", targetNamespace = "http://student.kuali.org/wsdl/clu")
52  @Transactional(readOnly=true,noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
53  public class CluServiceImpl implements CluService {
54  
55      private static final String SEARCH_KEY_DEPENDENCY_ANALYSIS = "lu.search.dependencyAnalysis";
56      private static final String SEARCH_KEY_BROWSE_PROGRAM = "lu.search.browseProgram";
57      private static final String SEARCH_KEY_BROWSE_VARIATIONS = "lu.search.browseVariations";
58      private static final String SEARCH_KEY_RESULT_COMPONENT = "lrc.search.resultComponent";
59      private static final String SEARCH_KEY_PROPOSALS_BY_COURSE_CODE = "lu.search.proposalsByCourseCode";
60      private static final String SEARCH_KEY_BROWSE_VERSIONS = "lu.search.clu.versions";
61      private static final String SEARCH_KEY_LU_RESULT_COMPONENTS = "lu.search.resultComponents";
62      private static final String SEARCH_KEY_CLUSET_SEARCH_GENERIC = "cluset.search.generic";
63      private static final String SEARCH_KEY_CLUSET_SEARCH_GENERICWITHCLUS = "cluset.search.genericWithClus";
64  
65      final Logger logger = Logger.getLogger(CluServiceImpl.class);
66      private LuDao luDao;
67      private ValidatorFactory validatorFactory;
68      private DictionaryService dictionaryServiceDelegate;
69      private SearchService searchDispatcher;
70      private SearchManager searchManager;
71  
72      public void setDictionaryServiceDelegate(
73              DictionaryService dictionaryServiceDelegate) {
74          this.dictionaryServiceDelegate = dictionaryServiceDelegate;
75      }
76  
77      public DictionaryService getDictionaryServiceDelegate() {
78          return dictionaryServiceDelegate;
79      }
80  
81      @Override
82      public List<TypeInfo> getSearchTypes( ContextInfo contextInfo) throws InvalidParameterException, MissingParameterException, OperationFailedException {
83          return searchManager.getSearchTypes(contextInfo);
84      }
85  
86      @Override
87      public TypeInfo getSearchType(@WebParam(name = "searchTypeKey") String searchTypeKey, @WebParam(name = "contextInfo") ContextInfo contextInfo) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException {
88          checkForMissingParameter(searchTypeKey, "searchTypeKey");
89          return searchManager.getSearchType(searchTypeKey, contextInfo);
90      }
91  
92      public SearchManager getSearchManager() {
93          return searchManager;
94      }
95  
96      public void setSearchManager(SearchManager searchManager) {
97          this.searchManager = searchManager;
98      }
99  
100     /**************************************************************************
101      * SETUP OPERATION *
102      **************************************************************************/
103     @Override
104     public List<TypeInfo> getDeliveryMethodTypes(ContextInfo context)
105             throws OperationFailedException {
106         return CluServiceAssembler.toGenericTypeInfoList(luDao.find(DeliveryMethodType.class));
107     }
108 
109     @Override
110     public TypeInfo getDeliveryMethodType(
111             String deliveryMethodTypeKey, ContextInfo context) throws DoesNotExistException,
112             InvalidParameterException, MissingParameterException,
113             OperationFailedException {
114 
115         checkForMissingParameter(deliveryMethodTypeKey, "deliveryMethodTypeKey");
116         try {
117             return CluServiceAssembler.toGenericTypeInfo(luDao.fetch(
118                     DeliveryMethodType.class, deliveryMethodTypeKey));
119         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
120             throw new DoesNotExistException(deliveryMethodTypeKey, ex);
121         }
122     }
123 
124     @Override
125     public List<TypeInfo> getInstructionalFormatTypes(ContextInfo context)
126             throws OperationFailedException {
127         return CluServiceAssembler.toGenericTypeInfoList(luDao.find(InstructionalFormatType.class));
128     }
129 
130     @Override
131     public TypeInfo getInstructionalFormatType(
132             String instructionalFormatTypeKey, ContextInfo context) throws DoesNotExistException,
133             InvalidParameterException, MissingParameterException,
134             OperationFailedException {
135         checkForMissingParameter(instructionalFormatTypeKey,
136                 "instructionalFormatTypeKey");
137         try {
138             return CluServiceAssembler.toGenericTypeInfo(luDao.fetch(
139                     InstructionalFormatType.class, instructionalFormatTypeKey));
140         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
141             throw new DoesNotExistException(instructionalFormatTypeKey, ex);
142         }
143     }
144 
145     @Override
146     public List<TypeInfo> getLuTypes(ContextInfo context) throws OperationFailedException {
147         return CluServiceAssembler.toGenericTypeInfoList(luDao.find(LuType.class));
148     }
149 
150     @Override
151     public TypeInfo getLuType(String luTypeKey, ContextInfo context) throws DoesNotExistException,
152             InvalidParameterException, MissingParameterException,
153             OperationFailedException {
154         checkForMissingParameter(luTypeKey, "luTypeKey");
155         try {
156             return CluServiceAssembler.toGenericTypeInfo(luDao.fetch(LuType.class,
157                     luTypeKey));
158         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
159             throw new DoesNotExistException(luTypeKey,ex);
160         }
161     }
162 
163     @Override
164     public TypeInfo getLuCodeType(String luCodeTypeKey, ContextInfo context)
165             throws DoesNotExistException, InvalidParameterException,
166             MissingParameterException, OperationFailedException {
167         try {
168             checkForMissingParameter(luCodeTypeKey, "luCodeTypeKey");
169             return CluServiceAssembler.toGenericTypeInfo(luDao.fetch(
170                     LuCodeType.class, luCodeTypeKey));
171         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
172             throw new DoesNotExistException(luCodeTypeKey, ex);
173         }
174     }
175 
176     @Override
177     public List<TypeInfo> getLuCodeTypes(ContextInfo context)
178             throws OperationFailedException {
179         return CluServiceAssembler.toGenericTypeInfoList(luDao.find(LuCodeType.class));
180     }
181 
182     @Override
183     public List<TypeInfo> getCluCluRelationTypes(ContextInfo context)
184             throws OperationFailedException {
185         return CluServiceAssembler.toLuLuRelationTypeInfos(luDao.find(LuLuRelationType.class));
186     }
187 
188     @Override
189     public TypeInfo getLuLuRelationType(String luLuRelationTypeKey, ContextInfo context)
190             throws OperationFailedException, MissingParameterException,
191             DoesNotExistException {
192         checkForMissingParameter(luLuRelationTypeKey, "luLuRelationTypeKey");
193 
194         LuLuRelationType luLuRelationType;
195         try {
196             luLuRelationType = luDao.fetch(LuLuRelationType.class,
197                     luLuRelationTypeKey);
198         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
199             throw new DoesNotExistException(luLuRelationTypeKey, ex);
200         }
201         return CluServiceAssembler.toLuLuRelationTypeInfo(luLuRelationType);
202     }
203 
204     @Override
205     public List<String> getAllowedLuLuRelationTypesForLuType(String luTypeKey,
206                                                              String relatedLuTypeKey, ContextInfo context) throws DoesNotExistException,
207             InvalidParameterException, MissingParameterException,
208             OperationFailedException {
209         checkForMissingParameter(luTypeKey, "luTypeKey");
210         checkForMissingParameter(relatedLuTypeKey, "relatedLuTypeKey");
211 
212         return luDao.getAllowedLuLuRelationTypesForLuType(luTypeKey,
213                 relatedLuTypeKey);
214     }
215 
216     @Override
217     public List<TypeInfo> getLuPublicationTypes(ContextInfo context)
218             throws OperationFailedException {
219         return CluServiceAssembler.toGenericTypeInfoList(luDao.find(LuPublicationType.class));
220     }
221 
222     @Override
223     public TypeInfo getLuPublicationType(
224             String luPublicationTypeKey, ContextInfo context) throws DoesNotExistException,
225             InvalidParameterException, MissingParameterException,
226             OperationFailedException {
227         checkForMissingParameter(luPublicationTypeKey, "luPublicationTypeKey");
228         try {
229             return CluServiceAssembler.toGenericTypeInfo(luDao.fetch(
230                     LuPublicationType.class, luPublicationTypeKey));
231         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
232             throw new DoesNotExistException(luPublicationTypeKey, ex);
233         }
234     }
235 
236     @Override
237     public List<String> getLuPublicationTypesForLuType(String luTypeKey, ContextInfo context)
238             throws DoesNotExistException, InvalidParameterException,
239             MissingParameterException, OperationFailedException {
240         throw new UnsupportedOperationException("getLuPublicationTypesForLuType");
241     }
242 
243     @Override
244     public List<TypeInfo> getCluResultTypes(ContextInfo context)
245             throws OperationFailedException {
246         return CluServiceAssembler.toGenericTypeInfoList(luDao.find(CluResultType.class));
247     }
248 
249     @Override
250     public TypeInfo getCluResultType(String cluResultTypeKey, ContextInfo context)
251             throws DoesNotExistException, InvalidParameterException,
252             MissingParameterException, OperationFailedException {
253         try {
254             return CluServiceAssembler.toGenericTypeInfo(luDao.fetch(
255                     CluResultType.class, cluResultTypeKey));
256         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
257             throw new DoesNotExistException(cluResultTypeKey, ex);
258         }
259     }
260 
261     @Override
262     public List<TypeInfo> getCluResultTypesForLuType(String luTypeKey, ContextInfo context)
263             throws DoesNotExistException, InvalidParameterException,
264             MissingParameterException, OperationFailedException {
265         checkForMissingParameter(luTypeKey, "luTypeKey");
266         return CluServiceAssembler.toGenericTypeInfoList(luDao.getAllowedCluResultTypesForLuType(luTypeKey));
267     }
268 
269     @Override
270     public List<TypeInfo> getResultUsageTypes(ContextInfo context)
271             throws OperationFailedException {
272         return CluServiceAssembler.toGenericTypeInfoList(luDao.find(ResultUsageType.class));
273     }
274 
275     @Override
276     public TypeInfo getResultUsageType(String resultUsageTypeKey, ContextInfo context)
277             throws DoesNotExistException, InvalidParameterException,
278             MissingParameterException, OperationFailedException {
279         checkForMissingParameter(resultUsageTypeKey, "resultUsageTypeKey");
280         try {
281             return CluServiceAssembler.toGenericTypeInfo(luDao.fetch(
282                     ResultUsageType.class, resultUsageTypeKey));
283         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
284             throw new DoesNotExistException(resultUsageTypeKey, ex);
285         }
286     }
287 
288     @Override
289     public List<String> getAllowedResultUsageTypesForLuType(String luTypeKey, ContextInfo context)
290             throws DoesNotExistException, InvalidParameterException,
291             MissingParameterException, OperationFailedException {
292         checkForMissingParameter(luTypeKey, "luTypeKey");
293 
294         return luDao.getAllowedResultUsageTypesForLuType(luTypeKey);
295     }
296 
297     @Override
298     public List<String> getAllowedResultComponentTypesForResultUsageType(
299             String resultUsageTypeKey, ContextInfo context) throws DoesNotExistException,
300             InvalidParameterException, MissingParameterException,
301             OperationFailedException {
302 
303         checkForMissingParameter(resultUsageTypeKey, "resultUsageTypeKey");
304 
305         return luDao.getAllowedResultComponentTypesForResultUsageType(resultUsageTypeKey);
306     }
307 
308     @Override
309     public TypeInfo getCluLoRelationType(
310             String cluLoRelationTypeKey, ContextInfo context) throws DoesNotExistException,
311             InvalidParameterException, MissingParameterException,
312             OperationFailedException {
313         checkForMissingParameter(cluLoRelationTypeKey, "cluLoRelationTypeKey");
314 
315         CluLoRelationType cluLoRelationType;
316         try {
317             cluLoRelationType = luDao.fetch(
318                     CluLoRelationType.class, cluLoRelationTypeKey);
319         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
320             throw new DoesNotExistException(cluLoRelationTypeKey, ex);
321         }
322         return CluServiceAssembler.toGenericTypeInfo(cluLoRelationType);
323     }
324 
325     @Override
326     public List<TypeInfo> getCluLoRelationTypes(ContextInfo context)
327             throws OperationFailedException {
328         return CluServiceAssembler.toGenericTypeInfoList(luDao.find(CluLoRelationType.class));
329     }
330 
331     @Override
332     public List<String> getAllowedCluLoRelationTypesForLuType(String luTypeKey, ContextInfo context)
333             throws DoesNotExistException, InvalidParameterException,
334             MissingParameterException, OperationFailedException {
335 
336         checkForMissingParameter(luTypeKey, luTypeKey);
337 
338         return luDao.getAllowedCluLoRelationTypesForLuType(luTypeKey);
339     }
340 
341     @Override
342     public List<TypeInfo> getCluSetTypes(ContextInfo context)
343             throws OperationFailedException {
344         return CluServiceAssembler.toGenericTypeInfoList(luDao.find(CluSetType.class));
345     }
346 
347     @Override
348     public TypeInfo getCluSetType(String cluSetTypeKey, ContextInfo context)
349             throws DoesNotExistException, InvalidParameterException,
350             MissingParameterException, OperationFailedException {
351         checkForMissingParameter(cluSetTypeKey, "cluSetTypeKey");
352         try {
353             return CluServiceAssembler.toGenericTypeInfo(luDao.fetch(
354                     CluSetType.class, cluSetTypeKey));
355         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
356             throw new DoesNotExistException(cluSetTypeKey, ex);
357         }
358     }
359 
360     /**************************************************************************
361      * READ OPERATION *
362      **************************************************************************/
363     // **** Core **********
364     @Override
365     public CluInfo getClu(String cluId, ContextInfo context) throws DoesNotExistException,
366             InvalidParameterException, MissingParameterException,
367             OperationFailedException {
368 
369         checkForMissingParameter(cluId, "cluId");
370 
371         Clu clu;
372         try {
373             clu = luDao.fetch(Clu.class, cluId);
374         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
375             throw new DoesNotExistException(cluId, ex);
376         }
377         return CluServiceAssembler.toCluInfo(clu);
378     }
379 
380     @Override
381     public List<CluInfo> getClusByIds(List<String> cluIds, ContextInfo context)
382             throws DoesNotExistException, InvalidParameterException,
383             MissingParameterException, OperationFailedException {
384         checkForMissingParameter(cluIds, "cluIds");
385         checkForEmptyList(cluIds, "cluIds");
386         List<Clu> clus = luDao.getClusByIdList(cluIds);
387         return CluServiceAssembler.toCluInfos(clus);
388     }
389 
390     @Override
391     public List<CluInfo> getClusByLuType(String luTypeKey, String luState, ContextInfo context)
392             throws DoesNotExistException, InvalidParameterException,
393             MissingParameterException, OperationFailedException {
394         checkForMissingParameter(luTypeKey, "luTypeKey");
395         checkForMissingParameter(luState, "lustate");
396         List<Clu> clus = luDao.getClusByLuType(luTypeKey, luState);
397         return CluServiceAssembler.toCluInfos(clus);
398     }
399 
400     @Override
401     public List<String> getCluIdsByLuType(String luTypeKey, String luState, ContextInfo context)
402             throws DoesNotExistException, InvalidParameterException,
403             MissingParameterException, OperationFailedException {
404         checkForMissingParameter(luTypeKey, "luTypeKey");
405         checkForMissingParameter(luState, "luState");
406         List<Clu> clus = luDao.getClusByLuType(luTypeKey, luState);
407         List<String> Ids = new ArrayList<String>(clus.size());
408         for (Clu clu : clus) {
409             Ids.add(clu.getId());
410         }
411         return Ids;
412     }
413 
414     // ****** Relations
415     @Override
416     public List<String> getAllowedCluCluRelationTypesByClu(String cluId,
417                                                            String relatedCluId, ContextInfo context) throws DoesNotExistException,
418             InvalidParameterException, MissingParameterException,
419             OperationFailedException {
420         checkForMissingParameter(cluId, "cluId");
421         checkForMissingParameter(relatedCluId, "relatedCluId");
422 
423         return luDao.getAllowedLuLuRelationTypesByCluId(cluId, relatedCluId);
424     }
425 
426     @Override
427     public List<CluInfo> getClusByRelatedCluAndRelationType(String relatedCluId,
428                                                             String luLuRelationTypeKey, ContextInfo context) throws DoesNotExistException,
429             InvalidParameterException, MissingParameterException,
430             OperationFailedException {
431         checkForMissingParameter(relatedCluId, "relatedCluId");
432         checkForMissingParameter(luLuRelationTypeKey, "luLuRelationTypeKey");
433 
434         List<Clu> clus = luDao.getClusByRelation(relatedCluId,
435                 luLuRelationTypeKey);
436         List<CluInfo> result = CluServiceAssembler.toCluInfos(clus);
437         return result;
438 
439     }
440 
441     @Override
442     public List<String> getCluIdsByRelatedCluAndRelationType(String relatedCluId,
443                                                              String cluCluRelationTypeKey,
444                                                              ContextInfo contextInfo)
445             throws DoesNotExistException,
446             InvalidParameterException,
447             MissingParameterException,
448             OperationFailedException,
449             PermissionDeniedException {
450 
451         checkForMissingParameter(relatedCluId, "relatedCluId");
452         checkForMissingParameter(cluCluRelationTypeKey, "cluCluRelationTypeKey");
453 
454         List<String> cluIds = luDao.getCluIdsByRelatedCluId(relatedCluId, cluCluRelationTypeKey);
455         return cluIds;
456     }
457 
458     @Override
459     public List<CluInfo> getRelatedClusByCluAndRelationType(String relatedCluId,
460                                                             String cluCLuRelationTypeKey,
461                                                             ContextInfo contextInfo)
462             throws DoesNotExistException,
463             InvalidParameterException,
464             MissingParameterException,
465             OperationFailedException,
466             PermissionDeniedException {
467         checkForMissingParameter(relatedCluId, "cluId");
468         checkForMissingParameter(cluCLuRelationTypeKey, "cluCLuRelationTypeKey");
469         List<Clu> relatedClus = luDao.getRelatedClusByCluId(relatedCluId,
470                 cluCLuRelationTypeKey);
471         return CluServiceAssembler.toCluInfos(relatedClus);
472     }
473 
474     @Override
475     public List<String> getRelatedCluIdsByCluAndRelationType(String cluId,
476                                                              String luLuRelationTypeKey, ContextInfo context) throws DoesNotExistException,
477             InvalidParameterException, MissingParameterException,
478             OperationFailedException {
479         checkForMissingParameter(cluId, "cluId");
480         checkForMissingParameter(luLuRelationTypeKey, "luLuRelationTypeKey");
481         List<String> relatedCluIds = luDao.getRelatedCluIdsByCluId(cluId,
482                 luLuRelationTypeKey);
483         return relatedCluIds;
484     }
485 
486     @Override
487     public CluCluRelationInfo getCluCluRelation(String cluCluRelationId, ContextInfo context)
488             throws DoesNotExistException, InvalidParameterException,
489             MissingParameterException, OperationFailedException {
490         checkForMissingParameter(cluCluRelationId, "cluCluRelationId");
491         try {
492             return CluServiceAssembler.toCluCluRelationInfo(luDao.fetch(
493                     CluCluRelation.class, cluCluRelationId));
494         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
495             throw new DoesNotExistException(cluCluRelationId, ex);
496         }
497     }
498 
499     @Override
500     public List<CluCluRelationInfo> getCluCluRelationsByClu(String cluId, ContextInfo context)
501             throws DoesNotExistException, InvalidParameterException,
502             MissingParameterException, OperationFailedException {
503         checkForMissingParameter(cluId, "cluId");
504         List<CluCluRelation> cluCluRelations = luDao.getCluCluRelationsByClu(cluId);
505         return CluServiceAssembler.toCluCluRelationInfos(cluCluRelations);
506     }
507 
508     // **** Publication
509     @Override
510     public List<CluPublicationInfo> getCluPublicationsByClu(String cluId, ContextInfo context)
511             throws DoesNotExistException, InvalidParameterException,
512             MissingParameterException, OperationFailedException {
513         checkForMissingParameter(cluId, "cluId");
514         List<CluPublication> cluPublications = luDao.getCluPublicationsByCluId(cluId);
515         return CluServiceAssembler.toCluPublicationInfos(cluPublications);
516     }
517 
518     @Override
519     public List<CluPublicationInfo> getCluPublicationsByType(
520             String luPublicationTypeKey, ContextInfo context) throws DoesNotExistException,
521             InvalidParameterException, MissingParameterException,
522             OperationFailedException {
523         checkForMissingParameter(luPublicationTypeKey, "luPublicationTypeKey");
524         List<CluPublication> cluPublications = luDao.getCluPublicationsByType(luPublicationTypeKey);
525         return CluServiceAssembler.toCluPublicationInfos(cluPublications);
526     }
527 
528     @Override
529     public CluPublicationInfo getCluPublication(String cluPublicationId, ContextInfo context)
530             throws DoesNotExistException, InvalidParameterException,
531             MissingParameterException, OperationFailedException {
532         checkForMissingParameter(cluPublicationId, "cluPublicationId");
533         CluPublication cluPublication;
534         try {
535             cluPublication = luDao.fetch(CluPublication.class, cluPublicationId);
536         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
537             throw new DoesNotExistException(cluPublicationId, ex);
538         }
539         return CluServiceAssembler.toCluPublicationInfo(cluPublication);
540     }
541 
542     // **** Results
543     @Override
544     public CluResultInfo getCluResult(String cluResultId, ContextInfo context)
545             throws DoesNotExistException, InvalidParameterException,
546             MissingParameterException, OperationFailedException {
547 
548         checkForMissingParameter(cluResultId, "cluResultId");
549 
550         CluResult cluResult;
551         try {
552             cluResult = luDao.fetch(CluResult.class, cluResultId);
553         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
554             throw new DoesNotExistException(cluResultId, ex);
555         }
556         return CluServiceAssembler.toCluResultInfo(cluResult);
557     }
558 
559     @Override
560     public List<CluResultInfo> getCluResultByClu(String cluId, ContextInfo context)
561             throws DoesNotExistException, InvalidParameterException,
562             MissingParameterException, OperationFailedException {
563 
564         checkForMissingParameter(cluId, "cluId");
565 
566         return CluServiceAssembler.toCluResultInfos(luDao.getCluResultByClu(cluId));
567     }
568 
569     @Override
570     public List<String> getCluIdsByResultUsageType(String resultUsageTypeKey, ContextInfo context)
571             throws DoesNotExistException, InvalidParameterException,
572             MissingParameterException, OperationFailedException {
573         return luDao.getCluIdsByResultUsageType(resultUsageTypeKey);
574     }
575 
576     @Override
577     public List<String> getCluIdsByResultComponent(String resultComponentId, ContextInfo context)
578             throws DoesNotExistException, InvalidParameterException,
579             MissingParameterException, OperationFailedException {
580         return luDao.getCluIdsByResultComponentId(resultComponentId);
581     }
582 
583     // **** Learning Objectives
584     @Override
585     public CluLoRelationInfo getCluLoRelation(String cluLoRelationId, ContextInfo context)
586             throws DoesNotExistException, InvalidParameterException,
587             MissingParameterException, OperationFailedException,
588             PermissionDeniedException {
589 
590         checkForMissingParameter(cluLoRelationId, "cluLoRelationId");
591 
592         CluLoRelation reltn;
593         try {
594             reltn = luDao.fetch(CluLoRelation.class, cluLoRelationId);
595         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
596             throw new DoesNotExistException(cluLoRelationId, ex);
597         }
598         return CluServiceAssembler.toCluLoRelationInfo(reltn);
599 
600     }
601 
602     @Override
603     public List<CluLoRelationInfo> getCluLoRelationsByClu(String cluId, ContextInfo context)
604             throws DoesNotExistException, InvalidParameterException,
605             MissingParameterException, OperationFailedException {
606 
607         checkForMissingParameter(cluId, "cluId");
608         List<CluLoRelation> cluLoRelations = luDao.getCluLoRelationsByClu(cluId);
609         return CluServiceAssembler.toCluLoRelationInfos(cluLoRelations);
610 
611     }
612 
613     @Override
614     public List<CluLoRelationInfo> getCluLoRelationsByLo(String loId, ContextInfo context)
615             throws DoesNotExistException, InvalidParameterException,
616             MissingParameterException, OperationFailedException {
617         checkForMissingParameter(loId, "loId");
618         List<CluLoRelation> cluLoRelations = luDao.getCluLoRelationsByLo(loId);
619         return CluServiceAssembler.toCluLoRelationInfos(cluLoRelations);
620     }
621 
622     // *** Resources
623     @Override
624     public List<String> getResourceRequirementsForClu(String cluId, ContextInfo context)
625             throws DoesNotExistException, InvalidParameterException,
626             MissingParameterException, OperationFailedException {
627         return new ArrayList<String>();
628     }
629 
630     // *** Sets
631     @Override
632     public CluSetInfo getCluSet(String cluSetId, ContextInfo contextInfo)
633             throws DoesNotExistException, InvalidParameterException,
634             MissingParameterException, OperationFailedException,
635             PermissionDeniedException {
636         checkForMissingParameter(cluSetId, "cluSetId");
637         CluSet cluSet;
638         try {
639             cluSet = luDao.fetch(CluSet.class, cluSetId);
640         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
641             throw new DoesNotExistException(cluSetId, ex);
642         }
643         CluSetInfo cluSetInfo = CluServiceAssembler.toCluSetInfo(cluSet);
644         setMembershipQuerySearchResult(cluSetInfo, contextInfo);
645         return cluSetInfo;
646     }
647 
648     @Override
649     public CluSetTreeViewInfo getCluSetTreeView(String cluSetId, ContextInfo context)
650             throws DoesNotExistException, InvalidParameterException,
651             MissingParameterException, OperationFailedException,
652             PermissionDeniedException {
653 
654         checkForMissingParameter(cluSetId, "cluSetId");
655         CluSetInfo cluSet = getCluSet(cluSetId, context);
656         if (cluSet == null) {
657             return null;
658         }
659 
660         CluSetTreeViewInfo cluSetTreeView = new CluSetTreeViewInfo();
661         getCluSetTreeViewHelper(cluSet, cluSetTreeView, context);
662         return cluSetTreeView;
663     }
664 
665     /**
666      * Go through the list of CluSets and retrieve all the information regarding child
667      * Clu Sets and associated Clus
668      *
669      * @param cluSetInfo
670      * @param cluSetTreeViewInfo
671      * @throws DoesNotExistException
672      * @throws InvalidParameterException
673      * @throws MissingParameterException
674      * @throws OperationFailedException
675      * @throws PermissionDeniedException
676      */
677     private void getCluSetTreeViewHelper(CluSetInfo cluSetInfo,
678                                          CluSetTreeViewInfo cluSetTreeViewInfo, ContextInfo context) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
679         cluSetTreeViewInfo.setName(cluSetInfo.getName());
680         cluSetTreeViewInfo.setDescr(cluSetInfo.getDescr());
681         cluSetTreeViewInfo.setEffectiveDate(cluSetInfo.getEffectiveDate());
682         cluSetTreeViewInfo.setExpirationDate(cluSetInfo.getExpirationDate());
683         cluSetTreeViewInfo.setAdminOrg(cluSetInfo.getAdminOrg());
684         cluSetTreeViewInfo.setIsReusable(cluSetInfo.getIsReusable());
685         cluSetTreeViewInfo.setIsReferenceable(cluSetInfo.getIsReferenceable());
686         cluSetTreeViewInfo.setMeta(cluSetInfo.getMeta());
687         cluSetTreeViewInfo.setAttributes(cluSetInfo.getAttributes());
688         cluSetTreeViewInfo.setTypeKey(cluSetInfo.getTypeKey());
689         cluSetTreeViewInfo.setStateKey(cluSetInfo.getStateKey());
690         cluSetTreeViewInfo.setId(cluSetInfo.getId());
691 
692         if (!cluSetInfo.getCluSetIds().isEmpty()) {
693             for (String cluSetId : cluSetInfo.getCluSetIds()) {
694                 CluSetInfo subCluSet = getCluSet(cluSetId, context);
695                 List<CluSetTreeViewInfo> cluSets =
696                         cluSetTreeViewInfo.getCluSets() == null
697                                 ? new ArrayList<CluSetTreeViewInfo>(0) : cluSetTreeViewInfo.getCluSets();
698 
699                 CluSetTreeViewInfo subCluSetTreeViewInfo = new CluSetTreeViewInfo();
700                 getCluSetTreeViewHelper(subCluSet, subCluSetTreeViewInfo, context);
701                 cluSets.add(subCluSetTreeViewInfo);
702 
703                 cluSetTreeViewInfo.setCluSets(cluSets);
704             }
705         }
706         List<CluInfo> clus = new ArrayList<CluInfo>();
707         for (String cluId : cluSetInfo.getCluIds()) {
708             if (cluId != null) {
709                 //Optimized version of clu translation. It seems like for now we only need the following information.
710                 //If more information is needed, then appropriate method in assembler has to be used.
711                 logger.info("CluID: " + cluId);
712                 Clu clu = luDao.getCurrentCluVersion(cluId);
713                 CluInfo cluInfo = new CluInfo();
714                 cluInfo.setId(clu.getId());
715                 cluInfo.setTypeKey(clu.getLuType().getId());
716                 cluInfo.setOfficialIdentifier(CluServiceAssembler.toCluIdentifierInfo(clu.getOfficialIdentifier()));
717                 clus.add(cluInfo);
718             }
719         }
720         cluSetTreeViewInfo.setClus(clus);
721     }
722 
723     @Override
724     public List<CluSetInfo> getCluSetsByIds(List<String> cluSetIds, ContextInfo context)
725             throws DoesNotExistException, InvalidParameterException,
726             MissingParameterException, OperationFailedException,
727             PermissionDeniedException {
728         checkForMissingParameter(cluSetIds, "cluSetIds");
729         checkForEmptyList(cluSetIds, "cluSetIds");
730         List<CluSet> cluSets = luDao.getCluSetInfoByIdList(cluSetIds);
731         return CluServiceAssembler.toCluSetInfos(cluSets);
732     }
733 
734     @Override
735     public List<String> getCluSetIdsFromCluSet(String cluSetId, ContextInfo context)
736             throws DoesNotExistException, InvalidParameterException,
737             MissingParameterException, OperationFailedException,
738             PermissionDeniedException {
739         checkForMissingParameter(cluSetId, "cluSetId");
740         CluSet cluSet;
741         try {
742             cluSet = luDao.fetch(CluSet.class, cluSetId);
743         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
744             throw new DoesNotExistException(cluSetId, ex);
745         }
746         List<String> Ids = new ArrayList<String>(cluSet.getCluVerIndIds().size());
747         if (cluSet.getCluSets() != null) {
748             for (CluSet cluSet2 : cluSet.getCluSets()) {
749                 Ids.add(cluSet2.getId());
750             }
751         }
752         return Ids;
753     }
754 
755     @Override
756     public Boolean isCluSetDynamic(String cluSetId, ContextInfo context)
757             throws DoesNotExistException, InvalidParameterException,
758             MissingParameterException, OperationFailedException,
759             PermissionDeniedException {
760         return null;
761     }
762 
763     @Override
764     public List<CluInfo> getClusFromCluSet(String cluSetId, ContextInfo context)
765             throws DoesNotExistException, InvalidParameterException,
766             MissingParameterException, OperationFailedException,
767             PermissionDeniedException {
768         checkForMissingParameter(cluSetId, "cluSetId");
769         CluSet cluSet;
770         try {
771             cluSet = luDao.fetch(CluSet.class, cluSetId);
772         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
773             throw new DoesNotExistException(cluSetId, ex);
774         }
775         List<CluInfo> clus = new ArrayList<CluInfo>(cluSet.getCluVerIndIds().size());
776         for (CluSetJoinVersionIndClu cluSetJnClu : cluSet.getCluVerIndIds()) {
777             clus.add(CluServiceAssembler.toCluInfo(luDao.getCurrentCluVersion(cluSetJnClu.getCluVersionIndId())));
778         }
779         return clus;
780     }
781 
782     @Override
783     public List<String> getCluIdsFromCluSet(String cluSetId, ContextInfo context)
784             throws DoesNotExistException, InvalidParameterException,
785             MissingParameterException, OperationFailedException,
786             PermissionDeniedException {
787         checkForMissingParameter(cluSetId, "cluSetId");
788         CluSet cluSet;
789         try {
790             cluSet = luDao.fetch(CluSet.class, cluSetId);
791         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
792             throw new DoesNotExistException(cluSetId, ex);
793         }
794         List<String> Ids = new ArrayList<String>(cluSet.getCluVerIndIds().size());
795         for (CluSetJoinVersionIndClu cluSetJnClu : cluSet.getCluVerIndIds()) {
796             Ids.add(cluSetJnClu.getCluVersionIndId());
797         }
798         return Ids;
799     }
800 
801     @Override
802     public List<CluInfo> getAllClusInCluSet(String cluSetId, ContextInfo context)
803             throws DoesNotExistException, InvalidParameterException,
804             MissingParameterException, OperationFailedException,
805             PermissionDeniedException {
806         checkForMissingParameter(cluSetId, "cluSetId");
807         List<String> cluIndIds = new ArrayList<String>();
808         CluSet cluSet;
809         try {
810             cluSet = luDao.fetch(CluSet.class, cluSetId);
811         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
812             throw new DoesNotExistException(cluSetId, ex);
813         }
814         findClusInCluSet(cluIndIds, cluSet);
815         List<CluInfo> infos = new ArrayList<CluInfo>(cluIndIds.size());
816         for (String cluIndId : cluIndIds) {
817             infos.add(CluServiceAssembler.toCluInfo(luDao.getCurrentCluVersion(cluIndId)));
818         }
819         return infos;
820     }
821 
822     private void findClusInCluSet(List<String> clus, CluSet parentCluSet)
823             throws DoesNotExistException {
824         List<String> processedCluSetIds = new ArrayList<String>();
825         doFindClusInCluSet(processedCluSetIds, clus, parentCluSet);
826     }
827 
828     private void doFindClusInCluSet(List<String> processedCluSetIds,
829                                     List<String> clus, CluSet parentCluSet) {
830         for (CluSetJoinVersionIndClu join : parentCluSet.getCluVerIndIds()) {
831             if (!clus.contains(join.getCluVersionIndId())) {
832                 clus.add(join.getCluVersionIndId());
833             }
834         }
835         if (parentCluSet.getCluSets() != null) {
836             for (CluSet cluSet : parentCluSet.getCluSets()) {
837                 // This condition avoIds infinite recursion problem
838                 if (!processedCluSetIds.contains(cluSet.getId())) {
839                     processedCluSetIds.add(cluSet.getId());
840                     doFindClusInCluSet(processedCluSetIds, clus, cluSet);
841                 }
842             }
843         }
844     }
845 
846     @Override
847     public List<String> getAllCluIdsInCluSet(String cluSetId, ContextInfo context)
848             throws DoesNotExistException, InvalidParameterException,
849             MissingParameterException, OperationFailedException,
850             PermissionDeniedException {
851         checkForMissingParameter(cluSetId, "cluSetId");
852         List<String> Ids = new ArrayList<String>();
853         CluSet cluSet;
854         try {
855             cluSet = luDao.fetch(CluSet.class, cluSetId);
856         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
857             throw new DoesNotExistException(cluSetId, ex);
858         }
859         findClusInCluSet(Ids, cluSet);
860         return Ids;
861     }
862 
863     @Override
864     public Boolean isCluInCluSet(String cluId, String cluSetId, ContextInfo context)
865             throws DoesNotExistException, InvalidParameterException,
866             MissingParameterException, OperationFailedException,
867             PermissionDeniedException {
868         checkForMissingParameter(cluId, "cluId");
869         checkForMissingParameter(cluSetId, "cluSetId");
870         return luDao.isCluInCluSet(cluId, cluSetId);
871     }
872 
873     /**************************************************************************
874      * MAINTENANCE OPERATIONS *
875      **************************************************************************/
876     @Override
877     public List<ValidationResultInfo> validateClu(String validationType,
878                                                   CluInfo cluInfo, ContextInfo context) throws DoesNotExistException,
879             InvalidParameterException, MissingParameterException,
880             OperationFailedException {
881         checkForMissingParameter(validationType, "validationType");
882         checkForMissingParameter(cluInfo, "cluInfo");
883 
884         ObjectStructureDefinition objStructure = this.getObjectStructure(CluInfo.class.getName());
885         Validator defaultValidator = validatorFactory.getValidator();
886         List<org.kuali.student.r2.common.dto.ValidationResultInfo> vris = defaultValidator.validateObject(cluInfo, objStructure, context);
887         return vris;
888     }
889 
890     @Override
891     @Transactional(readOnly = false)
892     public CluInfo createClu(String luTypeKey, CluInfo cluInfo, ContextInfo context)
893             throws DataValidationErrorException,
894             DoesNotExistException, InvalidParameterException,
895             MissingParameterException, OperationFailedException,
896             PermissionDeniedException {
897         Clu clu;
898         try {
899             clu = toCluForCreate(luTypeKey, cluInfo, context);
900         } catch (AlreadyExistsException ex) {
901             throw new OperationFailedException(ex.getMessage(), ex);
902         }
903         //Set current (since this is brand new and every verIndId needs one current)
904         if (clu.getVersion() == null) {
905             clu.setVersion(new Version());
906         }
907         clu.getVersion().setCurrentVersionStart(new Date());
908         luDao.create(clu);
909         return CluServiceAssembler.toCluInfo(clu);
910     }
911 
912     public Clu toCluForCreate(String luTypeKey, CluInfo cluInfo, ContextInfo context)
913             throws AlreadyExistsException, DataValidationErrorException,
914             DoesNotExistException, InvalidParameterException,
915             MissingParameterException, OperationFailedException,
916             PermissionDeniedException {
917         checkForMissingParameter(luTypeKey, "luTypeKey");
918         checkForMissingParameter(cluInfo, "cluInfo");
919 
920         // Validate CLU
921         List<ValidationResultInfo> val = validateClu("SYSTEM", cluInfo, context);
922         if (null != val && val.size() > 0) {
923             throw new DataValidationErrorException("Validation error!", val);
924         }
925 
926         Clu clu = new Clu();
927 
928         LuType luType;
929         try {
930             luType = luDao.fetch(LuType.class, luTypeKey);
931         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
932             throw new DoesNotExistException(luTypeKey, ex);
933         }
934         clu.setLuType(luType);
935 
936         if (cluInfo.getOfficialIdentifier() != null) {
937             clu.setOfficialIdentifier(CluServiceAssembler.createOfficialIdentifier(cluInfo, luDao));
938         }
939         clu.setAlternateIdentifiers(CluServiceAssembler.createAlternateIdentifiers(cluInfo, luDao));
940         if (cluInfo.getDescr() != null) {
941             LuRichText descr = CluServiceAssembler.toRichText(LuRichText.class, cluInfo.getDescr());
942             if (descr.getPlain() != null || descr.getFormatted() != null) {
943                 clu.setDescr(descr);
944             }
945         }
946 
947         if (clu.getAdminOrgs() == null) {
948             clu.setAdminOrgs(new ArrayList<CluAdminOrg>(0));
949         }
950         List<CluAdminOrg> adminOrgs = clu.getAdminOrgs();
951         for (AdminOrgInfo orgInfo : cluInfo.getAdminOrgs()) {
952             CluAdminOrg instructor = new CluAdminOrg();
953             BeanUtils.copyProperties(orgInfo, instructor,
954                     new String[]{"attributes"});
955             instructor.setAttributes(CluServiceAssembler.toGenericAttributes(
956                     CluAdminOrgAttribute.class, orgInfo.getAttributes(),
957                     instructor, luDao));
958             instructor.setClu(clu);
959             adminOrgs.add(instructor);
960         }
961 
962         if (cluInfo.getPrimaryInstructor() != null) {
963             CluInstructor primaryInstructor = new CluInstructor();
964             BeanUtils.copyProperties(cluInfo.getPrimaryInstructor(),
965                     primaryInstructor, new String[]{"id", "attributes"});
966             primaryInstructor.setAttributes(CluServiceAssembler.toGenericAttributes(CluInstructorAttribute.class, cluInfo.getPrimaryInstructor().getAttributes(),
967                     primaryInstructor, luDao));
968             clu.setPrimaryInstructor(primaryInstructor);
969         }
970 
971         if (clu.getInstructors() == null) {
972             clu.setInstructors(new ArrayList<CluInstructor>(0));
973         }
974         List<CluInstructor> instructors = clu.getInstructors();
975         for (CluInstructorInfo instructorInfo : cluInfo.getInstructors()) {
976             CluInstructor instructor = new CluInstructor();
977             BeanUtils.copyProperties(instructorInfo, instructor,
978                     new String[]{"id", "attributes"});
979             instructor.setAttributes(CluServiceAssembler.toGenericAttributes(
980                     CluInstructorAttribute.class, instructorInfo.getAttributes(), instructor, luDao));
981             instructors.add(instructor);
982         }
983 
984         if (cluInfo.getStdDuration() != null) {
985             clu.setStdDuration(CluServiceAssembler.toTimeAmount(cluInfo.getStdDuration()));
986         }
987 
988         if (clu.getLuCodes() == null) {
989             clu.setLuCodes(new ArrayList<LuCode>(0));
990         }
991         List<LuCode> luCodes = clu.getLuCodes();
992         for (LuCodeInfo luCodeInfo : cluInfo.getLuCodes()) {
993             LuCode luCode = new LuCode();
994             luCode.setAttributes(CluServiceAssembler.toGenericAttributes(
995                     LuCodeAttribute.class, luCodeInfo.getAttributes(), luCode,
996                     luDao));
997             BeanUtils.copyProperties(luCodeInfo, luCode, new String[]{
998                     "attributes", "meta", "descr", "typeKey"});
999             if (luCodeInfo.getDescr() != null) {
1000                 luCode.setDescr(luCodeInfo.getDescr().getPlain());
1001             }
1002             luCode.setType(luCodeInfo.getTypeKey());
1003             luCode.setClu(clu);
1004             luCodes.add(luCode);
1005         }
1006 
1007         if (clu.getOfferedAtpTypes() == null) {
1008             clu.setOfferedAtpTypes(new ArrayList<CluAtpTypeKey>(0));
1009         }
1010         List<CluAtpTypeKey> offeredAtpTypes = clu.getOfferedAtpTypes();
1011         for (String atpTypeKey : cluInfo.getOfferedAtpTypes()) {
1012             CluAtpTypeKey cluAtpTypeKey = new CluAtpTypeKey();
1013             cluAtpTypeKey.setAtpTypeKey(atpTypeKey);
1014             cluAtpTypeKey.setClu(clu);
1015             offeredAtpTypes.add(cluAtpTypeKey);
1016         }
1017 
1018         // FEE INFO
1019         if (cluInfo.getFeeInfo() != null) {
1020             CluFee cluFee = null;
1021             try {
1022                 cluFee = CluServiceAssembler.toCluFee(clu, false, cluInfo.getFeeInfo(), luDao);
1023             } catch (VersionMismatchException e) {
1024                 // Version Mismatch Should Happen only for updates
1025             }
1026             clu.setFee(cluFee);
1027         }
1028 
1029         if (cluInfo.getAccountingInfo() != null) {
1030             CluAccounting cluAccounting = new CluAccounting();
1031             cluAccounting.setAttributes(CluServiceAssembler.toGenericAttributes(
1032                     CluAccountingAttribute.class, cluInfo.getAccountingInfo().getAttributes(), cluAccounting, luDao));
1033             cluAccounting.setAffiliatedOrgs(CluServiceAssembler.toAffiliatedOrgs(false, cluAccounting.getAffiliatedOrgs(),
1034                     cluInfo.getAccountingInfo().getAffiliatedOrgs(),
1035                     luDao));
1036             clu.setAccounting(cluAccounting);
1037         }
1038 
1039         clu.setAttributes(CluServiceAssembler.toGenericAttributes(
1040                 CluAttribute.class, cluInfo.getAttributes(), clu, luDao));
1041 
1042 
1043         if (cluInfo.getIntensity() != null) {
1044             clu.setIntensity(CluServiceAssembler.toAmount(cluInfo.getIntensity()));
1045         }
1046 
1047         if (clu.getCampusLocations() == null) {
1048             clu.setCampusLocations(new ArrayList<CluCampusLocation>(0));
1049         }
1050         List<CluCampusLocation> locations = clu.getCampusLocations();
1051         for (String locationName : cluInfo.getCampusLocations()) {
1052             CluCampusLocation location = new CluCampusLocation();
1053             location.setCampusLocation(locationName);
1054             location.setClu(clu);
1055             locations.add(location);
1056         }
1057 
1058         if (clu.getAccreditations() == null) {
1059             clu.setAccreditations(new ArrayList<CluAccreditation>(0));
1060         }
1061         List<CluAccreditation> accreditations = clu.getAccreditations();
1062         for (AccreditationInfo accreditationInfo : cluInfo.getAccreditations()) {
1063             CluAccreditation accreditation = new CluAccreditation();
1064             BeanUtils.copyProperties(accreditationInfo, accreditation,
1065                     new String[]{"attributes", "meta"});
1066             accreditation.setAttributes(CluServiceAssembler.toGenericAttributes(
1067                     CluAccreditationAttribute.class, accreditationInfo.getAttributes(), accreditation, luDao));
1068             accreditations.add(accreditation);
1069         }
1070 
1071 
1072         // TODO: the following should be done via copyProperties
1073         // dictionary needs work
1074         // required field name allignment between CluInfo and Clu
1075                 if (cluInfo.getCanCreateLui() != null) {
1076                     clu.setCanCreateLui(cluInfo.getCanCreateLui());
1077                 }
1078 
1079                 if (cluInfo.getIsEnrollable() != null) {
1080                     clu.setEnrollable(cluInfo.getIsEnrollable());
1081                 }
1082 
1083                 if (cluInfo.getIsHasEarlyDropDeadline() != null) {
1084                     clu.setHasEarlyDropDeadline(cluInfo.getIsHasEarlyDropDeadline());
1085                 }
1086 
1087                 if (cluInfo.getIsHazardousForDisabledStudents() != null) {
1088                     clu.setHazardousForDisabledStudents(cluInfo.getIsHazardousForDisabledStudents());
1089                 }
1090 
1091 
1092          // Now copy all not standard properties
1093         BeanUtils.copyProperties(cluInfo, clu, new String[]{"luType",
1094                 "officialIdentifier", "alternateIdentifiers", "descr",
1095                 "luCodes", "primaryInstructor", "instructors", "stdDuration",
1096                 "offeredAtpTypes", "feeInfo", "accountingInfo", "attributes",
1097                 "meta", "version", "intensity",
1098                 "campusLocations", "accreditations",
1099                 "adminOrgs", "canCreateLui", "hasEarlyDropDeadline", ""});
1100 
1101         return clu;
1102     }
1103 
1104     @Override
1105     @Transactional(readOnly = false)
1106     public CluInfo updateClu(String cluId, CluInfo cluInfo, ContextInfo context)
1107             throws DataValidationErrorException, DoesNotExistException,
1108             InvalidParameterException, MissingParameterException,
1109             OperationFailedException, PermissionDeniedException,
1110             VersionMismatchException {
1111 
1112         checkForMissingParameter(cluId, "cluId");
1113         checkForMissingParameter(cluInfo, "cluInfo");
1114 
1115         // Validate CLU
1116         List<ValidationResultInfo> val = validateClu("SYSTEM", cluInfo, context);
1117         if (null != val && val.size() > 0) {
1118             throw new DataValidationErrorException("Validation error!", val);
1119         }
1120 
1121         Clu clu;
1122         try {
1123             clu = luDao.fetch(Clu.class, cluId);
1124         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1125             throw new DoesNotExistException(cluId, ex);
1126         }
1127 
1128         if (!String.valueOf(clu.getVersionNumber()).equals(
1129                 cluInfo.getMeta().getVersionInd())) {
1130             throw new VersionMismatchException(
1131                     "Clu to be updated is not the current version");
1132         }
1133 
1134         LuType luType;
1135         try {
1136             luType = luDao.fetch(LuType.class, cluInfo.getTypeKey());
1137         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1138             throw new DoesNotExistException(cluInfo.getTypeKey(), ex);
1139         }
1140         clu.setLuType(luType);
1141 
1142         if (cluInfo.getOfficialIdentifier() != null) {
1143             CluServiceAssembler.updateOfficialIdentifier(clu, cluInfo, luDao);
1144         } else if (clu.getOfficialIdentifier() != null) {
1145             luDao.delete(clu.getOfficialIdentifier());
1146         }
1147 
1148         // Update the list of Alternate Identifiers
1149         // Get a map of Id->object of all the currently persisted objects in the
1150         // list
1151         Map<String, CluIdentifier> oldAltIdMap = new HashMap<String, CluIdentifier>();
1152         CluServiceAssembler.updateAlternateIdentifier(oldAltIdMap, clu, cluInfo, luDao);
1153         // Now delete anything left over
1154         for (Entry<String, CluIdentifier> entry : oldAltIdMap.entrySet()) {
1155             luDao.delete(entry.getValue());
1156         }
1157 
1158         if (cluInfo.getDescr() != null && (cluInfo.getDescr().getPlain() != null || cluInfo.getDescr().getFormatted() != null)) {
1159             if (clu.getDescr() == null) {
1160                 clu.setDescr(new LuRichText());
1161             }
1162             BeanUtils.copyProperties(cluInfo.getDescr(), clu.getDescr());
1163         } else if (clu.getDescr() != null) {
1164             luDao.delete(clu.getDescr());
1165             clu.setDescr(null);//TODO is the is the best method of doing this? what if the user passes in a new made up id, does that mean we have orphaned richtexts?
1166         }
1167 
1168         if (cluInfo.getPrimaryInstructor() != null) {
1169             if (clu.getPrimaryInstructor() == null) {
1170                 clu.setPrimaryInstructor(new CluInstructor());
1171             }
1172             BeanUtils.copyProperties(cluInfo.getPrimaryInstructor(), clu.getPrimaryInstructor(), new String[]{"attributes"});
1173             clu.getPrimaryInstructor().setAttributes(
1174                     CluServiceAssembler.toGenericAttributes(
1175                             CluInstructorAttribute.class, cluInfo.getPrimaryInstructor().getAttributes(),
1176                             clu.getPrimaryInstructor(), luDao));
1177         } else if (clu.getPrimaryInstructor() != null) {
1178             luDao.delete(clu.getPrimaryInstructor());
1179         }
1180 
1181         // Update the List of instructors
1182         // Get a map of Id->object of all the currently persisted objects in the
1183         // list
1184         Map<String, CluInstructor> oldInstructorMap = new HashMap<String, CluInstructor>();
1185         for (CluInstructor cluInstructor : clu.getInstructors()) {
1186             oldInstructorMap.put(cluInstructor.getId(), cluInstructor);
1187         }
1188         clu.getInstructors().clear();
1189 
1190         // Loop through the new list, if the item exists already update and
1191         // remove from the list
1192         // otherwise create a new entry
1193         for (CluInstructorInfo instructorInfo : cluInfo.getInstructors()) {
1194             CluInstructor cluInstructor = oldInstructorMap.remove(instructorInfo.getId());
1195             if (cluInstructor == null) {
1196                 cluInstructor = new CluInstructor();
1197             }
1198             // Do Copy
1199             BeanUtils.copyProperties(instructorInfo, cluInstructor,
1200                     new String[]{"attributes"});
1201             cluInstructor.setAttributes(CluServiceAssembler.toGenericAttributes(
1202                     CluInstructorAttribute.class, instructorInfo.getAttributes(), cluInstructor, luDao));
1203             clu.getInstructors().add(cluInstructor);
1204         }
1205 
1206         // Now delete anything left over
1207         for (Entry<String, CluInstructor> entry : oldInstructorMap.entrySet()) {
1208             luDao.delete(entry.getValue());
1209         }
1210 
1211         if (cluInfo.getStdDuration() != null) {
1212             if (clu.getStdDuration() == null) {
1213                 clu.setStdDuration(new TimeAmount());
1214             }
1215             BeanUtils.copyProperties(cluInfo.getStdDuration(), clu.getStdDuration());
1216         } else if (clu.getStdDuration() != null) {
1217             luDao.delete(clu.getStdDuration());
1218         }
1219 
1220         // Update the LuCodes
1221         // Get a map of Id->object of all the currently persisted objects in the
1222         // list
1223         Map<String, LuCode> oldLuCodeMap = new HashMap<String, LuCode>();
1224         for (LuCode luCode : clu.getLuCodes()) {
1225             oldLuCodeMap.put(luCode.getId(), luCode);
1226         }
1227         clu.getLuCodes().clear();
1228 
1229         // Loop through the new list, if the item exists already update and
1230         // remove from the list
1231         // otherwise create a new entry
1232         for (LuCodeInfo luCodeInfo : cluInfo.getLuCodes()) {
1233             LuCode luCode = oldLuCodeMap.remove(luCodeInfo.getId());
1234             if (luCode == null) {
1235                 luCode = new LuCode();
1236             } else {
1237                 if (!String.valueOf(luCode.getVersionNumber()).equals(
1238                         luCodeInfo.getMeta().getVersionInd())) {
1239                     throw new VersionMismatchException(
1240                             "LuCode to be updated is not the current version");
1241                 }
1242             }
1243             // Do Copy
1244             luCode.setAttributes(CluServiceAssembler.toGenericAttributes(
1245                     LuCodeAttribute.class, luCodeInfo.getAttributes(), luCode,
1246                     luDao));
1247 
1248             luCode.setValue(luCodeInfo.getValue());
1249             if (luCodeInfo.getDescr() != null) {
1250                 luCode.setDescr(luCodeInfo.getDescr().getPlain());
1251             }
1252             luCode.setType(luCodeInfo.getTypeKey());
1253             luCode.setClu(clu);
1254 
1255             clu.getLuCodes().add(luCode);
1256         }
1257 
1258         // Now delete anything left over
1259         for (Entry<String, LuCode> entry : oldLuCodeMap.entrySet()) {
1260             luDao.delete(entry.getValue());
1261         }
1262 
1263         // Update the list of AtpTypeKeys
1264         // Get a map of Id->object of all the currently persisted objects in the
1265         // list
1266         Map<String, CluAtpTypeKey> oldOfferedAtpTypesMap = new HashMap<String, CluAtpTypeKey>();
1267         for (CluAtpTypeKey cluAtpTypeKey : clu.getOfferedAtpTypes()) {
1268             oldOfferedAtpTypesMap.put(cluAtpTypeKey.getAtpTypeKey(),
1269                     cluAtpTypeKey);
1270         }
1271         clu.getOfferedAtpTypes().clear();
1272 
1273         // Loop through the new list, if the item exists already update and
1274         // remove from the list
1275         // otherwise create a new entry
1276         for (String atpTypeKey : cluInfo.getOfferedAtpTypes()) {
1277             CluAtpTypeKey cluAtpTypeKey = oldOfferedAtpTypesMap.remove(atpTypeKey);
1278             if (cluAtpTypeKey == null) {
1279                 cluAtpTypeKey = new CluAtpTypeKey();
1280             }
1281             // Do Copy
1282             cluAtpTypeKey.setAtpTypeKey(atpTypeKey);
1283             cluAtpTypeKey.setClu(clu);
1284             clu.getOfferedAtpTypes().add(cluAtpTypeKey);
1285         }
1286 
1287         // Now delete anything left over
1288         for (Entry<String, CluAtpTypeKey> entry : oldOfferedAtpTypesMap.entrySet()) {
1289             luDao.delete(entry.getValue());
1290         }
1291 
1292         if (cluInfo.getFeeInfo() != null) {
1293             if (clu.getFee() == null) {
1294                 clu.setFee(CluServiceAssembler.toCluFee(clu, false, cluInfo.getFeeInfo(), luDao));
1295             } else {
1296                 clu.setFee(CluServiceAssembler.toCluFee(clu, true, cluInfo.getFeeInfo(), luDao));
1297             }
1298         } else if (clu.getFee() != null) {
1299             luDao.delete(clu.getFee());
1300             clu.setFee(null);
1301         }
1302 
1303         if (cluInfo.getAccountingInfo() != null) {
1304             if (clu.getAccounting() == null) {
1305                 clu.setAccounting(new CluAccounting());
1306             }
1307             clu.getAccounting().setAttributes(
1308                     CluServiceAssembler.toGenericAttributes(
1309                             CluAccountingAttribute.class, cluInfo.getAccountingInfo().getAttributes(), clu.getAccounting(), luDao));
1310             clu.getAccounting().setAffiliatedOrgs(CluServiceAssembler.toAffiliatedOrgs(true, clu.getAccounting().getAffiliatedOrgs(),
1311                     cluInfo.getAccountingInfo().getAffiliatedOrgs(),
1312                     luDao));
1313 
1314         } else if (clu.getAccounting() != null) {
1315             clu.setAccounting(null);
1316         }
1317 
1318         clu.setAttributes(CluServiceAssembler.toGenericAttributes(
1319                 CluAttribute.class, cluInfo.getAttributes(), clu, luDao));
1320 
1321         if (cluInfo.getIntensity() != null) {
1322             if (clu.getIntensity() == null) {
1323                 clu.setIntensity(new Amount());
1324             }
1325             clu.getIntensity().setUnitQuantity(cluInfo.getIntensity().getUnitQuantity());
1326             clu.getIntensity().setUnitType(cluInfo.getIntensity().getUnitTypeKey());
1327         } else if (clu.getIntensity() != null) {
1328             luDao.delete(clu.getIntensity());
1329         }
1330 
1331         // Update the list of campusLocations
1332         // Get a map of Id->object of all the currently persisted objects in the
1333         // list
1334         Map<String, CluCampusLocation> oldLocationsMap = new HashMap<String, CluCampusLocation>();
1335         for (CluCampusLocation campus : clu.getCampusLocations()) {
1336             oldLocationsMap.put(campus.getCampusLocation(), campus);
1337         }
1338         clu.getCampusLocations().clear();
1339 
1340         // Loop through the new list, if the item exists already update and
1341         // remove from the list
1342         // otherwise create a new entry
1343         for (String locationName : cluInfo.getCampusLocations()) {
1344             CluCampusLocation location = oldLocationsMap.remove(locationName);
1345             if (location == null) {
1346                 location = new CluCampusLocation();
1347             }
1348             // Do Copy
1349             location.setCampusLocation(locationName);
1350             location.setClu(clu);
1351             clu.getCampusLocations().add(location);
1352         }
1353 
1354         // Now delete anything left over
1355         for (Entry<String, CluCampusLocation> entry : oldLocationsMap.entrySet()) {
1356             luDao.delete(entry.getValue());
1357         }
1358 
1359         // Update the List of accreditations
1360         // Get a map of Id->object of all the currently persisted objects in the
1361         // list
1362         Map<String, CluAccreditation> oldAccreditationMap = new HashMap<String, CluAccreditation>();
1363         for (CluAccreditation cluAccreditation : clu.getAccreditations()) {
1364             oldAccreditationMap.put(cluAccreditation.getId(),
1365                     cluAccreditation);
1366         }
1367         clu.getAccreditations().clear();
1368 
1369         // Loop through the new list, if the item exists already update and
1370         // remove from the list
1371         // otherwise create a new entry
1372         for (AccreditationInfo accreditationInfo : cluInfo.getAccreditations()) {
1373             CluAccreditation cluAccreditation = null;
1374             if (accreditationInfo.getId() != null) {
1375                 cluAccreditation = oldAccreditationMap.remove(accreditationInfo.getId());
1376             }
1377 
1378             if (cluAccreditation == null) {
1379                 cluAccreditation = new CluAccreditation();
1380             }
1381             // Do Copy
1382             BeanUtils.copyProperties(accreditationInfo, cluAccreditation,
1383                     new String[]{"attributes", "meta"});
1384             cluAccreditation.setAttributes(CluServiceAssembler.toGenericAttributes(CluAccreditationAttribute.class,
1385                     accreditationInfo.getAttributes(),
1386                     cluAccreditation, luDao));
1387             clu.getAccreditations().add(cluAccreditation);
1388         }
1389 
1390         // Now delete anything left over
1391         for (Entry<String, CluAccreditation> entry : oldAccreditationMap.entrySet()) {
1392             luDao.delete(entry.getValue());
1393         }
1394 
1395         // Update the List of alternate admin orgs
1396         // Get a map of Id->object of all the currently persisted objects in the
1397         // list
1398         Map<String, CluAdminOrg> oldAdminOrgsMap = new HashMap<String, CluAdminOrg>();
1399         if (clu.getAdminOrgs() != null) {
1400             for (CluAdminOrg cluOrg : clu.getAdminOrgs()) {
1401                 oldAdminOrgsMap.put(cluOrg.getId(), cluOrg);
1402             }
1403         }
1404         clu.setAdminOrgs(new ArrayList<CluAdminOrg>());
1405 
1406         // Loop through the new list, if the item exists already update and
1407         // remove from the list
1408         // otherwise create a new entry
1409         for (AdminOrgInfo orgInfo : cluInfo.getAdminOrgs()) {
1410             CluAdminOrg cluOrg = null;
1411             if (orgInfo.getId() != null) {
1412                 cluOrg = oldAdminOrgsMap.remove(orgInfo.getId());
1413             }
1414 
1415             if (cluOrg == null) {
1416                 cluOrg = new CluAdminOrg();
1417             }
1418 
1419             // Do Copy
1420             BeanUtils.copyProperties(orgInfo, cluOrg,
1421                     new String[]{"attributes", "id"});
1422             cluOrg.setAttributes(CluServiceAssembler.toGenericAttributes(
1423                     CluAdminOrgAttribute.class, orgInfo.getAttributes(),
1424                     cluOrg, luDao));
1425             cluOrg.setClu(clu);
1426             clu.getAdminOrgs().add(cluOrg);
1427         }
1428 
1429         for (Entry<String, CluAdminOrg> entry : oldAdminOrgsMap.entrySet()) {
1430             luDao.delete(entry.getValue());
1431         }
1432 
1433         clu.setHasEarlyDropDeadline(cluInfo.getIsHasEarlyDropDeadline());
1434         clu.setHazardousForDisabledStudents(cluInfo.getIsHazardousForDisabledStudents());
1435         clu.setEnrollable(cluInfo.getIsEnrollable());
1436         clu.setCanCreateLui(cluInfo.getCanCreateLui());
1437 
1438         // Now copy all not standard properties
1439         BeanUtils.copyProperties(cluInfo, clu, new String[]{"luType",
1440                 "officialIdentifier", "alternateIdentifiers", "descr",
1441                 "luCodes", "primaryInstructor", "instructors", "stdDuration",
1442                 "offeredAtpTypes", "feeInfo", "accountingInfo", "attributes",
1443                 "meta", "version", "intensity",
1444                 "campusLocations", "accreditations",
1445                 "adminOrgs", "canCreateLui", "hasEarlyDropDeadline",
1446                 "hazardousForDisabledStudents", "enrollable", ""});
1447         Clu updated = null;
1448         try {
1449             updated = luDao.update(clu);
1450         } catch (Exception e) {
1451             logger.error("Exception occured: ", e);
1452         }
1453         return CluServiceAssembler.toCluInfo(updated);
1454     }
1455 
1456     @Override
1457     @Transactional(readOnly = false)
1458     public StatusInfo deleteClu(String cluId, ContextInfo context) throws DoesNotExistException,
1459             InvalidParameterException, MissingParameterException,
1460             DependentObjectsExistException, OperationFailedException,
1461             PermissionDeniedException {
1462         checkForMissingParameter(cluId, "cluId");
1463         try {
1464             luDao.delete(Clu.class, cluId);
1465         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1466             throw new DoesNotExistException(cluId, ex);
1467         }
1468 
1469         StatusInfo statusInfo = new StatusInfo();
1470         statusInfo.setSuccess(true);
1471 
1472         return statusInfo;
1473     }
1474 
1475     @Override
1476     @Transactional(readOnly = false)
1477     public CluInfo updateCluState(String cluId, String luState, ContextInfo context)
1478             throws DataValidationErrorException, DoesNotExistException,
1479             InvalidParameterException, MissingParameterException,
1480             OperationFailedException, PermissionDeniedException {
1481         // Check Missing params
1482         checkForMissingParameter(cluId, "cluId");
1483         checkForMissingParameter(luState, "luState");
1484         Clu clu;
1485         try {
1486             clu = luDao.fetch(Clu.class, cluId);
1487         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1488             throw new DoesNotExistException(cluId, ex);
1489         }
1490         clu.setState(luState);
1491         Clu updated = luDao.update(clu);
1492         return CluServiceAssembler.toCluInfo(updated);
1493     }
1494 
1495     @Override
1496     public List<ValidationResultInfo> validateCluCluRelation(String validationTypeKey,
1497                                                              String cluId, String relatedCluId,
1498                                                              String cluCluRelationTypeKey,
1499                                                              CluCluRelationInfo cluCluRelationInfo, ContextInfo contextInfo) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
1500 
1501         checkForMissingParameter(validationTypeKey, "validationTypeKey");
1502         checkForMissingParameter(cluCluRelationInfo, "cluCluRelationInfo");
1503 
1504         ObjectStructureDefinition objStructure = this.getObjectStructure(CluCluRelationInfo.class.getName());
1505         Validator defaultValidator = validatorFactory.getValidator();
1506 
1507         List<org.kuali.student.r2.common.dto.ValidationResultInfo> vris = defaultValidator.validateObject(cluCluRelationInfo, objStructure, null);
1508         return vris;
1509     }
1510 
1511     @Override
1512     @Transactional(readOnly = false)
1513     public CluCluRelationInfo createCluCluRelation(String cluId,
1514                                                    String relatedCluId, String luLuRelationTypeKey,
1515                                                    CluCluRelationInfo cluCluRelationInfo, ContextInfo context)
1516             throws DataValidationErrorException,
1517             DoesNotExistException, InvalidParameterException,
1518             MissingParameterException, OperationFailedException,
1519             PermissionDeniedException, CircularRelationshipException {
1520         checkForMissingParameter(cluId, "cluId");
1521         checkForMissingParameter(relatedCluId, "relatedCluId");
1522         checkForMissingParameter(luLuRelationTypeKey, "luLuRelationTypeKey");
1523         checkForMissingParameter(cluCluRelationInfo, "cluCluRelationInfo");
1524 
1525         if (cluId.equals(relatedCluId)) {
1526             throw new CircularRelationshipException(
1527                     "Can not relate a Clu to itself");
1528         }
1529 
1530         // Validate CluCluRelationInfo
1531         List<ValidationResultInfo> val =
1532                 validateCluCluRelation("SYSTEM", cluId, relatedCluId, luLuRelationTypeKey, cluCluRelationInfo, context);
1533         if (null != val && val.size() > 0) {
1534             throw new DataValidationErrorException("Validation error!", val);
1535         }
1536 
1537 
1538         Clu clu;
1539         try {
1540             clu = luDao.fetch(Clu.class, cluId);
1541         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1542             throw new DoesNotExistException(cluId, ex);
1543         }
1544         Clu relatedClu;
1545         try {
1546             relatedClu = luDao.fetch(Clu.class, relatedCluId);
1547         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1548             throw new DoesNotExistException(relatedCluId, ex);
1549         }
1550 
1551         CluCluRelation cluCluRelation = new CluCluRelation();
1552         BeanUtils.copyProperties(cluCluRelationInfo, cluCluRelation,
1553                 new String[]{"cluId", "relatedCluId",
1554                         "isCluRelationRequired", "attributes", "meta"});
1555 
1556         cluCluRelation.setClu(clu);
1557         cluCluRelation.setRelatedClu(relatedClu);
1558         cluCluRelation.setCluRelationRequired(cluCluRelationInfo.getIsCluRelationRequired() == null ? true : cluCluRelationInfo.getIsCluRelationRequired()); // TODO maybe this is unnecessary,
1559         // contract specifies not null
1560         cluCluRelation.setAttributes(CluServiceAssembler.toGenericAttributes(
1561                 CluCluRelationAttribute.class, cluCluRelationInfo.getAttributes(), cluCluRelation, luDao));
1562 
1563         LuLuRelationType luLuRelationType;
1564         try {
1565             luLuRelationType = luDao.fetch(LuLuRelationType.class,
1566                     luLuRelationTypeKey);
1567         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1568             throw new DoesNotExistException(luLuRelationTypeKey, ex);
1569         }
1570 
1571         cluCluRelation.setLuLuRelationType(luLuRelationType);
1572 
1573         luDao.create(cluCluRelation);
1574 
1575         return CluServiceAssembler.toCluCluRelationInfo(cluCluRelation);
1576     }
1577 
1578     @Override
1579     @Transactional(readOnly = false)
1580     public CluCluRelationInfo updateCluCluRelation(
1581             final String cluCluRelationId,
1582             final CluCluRelationInfo cluCluRelationInfo, ContextInfo context)
1583             throws DataValidationErrorException, DoesNotExistException,
1584             InvalidParameterException, MissingParameterException,
1585             OperationFailedException, PermissionDeniedException,
1586             VersionMismatchException {
1587         checkForMissingParameter(cluCluRelationId, "cluCluRelationId");
1588         checkForMissingParameter(cluCluRelationInfo, "cluCluRelationInfo");
1589 
1590         // Validate CluCluRelationInfo
1591         List<ValidationResultInfo> val =
1592                 validateCluCluRelation("SYSTEM",
1593                         cluCluRelationInfo.getCluId(),
1594                         cluCluRelationInfo.getRelatedCluId(),
1595                         cluCluRelationInfo.getTypeKey(),
1596                         cluCluRelationInfo,
1597                         context);
1598         if (null != val && val.size() > 0) {
1599             throw new DataValidationErrorException("Validation error!", val);
1600         }
1601 
1602         final CluCluRelation cluCluRelation;
1603         try {
1604             cluCluRelation = luDao.fetch(CluCluRelation.class,
1605                     cluCluRelationId);
1606         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1607             throw new DoesNotExistException(cluCluRelationId, ex);
1608         }
1609         BeanUtils.copyProperties(cluCluRelationInfo, cluCluRelation,
1610                 new String[]{"cluId", "relatedCluId",
1611                         "isCluRelationRequired", "attributes", "meta"});
1612         try {
1613             cluCluRelation.setClu(luDao.fetch(Clu.class, cluCluRelationInfo.getCluId()));
1614         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1615             throw new DoesNotExistException(cluCluRelationInfo.getCluId(), ex);
1616         }
1617         try {
1618             cluCluRelation.setRelatedClu(luDao.fetch(Clu.class, cluCluRelationInfo.getRelatedCluId()));
1619         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1620             throw new DoesNotExistException(cluCluRelationInfo.getRelatedCluId(), ex);
1621         }
1622         cluCluRelation.setCluRelationRequired(cluCluRelationInfo.getIsCluRelationRequired() == null ? true : cluCluRelationInfo.getIsCluRelationRequired()); // TODO maybe this is unnecessary,
1623         // contract specifies not null
1624         cluCluRelation.setAttributes(CluServiceAssembler.toGenericAttributes(
1625                 CluCluRelationAttribute.class, cluCluRelationInfo.getAttributes(), cluCluRelation, luDao));
1626         try {
1627             cluCluRelation.setLuLuRelationType(luDao.fetch(LuLuRelationType.class,
1628                     cluCluRelationInfo.getTypeKey()));
1629         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1630             throw new DoesNotExistException(cluCluRelationInfo.getTypeKey(), ex);
1631         }
1632 
1633         final CluCluRelation update = luDao.update(cluCluRelation);
1634 
1635         return CluServiceAssembler.toCluCluRelationInfo(update);
1636     }
1637 
1638     @Override
1639     @Transactional(readOnly = false)
1640     public StatusInfo deleteCluCluRelation(String cluCluRelationId, ContextInfo context)
1641             throws DoesNotExistException, InvalidParameterException,
1642             MissingParameterException, OperationFailedException,
1643             PermissionDeniedException {
1644         checkForMissingParameter(cluCluRelationId, "cluCluRelationId");
1645         try {
1646             luDao.delete(CluCluRelation.class, cluCluRelationId);
1647         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1648             throw new DoesNotExistException(cluCluRelationId, ex);
1649         }
1650 
1651         StatusInfo statusInfo = new StatusInfo();
1652         statusInfo.setSuccess(true);
1653 
1654         return statusInfo;
1655     }
1656 
1657     @Override
1658     public List<ValidationResultInfo> validateCluPublication(
1659             String validationType, String cluId, String luPublicationTypeKey,
1660             CluPublicationInfo cluPublicationInfo, ContextInfo context)
1661             throws DoesNotExistException, InvalidParameterException,
1662             MissingParameterException, OperationFailedException {
1663 
1664         checkForMissingParameter(validationType, "validationType");
1665         checkForMissingParameter(cluPublicationInfo, "cluPublicationInfo");
1666 
1667         ObjectStructureDefinition objStructure = this.getObjectStructure(CluPublicationInfo.class.getName());
1668         Validator defaultValidator = validatorFactory.getValidator();
1669         List<org.kuali.student.r2.common.dto.ValidationResultInfo> vris =
1670                 defaultValidator.validateObject(cluPublicationInfo, objStructure, null);
1671         return vris;
1672     }
1673 
1674     @Override
1675     @Transactional(readOnly = false)
1676     public CluPublicationInfo createCluPublication(String cluId,
1677                                                    String luPublicationType, CluPublicationInfo cluPublicationInfo, ContextInfo context)
1678             throws DataValidationErrorException,
1679             InvalidParameterException, MissingParameterException,
1680             OperationFailedException, PermissionDeniedException {
1681         checkForMissingParameter(cluId, "cluId");
1682         checkForMissingParameter(luPublicationType, "luPublicationType");
1683         checkForMissingParameter(cluPublicationInfo, "cluPublicationInfo");
1684 
1685         // Validate CLU
1686         List<ValidationResultInfo> val;
1687         try {
1688             val = validateCluPublication("SYSTEM", cluId, luPublicationType, cluPublicationInfo, context);
1689             if (null != val && val.size() > 0) {
1690                 throw new DataValidationErrorException("Validation error!", val);
1691             }
1692         } catch (DoesNotExistException e) {
1693             throw new OperationFailedException("Error creating clu", e);
1694         }
1695 
1696 
1697         CluPublication cluPub = new CluPublication();
1698         Clu clu;
1699         try {
1700             clu = luDao.fetch(Clu.class, cluId);
1701         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException e) {
1702             throw new InvalidParameterException("Clu does not exist for id:" + cluId);
1703         }
1704 
1705         CluPublicationType type;
1706         try {
1707             type = luDao.fetch(CluPublicationType.class, luPublicationType);
1708         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException e) {
1709             throw new InvalidParameterException("CluPublication Type does not exist for id:" + luPublicationType);
1710         }
1711 
1712         cluPub.setClu(clu);
1713         cluPub.setId(cluPublicationInfo.getId());
1714         cluPub.setEndCycle(cluPublicationInfo.getEndCycle());
1715         cluPub.setStartCycle(cluPublicationInfo.getStartCycle());
1716         cluPub.setEffectiveDate(cluPublicationInfo.getEffectiveDate());
1717         cluPub.setExpirationDate(cluPublicationInfo.getExpirationDate());
1718         cluPub.setState(cluPublicationInfo.getStateKey());
1719         cluPub.setType(type);
1720         cluPub.setAttributes(CluServiceAssembler.toGenericAttributes(CluPublicationAttribute.class, cluPublicationInfo.getAttributes(), cluPub, luDao));
1721         cluPub.setVariants(CluServiceAssembler.toCluPublicationVariants(cluPublicationInfo.getVariants(), cluPub, luDao));
1722 
1723         luDao.create(cluPub);
1724 
1725         return CluServiceAssembler.toCluPublicationInfo(cluPub);
1726     }
1727 
1728     @Override
1729     @Transactional(readOnly = false)
1730     public CluPublicationInfo updateCluPublication(String cluPublicationId,
1731                                                    CluPublicationInfo cluPublicationInfo, ContextInfo context)
1732             throws DataValidationErrorException, DoesNotExistException,
1733             InvalidParameterException, MissingParameterException,
1734             OperationFailedException, PermissionDeniedException,
1735             VersionMismatchException {
1736         checkForMissingParameter(cluPublicationId, "cluPublicationId");
1737         checkForMissingParameter(cluPublicationInfo, "cluPublicationInfo");
1738 
1739         // Validate CLU
1740         List<ValidationResultInfo> val;
1741 
1742         val = validateCluPublication("SYSTEM",
1743                 cluPublicationInfo.getCluId(),
1744                 cluPublicationInfo.getTypeKey(),
1745                 cluPublicationInfo, context);
1746         if (null != val && val.size() > 0) {
1747             throw new DataValidationErrorException("Validation error!", val);
1748         }
1749 
1750         CluPublication cluPub;
1751         try {
1752             cluPub = luDao.fetch(CluPublication.class, cluPublicationId);
1753         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1754             throw new DoesNotExistException(ex.getMessage(), ex);
1755         }
1756 
1757         if (!String.valueOf(cluPub.getVersionNumber()).equals(
1758                 cluPublicationInfo.getMeta().getVersionInd())) {
1759             throw new VersionMismatchException(
1760                     "CluPublication to be updated is not the current version");
1761         }
1762 
1763         Clu clu;
1764         try {
1765             clu = luDao.fetch(Clu.class, cluPublicationInfo.getCluId());
1766         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException e) {
1767             throw new InvalidParameterException("Clu does not exist for id:" + cluPublicationInfo.getCluId());
1768         }
1769 
1770         CluPublicationType type;
1771         try {
1772             type = luDao.fetch(CluPublicationType.class, cluPublicationInfo.getTypeKey());
1773         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException e) {
1774             throw new InvalidParameterException("CluPublication Type does not exist for id:" + cluPublicationInfo.getTypeKey());
1775         }
1776 
1777         // Update the list of variants
1778         // Get a map of Id->object of all the currently persisted objects in the
1779         // list
1780         Map<String, CluPublicationVariant> oldVariantMap = new HashMap<String, CluPublicationVariant>();
1781         for (CluPublicationVariant variant : cluPub.getVariants()) {
1782             oldVariantMap.put(variant.getKey(), variant);
1783         }
1784         cluPub.getVariants().clear();
1785 
1786         // Loop through the new list, if the item exists already update and
1787         // remove from the list otherwise create a new entry
1788         CluPublicationVariant variant = null;
1789         for (FieldInfo fieldInfo : cluPublicationInfo.getVariants()) {
1790             if (!oldVariantMap.containsKey(fieldInfo.getId())) {
1791                 // New variant key
1792                 variant = new CluPublicationVariant();
1793                 variant.setKey(fieldInfo.getId());
1794                 variant.setValue(fieldInfo.getValue());
1795             } else {
1796                 // Update existing variant
1797                 variant = oldVariantMap.get(fieldInfo.getId());
1798                 variant.setValue(fieldInfo.getValue());
1799                 oldVariantMap.remove(fieldInfo.getId());
1800             }
1801 
1802             cluPub.getVariants().add(variant);
1803         }
1804 
1805         // Now delete anything left over
1806         for (Entry<String, CluPublicationVariant> entry : oldVariantMap.entrySet()) {
1807             luDao.delete(entry.getValue());
1808         }
1809 
1810         cluPub.setClu(clu);
1811         cluPub.setEndCycle(cluPublicationInfo.getEndCycle());
1812         cluPub.setStartCycle(cluPublicationInfo.getStartCycle());
1813         cluPub.setEffectiveDate(cluPublicationInfo.getEffectiveDate());
1814         cluPub.setExpirationDate(cluPublicationInfo.getExpirationDate());
1815         cluPub.setState(cluPublicationInfo.getStateKey());
1816         cluPub.setType(type);
1817         cluPub.setAttributes(CluServiceAssembler.toGenericAttributes(CluPublicationAttribute.class, cluPublicationInfo.getAttributes(), cluPub, luDao));
1818 
1819         CluPublication updated = luDao.update(cluPub);
1820 
1821         return CluServiceAssembler.toCluPublicationInfo(updated);
1822     }
1823 
1824     @Override
1825     @Transactional(readOnly = false)
1826     public StatusInfo deleteCluPublication(String cluPublicationId, ContextInfo context)
1827             throws DoesNotExistException, InvalidParameterException,
1828             MissingParameterException, DependentObjectsExistException,
1829             OperationFailedException, PermissionDeniedException {
1830         checkForMissingParameter(cluPublicationId, "cluPublicationId");
1831         try {
1832             luDao.delete(CluPublication.class, cluPublicationId);
1833         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1834             throw new DoesNotExistException(cluPublicationId, ex);
1835         }
1836 
1837         StatusInfo statusInfo = new StatusInfo();
1838         statusInfo.setSuccess(true);
1839 
1840         return statusInfo;
1841     }
1842 
1843     @Override
1844     public List<ValidationResultInfo> validateCluResult(String validationType,
1845                                                         String cluId, String cluResultTypeKey,
1846                                                         CluResultInfo cluResultInfo, ContextInfo context)
1847             throws DoesNotExistException,
1848             InvalidParameterException, MissingParameterException,
1849             OperationFailedException {
1850         checkForMissingParameter(validationType, "validationType");
1851         checkForMissingParameter(cluResultInfo, "cluResultInfo");
1852 
1853         ObjectStructureDefinition objStructure = this.getObjectStructure(CluResultInfo.class.getName());
1854         Validator defaultValidator = validatorFactory.getValidator();
1855         List<org.kuali.student.r2.common.dto.ValidationResultInfo> vris = defaultValidator.validateObject(cluResultInfo, objStructure, null);
1856         return vris;
1857     }
1858 
1859     @Override
1860     @Transactional(readOnly = false)
1861     public CluResultInfo createCluResult(String cluId, String cluResultTypeKey,
1862                                          CluResultInfo cluResultInfo, ContextInfo context) throws
1863             DataValidationErrorException, InvalidParameterException,
1864             MissingParameterException, OperationFailedException,
1865             PermissionDeniedException, DoesNotExistException {
1866 
1867         checkForMissingParameter(cluId, "cluId");
1868         checkForMissingParameter(cluResultTypeKey, "cluResultTypeKey");
1869         checkForMissingParameter(cluResultInfo, "cluResultInfo");
1870 
1871         // Validate CluResult
1872         List<ValidationResultInfo> val = validateCluResult("SYSTEM",
1873                 cluResultInfo.getCluId(),
1874                 cluResultInfo.getTypeKey(),
1875                 cluResultInfo, context);
1876         if (null != val && val.size() > 0) {
1877             throw new DataValidationErrorException("Validation error!", val);
1878         }
1879 
1880         cluResultInfo.setTypeKey(cluResultTypeKey);
1881         cluResultInfo.setCluId(cluId);
1882 
1883         List<ResultOption> resOptList = new ArrayList<ResultOption>();
1884         for (ResultOptionInfo resOptInfo : cluResultInfo.getResultOptions()) {
1885             ResultOption resOpt = new ResultOption();
1886             BeanUtils.copyProperties(resOptInfo, resOpt, new String[]{"id",
1887                     "meta", "resultUsageType", "desc"});
1888 
1889             if (resOptInfo.getResultUsageTypeKey() != null) {
1890                 ResultUsageType resUsageType;
1891                 try {
1892                     resUsageType = luDao.fetch(ResultUsageType.class,
1893                             resOptInfo.getResultUsageTypeKey());
1894                 } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1895                     throw new DoesNotExistException(resOptInfo.getResultUsageTypeKey(), ex);
1896                 }
1897                 resOpt.setResultUsageType(resUsageType);
1898             }
1899             resOpt.setDesc(CluServiceAssembler.toRichText(LuRichText.class, resOptInfo.getDescr()));
1900             resOpt.setCreateId(context.getPrincipalId());
1901             resOpt.setCreateTime(context.getCurrentDate());
1902             luDao.create(resOpt);
1903             resOptList.add(resOpt);
1904         }
1905 
1906         CluResult cluResult = new CluResult();
1907         BeanUtils.copyProperties(cluResultInfo, cluResult, new String[]{"id",
1908                 "desc", "resultOptions", "meta"});
1909 
1910         cluResult.setDesc(CluServiceAssembler.toRichText(LuRichText.class, cluResultInfo.getDescr()));
1911         cluResult.setResultOptions(resOptList);
1912 
1913         Clu clu;
1914         try {
1915             clu = luDao.fetch(Clu.class, cluId);
1916         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1917             throw new DoesNotExistException(cluId, ex);
1918         }
1919         cluResult.setClu(clu);
1920 
1921         CluResultType type;
1922         try {
1923             type = luDao.fetch(CluResultType.class, cluResultTypeKey);
1924         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1925             throw new DoesNotExistException(cluResultTypeKey, ex);
1926         }
1927         cluResult.setCluResultType(type);
1928         cluResult.setCreateId(context.getPrincipalId());
1929         cluResult.setCreateTime(context.getCurrentDate());
1930 
1931         luDao.create(cluResult);
1932 
1933         return CluServiceAssembler.toCluResultInfo(cluResult);
1934     }
1935 
1936     @Override
1937     @Transactional(readOnly = false)
1938     public CluResultInfo updateCluResult(String cluResultId,
1939                                          CluResultInfo cluResultInfo, ContextInfo context) throws DataValidationErrorException,
1940             DoesNotExistException, InvalidParameterException,
1941             MissingParameterException, OperationFailedException,
1942             PermissionDeniedException, VersionMismatchException {
1943 
1944         checkForMissingParameter(cluResultId, "cluResultId");
1945         checkForMissingParameter(cluResultInfo, "cluResultInfo");
1946 
1947         // Validate CluResult
1948         List<ValidationResultInfo> val = validateCluResult("SYSTEM",
1949                 cluResultInfo.getCluId(),
1950                 cluResultInfo.getTypeKey(),
1951                 cluResultInfo,
1952                 context);
1953         if (null != val && val.size() > 0) {
1954             throw new DataValidationErrorException("Validation error!", val);
1955         }
1956 
1957         CluResult result;
1958         try {
1959             result = luDao.fetch(CluResult.class, cluResultId);
1960         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1961             throw new DoesNotExistException(cluResultId,ex);
1962         }
1963         if (!String.valueOf(result.getVersionNumber()).equals(
1964                 cluResultInfo.getMeta().getVersionInd())) {
1965             throw new VersionMismatchException(
1966                     "CluResult to be updated is not the current version");
1967         }
1968 
1969         // Update the list of resultoptions
1970         // Get a map of Id->object of all the currently persisted objects in the
1971         // list
1972         Map<String, ResultOption> oldResultOptionMap = new HashMap<String, ResultOption>();
1973         for (ResultOption opt : result.getResultOptions()) {
1974             oldResultOptionMap.put(opt.getId(), opt);
1975         }
1976         result.getResultOptions().clear();
1977 
1978         // Loop through the new list, if the item exists already update and
1979         // remove from the list otherwise create a new entry
1980         for (ResultOptionInfo resOptInfo : cluResultInfo.getResultOptions()) {
1981             ResultOption opt = oldResultOptionMap.remove(resOptInfo.getId());
1982             if (opt == null) {
1983                 // New result option
1984                 opt = new ResultOption();
1985                 // Copy properties
1986                 BeanUtils.copyProperties(resOptInfo, opt, new String[]{
1987                         "resultUsageType", "desc"});
1988                 opt.setCreateId(context.getPrincipalId());
1989                 opt.setCreateTime(context.getCurrentDate());
1990             } else {
1991                 try {
1992                     // Get existing result option
1993                     opt = luDao.fetch(ResultOption.class, resOptInfo.getId());
1994                 } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
1995                     throw new DoesNotExistException(resOptInfo.getId(), ex);
1996                 }
1997                 // Copy properties
1998                 BeanUtils.copyProperties(resOptInfo, opt, new String[]{
1999                         "id", "resultUsageType", "desc"});
2000             }
2001             if (resOptInfo.getResultUsageTypeKey() != null && !resOptInfo.getResultUsageTypeKey().isEmpty()) {
2002                 ResultUsageType resUsageType;
2003                 try {
2004                     resUsageType = luDao.fetch(ResultUsageType.class,
2005                             resOptInfo.getResultUsageTypeKey());
2006                 } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2007                     throw new DoesNotExistException(resOptInfo.getResultUsageTypeKey(), ex);
2008                 }
2009                 opt.setResultUsageType(resUsageType);
2010             }
2011             opt.setDesc(CluServiceAssembler.toRichText(LuRichText.class, resOptInfo.getDescr()));
2012             result.getResultOptions().add(opt);
2013         }
2014 
2015         // Now delete anything left over
2016         for (Entry<String, ResultOption> entry : oldResultOptionMap.entrySet()) {
2017             luDao.delete(entry.getValue());
2018         }
2019 
2020         BeanUtils.copyProperties(cluResultInfo, result, new String[]{"id",
2021                 "desc", "resultOptions"});
2022 
2023         result.setDesc(CluServiceAssembler.toRichText(LuRichText.class, cluResultInfo.getDescr()));
2024         CluResultType type;
2025         try {
2026             type = luDao.fetch(CluResultType.class, cluResultInfo.getTypeKey());
2027         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2028             throw new DoesNotExistException(cluResultInfo.getTypeKey(), ex);
2029         }
2030         result.setCluResultType(type);
2031 
2032         CluResult updated = luDao.update(result);
2033 
2034         return CluServiceAssembler.toCluResultInfo(updated);
2035     }
2036 
2037     @Override
2038     @Transactional(readOnly = false)
2039     public StatusInfo deleteCluResult(String cluResultId, ContextInfo context)
2040             throws DoesNotExistException, InvalidParameterException,
2041             MissingParameterException, DependentObjectsExistException,
2042             OperationFailedException, PermissionDeniedException {
2043 
2044         checkForMissingParameter(cluResultId, "cluResultId");
2045         try {
2046             luDao.delete(CluResult.class, cluResultId);
2047         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2048             throw new DoesNotExistException(cluResultId, ex);
2049         }
2050 
2051         StatusInfo statusInfo = new StatusInfo();
2052         statusInfo.setSuccess(true);
2053 
2054         return statusInfo;
2055     }
2056 
2057     @Override
2058     public List<ValidationResultInfo> validateCluLoRelation(
2059             String validationType,
2060             String cluId,
2061             String loId,
2062             String cluLoRelationType,
2063             CluLoRelationInfo cluLoRelationInfo, ContextInfo context)
2064             throws DoesNotExistException, InvalidParameterException,
2065             MissingParameterException, OperationFailedException {
2066 
2067         checkForMissingParameter(validationType, "validationType");
2068         checkForMissingParameter(cluLoRelationInfo, "cluLoRelationInfo");
2069 
2070         ObjectStructureDefinition objStructure = this.getObjectStructure(CluLoRelation.class.getName());
2071         Validator defaultValidator = validatorFactory.getValidator();
2072         List<org.kuali.student.r2.common.dto.ValidationResultInfo> vris = defaultValidator.validateObject(cluLoRelationInfo, objStructure, null);
2073         return vris;
2074     }
2075 
2076     @Override
2077     @Transactional(readOnly = false)
2078     public CluLoRelationInfo createCluLoRelation(String cluId, String loId,
2079                                                  String cluLoRelationType, CluLoRelationInfo cluLoRelationInfo, ContextInfo context)
2080             throws DoesNotExistException,
2081             InvalidParameterException, MissingParameterException,
2082             OperationFailedException, PermissionDeniedException, DataValidationErrorException {
2083         checkForMissingParameter(loId, "loId");
2084         checkForMissingParameter(cluId, "cluId");
2085         checkForEmptyList(cluLoRelationType, "cluLoRelationType");
2086         checkForEmptyList(cluLoRelationInfo, "cluLoRelationInfo");
2087 
2088         // Validate CluLoRelation
2089         List<ValidationResultInfo> val = validateCluLoRelation("SYSTEM", cluId, loId, cluLoRelationType, cluLoRelationInfo, context);
2090         if (null != val && val.size() > 0) {
2091             throw new DataValidationErrorException("Validation error!", val);
2092         }
2093 
2094         Clu clu;
2095         try {
2096             clu = luDao.fetch(Clu.class, cluId);
2097         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2098             throw new DoesNotExistException(cluId, ex);
2099         }
2100         if (clu == null) {
2101             throw new DoesNotExistException("Clu does not exist for id: "
2102                     + cluId);
2103         }
2104 
2105         CluLoRelationType cluLoRelationTypeEntity;
2106         try {
2107             cluLoRelationTypeEntity = luDao.fetch(CluLoRelationType.class, cluLoRelationType);
2108         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2109             throw new DoesNotExistException(cluLoRelationType, ex);
2110         }
2111         if (cluLoRelationTypeEntity == null) {
2112             throw new DoesNotExistException("CluLoRelationType does not exist for id: "
2113                     + cluLoRelationType);
2114         }
2115 
2116         // Check to see if this relation already exists
2117         List<CluLoRelation> reltns = luDao.getCluLoRelationsByCludIdAndLoId(
2118                 cluId, loId);
2119         if (reltns.size() > 0) {
2120             // TODO: take this check out? R1 had this throwing an aleady exists exception but it is possible 
2121             // though unlikely for there to be two different relations between the same clu and Lo
2122             // So we took out the AlreadyExistException in R2 BUT something might be depending on this
2123             // So I kept it throwing an exception but changed it to throw an OperationFailed instead
2124             throw new OperationFailedException(
2125                     "Relation already exists for cluId:" + cluId + " and Lo:"
2126                             + loId);
2127         }
2128 
2129         CluLoRelation cluLoRelation = new CluLoRelation();
2130         BeanUtils.copyProperties(cluLoRelationInfo, cluLoRelation,
2131                 new String[]{"cluId", "attributes", "meta", "type"});
2132 
2133         cluLoRelation.setClu(clu);
2134         cluLoRelation.setAttributes(CluServiceAssembler.toGenericAttributes(
2135                 CluLoRelationAttribute.class,
2136                 cluLoRelationInfo.getAttributes(), cluLoRelation, luDao));
2137         cluLoRelation.setType(cluLoRelationTypeEntity);
2138 
2139         luDao.create(cluLoRelation);
2140 
2141         return CluServiceAssembler.toCluLoRelationInfo(cluLoRelation);
2142     }
2143 
2144     @Override
2145     @Transactional(readOnly = false)
2146     public CluLoRelationInfo updateCluLoRelation(String cluLoRelationId,
2147                                                  CluLoRelationInfo cluLoRelationInfo, ContextInfo context)
2148             throws DataValidationErrorException, DoesNotExistException,
2149             InvalidParameterException, MissingParameterException,
2150             OperationFailedException, PermissionDeniedException,
2151             VersionMismatchException {
2152         checkForMissingParameter(cluLoRelationId, "cluLoRelationId");
2153         checkForMissingParameter(cluLoRelationInfo, "cluLoRelationInfo");
2154 
2155         // Validate CluLoRelation
2156         List<ValidationResultInfo> val = validateCluLoRelation("SYSTEM",
2157                 cluLoRelationInfo.getCluId(),
2158                 cluLoRelationInfo.getLoId(),
2159                 cluLoRelationInfo.getTypeKey(),
2160                 cluLoRelationInfo, context);
2161         if (null != val && val.size() > 0) {
2162             throw new DataValidationErrorException("Validation error!", val);
2163         }
2164 
2165         CluLoRelation reltn;
2166         try {
2167             reltn = luDao.fetch(CluLoRelation.class, cluLoRelationId);
2168         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2169             throw new DoesNotExistException(cluLoRelationId, ex);
2170         }
2171 
2172         if (!String.valueOf(reltn.getVersionNumber()).equals(
2173                 cluLoRelationInfo.getMeta().getVersionInd())) {
2174             throw new VersionMismatchException(
2175                     "CluLoRelation to be updated is not the current version");
2176         }
2177 
2178         Clu clu;
2179         try {
2180             clu = luDao.fetch(Clu.class, cluLoRelationInfo.getCluId());
2181         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2182             throw new DoesNotExistException(cluLoRelationInfo.getCluId(), ex);
2183         }
2184         if (clu == null) {
2185             throw new DoesNotExistException("Clu does not exist for id: "
2186                     + cluLoRelationInfo.getCluId());
2187         }
2188 
2189         CluLoRelationType cluLoRelationTypeEntity;
2190         try {
2191             cluLoRelationTypeEntity = luDao.fetch(CluLoRelationType.class, cluLoRelationInfo.getTypeKey());
2192         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2193             throw new DoesNotExistException(cluLoRelationInfo.getTypeKey(), ex);
2194         }
2195         if (cluLoRelationTypeEntity == null) {
2196             throw new DoesNotExistException("CluLoRelationType does not exist for id: "
2197                     + cluLoRelationInfo.getTypeKey());
2198         }
2199 
2200         BeanUtils.copyProperties(cluLoRelationInfo, reltn, new String[]{
2201                 "cluId", "attributes", "meta", "type"});
2202 
2203         reltn.setClu(clu);
2204         reltn.setAttributes(CluServiceAssembler.toGenericAttributes(
2205                 CluLoRelationAttribute.class,
2206                 cluLoRelationInfo.getAttributes(), reltn, luDao));
2207         reltn.setType(cluLoRelationTypeEntity);
2208         CluLoRelation updated = luDao.update(reltn);
2209 
2210         return CluServiceAssembler.toCluLoRelationInfo(updated);
2211     }
2212 
2213     @Override
2214     @Transactional(readOnly = false)
2215     public StatusInfo deleteCluLoRelation(String cluLoRelationId, ContextInfo context)
2216             throws DoesNotExistException, InvalidParameterException,
2217             MissingParameterException, OperationFailedException,
2218             PermissionDeniedException {
2219         checkForMissingParameter(cluLoRelationId, "cluLoRelationId");
2220 
2221         CluLoRelation reltn;
2222         try {
2223             reltn = luDao.fetch(CluLoRelation.class, cluLoRelationId);
2224         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2225             throw new DoesNotExistException(cluLoRelationId);
2226         }
2227         if (reltn == null) {
2228             throw new DoesNotExistException(
2229                     "CluLoRelation does not exist for id: " + cluLoRelationId);
2230         }
2231         try {
2232             luDao.delete(CluLoRelation.class, cluLoRelationId);
2233         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2234             throw new DoesNotExistException(cluLoRelationId, ex);
2235         }
2236 
2237         StatusInfo statusInfo = new StatusInfo();
2238         statusInfo.setSuccess(true);
2239 
2240         return statusInfo;
2241     }
2242 
2243     @Override
2244     @Transactional(readOnly = false)
2245     public StatusInfo addCluResourceRequirement(String resourceTypeKey,
2246                                                 String cluId, ContextInfo context) throws AlreadyExistsException, DoesNotExistException,
2247             InvalidParameterException, MissingParameterException,
2248             OperationFailedException, PermissionDeniedException {
2249         throw new OperationFailedException("not yet implemented");
2250     }
2251 
2252     @Override
2253     @Transactional(readOnly = false)
2254     public StatusInfo removeCluResourceRequirement(String resourceTypeKey,
2255                                                    String cluId, ContextInfo context) throws DoesNotExistException,
2256             InvalidParameterException, MissingParameterException,
2257             OperationFailedException, PermissionDeniedException {
2258         return null;
2259     }
2260 
2261     @Override
2262     public List<ValidationResultInfo> validateCluSet(String validationType,
2263                                                      String cluSetType,
2264                                                      CluSetInfo cluSetInfo, ContextInfo context) throws DoesNotExistException,
2265             InvalidParameterException, MissingParameterException,
2266             OperationFailedException {
2267         checkForMissingParameter(validationType, "validationType");
2268         checkForMissingParameter(cluSetInfo, "cluSetInfo");
2269 
2270         ObjectStructureDefinition objStructure = this.getObjectStructure(CluSetInfo.class.getName());
2271         Validator defaultValidator = validatorFactory.getValidator();
2272         List<org.kuali.student.r2.common.dto.ValidationResultInfo> vris = defaultValidator.validateObject(cluSetInfo, objStructure, null);
2273         return vris;
2274     }
2275 
2276     @Override
2277     @Transactional(readOnly = false)
2278     public CluSetInfo createCluSet(String cluSetType, CluSetInfo cluSetInfo, ContextInfo contextInfo)
2279             throws DataValidationErrorException,
2280             InvalidParameterException, MissingParameterException,
2281             OperationFailedException, PermissionDeniedException,
2282             UnsupportedActionException {
2283 
2284         checkForMissingParameter(cluSetType, "cluSetType");
2285         checkForMissingParameter(cluSetInfo, "cluSetInfo");
2286 
2287         cluSetInfo.setTypeKey(cluSetType);
2288 
2289         validateCluSet(cluSetInfo);
2290 
2291         // Validate CluSet
2292         List<ValidationResultInfo> val;
2293         try {
2294             val = validateCluSet("SYSTEM",
2295                     cluSetInfo.getTypeKey(),
2296                     cluSetInfo, contextInfo);
2297         } catch (DoesNotExistException e) {
2298             throw new DataValidationErrorException("Validation error! " + e.getMessage(), e);
2299         }
2300         if (null != val && val.size() > 0) {
2301             throw new DataValidationErrorException("Validation error!", val);
2302         }
2303 
2304         List<String> cluIds = getMembershipQuerySearchResult(cluSetInfo.getMembershipQuery(), contextInfo);
2305 
2306         CluSet cluSet = null;
2307         try {
2308             cluSet = CluServiceAssembler.toCluSetEntity(cluSetInfo, this.luDao);
2309         } catch (DoesNotExistException e) {
2310             throw new DataValidationErrorException("Creating CluSet entity failed. Clu or CluSet does not exist: " + e.getMessage(), e);
2311         }
2312 
2313         cluSet = luDao.create(cluSet);
2314 
2315         CluSetInfo newCluSetInfo = CluServiceAssembler.toCluSetInfo(cluSet);
2316 
2317         if (cluIds != null) {
2318             newCluSetInfo.getCluIds().addAll(cluIds);
2319         }
2320 
2321         return newCluSetInfo;
2322     }
2323 
2324     private void setMembershipQuerySearchResult(CluSetInfo cluSetInfo, ContextInfo contextInfo) throws MissingParameterException, PermissionDeniedException, OperationFailedException, InvalidParameterException {
2325         if (cluSetInfo.getMembershipQuery() == null) {
2326             return;
2327         }
2328         List<String> cluIds = getMembershipQuerySearchResult(cluSetInfo.getMembershipQuery(), contextInfo);
2329         cluSetInfo.getCluIds().addAll(cluIds);
2330     }
2331 
2332     private List<String> getMembershipQuerySearchResult(MembershipQueryInfo query, ContextInfo contextInfo) throws MissingParameterException, OperationFailedException, PermissionDeniedException, InvalidParameterException {
2333         if (query == null) {
2334             return null;
2335         }
2336 
2337         SearchRequestInfo request = new SearchRequestInfo();
2338         request.setSearchKey(query.getSearchTypeKey());
2339         request.setParams(query.getQueryParamValues());
2340 
2341         SearchResultInfo result = search(request, contextInfo);
2342 
2343         Set<String> cluIds = new HashSet<String>();
2344         List<SearchResultRowInfo> rows = result.getRows();
2345         for (SearchResultRowInfo row : rows) {
2346             List<SearchResultCellInfo> cells = row.getCells();
2347             for (SearchResultCellInfo cell : cells) {
2348                 if (cell.getKey().equals("lu.resultColumn.luOptionalVersionIndId") && cell.getValue() != null) {
2349                     cluIds.add(cell.getValue());
2350                 }
2351             }
2352         }
2353         return new ArrayList<String>(cluIds);
2354     }
2355 
2356     private void validateCluSet(CluSetInfo cluSetInfo) throws UnsupportedActionException {
2357         MembershipQueryInfo mqInfo = cluSetInfo.getMembershipQuery();
2358 
2359         if (cluSetInfo.getTypeKey() == null) {
2360             throw new UnsupportedActionException("CluSet type cannot be null. CluSet id=" + cluSetInfo.getId());
2361         } else if (mqInfo != null && mqInfo.getSearchTypeKey() != null && !mqInfo.getSearchTypeKey().isEmpty()
2362                 && (cluSetInfo.getCluIds().size() > 0 || cluSetInfo.getCluSetIds().size() > 0)) {
2363             throw new UnsupportedActionException("Dynamic CluSet cannot contain Clus and/or CluSets. CluSet id=" + cluSetInfo.getId());
2364         } else if (cluSetInfo.getCluIds().size() > 0 && cluSetInfo.getCluSetIds().size() > 0) {
2365             throw new UnsupportedActionException("CluSet cannot contain both Clus and CluSets. CluSet id=" + cluSetInfo.getId());
2366         }
2367     }
2368 
2369     @Override
2370     @Transactional(readOnly = false)
2371     public CluSetInfo updateCluSet(String cluSetId, CluSetInfo cluSetInfo, ContextInfo contextInfo)
2372             throws DataValidationErrorException, DoesNotExistException,
2373             InvalidParameterException, MissingParameterException,
2374             OperationFailedException, PermissionDeniedException,
2375             VersionMismatchException, CircularRelationshipException,
2376             UnsupportedActionException {
2377 
2378         // Check Missing params
2379         checkForMissingParameter(cluSetId, "cluSetId");
2380         checkForMissingParameter(cluSetInfo, "cluSetInfo");
2381 
2382         // Validate CluSet
2383         List<ValidationResultInfo> val = validateCluSet("SYSTEM",
2384                 cluSetInfo.getTypeKey(),
2385                 cluSetInfo,
2386                 contextInfo);
2387         if (null != val && val.size() > 0) {
2388             throw new DataValidationErrorException("Validation error!", val);
2389         }
2390 
2391         cluSetInfo.setId(cluSetId);
2392 
2393         validateCluSet(cluSetInfo);
2394 
2395         List<String> cluIds = getMembershipQuerySearchResult(cluSetInfo.getMembershipQuery(), contextInfo);
2396 
2397         CluSet cluSet;
2398         try {
2399             cluSet = luDao.fetch(CluSet.class, cluSetId);
2400         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2401             throw new DoesNotExistException(cluSetId, ex);
2402         }
2403 
2404         if (!cluSetInfo.getTypeKey().equals(cluSet.getType())) {
2405             throw new UnsupportedActionException("CluSet type is set at creation time and cannot be updated. CluSet id=" + cluSetId);
2406         }
2407 
2408         if (!String.valueOf(cluSet.getVersionNumber()).equals(
2409                 cluSetInfo.getMeta().getVersionInd())) {
2410             throw new VersionMismatchException(
2411                     "CluSet (id=" + cluSetId
2412                             + ") to be updated is not the current version "
2413                             + "(version=" + cluSetInfo.getMeta().getVersionInd()
2414                             + "), current version=" + cluSet.getVersionNumber());
2415         }
2416 
2417         // update the cluIds
2418         Map<String, CluSetJoinVersionIndClu> oldClus = new HashMap<String, CluSetJoinVersionIndClu>();
2419         for (CluSetJoinVersionIndClu join : cluSet.getCluVerIndIds()) {
2420             oldClus.put(join.getCluVersionIndId(), join);
2421         }
2422 
2423         cluSet.getCluVerIndIds().clear();
2424         // Loop through the new list, if the item exists already update and remove from the list otherwise create a new entry
2425         for (String newCluId : cluSetInfo.getCluIds()) {
2426             CluSetJoinVersionIndClu join = oldClus.remove(newCluId);
2427             if (join == null) {
2428                 join = new CluSetJoinVersionIndClu();
2429                 join.setCluSet(cluSet);
2430                 join.setCluVersionIndId(newCluId);
2431             }
2432             cluSet.getCluVerIndIds().add(join);
2433         }
2434 
2435         // Now delete anything left over
2436         for (Entry<String, CluSetJoinVersionIndClu> entry : oldClus.entrySet()) {
2437             luDao.delete(entry.getValue());
2438         }
2439 
2440         // clean up existing wrappers if any
2441         if (cluSetInfo.getId() != null) {
2442             CluSetInfo originalCluSet = getCluSet(cluSetInfo.getId(), contextInfo);
2443             List<CluSetInfo> origSubCSs = null;
2444             List<String> origSubCSIds = originalCluSet.getCluSetIds();
2445             if (origSubCSIds != null && !origSubCSIds.isEmpty()) {
2446                 origSubCSs = getCluSetsByIds(origSubCSIds, contextInfo);
2447             }
2448             if (origSubCSs != null) {
2449                 for (CluSetInfo origSubCS : origSubCSs) {
2450                     if (!origSubCS.getIsReusable()) {
2451                         deleteCluSet(origSubCS.getId(), contextInfo);
2452                     }
2453                 }
2454             }
2455         }
2456 
2457         // update the cluSetIds
2458         if (cluSet.getCluSets() == null) {
2459             cluSet.setCluSets(new ArrayList<CluSet>());
2460         }
2461         cluSet.setCluSets(null);
2462         if (!cluSetInfo.getCluSetIds().isEmpty()) {
2463             Set<String> newCluSetIds = new HashSet<String>(cluSetInfo.getCluSetIds());
2464             if (cluSet.getCluSets() != null) {
2465                 for (Iterator<CluSet> i = cluSet.getCluSets().iterator(); i.hasNext();) {
2466                     if (!newCluSetIds.remove(i.next().getId())) {
2467                         i.remove();
2468                     }
2469                 }
2470             }
2471             List<CluSet> cluSetList = luDao.getCluSetInfoByIdList(new ArrayList<String>(newCluSetIds));
2472             cluSet.setCluSets(cluSetList);
2473         }
2474 
2475         BeanUtils.copyProperties(cluSetInfo, cluSet, new String[]{"descr",
2476                 "attributes", "meta", "membershipQuery"});
2477         cluSet.setAttributes(CluServiceAssembler.toGenericAttributes(
2478                 CluSetAttribute.class, cluSetInfo.getAttributes(), cluSet, luDao));
2479         cluSet.setDescr(CluServiceAssembler.toRichText(LuRichText.class, cluSetInfo.getDescr()));
2480 
2481         MembershipQuery mq = CluServiceAssembler.toMembershipQueryEntity(cluSetInfo.getMembershipQuery());
2482         cluSet.setMembershipQuery(mq);
2483 
2484         CluSet updated = luDao.update(cluSet);
2485 
2486         CluSetInfo updatedCluSetInfo = CluServiceAssembler.toCluSetInfo(updated);
2487 
2488         if (cluIds != null) {
2489             updatedCluSetInfo.getCluIds().addAll(cluIds);
2490         }
2491 
2492         return updatedCluSetInfo;
2493     }
2494 
2495     @Override
2496     @Transactional(readOnly = false)
2497     public StatusInfo deleteCluSet(String cluSetId, ContextInfo context)
2498             throws DoesNotExistException, InvalidParameterException,
2499             MissingParameterException, OperationFailedException,
2500             PermissionDeniedException {
2501 
2502         checkForMissingParameter(cluSetId, "cluSetId");
2503         try {
2504             luDao.delete(CluSet.class, cluSetId);
2505         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2506             throw new DoesNotExistException(cluSetId, ex);
2507         }
2508 
2509         StatusInfo statusInfo = new StatusInfo();
2510         statusInfo.setSuccess(true);
2511 
2512         return statusInfo;
2513     }
2514 
2515     @Override
2516     @Transactional(readOnly = false)
2517     public StatusInfo addCluSetToCluSet(String cluSetId, String addedCluSetId, ContextInfo context)
2518             throws DoesNotExistException, InvalidParameterException,
2519             MissingParameterException, OperationFailedException,
2520             PermissionDeniedException, UnsupportedActionException,
2521             CircularRelationshipException {
2522         checkForMissingParameter(cluSetId, "cluSetId");
2523         checkForMissingParameter(addedCluSetId, "addedCluSetId");
2524 
2525         CluSet cluSet;
2526         try {
2527             cluSet = luDao.fetch(CluSet.class, cluSetId);
2528         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2529             throw new DoesNotExistException(cluSetId, ex);
2530         }
2531 
2532         checkCluSetAlreadyAdded(cluSet, addedCluSetId);
2533 
2534         CluSet addedCluSet;
2535         try {
2536             addedCluSet = luDao.fetch(CluSet.class, addedCluSetId);
2537         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2538             throw new DoesNotExistException(addedCluSetId, ex);
2539         }
2540 
2541         checkCluSetCircularReference(addedCluSet, cluSetId);
2542 
2543         if (cluSet.getCluSets() == null) {
2544             cluSet.setCluSets(new ArrayList<CluSet>());
2545         }
2546         cluSet.getCluSets().add(addedCluSet);
2547 
2548         luDao.update(cluSet);
2549 
2550         StatusInfo statusInfo = new StatusInfo();
2551         statusInfo.setSuccess(true);
2552 
2553         return statusInfo;
2554     }
2555 
2556     private void checkCluSetAlreadyAdded(CluSet cluSet, String cluSetIdToAdd)
2557             throws OperationFailedException {
2558         if (cluSet.getCluSets() != null) {
2559             for (CluSet childCluSet : cluSet.getCluSets()) {
2560                 if (childCluSet.getId().equals(cluSetIdToAdd)) {
2561                     throw new OperationFailedException("CluSet (id=" + cluSet.getId()
2562                             + ") already contains CluSet (id='" + cluSetIdToAdd + "')");
2563                 }
2564             }
2565         }
2566     }
2567 
2568     private void checkCluSetCircularReference(CluSet addedCluSet, String hostCluSetId)
2569             throws CircularRelationshipException {
2570         if (addedCluSet.getId().equals(hostCluSetId)) {
2571             throw new CircularRelationshipException(
2572                     "Cannot add a CluSet (id=" + hostCluSetId + ") to ifself");
2573         }
2574         if (addedCluSet.getCluSets() != null) {
2575             for (CluSet childSet : addedCluSet.getCluSets()) {
2576                 if (childSet.getId().equals(hostCluSetId)) {
2577                     throw new CircularRelationshipException(
2578                             "CluSet (id=" + hostCluSetId
2579                                     + ") already contains this CluSet (id="
2580                                     + childSet.getId() + ")");
2581                 }
2582                 checkCluSetCircularReference(childSet, hostCluSetId);
2583             }
2584         }
2585     }
2586 
2587     @Override
2588     @Transactional(readOnly = false)
2589     public StatusInfo removeCluSetFromCluSet(String cluSetId,
2590                                              String removedCluSetId, ContextInfo context) throws DoesNotExistException,
2591             InvalidParameterException, MissingParameterException,
2592             OperationFailedException, PermissionDeniedException,
2593             UnsupportedActionException {
2594 
2595         checkForMissingParameter(cluSetId, "cluSetId");
2596         checkForMissingParameter(removedCluSetId, "removedCluSetId");
2597 
2598         CluSet cluSet;
2599         try {
2600             cluSet = luDao.fetch(CluSet.class, cluSetId);
2601         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2602             throw new DoesNotExistException(cluSetId, ex);
2603         }
2604         if (cluSet.getCluSets() != null) {
2605             for (Iterator<CluSet> i = cluSet.getCluSets().iterator(); i.hasNext();) {
2606                 CluSet childCluSet = i.next();
2607                 if (childCluSet.getId().equals(removedCluSetId)) {
2608                     i.remove();
2609                     luDao.update(cluSet);
2610                     StatusInfo statusInfo = new StatusInfo();
2611                     statusInfo.setSuccess(true);
2612 
2613                     return statusInfo;
2614                 }
2615             }
2616         }
2617 
2618         StatusInfo statusInfo = new StatusInfo();
2619         statusInfo.setSuccess(false);
2620         statusInfo.setMessage("CluSet does not contain CluSet:"
2621                 + removedCluSetId);
2622 
2623         return statusInfo;
2624     }
2625 
2626     @Override
2627     @Transactional(readOnly = false)
2628     public StatusInfo addCluToCluSet(String cluId, String cluSetId, ContextInfo context)
2629             throws DoesNotExistException, InvalidParameterException,
2630             MissingParameterException, OperationFailedException,
2631             PermissionDeniedException, UnsupportedActionException {
2632 
2633         checkForMissingParameter(cluId, "cluId");
2634         checkForMissingParameter(cluSetId, "cluSetId");
2635 
2636         StatusInfo statusInfo = new StatusInfo();
2637 
2638         CluSet cluSet;
2639         try {
2640             cluSet = luDao.fetch(CluSet.class, cluSetId);
2641         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2642             throw new DoesNotExistException(cluSetId, ex);
2643         }
2644 
2645         if(!checkCluAlreadyAdded(cluSet, cluId)) {
2646             statusInfo.setSuccess(Boolean.FALSE);
2647             statusInfo.setMessage("CluSet already contains Clu (id='" + cluId + "')");
2648         }else {
2649             try {
2650                 luDao.getCurrentCluVersionInfo(cluId, CluServiceConstants.CLU_NAMESPACE_URI);
2651             } catch (NoResultException e) {
2652                 throw new DoesNotExistException("Could not get current clu version info by cluId", e);
2653             }
2654 
2655             CluSetJoinVersionIndClu join = new CluSetJoinVersionIndClu();
2656             join.setCluSet(cluSet);
2657             join.setCluVersionIndId(cluId);
2658 
2659             cluSet.getCluVerIndIds().add(join);
2660 
2661             luDao.update(cluSet);
2662 
2663 
2664             statusInfo.setSuccess(true);
2665         }
2666 
2667         return statusInfo;
2668     }
2669 
2670     private boolean checkCluAlreadyAdded(CluSet cluSet, String cluId)
2671             throws OperationFailedException {
2672         for (CluSetJoinVersionIndClu join : cluSet.getCluVerIndIds()) {
2673             if (join.getCluVersionIndId().equals(cluId)) {
2674                 return false;
2675             }
2676         }
2677         return true;
2678     }
2679 
2680     @Override
2681     @Transactional(readOnly = false)
2682     public StatusInfo removeCluFromCluSet(String cluId, String cluSetId, ContextInfo context)
2683             throws DoesNotExistException, InvalidParameterException,
2684             MissingParameterException, OperationFailedException,
2685             PermissionDeniedException, UnsupportedActionException {
2686 
2687         checkForMissingParameter(cluId, "cluId");
2688         checkForMissingParameter(cluSetId, "cluSetId");
2689 
2690         CluSet cluSet;
2691         try {
2692             cluSet = luDao.fetch(CluSet.class, cluSetId);
2693         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2694             throw new DoesNotExistException(cluSetId, ex);
2695         }
2696 
2697         for (Iterator<CluSetJoinVersionIndClu> i = cluSet.getCluVerIndIds().iterator(); i.hasNext();) {
2698             CluSetJoinVersionIndClu join = i.next();
2699             if (join.getCluVersionIndId().equals(cluId)) {
2700                 i.remove();
2701                 luDao.delete(join);
2702                 luDao.update(cluSet);
2703                 StatusInfo statusInfo = new StatusInfo();
2704                 statusInfo.setSuccess(true);
2705 
2706                 return statusInfo;
2707             }
2708         }
2709 
2710         StatusInfo statusInfo = new StatusInfo();
2711         statusInfo.setSuccess(false);
2712         statusInfo.setMessage("Clu set does not contain Clu:" + cluId);
2713 
2714         return statusInfo;
2715     }
2716 
2717     public LuDao getLuDao() {
2718         return luDao;
2719     }
2720 
2721     public void setLuDao(LuDao luDao) {
2722         this.luDao = luDao;
2723     }
2724 
2725     /**
2726      * Check for missing parameter and throw localized exception if missing
2727      *
2728      * @param param
2729      * @param paramName
2730      * @throws MissingParameterException
2731      */
2732     private void checkForMissingParameter(Object param, String paramName)
2733             throws MissingParameterException {
2734         if (param == null) {
2735             throw new MissingParameterException(paramName + " can not be null");
2736         }
2737     }
2738 
2739     /**
2740      * @param param
2741      * @param paramName
2742      * @throws MissingParameterException
2743      */
2744     private void checkForEmptyList(Object param, String paramName)
2745             throws MissingParameterException {
2746         if (param != null && param instanceof List<?>
2747                 && ((List<?>) param).size() == 0) {
2748             throw new MissingParameterException(paramName
2749                     + " can not be an empty list");
2750         }
2751     }
2752 
2753     @Override
2754     @Transactional(readOnly = false)
2755     public StatusInfo addCluSetsToCluSet(String cluSetId, List<String> cluSetIds, ContextInfo context)
2756             throws CircularRelationshipException,
2757             DoesNotExistException, InvalidParameterException,
2758             MissingParameterException, OperationFailedException,
2759             PermissionDeniedException, UnsupportedActionException {
2760 
2761         checkForMissingParameter(cluSetId, "cluSetId");
2762         checkForMissingParameter(cluSetIds, "cluSetIds");
2763         try {
2764             // Check that CluSet exists
2765             luDao.fetch(CluSet.class, cluSetId);
2766         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2767             throw new DoesNotExistException(cluSetId, ex);
2768         }
2769 
2770         for (String cluSetIdToAdd : cluSetIds) {
2771             StatusInfo status = addCluSetToCluSet(cluSetId, cluSetIdToAdd, context);
2772             if (!status.getIsSuccess()) {
2773                 return status;
2774             }
2775         }
2776 
2777         StatusInfo statusInfo = new StatusInfo();
2778         statusInfo.setSuccess(true);
2779 
2780         return statusInfo;
2781     }
2782 
2783     @Override
2784     @Transactional(readOnly = false)
2785     public StatusInfo addClusToCluSet(List<String> cluIds, String cluSetId, ContextInfo context)
2786             throws DoesNotExistException, InvalidParameterException,
2787             MissingParameterException, OperationFailedException,
2788             PermissionDeniedException, UnsupportedActionException {
2789         StatusInfo statusInfo = new StatusInfo();
2790 
2791         checkForMissingParameter(cluIds, "cluIds");
2792         checkForMissingParameter(cluSetId, "cluSetId");
2793 
2794         for (String cluId : cluIds) {
2795             StatusInfo status = addCluToCluSet(cluId, cluSetId, context);
2796             if (!status.getIsSuccess()) {
2797                 if(statusInfo.getMessage().isEmpty()){
2798                     statusInfo.setMessage(status.getMessage());
2799                 }else{
2800                     statusInfo.setMessage(statusInfo.getMessage()+"\n"+status.getMessage());
2801                 }
2802             }
2803         }
2804 
2805         statusInfo.setSuccess(true);
2806 
2807         return statusInfo;
2808     }
2809 
2810     public ValidatorFactory getValidatorFactory() {
2811         return validatorFactory;
2812     }
2813 
2814     public void setValidatorFactory(ValidatorFactory validatorFactory) {
2815         this.validatorFactory = validatorFactory;
2816     }
2817 
2818     /********* Versioning Methods ***************************/
2819     @Override
2820     @Transactional(readOnly = false)
2821     public CluInfo createNewCluVersion(String versionIndCluId,
2822                                        String versionComment,
2823                                        ContextInfo context)
2824             throws DataValidationErrorException, DoesNotExistException, InvalidParameterException,
2825             MissingParameterException, OperationFailedException, PermissionDeniedException {
2826         Clu latestClu;
2827         Clu currentClu;
2828         try {
2829             latestClu = luDao.getLatestCluVersion(versionIndCluId);
2830         } catch (NoResultException e) {
2831             throw new DoesNotExistException("There are no matching versions of this clu", e);
2832         }
2833         try {
2834             currentClu = luDao.getCurrentCluVersion(versionIndCluId);
2835         } catch (NoResultException e) {
2836             throw new DoesNotExistException("There is no current version of this clu. Only current clus can be versioned. Use setCurrentCluVersion to make a clu current.", e);
2837         }
2838 
2839         CluInfo cluInfo = CluServiceAssembler.toCluInfo(currentClu);
2840 
2841         // Reset the Clu
2842         clearCluIds(cluInfo);
2843 
2844         // Create the new Clu Version	    
2845         CluInfo newClu = null;
2846 
2847         try {
2848             Clu clu = toCluForCreate(cluInfo.getTypeKey(), cluInfo, context);
2849             //Set the Version data
2850             Version version = new Version();
2851             version.setSequenceNumber(latestClu.getVersion().getSequenceNumber() + 1);
2852             version.setVersionIndId(versionIndCluId);
2853             version.setCurrentVersionStart(null);
2854             version.setCurrentVersionEnd(null);
2855             version.setVersionComment(versionComment);
2856             version.setVersionedFromId(currentClu.getId());
2857             clu.setVersion(version);
2858             luDao.create(clu);
2859             newClu = CluServiceAssembler.toCluInfo(clu);
2860         } catch (AlreadyExistsException e) {
2861             throw new OperationFailedException("Error creating a new clu version", e);
2862         }
2863 
2864         return newClu;
2865     }
2866 
2867     private void clearCluIds(CluInfo clu) {
2868         // Clear out all Ids so a copy can be made
2869         clu.setStateKey(DtoConstants.STATE_DRAFT);// TODO check if this should be set from outside
2870         clu.setId(null);
2871 
2872         if (clu.getAccountingInfo() != null) {
2873             clu.getAccountingInfo().setId(null);
2874 
2875             for (AffiliatedOrgInfo affiliatedOrg : clu.getAccountingInfo().getAffiliatedOrgs()) {
2876                 affiliatedOrg.setId(null);
2877             }
2878         }
2879         for (AccreditationInfo accredation : clu.getAccreditations()) {
2880             accredation.setId(null);
2881         }
2882         for (AdminOrgInfo adminOrg : clu.getAdminOrgs()) {
2883             adminOrg.setId(null);
2884         }
2885         for (CluIdentifierInfo alternateIdentifier : clu.getAlternateIdentifiers()) {
2886             alternateIdentifier.setId(null);
2887         }
2888         if (clu.getFeeInfo() != null) {
2889             clu.getFeeInfo().setId(null);
2890             for (CluFeeRecordInfo cluFeeRecord : clu.getFeeInfo().getCluFeeRecords()) {
2891                 cluFeeRecord.setId(null);
2892                 for (AffiliatedOrgInfo affiliatedOrg : cluFeeRecord.getAffiliatedOrgs()) {
2893                     affiliatedOrg.setId(null);
2894                 }
2895             }
2896         }
2897         for (LuCodeInfo luCode : clu.getLuCodes()) {
2898             luCode.setId(null);
2899         }
2900         if (clu.getOfficialIdentifier() != null) {
2901             clu.getOfficialIdentifier().setId(null);
2902         }
2903     }
2904 
2905     /**
2906      * This method sets the CLU with ID of cluVersionId as the current version and will set the version end date of the previously current version to currentVersionStart or now() if null.  This will NOT update state of the current or previously current CLU.  All state changes must be handled either by the business service or from the atpService application.
2907      *
2908      * @param currentVersionStart if set to null, will default the current version start to the time when this method is called.
2909      * You can set this to a future date as well. 
2910      */
2911     @Override
2912     @Transactional(readOnly = false)
2913     public StatusInfo setCurrentCluVersion(String cluVersionId, Date currentVersionStart, ContextInfo context) throws DoesNotExistException, InvalidParameterException, MissingParameterException, IllegalVersionSequencingException, OperationFailedException, PermissionDeniedException {
2914         //Check params
2915         Date currentDbDate = new Date();//FIXME, this should be DB time
2916         if (currentVersionStart != null && currentVersionStart.compareTo(currentDbDate) < 0) {
2917             throw new InvalidParameterException("currentVersionStart must be in the future.");
2918         }
2919         //Default the currentVersionStart to the current date
2920         if (currentVersionStart == null) {
2921             currentVersionStart = currentDbDate;
2922         }
2923 
2924         //get the clu we are setting as current 
2925         Clu clu;
2926         try {
2927             clu = luDao.fetch(Clu.class, cluVersionId);
2928         } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2929             throw new DoesNotExistException(cluVersionId, ex);
2930         }
2931         String versionIndId = clu.getVersion().getVersionIndId();
2932 
2933         Clu oldClu = null;
2934         try {
2935             oldClu = luDao.getCurrentCluVersion(versionIndId);
2936         } catch (NoResultException e) {
2937         }
2938 
2939         //Check that the clu you are trying to version has a sequence number greater than the current clu
2940         if (oldClu != null) {
2941             if (clu.getVersion().getSequenceNumber() <= oldClu.getVersion().getSequenceNumber()) {
2942                 throw new OperationFailedException("Clu to make current must have been versioned from the current Clu");
2943             }
2944         } else {
2945             //Ignore the start date set if this is the first version (it will be set to the current time to avoid weird time problems)
2946             currentVersionStart = currentDbDate;
2947         }
2948 
2949 
2950         //Get any clus that are set to become current in the future, and clear their current dates
2951         List<VersionDisplayInfo> versionsInFuture = luDao.getVersionsInDateRange(versionIndId, null, currentDbDate, null);
2952         for (VersionDisplayInfo versionInFuture : versionsInFuture) {
2953             if (oldClu == null || !versionInFuture.getId().equals(oldClu.getId())) {
2954                 VersionEntity futureClu;
2955                 try {
2956                     futureClu = luDao.fetch(Clu.class, versionInFuture.getId());
2957                 } catch (org.kuali.student.r2.common.exceptions.DoesNotExistException ex) {
2958                     throw new DoesNotExistException(versionInFuture.getId(), ex);
2959                 }
2960                 futureClu.getVersion().setCurrentVersionStart(null);
2961                 futureClu.getVersion().setCurrentVersionEnd(null);
2962                 futureClu = luDao.update(futureClu);
2963             }
2964         }
2965 
2966         //If there is a current clu, set its end date to the new clu's start date
2967         if (oldClu != null) {
2968             oldClu.getVersion().setCurrentVersionEnd(currentVersionStart);
2969             oldClu = luDao.update(oldClu);
2970         }
2971 
2972         //Set the startdate of the new current clu
2973         clu.getVersion().setCurrentVersionStart(currentVersionStart);
2974         clu.getVersion().setCurrentVersionEnd(null);
2975         clu = luDao.update(clu);
2976 
2977         StatusInfo statusInfo = new StatusInfo();
2978         statusInfo.setSuccess(true);
2979         return statusInfo;
2980     }
2981 
2982     private SearchResultInfo doBrowseProgramSearch(ContextInfo contextInfo) throws MissingParameterException, PermissionDeniedException, OperationFailedException, InvalidParameterException {
2983         //This is our main result
2984         SearchResultInfo programSearchResults = searchManager.search(new SearchRequestInfo(SEARCH_KEY_BROWSE_PROGRAM), contextInfo);
2985 
2986         //These variations need to be mapped back to the program search results
2987         SearchResultInfo variationSearchResults = searchManager.search(new SearchRequestInfo(SEARCH_KEY_BROWSE_VARIATIONS),
2988                 contextInfo);
2989 
2990         //Get a mapping of program id to variation long name mapping:
2991         Map<String, List<String>> variationMapping = new HashMap<String, List<String>>();
2992         for (SearchResultRowInfo row : variationSearchResults.getRows()) {
2993             String programId = null;
2994             String variationLongName = null;
2995             for (SearchResultCellInfo cell : row.getCells()) {
2996                 if ("lu.resultColumn.cluId".equals(cell.getKey())) {
2997                     programId = cell.getValue();
2998                 } else if ("lu.resultColumn.luOptionalLongName".equals(cell.getKey())) {
2999                     variationLongName = cell.getValue();
3000                 }
3001             }
3002             List<String> variationLongNames = variationMapping.get(programId);
3003             if (variationLongNames == null) {
3004                 variationLongNames = new ArrayList<String>();
3005                 variationMapping.put(programId, variationLongNames);
3006             }
3007             variationLongNames.add(variationLongName);
3008         }
3009 
3010         //The result component types need to be mapped back as well
3011         SearchRequestInfo resultComponentSearchRequest = new SearchRequestInfo(SEARCH_KEY_RESULT_COMPONENT);
3012         resultComponentSearchRequest.addParam("lrc.queryParam.resultComponent.type",
3013                 "kuali.result.values.group.type.fixed");
3014         resultComponentSearchRequest.addParam("lrc.queryParam.resultComponent.resultScaleId",
3015                 "kuali.result.scale.degree");
3016 
3017         SearchResultInfo resultComponentSearchResults = searchDispatcher.search(resultComponentSearchRequest, contextInfo);
3018 
3019         //Get a mapping of result type id to result type name:
3020         Map<String, String> resultComponentMapping = new HashMap<String, String>();
3021         for (SearchResultRowInfo row : resultComponentSearchResults.getRows()) {
3022             String resultComponentTypeId = null;
3023             String resultComponentTypeName = null;
3024             for (SearchResultCellInfo cell : row.getCells()) {
3025                 if ("lrc.resultColumn.resultComponent.id".equals(cell.getKey())) {
3026                     resultComponentTypeId = cell.getValue();
3027                 } else if ("lrc.resultColumn.resultComponent.name".equals(cell.getKey())) {
3028                     resultComponentTypeName = cell.getValue();
3029                 }
3030             }
3031             resultComponentMapping.put(resultComponentTypeId, resultComponentTypeName);
3032         }
3033 
3034         Map<String, Set<SearchResultCellInfo>> orgIdToCellMapping = new HashMap<String, Set<SearchResultCellInfo>>();
3035         Map<String, Set<SearchResultCellInfo>> resultComponentToCellMapping = new HashMap<String, Set<SearchResultCellInfo>>();
3036         Map<String, Set<SearchResultCellInfo>> campusToCellMapping = new HashMap<String, Set<SearchResultCellInfo>>();
3037         Map<String, SearchResultCellInfo> progIdToOrgCellMapping = new HashMap<String, SearchResultCellInfo>();
3038         Map<String, SearchResultCellInfo> progIdToResultComponentCellMapping = new HashMap<String, SearchResultCellInfo>();
3039         Map<String, SearchResultCellInfo> progIdToCampusCellMapping = new HashMap<String, SearchResultCellInfo>();
3040 
3041         //We need to reduce the programSearchResults, translating variations, result options, etc and creating a mapping for org id translation
3042         for (Iterator<SearchResultRowInfo> rowIter = programSearchResults.getRows().iterator(); rowIter.hasNext();) {
3043             SearchResultRowInfo row = rowIter.next();
3044             String programId = null;
3045             String orgId = null;
3046             String resultComponentName = null;
3047             String campusCode = null;
3048             SearchResultCellInfo orgCell = null;
3049             SearchResultCellInfo resultComponentCell = null;
3050             SearchResultCellInfo variationCell = null;
3051             SearchResultCellInfo campusLocationCell = null;
3052 
3053             for (SearchResultCellInfo cell : row.getCells()) {
3054                 if ("lu.resultColumn.cluId".equals(cell.getKey())) {
3055                     programId = cell.getValue();
3056                 } else if ("lu.resultColumn.luOptionalAdminOrg".equals(cell.getKey())) {
3057                     orgId = cell.getValue();
3058                     orgCell = cell;
3059                 } else if ("lu.resultColumn.resultComponentId".equals(cell.getKey())) {
3060                     resultComponentName = resultComponentMapping.get(cell.getValue());
3061                     resultComponentCell = cell;
3062                 } else if ("lu.resultColumn.variationId".equals(cell.getKey())) {
3063                     variationCell = cell;
3064                 } else if ("lu.resultColumn.luOptionalCampusLocation".equals(cell.getKey())) {
3065                     campusLocationCell = cell;
3066                     campusCode = cell.getValue();
3067                 }
3068             }
3069             if (!progIdToOrgCellMapping.containsKey(programId)) {
3070                 //Add in the Variations
3071                 List<String> variations = variationMapping.get(programId);
3072                 variationCell.setValue("");
3073                 if (variations != null) {
3074                     for (Iterator<String> variationIter = variations.iterator(); variationIter.hasNext();) {
3075                         String variation = variationIter.next();
3076                         if (variationIter.hasNext()) {
3077                             variation += "<br/>";
3078                         }
3079                         variationCell.setValue(variationCell.getValue() + variation);
3080                     }
3081                 }
3082 
3083                 //Add the cell to the org id mapping
3084                 Set<SearchResultCellInfo> orgCells = orgIdToCellMapping.get(orgId);
3085                 if (orgCells == null) {
3086                     orgCells = new HashSet<SearchResultCellInfo>();
3087                     orgIdToCellMapping.put(orgId, orgCells);
3088                 }
3089                 orgCells.add(orgCell);
3090                 orgCell.setValue(null);
3091 
3092                 //Add this to the map
3093                 Set<SearchResultCellInfo> campusCells = campusToCellMapping.get(campusCode);
3094                 if (campusCells == null) {
3095                     campusCells = new HashSet<SearchResultCellInfo>();
3096                     campusToCellMapping.put(campusCode, campusCells);
3097                 }
3098                 campusCells.add(campusLocationCell);
3099                 campusLocationCell.setValue(null);
3100 
3101                 //Add this to the map
3102                 Set<SearchResultCellInfo> resultCells = resultComponentToCellMapping.get(resultComponentName);
3103                 if (resultCells == null) {
3104                     resultCells = new HashSet<SearchResultCellInfo>();
3105                     resultComponentToCellMapping.put(resultComponentName, resultCells);
3106                 }
3107                 resultCells.add(resultComponentCell);
3108                 resultComponentCell.setValue(null);
3109 
3110                 progIdToOrgCellMapping.put(programId, orgCell);
3111                 progIdToResultComponentCellMapping.put(programId, resultComponentCell);
3112                 progIdToCampusCellMapping.put(programId, campusLocationCell);
3113             } else {
3114                 //this row already exists so we need to concatenate the result component and add the org id
3115                 //Get the result component row
3116                 Set<SearchResultCellInfo> resultCells = resultComponentToCellMapping.get(resultComponentName);
3117                 if (resultCells == null) {
3118                     resultCells = new HashSet<SearchResultCellInfo>();
3119                     resultComponentToCellMapping.put(resultComponentName, resultCells);
3120                 }
3121                 resultCells.add(progIdToResultComponentCellMapping.get(programId));
3122 
3123                 //Add a new mapping to the org cell for this org id
3124                 Set<SearchResultCellInfo> orgCells = orgIdToCellMapping.get(orgId);
3125                 if (orgCells == null) {
3126                     orgCells = new HashSet<SearchResultCellInfo>();
3127                     orgIdToCellMapping.put(orgId, orgCells);
3128                 }
3129                 orgCells.add(progIdToOrgCellMapping.get(programId));
3130 
3131                 //Concatenate the campus location
3132                 Set<SearchResultCellInfo> campusCells = campusToCellMapping.get(campusCode);
3133                 if (campusCells == null) {
3134                     campusCells = new HashSet<SearchResultCellInfo>();
3135                     campusToCellMapping.put(campusCode, campusCells);
3136                 }
3137                 campusCells.add(progIdToCampusCellMapping.get(programId));
3138 
3139                 //Remove this row from results
3140                 rowIter.remove();
3141             }
3142         }
3143 
3144         if (!resultComponentToCellMapping.isEmpty()) {
3145             List<String> resultComponentNames = new ArrayList<String>(resultComponentToCellMapping.keySet());
3146             Collections.sort(resultComponentNames);
3147             for (String resultComponentName : resultComponentNames) {
3148                 //Concatenate resultComponent names in the holder cells
3149                 Set<SearchResultCellInfo> cells = resultComponentToCellMapping.get(resultComponentName);
3150                 if (cells != null) {
3151                     for (SearchResultCellInfo cell : cells) {
3152                         if (cell.getValue() == null) {
3153                             cell.setValue(resultComponentName);
3154                         } else {
3155                             cell.setValue(cell.getValue() + "<br/>" + resultComponentName);
3156                         }
3157                     }
3158                 }
3159             }
3160         }
3161 
3162         if (!campusToCellMapping.isEmpty()) {
3163             List<String> campusCodes = new ArrayList<String>(campusToCellMapping.keySet());
3164             Collections.sort(campusCodes);
3165             for (String campusCode : campusCodes) {
3166                 //Concatenate campus code names in the holder cells
3167                 Set<SearchResultCellInfo> cells = campusToCellMapping.get(campusCode);
3168                 if (cells != null) {
3169                     for (SearchResultCellInfo cell : cells) {
3170                         if (cell.getValue() == null) {
3171                             cell.setValue(campusCode);
3172                         } else {
3173                             cell.setValue(cell.getValue() + "<br/>" + campusCode);
3174                         }
3175                     }
3176                 }
3177             }
3178         }
3179 
3180         //Use the org search to Translate the orgIds into Org names and update the holder cells
3181         if (!orgIdToCellMapping.isEmpty()) {
3182             //Perform the Org search
3183             SearchRequestInfo orgIdTranslationSearchRequest = new SearchRequestInfo("org.search.generic");
3184             orgIdTranslationSearchRequest.addParam("org.queryParam.orgOptionalIds", new ArrayList<String>(
3185                     orgIdToCellMapping.keySet()));
3186             orgIdTranslationSearchRequest.setSortColumn("org.resultColumn.orgShortName");
3187             SearchResultInfo orgIdTranslationSearchResult = searchDispatcher.search(orgIdTranslationSearchRequest, contextInfo);
3188 
3189             //For each translation, update the result cell with the translated org name
3190             for (SearchResultRowInfo row : orgIdTranslationSearchResult.getRows()) {
3191 
3192                 //Get Params
3193                 String orgId = "";
3194                 String orgName = "";
3195                 for (SearchResultCellInfo cell : row.getCells()) {
3196                     if ("org.resultColumn.orgId".equals(cell.getKey())) {
3197                         orgId = cell.getValue();
3198                         continue;
3199                     } else if ("org.resultColumn.orgShortName".equals(cell.getKey())) {
3200                         orgName = cell.getValue();
3201                     }
3202                 }
3203 
3204                 //Concatenate org names in the holder cells
3205                 Set<SearchResultCellInfo> cells = orgIdToCellMapping.get(orgId);
3206                 if (cells != null) {
3207                     for (SearchResultCellInfo cell : cells) {
3208                         if (cell.getValue() == null) {
3209                             cell.setValue(orgName);
3210                         } else {
3211                             cell.setValue(cell.getValue() + "<br/>" + orgName);
3212                         }
3213                     }
3214                 }
3215             }
3216         }
3217 
3218         return programSearchResults;
3219     }
3220 
3221     private SearchResultInfo doDependencyAnalysisSearch(String cluId, ContextInfo contextInfo) throws MissingParameterException,
3222             DoesNotExistException, PermissionDeniedException, OperationFailedException, InvalidParameterException {
3223 
3224         checkForMissingParameter(cluId, "cluId");
3225 
3226         Clu triggerClu = luDao.fetch(Clu.class, cluId);
3227 
3228         List<String> cluVersionIndIds = new ArrayList<String>();
3229         cluVersionIndIds.add(triggerClu.getVersion().getVersionIndId());
3230 
3231         //Find all clusets that contain this course
3232         List<CluSet> cluSets = luDao.getCluSetsByCluVersionIndId(cluVersionIndIds);
3233 
3234         //Get a mapping of clusetId to cluset for easy referencing
3235         Map<String, CluSet> cluSetMap = new HashMap<String, CluSet>();
3236         if (cluSets != null) {
3237             for (CluSet cluSet : cluSets) {
3238                 cluSetMap.put(cluSet.getId(), cluSet);
3239             }
3240         }
3241 
3242         //Execute all dynamic queries to see if the target clu is in the cluset and add those clusets
3243         List<CluSet> dynamicCluSets = luDao.getAllDynamicCluSets();
3244         if (dynamicCluSets != null) {
3245             for (CluSet cluSet : dynamicCluSets) {
3246                 MembershipQueryInfo queryInfo = CluServiceAssembler.toMembershipQueryInfo(cluSet.getMembershipQuery());
3247                 List<String> memberCluVersionIndIds = getMembershipQuerySearchResult(queryInfo, contextInfo);
3248                 if (memberCluVersionIndIds != null) {
3249                     for (String cluVersionIndId : cluVersionIndIds) {
3250                         if (memberCluVersionIndIds.contains(cluVersionIndId)) {
3251                             cluSetMap.put(cluSet.getId(), cluSet);
3252                             break;
3253                         }
3254                     }
3255                 }
3256             }
3257         }
3258         //TODO Is it possible we need to search up the cluset hierarchies?
3259         //	If Cluset A contains clu 1 and cluset B contains cluset A, do we also return cluset B as a dependency?
3260 
3261         //Now we have the clu id and the list of clusets that the id appears in,
3262         //We need to do a statement service search to see what statements use these as
3263         //dependencies
3264         SearchRequestInfo statementSearchRequest = new SearchRequestInfo("stmt.search.dependencyAnalysis");
3265 
3266         statementSearchRequest.addParam("stmt.queryParam.cluSetIds", new ArrayList<String>(cluSetMap.keySet()));
3267         statementSearchRequest.addParam("stmt.queryParam.cluVersionIndIds", cluVersionIndIds);
3268 
3269         SearchResultInfo statementSearchResult = searchDispatcher.search(statementSearchRequest, contextInfo);
3270 
3271         //Create a search result for the return value
3272         SearchResultInfo searchResult = new SearchResultInfo();
3273 
3274         Map<String, List<SearchResultCellInfo>> orgIdToCellMapping = new HashMap<String, List<SearchResultCellInfo>>();
3275 
3276         //Now we need to take the statement ids and find the clus that relate to them
3277         //We will also transform the search result from the statement search result to
3278         //the dependency analysis search result
3279         Set<String> processed = new HashSet<String>();
3280         for (SearchResultRowInfo stmtRow : statementSearchResult.getRows()) {
3281 
3282             //Determine result column values
3283             String refObjId = null;
3284             String statementType = null;
3285             String statementTypeName = null;
3286             String rootId = null;
3287             String requirementComponentIds = null;
3288 
3289             for (SearchResultCellInfo stmtCell : stmtRow.getCells()) {
3290                 if ("stmt.resultColumn.refObjId".equals(stmtCell.getKey())) {
3291                     refObjId = stmtCell.getValue();
3292                     continue;
3293                 } else if ("stmt.resultColumn.statementTypeId".equals(stmtCell.getKey())) {
3294                     statementType = stmtCell.getValue();
3295                     continue;
3296                 } else if ("stmt.resultColumn.statementTypeName".equals(stmtCell.getKey())) {
3297                     statementTypeName = stmtCell.getValue();
3298                     continue;
3299                 } else if ("stmt.resultColumn.rootId".equals(stmtCell.getKey())) {
3300                     rootId = stmtCell.getValue();
3301                     continue;
3302                 } else if ("stmt.resultColumn.requirementComponentIds".equals(stmtCell.getKey())) {
3303                     requirementComponentIds = stmtCell.getValue();
3304                 }
3305             }
3306 
3307             //Find the clu
3308             Clu clu = luDao.fetch(Clu.class, refObjId);
3309 
3310             //Program statements are attached to dummy clus, so look up the parent program
3311             if ("kuali.lu.type.Requirement".equals(clu.getLuType().getId())) {
3312 
3313                 List<Clu> clus = luDao.getClusByRelatedCluId(clu.getId(),
3314                         "kuali.lu.lu.relation.type.hasProgramRequirement");
3315 
3316                 rootId = clu.getId();
3317 
3318                 if (clus == null || clus.size() == 0) {
3319                     throw new RuntimeException("Statement Dependency clu found, but no parent Program exists");
3320                 } else if (clus.size() > 1) {
3321                     throw new RuntimeException("Statement Dependency clu can only have one parent Program relation");
3322                 }
3323                 clu = clus.get(0);
3324             }
3325 
3326             //Only process clus that are not active and that we have not already processed
3327             String rowId = clu.getId() + "|" + statementType + "|" + rootId;
3328 
3329             if ("Active".equals(clu.getState()) && !processed.contains(rowId)) {
3330 
3331                 processed.add(rowId);
3332 
3333                 SearchResultRowInfo resultRow = new SearchResultRowInfo();
3334 
3335                 //Map the result cells
3336                 resultRow.addCell("lu.resultColumn.cluId", clu.getId());
3337                 resultRow.addCell("lu.resultColumn.cluType", clu.getLuType().getId());
3338                 resultRow.addCell("lu.resultColumn.luOptionalCode", clu.getOfficialIdentifier().getCode());
3339                 resultRow.addCell("lu.resultColumn.luOptionalShortName", clu.getOfficialIdentifier().getShortName());
3340                 resultRow.addCell("lu.resultColumn.luOptionalLongName", clu.getOfficialIdentifier().getLongName());
3341                 resultRow.addCell("lu.resultColumn.luOptionalDependencyType", statementType);
3342                 resultRow.addCell("lu.resultColumn.luOptionalDependencyTypeName", statementTypeName);
3343                 resultRow.addCell("lu.resultColumn.luOptionalDependencyRootId", rootId);
3344                 resultRow.addCell("lu.resultColumn.luOptionalDependencyRequirementComponentIds",
3345                         requirementComponentIds);
3346 
3347                 //Make a holder cell for the org names, to be populated later
3348                 SearchResultCellInfo orgIdsCell = new SearchResultCellInfo("lu.resultColumn.luOptionalOversightCommitteeIds",
3349                         null);
3350                 resultRow.getCells().add(orgIdsCell);
3351 
3352                 //Make a holder cell for the org ids, to be populated later
3353                 SearchResultCellInfo orgNamesCell = new SearchResultCellInfo(
3354                         "lu.resultColumn.luOptionalOversightCommitteeNames", null);
3355                 resultRow.getCells().add(orgNamesCell);
3356 
3357                 //For each curriculum oversight committee we want to look up the Org Name
3358                 //We're going to save a mapping of the org id to a holder cell so we can make just one org
3359                 //service call with all the org ids, and update the holder cells later.
3360                 boolean differentAdminOrg = true;
3361                 for (CluAdminOrg adminOrg : clu.getAdminOrgs()) {
3362                     if ("kuali.adminOrg.type.CurriculumOversight".equals(adminOrg.getType()) ||
3363                             "kuali.adminOrg.type.CurriculumOversightUnit".equals(adminOrg.getType())) {
3364 
3365                         //Add the cell to the mapping for that perticular org id
3366                         List<SearchResultCellInfo> cells = orgIdToCellMapping.get(adminOrg.getOrgId());
3367                         if (cells == null) {
3368                             cells = new ArrayList<SearchResultCellInfo>();
3369                             orgIdToCellMapping.put(adminOrg.getOrgId(), cells);
3370                         }
3371                         cells.add(orgNamesCell);
3372 
3373                         //Add the orgid to the orgIds cell so there is a comma delimited list of org ids
3374                         if (orgIdsCell.getValue() == null) {
3375                             orgIdsCell.setValue(adminOrg.getId());
3376                         } else {
3377                             orgIdsCell.setValue(orgIdsCell.getValue() + "," + adminOrg.getId());
3378                         }
3379 
3380                         for (CluAdminOrg triggerAdminOrg : triggerClu.getAdminOrgs()) {
3381                             if (triggerAdminOrg.getOrgId().equals(adminOrg.getOrgId())) {
3382                                 differentAdminOrg = false;
3383                             }
3384                         }
3385                     }
3386                 }
3387                 resultRow.addCell("lu.resultColumn.luOptionalDependencyRequirementDifferentAdminOrg",
3388                         String.valueOf(differentAdminOrg));
3389 
3390                 //Add the result row
3391                 searchResult.getRows().add(resultRow);
3392             }
3393         }
3394 
3395         //Use the org search to Translate the orgIds into Org names and update the holder cells
3396         if (!orgIdToCellMapping.isEmpty()) {
3397             //Perform the Org search
3398             SearchRequestInfo orgIdTranslationSearchRequest = new SearchRequestInfo("org.search.generic");
3399             orgIdTranslationSearchRequest.addParam("org.queryParam.orgOptionalIds", new ArrayList<String>(
3400                     orgIdToCellMapping.keySet()));
3401             SearchResultInfo orgIdTranslationSearchResult = searchDispatcher.search(orgIdTranslationSearchRequest, contextInfo);
3402 
3403             //For each translation, update the result cell with the translated org name
3404             for (SearchResultRowInfo row : orgIdTranslationSearchResult.getRows()) {
3405 
3406                 //Get Params
3407                 String orgId = "";
3408                 String orgName = "";
3409                 for (SearchResultCellInfo cell : row.getCells()) {
3410                     if ("org.resultColumn.orgId".equals(cell.getKey())) {
3411                         orgId = cell.getValue();
3412                         continue;
3413                     } else if ("org.resultColumn.orgShortName".equals(cell.getKey())) {
3414                         orgName = cell.getValue();
3415                     }
3416                 }
3417 
3418                 //Concatenate org names in the holder cells
3419                 List<SearchResultCellInfo> cells = orgIdToCellMapping.get(orgId);
3420                 if (cells != null) {
3421                     for (SearchResultCellInfo cell : cells) {
3422                         if (cell.getValue() == null) {
3423                             cell.setValue(orgName);
3424                         } else {
3425                             cell.setValue(cell.getValue() + ", " + orgName);
3426                         }
3427                     }
3428                 }
3429             }
3430         }
3431 
3432         //Add in CluSets and ignore ones named AdHoc
3433         for (CluSet cluSet : cluSetMap.values()) {
3434             if (!"AdHock".equals(cluSet.getName())) {
3435 
3436                 SearchResultRowInfo resultRow = new SearchResultRowInfo();
3437 
3438                 resultRow.addCell("lu.resultColumn.cluId", cluSet.getId());
3439                 resultRow.addCell("lu.resultColumn.luOptionalShortName", cluSet.getName());
3440                 resultRow.addCell("lu.resultColumn.luOptionalLongName", cluSet.getName());
3441                 resultRow.addCell("lu.resultColumn.luOptionalDependencyType", "cluSet");
3442                 resultRow.addCell("lu.resultColumn.luOptionalDependencyTypeName", "Course Set");
3443 
3444                 searchResult.getRows().add(resultRow);
3445             }
3446         }
3447 
3448         //Get any joints here and add them into the results
3449         List<Clu> joints = luDao.getClusByRelation(cluId, "kuali.lu.relation.type.co-located");
3450         if (joints != null) {
3451             for (Clu clu : joints) {
3452 
3453                 SearchResultRowInfo resultRow = new SearchResultRowInfo();
3454 
3455                 resultRow.addCell("lu.resultColumn.cluId", clu.getId());
3456                 resultRow.addCell("lu.resultColumn.luOptionalCode", clu.getOfficialIdentifier().getCode());
3457                 resultRow.addCell("lu.resultColumn.luOptionalShortName", clu.getOfficialIdentifier().getShortName());
3458                 resultRow.addCell("lu.resultColumn.luOptionalLongName", clu.getOfficialIdentifier().getLongName());
3459                 resultRow.addCell("lu.resultColumn.luOptionalDependencyType", "joint");
3460                 resultRow.addCell("lu.resultColumn.luOptionalDependencyTypeName", "jointly offered");
3461 
3462                 searchResult.getRows().add(resultRow);
3463             }
3464         }
3465 
3466         //Lookup cross-listings and add to the results
3467         for (CluIdentifier altId : triggerClu.getAlternateIdentifiers()) {
3468             if ("kuali.lu.type.CreditCourse.identifier.crosslisting".equals(altId.getType())) {
3469                 SearchResultRowInfo resultRow = new SearchResultRowInfo();
3470 
3471                 resultRow.addCell("lu.resultColumn.luOptionalCode", altId.getCode());
3472                 resultRow.addCell("lu.resultColumn.luOptionalShortName", altId.getShortName());
3473                 resultRow.addCell("lu.resultColumn.luOptionalLongName", altId.getLongName());
3474                 resultRow.addCell("lu.resultColumn.luOptionalDependencyType", "crossListed");
3475                 resultRow.addCell("lu.resultColumn.luOptionalDependencyTypeName", "cross-listed");
3476 
3477                 searchResult.getRows().add(resultRow);
3478             }
3479         }
3480 
3481         //Sort results by Code
3482         Collections.sort(searchResult.getRows(), new SearchResultRowComparator("lu.resultColumn.luOptionalCode"));
3483 
3484         return searchResult;
3485     }
3486 
3487     public class SearchResultRowComparator implements Comparator<SearchResultRowInfo> {
3488         private String sortColumn;
3489 
3490         SearchResultRowComparator(String sortColumn) {
3491             super();
3492             this.sortColumn = sortColumn;
3493         }
3494 
3495         @Override
3496         public int compare(SearchResultRowInfo o1, SearchResultRowInfo o2) {
3497             String o1SortValue = null;
3498             String o2SortValue = null;
3499             for (SearchResultCellInfo cell : o1.getCells()) {
3500                 if (sortColumn.equals(cell.getKey())) {
3501                     o1SortValue = cell.getValue();
3502                     break;
3503                 }
3504             }
3505             for (SearchResultCellInfo cell : o2.getCells()) {
3506                 if (sortColumn.equals(cell.getKey())) {
3507                     o2SortValue = cell.getValue();
3508                     break;
3509                 }
3510             }
3511             if (o1SortValue != null) {
3512                 if (o2SortValue == null) {
3513                     return 1;
3514                 }
3515                 return o1SortValue.compareTo(o2SortValue);
3516             }
3517             if (o2SortValue == null) {
3518                 return 0;
3519             }
3520             return -1;
3521         }
3522 
3523     }
3524 
3525     private SearchResultInfo doSearchProposalsByCourseCode(String courseCode, ContextInfo contextInfo) throws MissingParameterException, PermissionDeniedException, OperationFailedException, InvalidParameterException {
3526         if(courseCode==null||courseCode.isEmpty()){
3527             return new SearchResultInfo();
3528         }
3529         //First do a search of courses with said code
3530         SearchRequestInfo sr = new SearchRequestInfo("lu.search.mostCurrent.union");
3531         sr.addParam("lu.queryParam.luOptionalCode", courseCode);
3532         sr.addParam("lu.queryParam.luOptionalType","kuali.lu.type.CreditCourse");
3533         SearchResultInfo results = search(sr, contextInfo);
3534         Map<String,String> cluIdToCodeMap = new HashMap<String,String>();
3535         for(SearchResultRowInfo row:results.getRows()){
3536             String cluId = null;
3537             String code = null;
3538             for(SearchResultCellInfo cell:row.getCells()){
3539                 if("lu.resultColumn.cluId".equals(cell.getKey())){
3540                     cluId = cell.getValue();
3541                 }else if("lu.resultColumn.luOptionalCode".equals(cell.getKey())){
3542                     code = cell.getValue();
3543                 }
3544             }
3545             //Create a mapping of Clu Id to code to dereference later
3546             if(code!=null&&cluId!=null){
3547                 cluIdToCodeMap.put(cluId, code);
3548             }
3549         }
3550 
3551         //Do a search for proposals that refer to the clu ids we found
3552         sr = new SearchRequestInfo("proposal.search.proposalsForReferenceIds");
3553         sr.addParam("proposal.queryParam.proposalOptionalReferenceIds", new ArrayList<String>(cluIdToCodeMap.keySet()));
3554         results = searchDispatcher.search(sr, contextInfo);
3555         for(SearchResultRowInfo row:results.getRows()){
3556             String cluId = null;
3557             SearchResultCellInfo proposalNameCell = null;
3558 
3559             for(SearchResultCellInfo cell:row.getCells()){
3560                 if("proposal.resultColumn.proposalOptionalName".equals(cell.getKey())){
3561                     proposalNameCell = cell;
3562                     cell.setKey("lu.resultColumn.proposalOptionalName");
3563                 }else if("proposal.resultColumn.proposalOptionalReferenceId".equals(cell.getKey())){
3564                     cluId = cell.getValue();
3565                     cell.setKey("lu.resultColumn.proposalOptionalReferenceId");
3566                 }else if("proposal.resultColumn.proposalId".equals(cell.getKey())){
3567                     cell.setKey("lu.resultColumn.proposalId");
3568                 }
3569             }
3570             //update the name of the proposal to reflect the course number
3571             proposalNameCell.setValue(cluIdToCodeMap.get(cluId)+" ("+proposalNameCell.getValue()+")");
3572         }
3573 
3574         return results;
3575     }
3576 
3577     /**
3578      * Looks up Atp descriptions and adds to search results
3579      * @param searchRequest
3580      * @return
3581      * @throws MissingParameterException
3582      */
3583     private SearchResultInfo doBrowseVersionsSearch(SearchRequestInfo searchRequest, ContextInfo contextInfo) throws MissingParameterException, PermissionDeniedException, OperationFailedException, InvalidParameterException {
3584         SearchResultInfo searchResult = searchManager.search(searchRequest, contextInfo);
3585 
3586         Map<String,List<SearchResultCellInfo>> atpIdToCellMapping = new HashMap<String,List<SearchResultCellInfo>>();
3587 
3588         for(SearchResultRowInfo row:searchResult.getRows()){
3589             for(SearchResultCellInfo cell:row.getCells()){
3590                 if(cell.getValue()!=null &&
3591                         ("lu.resultColumn.luOptionalExpFirstAtpDisplay".equals(cell.getKey()) ||
3592                                 "lu.resultColumn.luOptionalLastAtpDisplay".equals(cell.getKey()))) {
3593                     List<SearchResultCellInfo> cells = atpIdToCellMapping.get(cell.getValue());
3594                     if(cells==null){
3595                         cells = new ArrayList<SearchResultCellInfo>();
3596                         atpIdToCellMapping.put(cell.getValue(), cells);
3597                     }
3598                     cells.add(cell);
3599                 }
3600             }
3601         }
3602         //Now do an atp search to translate ids to names
3603 
3604         SearchRequestInfo atpSearchRequest = new SearchRequestInfo("atp.search.advancedAtpSearch");
3605         atpSearchRequest.addParam("atp.advancedAtpSearchParam.optionalAtpIds", new ArrayList<String>(atpIdToCellMapping.keySet()));
3606         SearchResultInfo atpSearchResults = searchDispatcher.search(atpSearchRequest, contextInfo);
3607         for(SearchResultRowInfo row:atpSearchResults.getRows()){
3608             String atpId = null;
3609             String atpName = null;
3610             for(SearchResultCellInfo cell:row.getCells()){
3611                 if("atp.resultColumn.atpId".equals(cell.getKey())){
3612                     atpId = cell.getValue();
3613                 }else if("atp.resultColumn.atpShortName".equals(cell.getKey())){
3614                     atpName = cell.getValue();
3615                 }
3616             }
3617             if(atpId!=null && atpIdToCellMapping.get(atpId)!=null){
3618                 for(SearchResultCellInfo cell : atpIdToCellMapping.get(atpId)){
3619                     cell.setValue(atpName);
3620                 }
3621             }
3622         }
3623 
3624         return searchResult;
3625     }
3626 
3627     /**
3628      * Does a cross search to first get result componets from the lu search and then use an LRC search to get the result component names
3629      * @param cluSearchRequest
3630      * @return
3631      * @throws MissingParameterException
3632      */
3633     private SearchResultInfo doResultComponentTypesForCluSearch(SearchRequestInfo cluSearchRequest, ContextInfo contextInfo) throws MissingParameterException, PermissionDeniedException, OperationFailedException, InvalidParameterException {
3634 
3635         SearchResultInfo searchResult = searchManager.search(cluSearchRequest, contextInfo);
3636 
3637         //Get the result Component Ids using a search
3638         Map<String,List<SearchResultRowInfo>> rcIdToRowMapping = new HashMap<String,List<SearchResultRowInfo>>();
3639 
3640         //Get a mapping of ids to translate
3641         for(SearchResultRowInfo row:searchResult.getRows()){
3642             for(SearchResultCellInfo cell:row.getCells()){
3643                 if(cell.getValue()!=null &&
3644                         "lu.resultColumn.resultComponentId".equals(cell.getKey())) {
3645                     List<SearchResultRowInfo> rows = rcIdToRowMapping.get(cell.getValue());
3646                     if(rows==null){
3647                         rows = new ArrayList<SearchResultRowInfo>();
3648                         rcIdToRowMapping.put(cell.getValue(), rows);
3649                     }
3650                     rows.add(row);
3651                 }
3652             }
3653         }
3654 
3655         //Get the LRC names to match the ids
3656         SearchRequestInfo lrcSearchRequest = new SearchRequestInfo(SEARCH_KEY_RESULT_COMPONENT);
3657         lrcSearchRequest.addParam("lrc.queryParam.resultComponent.idRestrictionList", new ArrayList<String>(rcIdToRowMapping.keySet()));
3658         SearchResultInfo lrcSearchResults = searchDispatcher.search(lrcSearchRequest, contextInfo);
3659 
3660         //map the names back to the original search results
3661         for(SearchResultRowInfo row:lrcSearchResults.getRows()){
3662             String lrcId = null;
3663             String lrcName = null;
3664             for(SearchResultCellInfo cell:row.getCells()){
3665                 if("lrc.resultColumn.resultComponent.id".equals(cell.getKey())){
3666                     lrcId = cell.getValue();
3667                 }else if("lrc.resultColumn.resultComponent.name".equals(cell.getKey())){
3668                     lrcName = cell.getValue();
3669                 }
3670             }
3671             if(lrcId!=null && rcIdToRowMapping.get(lrcId)!=null){
3672                 for(SearchResultRowInfo resultRow : rcIdToRowMapping.get(lrcId)){
3673                     resultRow.addCell("lu.resultColumn.resultComponentName",lrcName);
3674                 }
3675             }
3676         }
3677 
3678         return searchResult;
3679     }
3680 
3681     @Override
3682     @Transactional(readOnly=true)
3683     public SearchResultInfo search(SearchRequestInfo searchRequest, ContextInfo contextInfo) throws MissingParameterException, PermissionDeniedException, OperationFailedException, InvalidParameterException {
3684         checkForMissingParameter(searchRequest, "searchRequest");
3685 
3686         if (SEARCH_KEY_DEPENDENCY_ANALYSIS.equals(searchRequest.getSearchKey())) {
3687             String cluId = null;
3688             for (SearchParamInfo param : searchRequest.getParams()) {
3689                 if ("lu.queryParam.luOptionalCluId".equals(param.getKey())) {
3690                     cluId = (String) param.getValues().get(0);
3691                     break;
3692                 }
3693             }
3694             try {
3695                 return doDependencyAnalysisSearch(cluId, contextInfo);
3696             } catch (DoesNotExistException e) {
3697                 throw new RuntimeException("Error performing search");//FIXME should be more checked service exceptions thrown
3698             }
3699         } else if (SEARCH_KEY_BROWSE_PROGRAM.equals(searchRequest.getSearchKey())) {
3700             return doBrowseProgramSearch(contextInfo);
3701         }else if(SEARCH_KEY_PROPOSALS_BY_COURSE_CODE.equals(searchRequest.getSearchKey())){
3702             String courseCode = null;
3703             for(SearchParamInfo param:searchRequest.getParams()){
3704                 if("lu.queryParam.luOptionalCode".equals(param.getKey())){
3705                     courseCode = (String)param.getValues().get(0);
3706                     break;
3707                 }
3708             }
3709             return doSearchProposalsByCourseCode(courseCode, contextInfo);
3710         }else if(SEARCH_KEY_BROWSE_VERSIONS.equals(searchRequest.getSearchKey())){
3711             return doBrowseVersionsSearch(searchRequest, contextInfo);
3712         }else if(SEARCH_KEY_LU_RESULT_COMPONENTS.equals(searchRequest.getSearchKey())){
3713             return doResultComponentTypesForCluSearch(searchRequest, contextInfo);
3714         }else if(SEARCH_KEY_CLUSET_SEARCH_GENERIC.equals(searchRequest.getSearchKey())){
3715             //If any clu specific params are set, use a search key that has the clu defined in the JPQL
3716             for(SearchParamInfo param:searchRequest.getParams()){
3717                 if(param.getKey().contains("queryParam.luOptional")){
3718                     searchRequest.setSearchKey(SEARCH_KEY_CLUSET_SEARCH_GENERICWITHCLUS);
3719                     break;
3720                 }
3721             }
3722         }
3723         return searchManager.search(searchRequest, contextInfo);
3724 
3725 
3726     }
3727 
3728     @Override
3729     public VersionDisplayInfo getLatestVersion(String refObjectTypeURI, String refObjectId, ContextInfo context)
3730             throws DoesNotExistException, InvalidParameterException,
3731             MissingParameterException, OperationFailedException, PermissionDeniedException {
3732         VersionDisplayInfo versionInfo = null;
3733         if (CluServiceConstants.CLU_NAMESPACE_URI.equals(refObjectTypeURI)) {
3734             try {
3735                 versionInfo = this.getLatestVersion(refObjectId, refObjectTypeURI, context);
3736             } catch (NoResultException e) {
3737                 throw new DoesNotExistException("getLatestVersion returned no result", e);
3738             }
3739         } else {
3740             throw new UnsupportedOperationException("This method does not know how to handle object type:" + refObjectTypeURI);
3741         }
3742         return versionInfo;
3743     }
3744 
3745     @Override
3746     public VersionDisplayInfo getCurrentVersion(String refObjectTypeURI, String refObjectId, ContextInfo context)
3747             throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
3748         VersionDisplayInfo versionInfo = null;
3749         if (CluServiceConstants.CLU_NAMESPACE_URI.equals(refObjectTypeURI)) {
3750             try {
3751                 versionInfo = luDao.getCurrentCluVersionInfo(refObjectId, refObjectTypeURI);
3752             } catch (NoResultException e) {
3753                 throw new DoesNotExistException("getCurrentCluVersionInfo could not get current CLU version info", e);
3754             }
3755         } else {
3756             throw new UnsupportedOperationException("This method does not know how to handle object type:" + refObjectTypeURI);
3757         }
3758         return versionInfo;
3759     }
3760 
3761     @Override
3762     public VersionDisplayInfo getCurrentVersionOnDate(String refObjectTypeURI, String refObjectId, Date date, ContextInfo context) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
3763         VersionDisplayInfo versionInfo = null;
3764         if (CluServiceConstants.CLU_NAMESPACE_URI.equals(refObjectTypeURI)) {
3765             try {
3766                 versionInfo = luDao.getCurrentVersionOnDate(refObjectId, refObjectTypeURI, date);
3767             } catch (NoResultException e) {
3768                 throw new DoesNotExistException("getCurrentCluVersionInfo could not get current CLU version info", e);
3769             }
3770         } else {
3771             throw new UnsupportedOperationException("This method does not know how to handle object type:" + refObjectTypeURI);
3772         }
3773         return versionInfo;
3774     }
3775 
3776     @Override
3777     public VersionDisplayInfo getFirstVersion(String refObjectTypeURI, String refObjectId, ContextInfo context)
3778             throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
3779         VersionDisplayInfo versionInfo = null;
3780         if (CluServiceConstants.CLU_NAMESPACE_URI.equals(refObjectTypeURI)) {
3781             try {
3782                 versionInfo = luDao.getFirstVersion(refObjectId, refObjectTypeURI);
3783             } catch (NoResultException e) {
3784                 throw new DoesNotExistException("getFirstVersion could not get first version", e);
3785             }
3786         } else {
3787             throw new UnsupportedOperationException("This method does not know how to handle object type:" + refObjectTypeURI);
3788         }
3789         return versionInfo;
3790     }
3791 
3792     @Override
3793     public VersionDisplayInfo getVersionBySequenceNumber(String refObjectTypeURI, String refObjectId,
3794                                                          Long sequence, ContextInfo context)
3795             throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
3796         VersionDisplayInfo versionInfo = null;
3797         if (CluServiceConstants.CLU_NAMESPACE_URI.equals(refObjectTypeURI)) {
3798             try {
3799                 versionInfo = luDao.getVersionBySequenceNumber(refObjectId, refObjectTypeURI, sequence);
3800             } catch (NoResultException e) {
3801                 throw new DoesNotExistException("getVersionBySequenceNumber", e);
3802             }
3803         } else {
3804             throw new UnsupportedOperationException("This method does not know how to handle object type:" + refObjectTypeURI);
3805         }
3806         return versionInfo;
3807     }
3808 
3809     @Override
3810     public List<VersionDisplayInfo> getVersions(String refObjectTypeURI, String refObjectId, ContextInfo context)
3811             throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
3812         List<VersionDisplayInfo> versionInfos = null;
3813         if (CluServiceConstants.CLU_NAMESPACE_URI.equals(refObjectTypeURI)) {
3814             versionInfos = luDao.getVersions(refObjectId, refObjectTypeURI);
3815             if (versionInfos == null) {
3816                 versionInfos = Collections.<VersionDisplayInfo>emptyList();
3817             }
3818         } else {
3819             throw new UnsupportedOperationException("This method does not know how to handle object type:" + refObjectTypeURI);
3820         }
3821         return versionInfos;
3822     }
3823 
3824     @Override
3825     public List<VersionDisplayInfo> getVersionsInDateRange(String refObjectTypeURI,
3826                                                            String refObjectId,
3827                                                            Date from,
3828                                                            Date to,
3829                                                            ContextInfo context)
3830             throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
3831         List<VersionDisplayInfo> versionInfos = null;
3832         if (CluServiceConstants.CLU_NAMESPACE_URI.equals(refObjectTypeURI)) {
3833             versionInfos = luDao.getVersionsInDateRange(refObjectId, refObjectTypeURI, from, to);
3834             if (versionInfos == null) {
3835                 versionInfos = Collections.<VersionDisplayInfo>emptyList();
3836             }
3837         } else {
3838             throw new UnsupportedOperationException("This method does not know how to handle object type:" + refObjectTypeURI);
3839         }
3840         return versionInfos;
3841     }
3842 
3843     public void setSearchDispatcher(SearchService searchDispatcher) {
3844         this.searchDispatcher = searchDispatcher;
3845     }
3846 
3847     private ObjectStructureDefinition getObjectStructure(String objectTypeKey) {
3848         return dictionaryServiceDelegate.getObjectStructure(objectTypeKey);
3849     }
3850 
3851     /* (non-Javadoc)
3852      * @see org.kuali.student.r2.lum.clu.service.CluService#getCluResultsByClus(java.util.List, org.kuali.student.r2.common.dto.ContextInfo)
3853      */
3854     @Override
3855     public List<CluResultInfo> getCluResultsByClus(
3856              List<String> cluIds,
3857              ContextInfo contextInfo)
3858             throws DoesNotExistException, InvalidParameterException,
3859             MissingParameterException, OperationFailedException {
3860         checkForMissingParameter(cluIds, "cluIds");
3861         checkForEmptyList(cluIds, "cluIds");
3862         return CluServiceAssembler.toCluResultInfos(luDao.getCluResultsByClus(cluIds));
3863 
3864     }
3865 
3866     /* (non-Javadoc)
3867      * @see org.kuali.student.r2.lum.clu.service.CluService#searchForClus(org.kuali.rice.core.api.criteria.QueryByCriteria, org.kuali.student.r2.common.dto.ContextInfo)
3868      */
3869     @Override
3870     public List<CluInfo> searchForClus(
3871              QueryByCriteria criteria,
3872              ContextInfo contextInfo)
3873             throws InvalidParameterException, MissingParameterException,
3874             OperationFailedException, PermissionDeniedException {
3875         // TODO Auto-generated method stub
3876         throw new UnsupportedOperationException("not implemented");
3877     }
3878 
3879     /* (non-Javadoc)
3880      * @see org.kuali.student.r2.lum.clu.service.CluService#searchForCluIds(org.kuali.rice.core.api.criteria.QueryByCriteria, org.kuali.student.r2.common.dto.ContextInfo)
3881      */
3882     @Override
3883     public List<String> searchForCluIds(
3884              QueryByCriteria criteria,
3885              ContextInfo contextInfo)
3886             throws InvalidParameterException, MissingParameterException,
3887             OperationFailedException, PermissionDeniedException {
3888         // TODO Auto-generated method stub
3889         throw new UnsupportedOperationException("not implemented");
3890     }
3891 
3892     /* (non-Javadoc)
3893      * @see org.kuali.student.r2.lum.clu.service.CluService#searchForCluCluRelations(org.kuali.rice.core.api.criteria.QueryByCriteria, org.kuali.student.r2.common.dto.ContextInfo)
3894      */
3895     @Override
3896     public List<CluCluRelationInfo> searchForCluCluRelations(
3897              QueryByCriteria criteria,
3898              ContextInfo contextInfo)
3899             throws InvalidParameterException, MissingParameterException,
3900             OperationFailedException, PermissionDeniedException {
3901         // TODO Auto-generated method stub
3902         throw new UnsupportedOperationException("not implemented");
3903     }
3904 
3905     /* (non-Javadoc)
3906      * @see org.kuali.student.r2.lum.clu.service.CluService#searchForCluCluRelationIds(org.kuali.rice.core.api.criteria.QueryByCriteria, org.kuali.student.r2.common.dto.ContextInfo)
3907      */
3908     @Override
3909     public List<String> searchForCluCluRelationIds(
3910              QueryByCriteria criteria,
3911              ContextInfo contextInfo)
3912             throws InvalidParameterException, MissingParameterException,
3913             OperationFailedException, PermissionDeniedException {
3914         // TODO Auto-generated method stub
3915         throw new UnsupportedOperationException("not implemented");
3916     }
3917 
3918     /* (non-Javadoc)
3919      * @see org.kuali.student.r2.lum.clu.service.CluService#searchForCluLoRelations(org.kuali.rice.core.api.criteria.QueryByCriteria, org.kuali.student.r2.common.dto.ContextInfo)
3920      */
3921     @Override
3922     public List<CluLoRelationInfo> searchForCluLoRelations(
3923              QueryByCriteria criteria,
3924              ContextInfo contextInfo)
3925             throws InvalidParameterException, MissingParameterException,
3926             OperationFailedException, PermissionDeniedException {
3927         // TODO Auto-generated method stub
3928         throw new UnsupportedOperationException("not implemented");
3929     }
3930 
3931     /* (non-Javadoc)
3932      * @see org.kuali.student.r2.lum.clu.service.CluService#searchForCluLoRelationIds(org.kuali.rice.core.api.criteria.QueryByCriteria, org.kuali.student.r2.common.dto.ContextInfo)
3933      */
3934     @Override
3935     public List<String> searchForCluLoRelationIds(
3936              QueryByCriteria criteria,
3937              ContextInfo contextInfo)
3938             throws InvalidParameterException, MissingParameterException,
3939             OperationFailedException, PermissionDeniedException {
3940         // TODO Auto-generated method stub
3941         throw new UnsupportedOperationException("not implemented");
3942     }
3943 
3944     /* (non-Javadoc)
3945      * @see org.kuali.student.r2.lum.clu.service.CluService#searchForCluPublications(org.kuali.rice.core.api.criteria.QueryByCriteria, org.kuali.student.r2.common.dto.ContextInfo)
3946      */
3947     @Override
3948     public List<CluPublicationInfo> searchForCluPublications(
3949              QueryByCriteria criteria,
3950              ContextInfo contextInfo)
3951             throws InvalidParameterException, MissingParameterException,
3952             OperationFailedException, PermissionDeniedException {
3953         // TODO Auto-generated method stub
3954         throw new UnsupportedOperationException("not implemented");
3955     }
3956 
3957     /* (non-Javadoc)
3958      * @see org.kuali.student.r2.lum.clu.service.CluService#searchForCluPublicationIds(org.kuali.rice.core.api.criteria.QueryByCriteria, org.kuali.student.r2.common.dto.ContextInfo)
3959      */
3960     @Override
3961     public List<String> searchForCluPublicationIds(
3962              QueryByCriteria criteria,
3963              ContextInfo contextInfo)
3964             throws InvalidParameterException, MissingParameterException,
3965             OperationFailedException, PermissionDeniedException {
3966         // TODO Auto-generated method stub
3967         throw new UnsupportedOperationException("not implemented");
3968     }
3969 
3970     /* (non-Javadoc)
3971      * @see org.kuali.student.r2.lum.clu.service.CluService#searchForCluResults(org.kuali.rice.core.api.criteria.QueryByCriteria, org.kuali.student.r2.common.dto.ContextInfo)
3972      */
3973     @Override
3974     public List<CluResultInfo> searchForCluResults(
3975              QueryByCriteria criteria,
3976              ContextInfo contextInfo)
3977             throws InvalidParameterException, MissingParameterException,
3978             OperationFailedException, PermissionDeniedException {
3979         // TODO Auto-generated method stub
3980         throw new UnsupportedOperationException("not implemented");
3981     }
3982 
3983     /* (non-Javadoc)
3984      * @see org.kuali.student.r2.lum.clu.service.CluService#searchForCluResultIds(org.kuali.rice.core.api.criteria.QueryByCriteria, org.kuali.student.r2.common.dto.ContextInfo)
3985      */
3986     @Override
3987     public List<String> searchForCluResultIds(
3988              QueryByCriteria criteria,
3989              ContextInfo contextInfo)
3990             throws InvalidParameterException, MissingParameterException,
3991             OperationFailedException, PermissionDeniedException {
3992         // TODO Auto-generated method stub
3993         throw new UnsupportedOperationException("not implemented");
3994     }
3995     
3996     
3997 }