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