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