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