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