001/**
002 * Copyright 2013 The Kuali Foundation Licensed under the
003 * Educational Community License, Version 2.0 (the "License"); you may
004 * not use this file except in compliance with the License. You may
005 * obtain a copy of the License at
006 *
007 * http://www.osedu.org/licenses/ECL-2.0
008 *
009 * Unless required by applicable law or agreed to in writing,
010 * software distributed under the License is distributed on an "AS IS"
011 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
012 * or implied. See the License for the specific language governing
013 * permissions and limitations under the License.
014 *
015 */
016package org.kuali.student.enrollment.class2.autogen.service.impl;
017
018import org.apache.commons.lang.BooleanUtils;
019import org.apache.commons.lang.StringUtils;
020import org.kuali.rice.core.api.criteria.PredicateFactory;
021import org.kuali.rice.core.api.criteria.QueryByCriteria;
022import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
023import org.kuali.rice.kim.api.KimConstants;
024import org.kuali.rice.kim.api.identity.IdentityService;
025import org.kuali.rice.kim.api.identity.entity.EntityDefault;
026import org.kuali.rice.kim.api.identity.entity.EntityDefaultQueryResults;
027import org.kuali.rice.kim.api.identity.principal.Principal;
028import org.kuali.rice.kim.api.permission.PermissionService;
029import org.kuali.rice.kim.api.services.KimApiServiceLocator;
030import org.kuali.rice.krad.util.GlobalVariables;
031import org.kuali.rice.krad.util.KRADConstants;
032import org.kuali.student.common.uif.util.GrowlIcon;
033import org.kuali.student.common.uif.util.KSUifUtils;
034import org.kuali.student.enrollment.class2.autogen.controller.ARGUtil;
035import org.kuali.student.enrollment.class2.autogen.dto.ScheduleCalcContainer;
036import org.kuali.student.enrollment.class2.autogen.dto.ScheduleRequestCalcContainer;
037import org.kuali.student.enrollment.class2.autogen.form.ARGCourseOfferingManagementForm;
038import org.kuali.student.enrollment.class2.autogen.service.ARGCourseOfferingManagementViewHelperService;
039import org.kuali.student.enrollment.class2.autogen.util.ARGToolbarUtil;
040import org.kuali.student.enrollment.class2.courseoffering.dto.ActivityOfferingClusterWrapper;
041import org.kuali.student.enrollment.class2.courseoffering.dto.ActivityOfferingWrapper;
042import org.kuali.student.enrollment.class2.courseoffering.dto.CourseOfferingContextBar;
043import org.kuali.student.enrollment.class2.courseoffering.dto.CourseOfferingListSectionWrapper;
044import org.kuali.student.enrollment.class2.courseoffering.dto.CourseOfferingWrapper;
045import org.kuali.student.enrollment.class2.courseoffering.dto.RegistrationGroupWrapper;
046import org.kuali.student.enrollment.class2.courseoffering.service.impl.CO_AO_RG_ViewHelperServiceImpl;
047import org.kuali.student.enrollment.class2.courseoffering.util.CourseOfferingConstants;
048import org.kuali.student.enrollment.class2.courseoffering.util.CourseOfferingResourceLoader;
049import org.kuali.student.enrollment.class2.courseoffering.util.CourseOfferingViewHelperUtil;
050import org.kuali.student.enrollment.class2.courseoffering.util.ManageSocConstants;
051import org.kuali.student.enrollment.class2.courseoffering.util.RegistrationGroupConstants;
052import org.kuali.student.enrollment.class2.scheduleofclasses.dto.ActivityOfferingDisplayWrapper;
053import org.kuali.student.enrollment.class2.scheduleofclasses.util.ScheduleOfClassesConstants;
054import org.kuali.student.enrollment.courseoffering.dto.ActivityOfferingClusterInfo;
055import org.kuali.student.enrollment.courseoffering.dto.ActivityOfferingDisplayInfo;
056import org.kuali.student.enrollment.courseoffering.dto.ActivityOfferingInfo;
057import org.kuali.student.enrollment.courseoffering.dto.ActivityOfferingSetInfo;
058import org.kuali.student.enrollment.courseoffering.dto.CourseOfferingInfo;
059import org.kuali.student.enrollment.courseoffering.dto.FormatOfferingInfo;
060import org.kuali.student.enrollment.courseoffering.dto.RegistrationGroupInfo;
061import org.kuali.student.enrollment.courseoffering.service.CourseOfferingService;
062import org.kuali.student.enrollment.courseofferingset.dto.SocInfo;
063import org.kuali.student.enrollment.courseofferingset.service.CourseOfferingSetService;
064import org.kuali.student.enrollment.lpr.dto.LprInfo;
065import org.kuali.student.enrollment.lpr.service.LprService;
066import org.kuali.student.r2.common.constants.CommonServiceConstants;
067import org.kuali.student.r2.common.dto.ContextInfo;
068import org.kuali.student.r2.common.dto.RichTextInfo;
069import org.kuali.student.r2.common.dto.StatusInfo;
070import org.kuali.student.r2.common.dto.TimeOfDayInfo;
071import org.kuali.student.r2.common.dto.ValidationResultInfo;
072import org.kuali.student.r2.common.exceptions.DoesNotExistException;
073import org.kuali.student.r2.common.exceptions.InvalidParameterException;
074import org.kuali.student.r2.common.exceptions.MissingParameterException;
075import org.kuali.student.r2.common.exceptions.OperationFailedException;
076import org.kuali.student.r2.common.exceptions.PermissionDeniedException;
077import org.kuali.student.r2.common.infc.ValidationResult;
078import org.kuali.student.r2.common.permutation.PermutationUtils;
079import org.kuali.student.r2.common.util.ContextUtils;
080import org.kuali.student.r2.common.util.constants.CourseOfferingServiceConstants;
081import org.kuali.student.r2.common.util.constants.CourseOfferingSetServiceConstants;
082import org.kuali.student.r2.common.util.constants.LprServiceConstants;
083import org.kuali.student.r2.common.util.constants.LuiServiceConstants;
084import org.kuali.student.r2.common.util.date.DateFormatters;
085import org.kuali.student.r2.core.acal.dto.KeyDateInfo;
086import org.kuali.student.r2.core.acal.dto.TermInfo;
087import org.kuali.student.r2.core.acal.service.AcademicCalendarService;
088import org.kuali.student.r2.core.atp.dto.AtpInfo;
089import org.kuali.student.r2.core.atp.service.AtpService;
090import org.kuali.student.r2.core.class1.search.ActivityOfferingSearchServiceImpl;
091import org.kuali.student.r2.core.class1.search.CoreSearchServiceImpl;
092import org.kuali.student.r2.core.class1.search.CourseOfferingManagementSearchImpl;
093import org.kuali.student.r2.core.class1.state.dto.StateInfo;
094import org.kuali.student.r2.core.class1.type.dto.TypeInfo;
095import org.kuali.student.r2.core.class1.type.dto.TypeTypeRelationInfo;
096import org.kuali.student.r2.core.constants.AcademicCalendarServiceConstants;
097import org.kuali.student.r2.core.constants.AtpServiceConstants;
098import org.kuali.student.r2.core.constants.TypeServiceConstants;
099import org.kuali.student.r2.core.room.dto.BuildingInfo;
100import org.kuali.student.r2.core.room.dto.RoomInfo;
101import org.kuali.student.r2.core.scheduling.constants.SchedulingServiceConstants;
102import org.kuali.student.r2.core.scheduling.dto.ScheduleRequestComponentInfo;
103import org.kuali.student.r2.core.scheduling.dto.ScheduleRequestInfo;
104import org.kuali.student.r2.core.scheduling.dto.TimeSlotInfo;
105import org.kuali.student.r2.core.scheduling.infc.ScheduleComponentDisplay;
106import org.kuali.student.r2.core.scheduling.util.SchedulingServiceUtil;
107import org.kuali.student.r2.core.search.dto.SearchRequestInfo;
108import org.kuali.student.r2.core.search.dto.SearchResultCellInfo;
109import org.kuali.student.r2.core.search.dto.SearchResultInfo;
110import org.kuali.student.r2.core.search.dto.SearchResultRowInfo;
111import org.kuali.student.r2.core.search.service.SearchService;
112import org.kuali.student.r2.lum.course.dto.ActivityInfo;
113import org.kuali.student.r2.lum.course.dto.CourseInfo;
114import org.kuali.student.r2.lum.course.dto.CourseJointInfo;
115import org.kuali.student.r2.lum.course.dto.FormatInfo;
116import org.kuali.student.r2.lum.course.service.CourseService;
117import org.kuali.student.r2.lum.lrc.service.LRCService;
118
119import javax.xml.namespace.QName;
120import java.util.ArrayList;
121import java.util.Arrays;
122import java.util.Calendar;
123import java.util.Collection;
124import java.util.Collections;
125import java.util.Comparator;
126import java.util.Date;
127import java.util.Formatter;
128import java.util.HashMap;
129import java.util.HashSet;
130import java.util.List;
131import java.util.Locale;
132import java.util.Map;
133import java.util.Set;
134
135import static java.util.Arrays.asList;
136
137/**
138 * This class implements Course offering management functionality using auto-generated registration groups
139 *
140 * @author Kuali Student Team
141 */
142public class ARGCourseOfferingManagementViewHelperServiceImpl extends CO_AO_RG_ViewHelperServiceImpl implements ARGCourseOfferingManagementViewHelperService {
143
144    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ARGCourseOfferingManagementViewHelperServiceImpl.class);
145
146    private AcademicCalendarService acalService = null;
147    private CourseOfferingService coService = null;
148    private SearchService searchService = null;
149
150    private CourseService courseService;
151    private LRCService lrcService;
152    private AtpService atpService;
153    private CourseOfferingSetService socService;
154
155    private static LprService lprService;
156    private static PermissionService permissionService;
157    private static IdentityService identityService;
158
159    /**
160     * This method fetches the <code>TermInfo</code> and validate for exact match
161     *
162     * @param form input ARGCourseOfferingManagementForm
163     * @throws Exception
164     */
165    public void populateTerm(ARGCourseOfferingManagementForm form) throws Exception {
166
167        String termCode = form.getTermCode();
168
169        form.getCourseOfferingResultList().clear();
170
171        QueryByCriteria.Builder qbcBuilder = QueryByCriteria.Builder.create();
172        qbcBuilder.setPredicates(PredicateFactory.equal("atpCode", termCode));
173
174        QueryByCriteria criteria = qbcBuilder.build();
175
176        List<TermInfo> terms = getAcalService().searchForTerms(criteria, createContextInfo());
177
178        if (terms.isEmpty()) {
179            GlobalVariables.getMessageMap().putError("termCode", CourseOfferingConstants.COURSEOFFERING_MSG_ERROR_NO_TERM_IS_FOUND, termCode);
180        } else if (terms.size() > 1) {
181            GlobalVariables.getMessageMap().putError("termCode", CourseOfferingConstants.COURSEOFFERING_MSG_ERROR_FOUND_MORE_THAN_ONE_TERM, termCode);
182        } else {
183            form.setTermInfo(terms.get(0));
184
185            //Checking soc
186            List<String> socIds;
187            try {
188                socIds = getSocService().getSocIdsByTerm(form.getTermInfo().getId(), createContextInfo());
189            } catch (Exception e) {
190                throw convertServiceExceptionsToUI(e);
191            }
192
193            if (socIds.isEmpty()) {
194                GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, ManageSocConstants.MessageKeys.ERROR_SOC_NOT_EXISTS);
195            } else {
196                setSocStateKeys(form, socIds);
197
198                blockUserIfSocStateIs( form, CourseOfferingSetServiceConstants.SOC_SCHEDULING_STATE_IN_PROGRESS, ManageSocConstants.MessageKeys.ERROR_CANNOT_ACCESS_COURSE_OFFERING_WHILE_SOC_IS_IN_PROGRESS );
199                blockUserIfSocStateIs( form, CourseOfferingSetServiceConstants.PUBLISHING_SOC_STATE_KEY, ManageSocConstants.MessageKeys.ERROR_CANNOT_ACCESS_COURSE_OFFERING_WHILE_SOC_IS_PUBLISHING);
200
201                // setting term first day of classes
202                List<KeyDateInfo> keyDateInfoList = getAcalService().getKeyDatesForTerm(form.getTermInfo().getId(), createContextInfo());
203                Date termClassStartDate = null;
204                for (KeyDateInfo keyDateInfo : keyDateInfoList) {
205                    if (keyDateInfo.getTypeKey().equalsIgnoreCase(AtpServiceConstants.MILESTONE_SEATPOOL_FIRST_DAY_OF_CLASSES_TYPE_KEY) && keyDateInfo.getStartDate() != null) {
206                        termClassStartDate = keyDateInfo.getStartDate();
207                        break;
208                    }
209                }
210                for (KeyDateInfo keyDateInfo : keyDateInfoList) {
211
212                    if (keyDateInfo.getTypeKey().equalsIgnoreCase(AtpServiceConstants.MILESTONE_INSTRUCTIONAL_PERIOD_TYPE_KEY) && keyDateInfo.getStartDate() != null && keyDateInfo.getEndDate() != null) {
213                        termClassStartDate = keyDateInfo.getStartDate();
214                        break;
215                    }
216                }
217                form.setTermClassStartDate(termClassStartDate);
218            }
219        }
220
221    }
222
223    private void blockUserIfSocStateIs( ARGCourseOfferingManagementForm form, String socStateKeyToBlockUserOn, String errorMessageKey ) {
224        errorMessageKey = StringUtils.defaultIfEmpty( errorMessageKey, ManageSocConstants.MessageKeys.ERROR_CANNOT_ACCESS_COURSE_OFFERING_WHILE_SOC_INVALID_STATE_DEFAULT);
225
226        if( StringUtils.equalsIgnoreCase( form.getSocStateKey(), socStateKeyToBlockUserOn ) ) {
227            GlobalVariables.getMessageMap().putError( KRADConstants.GLOBAL_ERRORS, errorMessageKey );
228        }
229    }
230
231    /**
232     * This method loads all the course offerings for a term and course code.
233     *
234     * @param termId Term Id
235     * @param courseCode Course Code
236     * @param form Input Form
237     * @throws Exception
238     */
239    public void loadCourseOfferingsByTermAndCourseCode(String termId, String courseCode, ARGCourseOfferingManagementForm form) throws Exception {
240
241        SearchRequestInfo searchRequest = new SearchRequestInfo(CourseOfferingManagementSearchImpl.CO_MANAGEMENT_SEARCH.getKey());
242        searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.COURSE_CODE, courseCode);
243        searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.ATP_ID, termId);
244        searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.CROSS_LIST_SEARCH_ENABLED, BooleanUtils.toStringTrueFalse(true));
245
246        // Check to see if the "exact co code match" param has been set. If so, add a query param.
247        boolean isExactMatchSearch = BooleanUtils.toBoolean(form.getViewRequestParameters().get(CourseOfferingManagementSearchImpl.SearchParameters.IS_EXACT_MATCH_CO_CODE_SEARCH));
248        if (isExactMatchSearch) {
249            searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.IS_EXACT_MATCH_CO_CODE_SEARCH, BooleanUtils.toStringTrueFalse(true));
250        }
251
252        loadCourseOfferings(searchRequest, form);
253
254        if (form.getCourseOfferingResultList().isEmpty()) {
255            LOG.error("Error: Can't find any Course Offering for a Course Code: " + courseCode + " in term: " + termId);
256            GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, CourseOfferingConstants.COURSEOFFERING_MSG_ERROR_NO_COURSE_OFFERING_IS_FOUND, "Course Code", courseCode, termId);
257        }
258
259        form.setContextBar( CourseOfferingContextBar.NEW_INSTANCE(form.getTermInfo(), form.getSocStateKey(),
260                getStateService(), getAcalService(), createContextInfo()) );
261    }
262
263    /**
264     * This method loads all the course offerings for a term and subject area/code.
265     *
266     * @param termId      term id
267     * @param subjectCode subject area
268     * @param form        course offering management form
269     * @throws Exception
270     */
271    public void loadCourseOfferingsByTermAndSubjectCode(String termId, String subjectCode, ARGCourseOfferingManagementForm form) throws Exception {
272
273        SearchRequestInfo searchRequest = new SearchRequestInfo(CourseOfferingManagementSearchImpl.CO_MANAGEMENT_SEARCH.getKey());
274        searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.SUBJECT_AREA, subjectCode);
275        searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.ATP_ID, termId);
276        searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.CROSS_LIST_SEARCH_ENABLED, BooleanUtils.toStringTrueFalse(true));
277
278        loadCourseOfferings(searchRequest, form);
279
280        if (form.getCourseOfferingResultList().isEmpty()) {
281            LOG.error("Error: Can't find any Course Offering for a Subject Code: " + subjectCode + " in term: " + termId);
282            GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, CourseOfferingConstants.COURSEOFFERING_MSG_ERROR_NO_COURSE_OFFERING_IS_FOUND, "Subject Code", subjectCode, termId);
283        }
284
285    }
286
287    /**
288     * This method fetches, prepares and sets
289     * activityWrapperList for View All Activities tab,
290     * clusterResultList for View byClusters
291     * rgResultList for View Registration Groupsall the course offerings for a term and course/subject code.
292     *
293     * @param form input form
294     * @param theCourseOffering course offering
295     * @throws Exception
296     */
297    public void build_AOs_RGs_AOCs_Lists_For_TheCourseOffering(ARGCourseOfferingManagementForm form, CourseOfferingWrapper theCourseOffering) throws Exception {
298        //KSENROLL-6102 performance improvements, delete this code when performance work is complete
299
300        //New Search Stuff!
301        String coId = form.getCurrentCourseOfferingWrapper().getCourseOfferingId();
302
303        //First search for AOs and Cluster information
304        SearchRequestInfo sr = new SearchRequestInfo(ActivityOfferingSearchServiceImpl.AOS_AND_CLUSTERS_BY_CO_ID_SEARCH_KEY);
305        sr.addParam(ActivityOfferingSearchServiceImpl.SearchParameters.CO_ID, coId);
306        SearchResultInfo results = searchService.search(sr, null);
307
308        Map<String, List<ActivityOfferingWrapper>> sch2aoMap = new HashMap<String, List<ActivityOfferingWrapper>>();
309        List<String> aoIdsWithoutSch = new ArrayList<String>();
310        Map<String, ActivityOfferingClusterWrapper> clusterMap = new HashMap<String, ActivityOfferingClusterWrapper>();
311        Map<String, ActivityOfferingWrapper> aoMap = new HashMap<String, ActivityOfferingWrapper>();
312        Map<String, FormatOfferingInfo> foIds = new HashMap<String, FormatOfferingInfo>();
313        Map<String, List<ScheduleCalcContainer>> ao2sch = new HashMap<String, List<ScheduleCalcContainer>>();
314        Map<String, List<ScheduleRequestCalcContainer>> ao2schReq = new HashMap<String, List<ScheduleRequestCalcContainer>>();
315        ContextInfo contextInfo = ContextUtils.createDefaultContextInfo();
316
317        //Parse the search results
318        List<ActivityOfferingWrapper> wrappers = processAoClusterData(results, sch2aoMap, clusterMap, aoMap, foIds, aoIdsWithoutSch, contextInfo);
319
320        processAosData(coId, clusterMap);
321
322        //Sort Activity Wrappers and Clusters
323        ArrayList<ActivityOfferingClusterWrapper> clusterWrapperList = new ArrayList<ActivityOfferingClusterWrapper>(clusterMap.values());
324        if(clusterWrapperList.size() > 1){
325            Collections.sort(clusterWrapperList, new Comparator<ActivityOfferingClusterWrapper>() {
326                @Override
327                public int compare(ActivityOfferingClusterWrapper o1, ActivityOfferingClusterWrapper o2) {
328                    int nameComparison = o1.getAoCluster().getPrivateName().compareToIgnoreCase(o2.getAoCluster().getPrivateName());
329                    int formatComparison = o1.getFormatNameForDisplay().compareToIgnoreCase(o2.getFormatNameForDisplay());
330
331                    if(formatComparison==0){
332                        return nameComparison;
333                    } else {
334                        return formatComparison;
335                    }
336                }
337            });
338        }
339
340        if(wrappers.size() >1){
341            Collections.sort(wrappers, new Comparator<ActivityOfferingWrapper>() {
342
343                @Override
344                public int compare(ActivityOfferingWrapper o1, ActivityOfferingWrapper o2) {
345                    int typeComparison = (o1.getTypeName().compareTo(o2.getTypeName())) * -1;
346                    int nameComparison = o1.getActivityCode().compareTo(o2.getActivityCode());
347                    if(typeComparison==0){
348                        return  nameComparison;
349                    } else {
350                        return typeComparison;
351                    }
352                }
353            });
354        }
355
356        //Set the items in the form
357        form.setActivityWrapperList(wrappers);
358        form.getClusterResultList().clear();
359        form.getClusterResultList().addAll(clusterWrapperList);
360
361        //fix for KSENROLL-7886: foIds was populated in processAoClusterData method.
362        //However if one FO does not have a cluster and AO, that FO won't be added to foIds, which causes NPE error later.
363        foIds = new HashMap<String, FormatOfferingInfo>();
364        List<FormatOfferingInfo> foList = getCourseOfferingService().getFormatOfferingsByCourseOffering(coId,ContextUtils.createDefaultContextInfo());
365        for(FormatOfferingInfo fo:foList){
366            foIds.put(fo.getId(), fo);
367        }
368        //Get the mapping of formatids to AO types
369        //by Bonnie: why do we need this method? It causes the dropdown list
370        //because of this method, in manage the CO page, when click Add Activity button from toolbar
371        //in the popover form, Activity Type dropdown list would display every AO type twice. I comment this method out for now
372//        processRelatedTypeKeysForFos(coId, foIds, contextInfo);
373
374        form.setFoId2aoTypeMap(foIds);
375
376        if (!aoMap.keySet().isEmpty()) {
377            //Process Colocated
378            sr = new SearchRequestInfo(ActivityOfferingSearchServiceImpl.COLOCATED_AOS_BY_AO_IDS_SEARCH_KEY);
379            sr.addParam(ActivityOfferingSearchServiceImpl.SearchParameters.AO_IDS, new ArrayList<String>(aoMap.keySet()));
380            results = searchService.search(sr, null);
381
382            processColocated(results, aoMap);
383
384
385            //Addin LPR data
386            processInstructors(aoMap, ContextUtils.createDefaultContextInfo());
387
388            //Search for schedule information
389            if (!sch2aoMap.isEmpty()){
390                sr = new SearchRequestInfo(CoreSearchServiceImpl.SCH_AND_ROOM_SEARH_BY_ID_SEARCH_KEY);
391                sr.addParam(CoreSearchServiceImpl.SearchParameters.SCHEDULE_IDS, new ArrayList<String>(sch2aoMap.keySet()));
392                results = searchService.search(sr, null);
393
394                //processSchData(results, sch2aoMap, aoIdsWithoutSch, aoMap, ContextUtils.createDefaultContextInfo());
395
396                // the next two methods pull scheduling data from the DB and put them into the ao2sch map
397                processScheduleInfo(results, sch2aoMap, ao2sch);
398            }
399            processScheduleRequestsForAos(aoIdsWithoutSch, ao2schReq, contextInfo);
400
401            // this takes the scheduling data and puts it into the screen form
402            processScheduleData(aoMap, ao2sch, ao2schReq);
403
404            //Search for registration group information
405            sr = new SearchRequestInfo(ActivityOfferingSearchServiceImpl.REG_GROUPS_BY_CO_ID_SEARCH_KEY);
406            sr.addParam(ActivityOfferingSearchServiceImpl.SearchParameters.CO_ID, form.getCurrentCourseOfferingWrapper().getCourseOfferingId());
407            results = searchService.search(sr, null);
408
409            List<RegistrationGroupWrapper> rgWrappers = processRgData(results, clusterMap, aoMap);
410
411            //Sort rgWrappers
412            if(rgWrappers.size()>1){
413                Collections.sort(rgWrappers, new Comparator<RegistrationGroupWrapper>() {
414                    @Override
415                    public int compare(RegistrationGroupWrapper o1, RegistrationGroupWrapper o2) {
416                        return o1.getRgInfo().getName().compareTo(o2.getRgInfo().getName());
417                    }
418                });
419            }
420
421            form.setRgResultList(rgWrappers);
422
423            form.setHasMoreThanOneCluster(clusterMap.size() > 1);
424
425
426            //Validate Reg Groups
427            Date startOfValidation = new Date();
428            int i = 0;
429            for (ActivityOfferingClusterWrapper cluster : clusterWrapperList) {
430                List<RegistrationGroupInfo> rgInfos = new ArrayList<RegistrationGroupInfo>();
431                for (RegistrationGroupWrapper rgWrapper : cluster.getRgWrapperList()) {
432                    rgInfos.add(rgWrapper.getRgInfo());
433                }
434                List<ActivityOfferingInfo> aoInfos = new ArrayList<ActivityOfferingInfo>();
435                for (ActivityOfferingWrapper aoWrapper : cluster.getAoWrapperList()) {
436                    aoInfos.add(aoWrapper.getAoInfo());
437                }
438                _validateRegistrationGroupsPerCluster(rgInfos, aoInfos, cluster, form, i, ao2sch, ao2schReq, aoMap);
439
440                // Test the Cluster for Multiple AO types and Term types
441                _validateMulitpleTermsPerCluster(form.getFoId2aoTypeMap().get(cluster.getFormatOfferingId()).getActivityOfferingTypeKeys(),cluster, i);
442
443                i++;
444            }
445            Date endOfValidation = new Date();
446            LOG.info("Time of RG Validation:" + (endOfValidation.getTime() - startOfValidation.getTime()) + "ms");
447        }
448        // Normally we would use the KeyValue finder for this, but since we HAVE all the data, why waste sql calls
449        // replaces : ARGActivitiesForCreateAOKeyValues.java
450        //List<KeyValue>
451
452
453    }
454
455    private void _validateMulitpleTermsPerCluster(List<String> aoTypeKeys,ActivityOfferingClusterWrapper cluster, int clusterIndex ){
456        // Test the Cluster for Multiple AO types and Term types
457        if(aoTypeKeys.size()>1){
458            List<String> termIds = new ArrayList<String>();
459
460            // Tests for multiple subTerms
461            for(ActivityOfferingWrapper aoWrapper : cluster.getAoWrapperList()){
462                if(termIds.size()==0){
463                    termIds.add(aoWrapper.getSubTermId());
464                    continue;
465                }
466                boolean newTerm = false;
467                for(String id : termIds){
468                    if(id == null) continue;
469                    if(aoWrapper.getSubTermId().compareTo(id)!=0){
470                        newTerm=true;
471                    }
472                }
473                if(newTerm) termIds.add(aoWrapper.getSubTermId());
474            }
475            if(termIds.size()>1){
476                GlobalVariables.getMessageMap().putWarningForSectionId("activityOfferingsPerCluster_line" + clusterIndex, RegistrationGroupConstants.MSG_ERROR_CLUSTER_MULTIPLE_TERMS, cluster.getAoCluster().getPrivateName());
477            }
478
479            // Test for multiple Terms
480            termIds = new ArrayList<String>();
481            for(ActivityOfferingWrapper aoWrapper : cluster.getAoWrapperList()){
482                if(termIds.size()==0){
483                    termIds.add(aoWrapper.getTermId());
484                    continue;
485                }
486                boolean newTerm = false;
487                for(String id : termIds){
488                    if(id == null) continue;
489                    if(aoWrapper.getTermId().compareTo(id)!=0){
490                        newTerm=true;
491                    }
492                }
493                if(newTerm) termIds.add(aoWrapper.getTermId());
494            }
495            if(termIds.size()>1){
496                GlobalVariables.getMessageMap().putWarningForSectionId("activityOfferingsPerCluster_line" + clusterIndex, RegistrationGroupConstants.MSG_ERROR_CLUSTER_MULTIPLE_TERMS, cluster.getAoCluster().getPrivateName());
497            }
498        }
499    }
500
501    private void processColocated(SearchResultInfo searchResults, Map<String, ActivityOfferingWrapper> aoMap) {
502
503        for (SearchResultRowInfo row : searchResults.getRows()) {
504            String aoId = null;
505            String aoCode = null;
506            String coCode = null;
507
508            for (SearchResultCellInfo cell : row.getCells()) {
509                if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AO_ID.equals(cell.getKey())) {
510                    aoId = cell.getValue();
511                } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AO_CODE.equals(cell.getKey())) {
512                    aoCode = cell.getValue();
513                } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.CO_CODE.equals(cell.getKey())) {
514                    coCode = cell.getValue();
515                }
516            }
517            ActivityOfferingWrapper aoWrapper = aoMap.get(aoId);
518            aoWrapper.setColocatedAO(true);
519            if (aoWrapper.getColocatedAoInfo() == null || aoWrapper.getColocatedAoInfo().isEmpty()) {
520                aoWrapper.setColocatedAoInfo(coCode + " " + aoCode);
521            } else {
522                aoWrapper.setColocatedAoInfo(aoWrapper.getColocatedAoInfo() + "<br/>" + coCode + " " + aoCode);
523            }
524
525        }
526    }
527
528    private void processInstructors(Map<String, ActivityOfferingWrapper> aoMap, ContextInfo contextInfo) throws InvalidParameterException, MissingParameterException, DoesNotExistException, PermissionDeniedException, OperationFailedException {
529        List<LprInfo> lprInfos = getLprService().getLprsByLuis(new ArrayList<String>(aoMap.keySet()), contextInfo);
530        if (lprInfos != null) {
531            Map<String, Set<String>> principalId2aoIdMap = new HashMap<String, Set<String>>();
532            for (LprInfo lprInfo : lprInfos) {
533                //  Only include the main instructor.
534                if ( ! StringUtils.equals(lprInfo.getTypeKey(), LprServiceConstants.INSTRUCTOR_MAIN_TYPE_KEY)) {
535                    continue;
536                }
537                Set<String> aoIds = principalId2aoIdMap.get(lprInfo.getPersonId());
538                if (aoIds == null) {
539                    aoIds = new HashSet<String>();
540                    principalId2aoIdMap.put(lprInfo.getPersonId(), aoIds);
541                }
542                aoIds.add(lprInfo.getLuiId());
543            }
544            if (!principalId2aoIdMap.keySet().isEmpty()) {
545                EntityDefaultQueryResults results = getInstructorsInfoFromKim(new ArrayList<String>(principalId2aoIdMap.keySet()));
546
547                for (EntityDefault entity : results.getResults()) {
548                    for (Principal principal : entity.getPrincipals()) {
549                        Set<String> aoIds = principalId2aoIdMap.get(principal.getPrincipalId());
550                        if (aoIds != null) {
551                            for (String aoId : aoIds) {
552                                ActivityOfferingWrapper activityOfferingWrapper = aoMap.get(aoId);
553                                if(entity.getName() != null) {
554                                    activityOfferingWrapper.setInstructorDisplayNames(entity.getName().getCompositeName(), true);
555                                } else {
556                                    activityOfferingWrapper.setInstructorDisplayNames(principal.getPrincipalId());
557                                }
558                            }
559                        }
560                    }
561                }
562
563            }
564        }
565    }
566
567    /**
568     * Add scheduling information to the map if there are no "actual" schedules already in place for a particular AO.
569     *
570     *
571     * @param aoIdsWithoutSch list of aoIds without schedule information
572     * @param ao2sch map of aos to schedules
573     *@param contextInfo  @throws Exception
574     */
575    protected void processScheduleRequestsForAos(Collection<String> aoIdsWithoutSch, Map<String, List<ScheduleRequestCalcContainer>> ao2sch, ContextInfo contextInfo) throws Exception {
576
577        if (!aoIdsWithoutSch.isEmpty()) {
578            Set<String> buildingIds = new HashSet<String>();
579            Set<String> roomIds = new HashSet<String>();
580            Set<String> timeslotIds = new HashSet<String>();
581
582            Map<String, BuildingInfo> buildingIdMap = new HashMap<String, BuildingInfo>();
583            Map<String, RoomInfo> roomIdMap = new HashMap<String, RoomInfo>();
584            Map<String, TimeSlotInfo> timeslotIdMap = new HashMap<String, TimeSlotInfo>();
585
586            Map<String, List<ScheduleRequestInfo>>  ao2srMap = new HashMap<String, List<ScheduleRequestInfo>>();
587            HashSet<String> aoIdsSet = new HashSet<String>(aoIdsWithoutSch);
588            for (String aoId : aoIdsSet) {
589                ao2srMap.put(aoId, new ArrayList<ScheduleRequestInfo>());
590                List<ScheduleRequestInfo> srList = getSchedulingService().getScheduleRequestsByRefObjects(CourseOfferingServiceConstants.REF_OBJECT_URI_ACTIVITY_OFFERING, new ArrayList<String>(asList(aoId)), contextInfo);
591                ao2srMap.get(aoId).addAll(srList);
592                for (ScheduleRequestInfo sr : srList) {
593                    for (ScheduleRequestComponentInfo schRequestCom : sr.getScheduleRequestComponents()) {
594                        buildingIds.addAll(schRequestCom.getBuildingIds());
595                        roomIds.addAll(schRequestCom.getRoomIds());
596                        timeslotIds.addAll(schRequestCom.getTimeSlotIds());
597                    }
598                }
599
600            }
601
602            //Lookup the information using bulk service calls and store in maps
603            List<TimeSlotInfo> timeSlotInfos = getSchedulingService().getTimeSlotsByIds(new ArrayList<String>(timeslotIds), contextInfo);
604            for (TimeSlotInfo timeSlotInfo : timeSlotInfos) {
605                timeslotIdMap.put(timeSlotInfo.getId(), timeSlotInfo);
606            }
607            List<RoomInfo> roomInfos = getRoomService().getRoomsByIds(new ArrayList<String>(roomIds), contextInfo);
608            for (RoomInfo roomInfo : roomInfos) {
609                roomIdMap.put(roomInfo.getId(), roomInfo);
610                buildingIds.add(roomInfo.getBuildingId());
611            }
612            List<BuildingInfo> buildingInfos = getRoomService().getBuildingsByIds(new ArrayList<String>(buildingIds), contextInfo);
613            for (BuildingInfo buildingInfo : buildingInfos) {
614                buildingIdMap.put(buildingInfo.getId(), buildingInfo);
615            }
616
617            for (String aoId : aoIdsSet) {
618                List<ScheduleRequestCalcContainer> srccList = new ArrayList<ScheduleRequestCalcContainer>();
619                ao2sch.put(aoId, srccList);
620
621                for (ScheduleRequestInfo sr : ao2srMap.get(aoId)) {
622                    for (ScheduleRequestComponentInfo src : sr.getScheduleRequestComponents()) {
623                        
624                        List<RoomInfo> rooms = new ArrayList<RoomInfo>();
625                        List<BuildingInfo> buildings = new ArrayList<BuildingInfo>();
626                        List<TimeSlotInfo> timeSlots = new ArrayList<TimeSlotInfo>();
627                        
628                        for (String roomId : src.getRoomIds()) {
629                            rooms.add(roomIdMap.get(roomId));
630                            buildings.add(buildingIdMap.get(roomIdMap.get(roomId).getBuildingId()));
631                        }
632                        for (String timeSlotId : src.getTimeSlotIds()) {
633                            TimeSlotInfo timeSlotInfo = timeslotIdMap.get(timeSlotId);
634                            timeSlots.add(timeSlotInfo);
635
636                        }
637                        ScheduleRequestCalcContainer srcc = new ScheduleRequestCalcContainer(aoId, sr.getId(),
638                                CourseOfferingServiceConstants.REF_OBJECT_URI_ACTIVITY_OFFERING,
639                                timeSlots,
640                                rooms,
641                                buildings,
642                                src.getIsTBA());
643                        srccList.add(srcc);
644                    }
645                }
646            }
647        }
648    }
649
650    private void processScheduleInfo(SearchResultInfo searchResults, Map<String, List<ActivityOfferingWrapper>> sch2aoMap, Map<String, List<ScheduleCalcContainer>> ao2sch) throws InvalidParameterException, MissingParameterException, PermissionDeniedException, OperationFailedException, DoesNotExistException {
651        //Process the search results
652
653        for (SearchResultRowInfo row : searchResults.getRows()) {
654            String schId = null;
655            String startTime = null;
656            String endTime = null;
657            Boolean tbaInd = null;
658            String roomCode = null;
659            String buildingName = null;
660            String weekdays = null;
661            for (SearchResultCellInfo cell : row.getCells()) {
662                if (CoreSearchServiceImpl.SearchResultColumns.SCH_ID.equals(cell.getKey())) {
663                    schId = cell.getValue();
664                } else if (CoreSearchServiceImpl.SearchResultColumns.START_TIME.equals(cell.getKey())) {
665                    startTime = cell.getValue();
666                } else if (CoreSearchServiceImpl.SearchResultColumns.END_TIME.equals(cell.getKey())) {
667                    endTime = cell.getValue();
668                } else if (CoreSearchServiceImpl.SearchResultColumns.TBA_IND.equals(cell.getKey())) {
669                    tbaInd = Boolean.parseBoolean(cell.getValue());
670                } else if (CoreSearchServiceImpl.SearchResultColumns.ROOM_CODE.equals(cell.getKey())) {
671                    roomCode = cell.getValue();
672                } else if (CoreSearchServiceImpl.SearchResultColumns.BLDG_NAME.equals(cell.getKey())) {
673                    buildingName = cell.getValue();
674                } else if (CoreSearchServiceImpl.SearchResultColumns.WEEKDAYS.equals(cell.getKey())) {
675                    weekdays = cell.getValue();
676                }
677            }
678            for(ActivityOfferingWrapper aoWrapper:sch2aoMap.get(schId)){
679
680                ScheduleCalcContainer scheduleCalcContainer = new ScheduleCalcContainer(aoWrapper.getId(), schId, SchedulingServiceConstants.SCHEDULE_TYPE_SCHEDULE, startTime, endTime, weekdays, roomCode, buildingName, tbaInd);
681                if (ao2sch.containsKey(aoWrapper.getId())) {
682                    ao2sch.get(aoWrapper.getId()).add(scheduleCalcContainer);
683                } else {
684                    List<ScheduleCalcContainer> schList = new ArrayList<ScheduleCalcContainer>();
685                    schList.add(scheduleCalcContainer);
686                    ao2sch.put(aoWrapper.getId(), schList);
687                }
688            }
689        }
690    }
691
692    private void processScheduleData(Map<String, ActivityOfferingWrapper> aoMap, Map<String, List<ScheduleCalcContainer>> ao2sch, Map<String, List<ScheduleRequestCalcContainer>> ao2schReq) throws Exception {
693        for (Map.Entry<String, ActivityOfferingWrapper> aoEntry : aoMap.entrySet()) {
694            ActivityOfferingWrapper aoWrapper = aoEntry.getValue();
695            String aoId = aoEntry.getKey();
696
697            if (ao2sch.containsKey(aoId)) {
698                List<ScheduleCalcContainer> schedList = ao2sch.get(aoId);
699                boolean newRow = false;
700                for (ScheduleCalcContainer sched : schedList) {
701//                    aoWrapper.setScheduleInfo(new ScheduleInfo());
702                    aoWrapper.setStartTimeDisplay(sched.getStart().isEmpty() ? sched.getStart() : DateFormatters.HOUR_MINUTE_AM_PM_TIME_FORMATTER.format(new Date(Long.parseLong(sched.getStart()))), newRow);
703                    aoWrapper.setEndTimeDisplay(sched.getEnd().isEmpty() ? sched.getEnd() : DateFormatters.HOUR_MINUTE_AM_PM_TIME_FORMATTER.format(new Date(Long.parseLong(sched.getEnd()))), newRow);
704                    aoWrapper.setBuildingName(sched.getBldgName(), newRow);
705                    aoWrapper.setRoomName(sched.getRoomCode(), newRow);
706                    aoWrapper.setDaysDisplayName(sched.getWeekdays(), newRow);
707                    aoWrapper.setTbaDisplayName(sched.getTbaInd(), newRow);
708                    newRow = true;
709                }
710
711            } else if (ao2schReq.containsKey((aoId))) {
712                List<ScheduleRequestCalcContainer> schedList = ao2schReq.get(aoId);
713                for (ScheduleRequestCalcContainer sched : schedList) {
714                    boolean newLine = aoWrapper.getTbaDisplayName() != null && !aoWrapper.getTbaDisplayName().isEmpty();
715                    for (RoomInfo room : sched.getRooms()) {
716                        aoWrapper.setRoomName(room.getRoomCode(), newLine, "uif-scheduled-dl");
717                    }
718                    for (BuildingInfo bldg : sched.getBldgs()) {
719                        aoWrapper.setBuildingName(bldg.getName(), newLine, "uif-scheduled-dl");
720                    }
721                    for (TimeSlotInfo timeSlotInfo : sched.getTimeSlots()) {
722                        if (timeSlotInfo.getStartTime() != null && timeSlotInfo.getStartTime().getMilliSeconds() != null) {
723                            aoWrapper.setStartTimeDisplay(DateFormatters.HOUR_MINUTE_AM_PM_TIME_FORMATTER.format(new Date(timeSlotInfo.getStartTime().getMilliSeconds())), newLine, "uif-scheduled-dl");
724                        }
725                        if (timeSlotInfo.getEndTime() != null && timeSlotInfo.getEndTime().getMilliSeconds() != null) {
726                            aoWrapper.setEndTimeDisplay(DateFormatters.HOUR_MINUTE_AM_PM_TIME_FORMATTER.format(new Date(timeSlotInfo.getEndTime().getMilliSeconds())), newLine, "uif-scheduled-dl");
727                        }
728                        if (timeSlotInfo.getWeekdays() != null && !timeSlotInfo.getWeekdays().isEmpty()) {
729                            aoWrapper.setDaysDisplayName(SchedulingServiceUtil.weekdaysList2WeekdaysString(timeSlotInfo.getWeekdays()), newLine, "uif-scheduled-dl");
730                        }
731                    }
732                    aoWrapper.setTbaDisplayName(sched.getTbaInd(), true);
733                }
734
735            }
736        }
737    }
738
739    private List<RegistrationGroupWrapper> processRgData(SearchResultInfo searchResults, Map<String, ActivityOfferingClusterWrapper> clusterMap, Map<String, ActivityOfferingWrapper> aoMap) throws InvalidParameterException, MissingParameterException, DoesNotExistException, PermissionDeniedException, OperationFailedException {
740        Map<String, RegistrationGroupWrapper> rgMap = new HashMap<String, RegistrationGroupWrapper>();
741        Map<String, List<ActivityOfferingWrapper>> storedAOs = new HashMap<String, List<ActivityOfferingWrapper>>();
742
743        for (SearchResultRowInfo row : searchResults.getRows()) {
744
745            String aoId = null;
746            String rgId = null;
747            String rgName = null;
748            String rgState = null;
749            for (SearchResultCellInfo cell : row.getCells()) {
750                if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AO_ID.equals(cell.getKey())) {
751                    aoId = cell.getValue();
752                } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.RG_ID.equals(cell.getKey())) {
753                    rgId = cell.getValue();
754                } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.RG_NAME.equals(cell.getKey())) {
755                    rgName = cell.getValue();
756                } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.RG_STATE.equals(cell.getKey())) {
757                    rgState = cell.getValue();
758                }
759            }
760            ActivityOfferingWrapper aoWrapper = aoMap.get(aoId);
761            RegistrationGroupWrapper rgWrapper = rgMap.get(rgId);
762
763            if (rgWrapper == null) {
764
765                storedAOs.put(rgId, new ArrayList<ActivityOfferingWrapper>());
766
767                ActivityOfferingClusterWrapper clusterWrapper = clusterMap.get(aoWrapper.getAoClusterID());
768
769                rgWrapper = new RegistrationGroupWrapper();
770                rgWrapper.setAoCluster(clusterWrapper.getAoCluster());
771                rgWrapper.setAoClusterName(clusterWrapper.getClusterNameForDisplay());
772                rgWrapper.setStateKey(rgState, getStateService().getState(rgState, ContextUtils.createDefaultContextInfo()).getName());
773                rgWrapper.getRgInfo().setName(rgName);
774                rgWrapper.getRgInfo().setId(rgId);
775
776                rgWrapper.setAoMaxEnrText("");
777                rgWrapper.setAoStateNameText("");
778                rgWrapper.setAoActivityCodeText("");
779                rgWrapper.setAoTypeNameText("");
780                rgWrapper.setStartTimeDisplay("");
781                rgWrapper.setEndTimeDisplay("");
782                rgWrapper.setDaysDisplayName("");
783                rgWrapper.setRoomName("");
784                rgWrapper.setBuildingName("");
785                rgWrapper.setAoInstructorText("");
786
787                rgMap.put(rgId, rgWrapper);
788                clusterWrapper.getRgWrapperList().add(rgWrapper);
789            }
790            storedAOs.get(rgId).add(aoWrapper);
791        }
792
793        List<String> keyList = new ArrayList<String>(storedAOs.keySet());
794        for(int i=0;i<keyList.size();i++){
795
796            RegistrationGroupWrapper rgWrapper = rgMap.get(keyList.get(i));
797            List<ActivityOfferingWrapper> aoList = storedAOs.get(keyList.get(i));
798
799            //Sort aoList
800            Collections.sort(aoList,new Comparator<ActivityOfferingWrapper>() {
801                @Override
802                public int compare(ActivityOfferingWrapper ao1, ActivityOfferingWrapper ao2) {
803                    if(ao1.getTypeName().compareTo("Lecture")==0){
804                        return -1;
805                    }
806                    if(ao2.getTypeName().compareTo("Lecture")==0){
807                        return 1;
808                    }
809                    return ao1.getTypeName().compareTo(ao2.getTypeName());
810                }
811            });
812
813            for(int j=0;j<aoList.size();j++){
814                boolean newLine = true;
815                if(j==0){
816                    newLine=false;
817                }
818                ActivityOfferingWrapper aoWrapper = aoList.get(j);
819
820                rgWrapper.getRgInfo().getActivityOfferingIds().add(aoWrapper.getAoInfo().getId());
821
822                //if there are more than one instructors re-arrange the rows
823                StringBuilder lineBreaksInstructorsSB = new StringBuilder();
824                if (aoWrapper.getInstructorDisplayNames() != null && StringUtils.contains(aoWrapper.getInstructorDisplayNames(),("<br>"))) {   //more than one instructor
825                    String s = aoWrapper.getInstructorDisplayNames();
826                    for( int k=0; k<s.length(); k++ ) {    //add lines according to number of instructors
827                        if( s.contains("<br>")) {
828                            lineBreaksInstructorsSB.append("<br/>");
829                            s = s.substring(s.indexOf("<br>")+4);
830                        } else {
831                            break;
832                        }
833                    }
834                }
835                String lineBreaksInstructors = lineBreaksInstructorsSB.toString();
836
837                //if there are more than one delivery logistics re-arrange the rows
838                StringBuilder lineBreaksDeliveriesSB = new StringBuilder();
839                if (aoWrapper.getDaysDisplayName() != null && StringUtils.contains(aoWrapper.getDaysDisplayName(),"<br>")) {   //more than one delivery logistics
840                    String s = aoWrapper.getDaysDisplayName();
841                    for( int k=0; k<s.length(); k++ ) {    //add lines according to number of delivery logistics
842                        if( s.contains("<br>")) {
843                            lineBreaksDeliveriesSB.append("<br/>");
844                            s = s.substring(s.indexOf("<br>")+4);
845                        } else {
846                            break;
847                        }
848                    }
849                }
850                String lineBreaksDeliveries = lineBreaksDeliveriesSB.toString();
851
852                String lineBreaks;
853                //Set different line breaks according to number of instructors and number of delivery logistics (can it be simpler?)
854                if (lineBreaksInstructors.length() < lineBreaksDeliveries.length()) {
855                    lineBreaks = lineBreaksDeliveries;
856                    lineBreaksDeliveries = lineBreaksDeliveries.substring(0, lineBreaksDeliveries.length() - lineBreaksInstructors.length());
857                    lineBreaksInstructors = "";
858                } else {
859                    lineBreaks = lineBreaksInstructors;
860                    lineBreaksInstructors = lineBreaksInstructors.substring(0, lineBreaksInstructors.length() - lineBreaksDeliveries.length());
861                    lineBreaksDeliveries = "";
862                }
863
864                //Set the wrapper
865                rgWrapper.setAoMaxEnrText(rgWrapper.getAoMaxEnrText() + (newLine ? "<br/>" : "") + (aoWrapper.getAoInfo().getMaximumEnrollment() == null ? "" : aoWrapper.getAoInfo().getMaximumEnrollment()) + lineBreaks);
866                rgWrapper.setAoStateNameText(rgWrapper.getAoStateNameText() + (newLine ? "<br/>" : "") + aoWrapper.getStateName() + lineBreaks);
867                //sub-term icon and tooltip setup
868                if(!aoWrapper.getSubTermName().equals("None")){  //sub-term? > icon + name and dates
869                    StringBuilder sb = new StringBuilder();
870                    sb.append(rgWrapper.getAoActivityCodeText());
871                    sb.append(newLine ? "<br/>" : "");
872                    sb.append(aoWrapper.getAoInfo().getActivityCode());
873                    sb.append("&nbsp;&nbsp;&nbsp;<img src=\"../ks-enroll/images/subterm_icon.png\" title=\"This activity is in ");
874                    sb.append(aoWrapper.getSubTermName());
875                    sb.append(" -\n");
876                    sb.append(aoWrapper.getTermStartEndDate());
877                    sb.append("\">");
878                    sb.append(lineBreaks);
879                    rgWrapper.setAoActivityCodeText(sb.toString());
880//                    rgWrapper.setAoActivityCodeText(rgWrapper.getAoActivityCodeText() + (newLine ? "<br/>" : "") + aoWrapper.getAoInfo().getActivityCode()
881//                            + "&nbsp;&nbsp;&nbsp;<img src=\"../ks-enroll/images/subterm_icon.png\" title=\"This activity is in "+aoWrapper.getSubTermName()+" -\n"+aoWrapper.getTermStartEndDate()+"\">" + lineBreaks);
882                } else {
883                    rgWrapper.setAoActivityCodeText(rgWrapper.getAoActivityCodeText() + (newLine ? "<br/>" : "") + aoWrapper.getAoInfo().getActivityCode() + lineBreaks);
884                }
885                rgWrapper.setAoTypeNameText(rgWrapper.getAoTypeNameText() + (newLine ? "<br/>" : "") + aoWrapper.getTypeName() + lineBreaks);
886                rgWrapper.setStartTimeDisplay(rgWrapper.getStartTimeDisplay() + (newLine ? "<br/>" : "") + aoWrapper.getStartTimeDisplay() + lineBreaksInstructors);
887                rgWrapper.setEndTimeDisplay(rgWrapper.getEndTimeDisplay() + (newLine ? "<br/>" : "") + aoWrapper.getEndTimeDisplay() + lineBreaksInstructors);
888                rgWrapper.setDaysDisplayName(rgWrapper.getDaysDisplayName() + (newLine ? "<br/>" : "") + aoWrapper.getDaysDisplayName() + lineBreaksInstructors);
889                rgWrapper.setRoomName(rgWrapper.getRoomName() + (newLine ? "<br/>" : "") + aoWrapper.getRoomName() + lineBreaksInstructors);
890                rgWrapper.setBuildingName(rgWrapper.getBuildingName() + (newLine ? "<br/>" : "") + aoWrapper.getBuildingName() + lineBreaksInstructors);
891                rgWrapper.setAoInstructorText(rgWrapper.getAoInstructorText() + (newLine ? "<br/>" : "") + (aoWrapper.getInstructorDisplayNames() == null ? "" : aoWrapper.getInstructorDisplayNames()) + lineBreaksDeliveries);
892
893            }
894        }
895
896
897
898        return new ArrayList<RegistrationGroupWrapper>(rgMap.values());
899    }
900
901    /**
902     * After the performance improvements, the clusterMap does not contain set information. So, lets add it here.
903     * This method will use the dto to grab a full AOC.
904     *
905     * @param coId Course Offering Id
906     * @param clusterMap map of AoIds to AO cluster wrappers
907     * @throws InvalidParameterException
908     * @throws MissingParameterException
909     * @throws DoesNotExistException
910     * @throws PermissionDeniedException
911     * @throws OperationFailedException
912     */
913    protected void processAosData(String coId, Map<String, ActivityOfferingClusterWrapper> clusterMap)
914            throws InvalidParameterException, MissingParameterException, DoesNotExistException, PermissionDeniedException, OperationFailedException {
915
916        List<ActivityOfferingClusterInfo> aoClusters = ARGUtil.getArgServiceAdapter().getActivityOfferingClusterByCourseOffering(coId);
917
918        //A fix for KSENROLL-8097: clusterMap was populated in processAoClusterData method.
919        //However if one cluster does not have an AO, that cluster sometimes won't be added to clusterMap, which causes NPE error later.
920        //The walkaround solution here is to add the missing cluster to clusterMap
921        for (ActivityOfferingClusterInfo aoc : aoClusters) {
922            if (clusterMap.get(aoc.getId()) != null){
923                clusterMap.get(aoc.getId()).setAoCluster(aoc);
924            }
925            else{
926                    ActivityOfferingClusterWrapper aoClusterWrapper = new ActivityOfferingClusterWrapper();
927                    aoClusterWrapper.setAoCluster(aoc);
928                    aoClusterWrapper.setActivityOfferingClusterId(aoc.getId());
929                    aoClusterWrapper.setClusterNameForDisplay(aoc.getName());
930                    String formatName = getCourseOfferingService().getFormatOffering(aoc.getFormatOfferingId(),ContextUtils.createDefaultContextInfo()).getName();
931                    aoClusterWrapper.setFormatNameForDisplay(formatName);
932                    aoClusterWrapper.setFormatOfferingId(aoc.getFormatOfferingId());
933                    clusterMap.put(aoc.getId(), aoClusterWrapper);
934            }
935        }
936
937    }
938
939    private List<ActivityOfferingWrapper> processAoClusterData(SearchResultInfo searchResults,
940                                                               Map<String, List<ActivityOfferingWrapper>> sch2aoMap,
941                                                               Map<String, ActivityOfferingClusterWrapper> clusterMap,
942                                                               Map<String, ActivityOfferingWrapper> aoMap,
943                                                               Map<String, FormatOfferingInfo> foIds,
944                                                               List<String> aoIdsWithoutSch,
945                                                               ContextInfo contextInfo)
946            throws InvalidParameterException, MissingParameterException, DoesNotExistException, PermissionDeniedException, OperationFailedException {
947
948        List<ActivityOfferingWrapper> activityOfferingWrappers = new ArrayList<ActivityOfferingWrapper>();
949        ActivityOfferingWrapper aoWrapperStored = new ActivityOfferingWrapper();  //storage for sub-term only to compare
950
951        for (SearchResultRowInfo row : searchResults.getRows()) {
952
953            ActivityOfferingWrapper aoWrapper = new ActivityOfferingWrapper();
954            String aocPrivateName = null;
955            String foId = null;
956            String formatId = null;
957            String foName = null;
958            List<String>  scheduleIds = new ArrayList<String>();
959
960            for (SearchResultCellInfo cell : row.getCells()) {
961
962                if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AO_ID.equals(cell.getKey())) {
963                    aoWrapper.getAoInfo().setId(cell.getValue());
964                } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AO_CODE.equals(cell.getKey())) {
965                    aoWrapper.setActivityCode(cell.getValue());
966                    aoWrapper.getAoInfo().setActivityCode(cell.getValue());
967                } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AO_MAX_SEATS.equals(cell.getKey())) {
968                    aoWrapper.getAoInfo().setMaximumEnrollment(cell.getValue() == null ? null : Integer.parseInt(cell.getValue()));
969                } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AO_STATE.equals(cell.getKey())) {
970                    aoWrapper.getAoInfo().setStateKey(cell.getValue());
971                    if (cell.getValue() != null) {
972                        StateInfo stateInfo = getStateService().getState(cell.getValue(), contextInfo);
973                        aoWrapper.setStateName(stateInfo.getName());
974                    }
975                } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AO_TYPE.equals(cell.getKey())) {
976                    aoWrapper.getAoInfo().setTypeKey(cell.getValue());
977                    if (cell.getValue() != null) {
978                        TypeInfo typeInfo = getTypeService().getType(cell.getValue(), contextInfo);
979                        aoWrapper.setTypeKey(cell.getValue());
980                        aoWrapper.setTypeName(typeInfo.getName());
981                    }
982                } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.SCHEDULE_ID.equals(cell.getKey())) {
983
984                    if(cell.getValue()!=null){
985                        scheduleIds.add(cell.getValue());
986
987                    }
988
989                } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.FO_ID.equals(cell.getKey())) {
990                    aoWrapper.getAoInfo().setFormatOfferingId(cell.getValue());
991                    foId = cell.getValue();
992                } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.FO_NAME.equals(cell.getKey())) {
993                    aoWrapper.setFormatOfferingName(cell.getValue());
994                    aoWrapper.getAoInfo().setFormatOfferingName(cell.getValue());
995                    foName = cell.getValue();
996                } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.FORMAT_ID.equals(cell.getKey())) {
997                    formatId = cell.getValue();
998                } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AOC_ID.equals(cell.getKey())) {
999                    aoWrapper.setAoClusterID(cell.getValue());
1000                } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AOC_NAME.equals(cell.getKey())) {
1001                    aoWrapper.setAoClusterName(cell.getValue());
1002                } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.AOC_PRIVATE_NAME.equals(cell.getKey())) {
1003                    aocPrivateName = cell.getValue();
1004                } else if (ActivityOfferingSearchServiceImpl.SearchResultColumns.ATP_ID.equals(cell.getKey())) {
1005                    aoWrapper.getAoInfo().setTermId(cell.getValue());
1006                }
1007            }
1008
1009            aoWrapper.getAoInfo().setScheduleIds(scheduleIds);
1010            for(String scheduleId : scheduleIds){
1011                List<ActivityOfferingWrapper> list = sch2aoMap.get(scheduleId);
1012                if(list == null){
1013                    list = new ArrayList<ActivityOfferingWrapper>();
1014                    sch2aoMap.put(scheduleId, list);
1015                }
1016                list.add(aoWrapper);//Add to schedule map
1017            }
1018            if (aoWrapper.getAoClusterID() != null) {
1019                ActivityOfferingClusterWrapper aoClusterWrapper = clusterMap.get(aoWrapper.getAoClusterID());
1020                if (aoClusterWrapper == null) {
1021                    aoClusterWrapper = new ActivityOfferingClusterWrapper();
1022                    ActivityOfferingClusterInfo activityOfferingClusterInfo = new ActivityOfferingClusterInfo();
1023                    activityOfferingClusterInfo.setFormatOfferingId(aoWrapper.getAoInfo().getFormatOfferingId());
1024                    activityOfferingClusterInfo.setId(aoWrapper.getAoClusterID());
1025                    activityOfferingClusterInfo.setName(aoWrapper.getAoClusterName());
1026                    activityOfferingClusterInfo.setPrivateName(aocPrivateName);
1027                    activityOfferingClusterInfo.setTypeKey(CourseOfferingServiceConstants.AOC_ROOT_TYPE_KEY);
1028                    activityOfferingClusterInfo.setStateKey(CourseOfferingServiceConstants.AOC_ACTIVE_STATE_KEY);
1029                    aoClusterWrapper.setAoCluster(activityOfferingClusterInfo);
1030                    aoClusterWrapper.setActivityOfferingClusterId(aoWrapper.getAoClusterID());
1031                    aoClusterWrapper.setClusterNameForDisplay(aoWrapper.getAoClusterName());
1032                    aoClusterWrapper.setFormatNameForDisplay(aoWrapper.getAoInfo().getFormatOfferingName());
1033                    aoClusterWrapper.setFormatOfferingId(aoWrapper.getAoInfo().getFormatOfferingId());
1034                    clusterMap.put(aoWrapper.getAoClusterID(), aoClusterWrapper);
1035                }
1036                if (aoWrapper.getId() != null) {
1037                    aoClusterWrapper.getAoWrapperList().add(aoWrapper);
1038
1039                    aoMap.put(aoWrapper.getAoInfo().getId(), aoWrapper);
1040
1041                    //Check if there is a schedule id, if not add it to the list to get RDLs
1042                    if (aoWrapper.getAoInfo().getScheduleIds() == null || aoWrapper.getAoInfo().getScheduleIds().isEmpty()) {
1043                        aoIdsWithoutSch.add(aoWrapper.getAoInfo().getId());
1044                    }
1045
1046                    //Check for sub-term or term and populate accordingly
1047                    //If no change of AO.getTermId() > avoid service calls and populate sub-term info as it has been stored in aoWrapperStored
1048                    if(aoWrapper.getAoInfo().getTermId().equals(aoWrapperStored.getAoInfo().getTermId())){
1049                        aoWrapper.setHasSubTerms(aoWrapperStored.getHasSubTerms());
1050                        aoWrapper.setSubTermId(aoWrapperStored.getSubTermId());
1051                        aoWrapper.setSubTermName(aoWrapperStored.getSubTermName());
1052                        aoWrapper.setTerm(aoWrapperStored.getTerm());
1053                        aoWrapper.setTermName(aoWrapperStored.getTermName());
1054                        aoWrapper.setTermDisplayString(aoWrapperStored.getTermDisplayString());
1055                        aoWrapper.setTermStartEndDate(aoWrapperStored.getTermStartEndDate());
1056                    } else {
1057                        TermInfo term;
1058                        TermInfo subTerm;
1059                        aoWrapper.setHasSubTerms(false);
1060                        aoWrapper.setSubTermName("None");
1061                        aoWrapper.setSubTermId("");
1062                        List<TermInfo> terms = getAcalService().getContainingTerms(aoWrapper.getAoInfo().getTermId(), contextInfo);
1063                        if (terms == null || terms.isEmpty()) {
1064                            term = getAcalService().getTerm(aoWrapper.getAoInfo().getTermId(), contextInfo);
1065                            // checking if we can have sub-terms for giving term
1066                            List<TermInfo> subTerms = getAcalService().getIncludedTermsInTerm(aoWrapper.getAoInfo().getTermId(), contextInfo);
1067                            if(!subTerms.isEmpty()) {
1068                                aoWrapper.setHasSubTerms(true);
1069                            }
1070                        } else {
1071                            subTerm = getAcalService().getTerm(aoWrapper.getAoInfo().getTermId(), contextInfo);
1072                            term = terms.get(0);
1073                            aoWrapper.setHasSubTerms(true);
1074                            aoWrapper.setSubTermId(subTerm.getId());
1075                            TypeInfo subTermType = getTypeService().getType(subTerm.getTypeKey(), contextInfo);
1076                            aoWrapper.setSubTermName(subTermType.getName());
1077                            aoWrapper.setTermStartEndDate(ARGUtil.getTermStartEndDate(subTerm.getId(), subTerm));
1078                        }
1079                        aoWrapper.setTerm(term);
1080                        if (term != null) {
1081                            aoWrapper.setTermName(term.getName());
1082                        }
1083                        aoWrapper.setTermDisplayString(ARGUtil.getTermDisplayString(aoWrapper.getAoInfo().getTermId(), term));
1084                        aoWrapperStored = aoWrapper; //update for next comparison
1085                    }
1086                    // end sub-terms
1087                    activityOfferingWrappers.add(aoWrapper);
1088                }
1089            }
1090
1091            FormatOfferingInfo fo = foIds.get(foId);
1092            if (fo == null) {
1093                fo = new FormatOfferingInfo();
1094                fo.setId(foId);
1095                fo.setName(foName);
1096                fo.setFormatId(formatId);
1097                foIds.put(foId, fo);
1098            }
1099
1100        }
1101
1102        return activityOfferingWrappers;
1103    }
1104
1105    protected EntityDefaultQueryResults getInstructorsInfoFromKim(List<String> principalIds) {
1106        QueryByCriteria.Builder qbcBuilder = QueryByCriteria.Builder.create();
1107        qbcBuilder.setPredicates(
1108                PredicateFactory.in("principals.principalId", principalIds.toArray())
1109        );
1110
1111        QueryByCriteria criteria = qbcBuilder.build();
1112
1113        EntityDefaultQueryResults entityResults = getIdentityService().findEntityDefaults(criteria);
1114
1115        return entityResults;
1116    }
1117
1118    /**
1119     * This method will indicate to the user if the cluster canot be generated because the AO Set does not contain
1120     * enough activities that meet the requirements of the FormatOffering
1121     *
1122     * @param aoTypeKeys list of AO Type Keys
1123     * @param aoList List Of Activity Offerings
1124     * @param aoClusterWrapper AO Cluster
1125     * @param clusterIndex     Used to tack the warning message onto a particular part of the screen
1126     * @param contextInfo context of the call
1127     * @throws Exception
1128     */
1129    protected void _performAOCompletePerClusterValidation(List<String> aoTypeKeys, List<ActivityOfferingInfo> aoList,
1130                                                          ActivityOfferingClusterWrapper aoClusterWrapper, int clusterIndex, ContextInfo contextInfo) throws Exception {
1131        Map<String, Boolean> completeAoSet = new HashMap<String, Boolean>(); // using a map to store what's required
1132
1133        for (String aoType : aoTypeKeys) {
1134            completeAoSet.put(aoType, false);
1135        }
1136
1137        for (ActivityOfferingInfo aoInfo : aoList) {
1138            completeAoSet.put(aoInfo.getTypeKey(), true); // This is used to determine if the AO Set has all FO types
1139        }
1140
1141        StringBuilder aoCompleteWarningMessageSB = new StringBuilder();
1142        String delim = "";
1143        for (Map.Entry<String,Boolean> completeAoEntry : completeAoSet.entrySet()) {
1144            if (!completeAoEntry.getValue()) {
1145                // type service should be cached so this shouldn't be slow
1146                aoCompleteWarningMessageSB.append(delim + getTypeService().getType(completeAoEntry.getKey(), contextInfo).getName());
1147                delim = ", ";
1148            }
1149        }
1150        if (!(aoCompleteWarningMessageSB.length()==0)) {
1151            aoClusterWrapper.setRgStatus(RegistrationGroupConstants.RGSTATUS_NO_RG_GENERATED);
1152            aoClusterWrapper.setRgMessageStyle(ActivityOfferingClusterWrapper.RG_MESSAGE_ALL);
1153            aoClusterWrapper.setHasAllRegGroups(true);
1154            String clusterName = aoClusterWrapper.getClusterNameForDisplay();
1155            String formatName = aoClusterWrapper.getFormatNameForDisplay();
1156            GlobalVariables.getMessageMap().putWarningForSectionId("activityOfferingsPerCluster_line" + clusterIndex, CourseOfferingConstants.REGISTRATIONGROUP_INCOMPLETE_AOSET, clusterName, formatName, aoCompleteWarningMessageSB.toString());
1157        }
1158
1159    }
1160
1161    /*
1162    * Perform several validations:
1163    * 1. check if All RGs have been generated given AOs in a cluster
1164    *    if not, set rgStatus="Only Some Registration Groups Generated" and
1165    *            set hasAllRegGroups=false, therefore "Generate Registration Group" action link will show up
1166    *    if yes, set rgStatus="All Registration Groups Generated" and
1167    *            set hasAllRegGroups=true, therefore "View Registration Group" action link will show up
1168    * 2. when #1 validation result is yes, need to perform max enrollment validation.
1169    * 3. when #1 validation result is yes, need to perform time conflict validation
1170    *
1171    */
1172    private void _validateRegistrationGroupsPerCluster(List<RegistrationGroupInfo> rgInfos, List<ActivityOfferingInfo> aoList,
1173                                                       ActivityOfferingClusterWrapper aoClusterWrapper,
1174                                                       ARGCourseOfferingManagementForm theForm, int clusterIndex, Map<String, List<ScheduleCalcContainer>> ao2sch, Map<String, List<ScheduleRequestCalcContainer>> ao2schReq, Map<String, ActivityOfferingWrapper> aoMap) throws Exception {
1175
1176        Map<String, List<String>> activityOfferingTypeToAvailableActivityOfferingMap =
1177                _constructActivityOfferingTypeToAvailableActivityOfferingMap(aoList);
1178
1179        List<List<String>> generatedPermutations = new ArrayList<List<String>>();
1180        List<List<String>> foundList = new ArrayList<List<String>>();
1181
1182        PermutationUtils.generatePermutations(new ArrayList<String>(
1183                activityOfferingTypeToAvailableActivityOfferingMap.keySet()),
1184                new ArrayList<String>(),
1185                activityOfferingTypeToAvailableActivityOfferingMap,
1186                generatedPermutations);
1187
1188        List<RegistrationGroupInfo> rgInfosCopy = new ArrayList<RegistrationGroupInfo>(rgInfos.size());
1189        for (RegistrationGroupInfo rgInfo : rgInfos) {
1190            rgInfosCopy.add(rgInfo);
1191        }
1192
1193        for (List<String> activityOfferingPermutation : generatedPermutations) {
1194            for (RegistrationGroupInfo rgInfo : rgInfosCopy) {
1195                if (_hasGeneratedRegGroup(activityOfferingPermutation, rgInfo)) {
1196                    rgInfosCopy.remove(rgInfo);
1197                    foundList.add(activityOfferingPermutation);
1198                    break;
1199                }
1200            }
1201        }
1202        if (generatedPermutations.size() != foundList.size()) {
1203            aoClusterWrapper.setRgStatus(RegistrationGroupConstants.RGSTATUS_SOME_RG_GENERATED);
1204            aoClusterWrapper.setRgMessageStyle(ActivityOfferingClusterWrapper.RG_MESSAGE_PARTIAL);
1205            aoClusterWrapper.setHasAllRegGroups(false);
1206        } else {
1207            aoClusterWrapper.setRgStatus(RegistrationGroupConstants.RGSTATUS_ALL_RG_GENERATED);
1208            aoClusterWrapper.setRgMessageStyle(ActivityOfferingClusterWrapper.RG_MESSAGE_ALL);
1209            aoClusterWrapper.setHasAllRegGroups(true);
1210            // perform max enrollment validation
1211            _performMaxEnrollmentValidation(aoMap, aoClusterWrapper.getAoCluster(), clusterIndex);
1212            //validate AO time conflict in RG
1213            _performRGTimeConflictValidation(aoClusterWrapper.getAoCluster(), rgInfos, clusterIndex, ao2sch, ao2schReq);
1214        }
1215
1216        _performAOCompletePerClusterValidation(theForm.getFoId2aoTypeMap().get(aoClusterWrapper.getFormatOfferingId()).getActivityOfferingTypeKeys(), aoList, aoClusterWrapper, clusterIndex, ContextUtils.createDefaultContextInfo());
1217
1218        if (!rgInfosCopy.isEmpty()) {
1219            GlobalVariables.getMessageMap().putWarningForSectionId("registrationGroupsPerFormatSection", CourseOfferingConstants.REGISTRATIONGROUP_INVALID_REGGROUPS);
1220        }
1221    }
1222
1223    private Map<String, List<String>> _constructActivityOfferingTypeToAvailableActivityOfferingMap(List<ActivityOfferingInfo> aoList) {
1224        Map<String, List<String>> activityOfferingTypeToAvailableActivityOfferingMap = new HashMap<String, List<String>>();
1225
1226        for (ActivityOfferingInfo info : aoList) {
1227            String activityType = info.getTypeKey();
1228            List<String> activityList = activityOfferingTypeToAvailableActivityOfferingMap
1229                    .get(activityType);
1230
1231            if (activityList == null) {
1232                activityList = new ArrayList<String>();
1233                activityOfferingTypeToAvailableActivityOfferingMap.put(
1234                        activityType, activityList);
1235            }
1236
1237            activityList.add(info.getId());
1238
1239        }
1240        return activityOfferingTypeToAvailableActivityOfferingMap;
1241    }
1242
1243    private boolean _hasGeneratedRegGroup(List<String> activityOfferingPermutation, RegistrationGroupInfo rgInfo) {
1244        boolean isMatched = true;
1245        List<String> aoIds = rgInfo.getActivityOfferingIds();
1246        List<String> aoIdsCopy = new ArrayList<String>(aoIds.size());
1247        for (String aoId : aoIds) {
1248            aoIdsCopy.add(aoId);
1249        }
1250        List<String> foundList = new ArrayList<String>();
1251        for (String activityOfferingPermutationItem : activityOfferingPermutation) {
1252            for (String aoId : aoIdsCopy) {
1253                if (activityOfferingPermutationItem.equals(aoId)) {
1254                    aoIdsCopy.remove(aoId);
1255                    foundList.add(activityOfferingPermutationItem);
1256                    break;
1257                }
1258            }
1259        }
1260        if (activityOfferingPermutation.size() != foundList.size() || !aoIdsCopy.isEmpty()) {
1261            isMatched = false;
1262        }
1263        return isMatched;
1264    }
1265
1266
1267    private void _performMaxEnrollmentValidation(Map<String, ActivityOfferingWrapper> aoMap, ActivityOfferingClusterInfo aoCluster, int clusterIndex) throws Exception {
1268
1269        int aoSetMaxEnrollNumber = 0;
1270        int currentAoSetMaxEnrollNumber = 0;
1271        int listIndex = 0;
1272        ValidationResultInfo validationResultInfo = new ValidationResultInfo();
1273
1274        //The max enrollment numbers of all the aoSets in the given AOC are the same. The validation passes.
1275        validationResultInfo.setLevel(ValidationResult.ErrorLevel.OK);
1276        validationResultInfo.setMessage("Sum of enrollment for each AO type is equal");
1277
1278        for (ActivityOfferingSetInfo aos : aoCluster.getActivityOfferingSets()) {
1279            for (String aoId : aos.getActivityOfferingIds()) {
1280                ActivityOfferingWrapper aoWrapper = aoMap.get(aoId);
1281                ActivityOfferingInfo aoInfo = aoWrapper.getAoInfo();
1282                if (aoInfo != null && aoInfo.getMaximumEnrollment() != null) {
1283                    aoSetMaxEnrollNumber += aoInfo.getMaximumEnrollment();
1284                }
1285            }
1286
1287            if (listIndex == 0) {
1288                currentAoSetMaxEnrollNumber = aoSetMaxEnrollNumber;
1289            } else {
1290                if (aoSetMaxEnrollNumber != currentAoSetMaxEnrollNumber) {
1291
1292                    validationResultInfo.setLevel(ValidationResult.ErrorLevel.WARN);
1293                    validationResultInfo.setMessage("Sum of enrollment for each AO type is not equal");
1294                    break;
1295                }
1296            }
1297            aoSetMaxEnrollNumber = 0;
1298            listIndex++;
1299        }
1300
1301        if (validationResultInfo.isWarn()) {
1302            GlobalVariables.getMessageMap().putWarningForSectionId("activityOfferingsPerCluster_line" + clusterIndex, RegistrationGroupConstants.MSG_WARNING_MAX_ENROLLMENT, aoCluster.getPrivateName());
1303        }
1304    }
1305
1306    private List<Integer> _performRGTimeConflictValidation(ActivityOfferingClusterInfo aoCluster, List<RegistrationGroupInfo> registrationGroupInfos, int clusterIndex, Map<String, List<ScheduleCalcContainer>> ao2sch, Map<String, List<ScheduleRequestCalcContainer>> ao2schReq) throws Exception {
1307        List<Integer> rgIndexList = new ArrayList<Integer>();
1308        rgIndexList.clear();
1309
1310        if (aoCluster != null && registrationGroupInfos != null && !registrationGroupInfos.isEmpty()) {
1311            int rgIndex = 0;
1312            for (RegistrationGroupInfo registrationGroupInfo : registrationGroupInfos) {
1313
1314                List<ValidationResultInfo> validationResultInfoList = new ArrayList<ValidationResultInfo>();
1315                // Lets build a list of all schedules that need to be compared for this registration group.
1316                List<TimeSlotInfo> shed2Check = new ArrayList<TimeSlotInfo>();
1317                for (String aoId : registrationGroupInfo.getActivityOfferingIds()) {
1318                    if (ao2sch.get(aoId) != null) {
1319                        for (ScheduleCalcContainer sched : ao2sch.get(aoId)) {
1320                            shed2Check.add(toTimeSlotInfo(sched));
1321                        }
1322                    }
1323                    if (ao2schReq.get(aoId) != null) {
1324                        for (ScheduleRequestCalcContainer sched : ao2schReq.get(aoId)) {
1325                            shed2Check.addAll(sched.getTimeSlots());
1326                        }
1327                    }
1328                }
1329
1330                for (TimeSlotInfo outerEntry : shed2Check) {
1331                    for (TimeSlotInfo innerEntry : shed2Check) {
1332                        if (outerEntry.equals(innerEntry)) {
1333                            break;
1334                        }
1335                        if (SchedulingServiceUtil.areTimeSlotsInConflict(outerEntry, innerEntry)) {
1336                            ValidationResultInfo validationResultInfo = new ValidationResultInfo();
1337                            validationResultInfo.setLevel(ValidationResult.ErrorLevel.WARN);
1338                            validationResultInfo.setMessage("time conflict between AO: " + outerEntry.getId() + " and AO: " + innerEntry.getId());
1339                            validationResultInfoList.add(validationResultInfo);
1340                        }
1341                    }
1342                }
1343
1344                if (!validationResultInfoList.isEmpty() && validationResultInfoList.get(0).isWarn()) {
1345                    //Why are we alter the RG state? Commenting out for now.
1346                    //getCourseOfferingService().changeRegistrationGroupState(registrationGroupInfo.getId(), LuiServiceConstants.REGISTRATION_GROUP_INVALID_STATE_KEY, ContextUtils.createDefaultContextInfo());
1347                    rgIndexList.add(rgIndex);
1348                }
1349
1350                rgIndex++;
1351            }
1352
1353            if (!rgIndexList.isEmpty()) {
1354                GlobalVariables.getMessageMap().putWarningForSectionId("activityOfferingsPerCluster_line" + clusterIndex, RegistrationGroupConstants.MSG_WARNING_AO_TIMECONFLICT, aoCluster.getPrivateName());
1355            }
1356        }
1357
1358        return rgIndexList;
1359    }
1360
1361    public TimeSlotInfo toTimeSlotInfo(ScheduleCalcContainer sched) {
1362        TimeSlotInfo info = new TimeSlotInfo();
1363
1364
1365        info.setId(null);
1366        info.setTypeKey(null);
1367        info.setStateKey(null);
1368        info.setName(null);
1369        info.setWeekdays(SchedulingServiceUtil.weekdaysString2WeekdaysList(sched.getWeekdays()));
1370
1371        info.setStartTime(new TimeOfDayInfo());
1372        info.getStartTime().setMilliSeconds(((sched.getStart() != null && !"".equals(sched.getStart())) ? new Long(sched.getStart()) : null));
1373
1374        info.setEndTime(new TimeOfDayInfo());
1375        info.getEndTime().setMilliSeconds(((sched.getEnd() != null && !"".equals(sched.getEnd())) ? new Long(sched.getEnd()) : null));
1376
1377        info.setDescr(new RichTextInfo());
1378        info.getDescr().setFormatted(null);
1379        info.getDescr().setPlain(null);
1380
1381        info.setMeta(null);
1382
1383        return info;
1384    }
1385
1386    /**
1387     * This method fetches all the course offerings for a term and course/subject code.
1388     *
1389     * @param searchRequest search request
1390     * @param form input form
1391     * @throws Exception
1392     * @see CourseOfferingManagementSearchImpl Actual CO search happens here
1393     */
1394    protected void loadCourseOfferings(SearchRequestInfo searchRequest, ARGCourseOfferingManagementForm form) throws Exception {
1395
1396        ContextInfo contextInfo = createContextInfo();
1397
1398        SearchResultInfo searchResult = getSearchService().search(searchRequest, contextInfo);
1399
1400        form.getCourseOfferingResultList().clear();
1401
1402        for (SearchResultRowInfo row : searchResult.getRows()) {
1403            CourseOfferingListSectionWrapper coListWrapper = new CourseOfferingListSectionWrapper();
1404
1405            for (SearchResultCellInfo cellInfo : row.getCells()) {
1406
1407                String value = StringUtils.EMPTY;
1408                if (cellInfo.getValue() != null) {
1409                    value = cellInfo.getValue();
1410                }
1411
1412                if (CourseOfferingManagementSearchImpl.SearchResultColumns.CODE.equals(cellInfo.getKey())) {
1413                    coListWrapper.setCourseOfferingCode(value);
1414                } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.DESC.equals(cellInfo.getKey())) {
1415                    coListWrapper.setCourseOfferingDesc(value);
1416                } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.STATE.equals(cellInfo.getKey())) {
1417                    coListWrapper.setCourseOfferingStateKey(value);
1418                    coListWrapper.setCourseOfferingStateDisplay(getStateInfo(value).getName());
1419                } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.CREDIT_OPTION.equals(cellInfo.getKey())) {
1420                    coListWrapper.setCourseOfferingCreditOptionKey(value);
1421                } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.GRADING_OPTION.equals(cellInfo.getKey())) {
1422                    coListWrapper.setCourseOfferingGradingOptionKey(value);
1423                } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.GRADING_OPTION_NAME.equals(cellInfo.getKey())) {
1424                    coListWrapper.setCourseOfferingGradingOptionDisplay(cellInfo.getValue());
1425                } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.CREDIT_OPTION_NAME.equals(cellInfo.getKey())) {
1426                    coListWrapper.setCourseOfferingCreditOptionDisplay(cellInfo.getValue());
1427                } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.DEPLOYMENT_ORG_ID.equals(cellInfo.getKey())) {
1428                    coListWrapper.setAdminOrg(cellInfo.getValue());
1429                } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.CO_ID.equals(cellInfo.getKey())) {
1430                    coListWrapper.setCourseOfferingId(value);
1431                } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.SUBJECT_AREA.equals(cellInfo.getKey())) {
1432                    coListWrapper.setSubjectArea(value);
1433                } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.IS_CROSS_LISTED.equals(cellInfo.getKey())) {
1434                    coListWrapper.setCrossListed(BooleanUtils.toBoolean(value));
1435                } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.CROSS_LISTED_COURSES.equals(cellInfo.getKey())) {
1436                    coListWrapper.setAlternateCOCodes(Arrays.asList(StringUtils.split(value, ",")));
1437                } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.OWNER_CODE.equals(cellInfo.getKey())) {
1438                    coListWrapper.setOwnerCode(value);
1439                } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.OWNER_ALIASES.equals(cellInfo.getKey())) {
1440                    coListWrapper.setOwnerAliases(Arrays.asList(StringUtils.split(value, ",")));
1441                }
1442
1443            }
1444            form.getCourseOfferingResultList().add(coListWrapper);
1445        }
1446    }
1447
1448
1449    /**
1450     * This method loads the previous and next course offerings for navigation purpose.
1451     *
1452     * @param form input form
1453     */
1454    public void loadPreviousAndNextCourseOffering(ARGCourseOfferingManagementForm form) {
1455        try {
1456            ContextInfo contextInfo = createContextInfo();
1457
1458            /**
1459             * Get all the course offerings for a term and subject area.
1460             */
1461            SearchRequestInfo searchRequest = new SearchRequestInfo(CourseOfferingManagementSearchImpl.CO_MANAGEMENT_SEARCH.getKey());
1462            searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.SUBJECT_AREA, form.getSubjectCode());
1463            searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.ATP_ID, form.getTermInfo().getId());
1464            searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.CROSS_LIST_SEARCH_ENABLED, BooleanUtils.toStringTrueFalse(true));
1465
1466            SearchResultInfo searchResult = getSearchService().search(searchRequest, contextInfo);
1467            List<CourseOfferingWrapper> availableCOs = new ArrayList<CourseOfferingWrapper>();
1468
1469            for (SearchResultRowInfo row : searchResult.getRows()) {
1470
1471                String courseOfferingCode = "";
1472                String courseOfferingId = "";
1473                String courseOfferingDesc = "";
1474                boolean isCrossListed = false;
1475                List<String> alternateCodes = null;
1476
1477                for (SearchResultCellInfo cellInfo : row.getCells()) {
1478
1479                    String value = StringUtils.EMPTY;
1480                    if (cellInfo.getValue() != null) {
1481                        value = cellInfo.getValue();
1482                    }
1483
1484                    if (CourseOfferingManagementSearchImpl.SearchResultColumns.CODE.equals(cellInfo.getKey())) {
1485                        courseOfferingCode = value;
1486                    } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.CO_ID.equals(cellInfo.getKey())) {
1487                        courseOfferingId = value;
1488                    } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.DESC.equals(cellInfo.getKey())) {
1489                        courseOfferingDesc = value;
1490                    } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.IS_CROSS_LISTED.equals(cellInfo.getKey())) {
1491                        isCrossListed = BooleanUtils.toBoolean(value);
1492                    } else if (CourseOfferingManagementSearchImpl.SearchResultColumns.CROSS_LISTED_COURSES.equals(cellInfo.getKey())) {
1493                        alternateCodes = Arrays.asList(StringUtils.split(value, ","));
1494                    }
1495
1496                }
1497
1498                CourseOfferingWrapper coWrapper = new CourseOfferingWrapper(isCrossListed, courseOfferingCode, courseOfferingDesc, alternateCodes, courseOfferingId);
1499                availableCOs.add(coWrapper);
1500            }
1501
1502            /**
1503             * Find the current course offering index and set the previous and next course offerings if exists.
1504             */
1505            for (CourseOfferingWrapper coWrapper : availableCOs) {
1506                if (StringUtils.equals(coWrapper.getCourseOfferingCode(), form.getCurrentCourseOfferingWrapper().getCourseOfferingCode())) {
1507                    int currentIndex = availableCOs.indexOf(coWrapper);
1508                    form.setInputCode(coWrapper.getCourseOfferingCode());
1509                    if (currentIndex > 0) {
1510                        form.setPreviousCourseOfferingWrapper(availableCOs.get(currentIndex - 1));
1511                    } else {
1512                        form.setPreviousCourseOfferingWrapper(null);
1513                    }
1514                    if (currentIndex < availableCOs.size() - 1) {
1515                        form.setNextCourseOfferingWrapper(availableCOs.get(currentIndex + 1));
1516                    } else {
1517                        form.setNextCourseOfferingWrapper(null);
1518                    }
1519                    break;
1520                }
1521            }
1522
1523
1524        } catch (Exception e) {
1525            throw new RuntimeException(e);
1526        }
1527    }
1528
1529    public void createActivityOfferings(String formatOfferingId, String activityId, int noOfActivityOfferings, ARGCourseOfferingManagementForm form) {
1530        String termcode;
1531        FormatOfferingInfo formatOfferingInfo;
1532        TypeInfo activityOfferingType = null;
1533        CourseInfo course;
1534        String clusterId = form.getClusterIdForNewAO();
1535        ContextInfo contextInfo = createContextInfo();
1536        List<ActivityOfferingClusterInfo> clusters;
1537        ActivityOfferingClusterInfo defaultCluster;
1538
1539        //create default cluster if there is no cluster for the FO yet
1540        try {
1541            clusters = getCourseOfferingService().getActivityOfferingClustersByFormatOffering(formatOfferingId, contextInfo);
1542            if (clusters == null || clusters.size() <= 0) {
1543                defaultCluster = ARGUtil.getArgServiceAdapter().createDefaultCluster(formatOfferingId, contextInfo);
1544                if (defaultCluster != null) {
1545                    clusterId = defaultCluster.getId();
1546                }
1547            }
1548        } catch (Exception e) {
1549            throw new RuntimeException(e);
1550        }
1551
1552        //the AO clusters associated with the given FO
1553        CourseOfferingInfo courseOffering = form.getCurrentCourseOfferingWrapper().getCourseOfferingInfo();
1554
1555        // find the Activity object that matches the activity id selected
1556        ActivityInfo activity = null;
1557
1558        // Get the format object for the id selected
1559        try {
1560            formatOfferingInfo = getCourseOfferingService().getFormatOffering(formatOfferingId, contextInfo);
1561            course = getCourseService().getCourse(courseOffering.getCourseId(), contextInfo);
1562            for (FormatInfo f : course.getFormats()) {
1563                if (f.getId().equals(formatOfferingInfo.getFormatId())) {
1564                    for (ActivityInfo info : f.getActivities()) {
1565                        if (StringUtils.equals(activityId, info.getId())) {
1566                            activity = info;
1567                        }
1568                    }
1569                    break;
1570                }
1571            }
1572        } catch (Exception e) {
1573            throw new RuntimeException(e);
1574        }
1575
1576        // Get the matching activity offering type for the selected activity
1577        try {
1578            for (String aoTypeKey : formatOfferingInfo.getActivityOfferingTypeKeys()) {
1579                List<TypeTypeRelationInfo> typeTypeRelationInfos = getTypeService().getTypeTypeRelationsByRelatedTypeAndType(aoTypeKey, TypeServiceConstants.TYPE_TYPE_RELATION_ALLOWED_TYPE_KEY, ContextUtils.getContextInfo());
1580                if (typeTypeRelationInfos != null && typeTypeRelationInfos.size() > 0) {
1581                    if (StringUtils.equals(activity.getTypeKey(), typeTypeRelationInfos.get(0).getOwnerTypeKey())) {
1582                        activityOfferingType = getTypeService().getType(aoTypeKey, contextInfo);
1583                    }
1584
1585                }
1586            }
1587        } catch (Exception e) {
1588            throw new RuntimeException(e);
1589        }
1590
1591        try {
1592            AtpInfo termAtp = getAtpService().getAtp(courseOffering.getTermId(), contextInfo);
1593            termcode = termAtp.getCode();
1594        } catch (Exception e) {
1595            throw new RuntimeException(e);
1596        }
1597
1598        for (int i = 0; i < noOfActivityOfferings; i++) {
1599            ActivityOfferingInfo aoInfo = new ActivityOfferingInfo();
1600            aoInfo.setActivityId(activityId);
1601            aoInfo.setFormatOfferingId(formatOfferingInfo.getId());
1602            aoInfo.setTypeKey(activityOfferingType.getKey());
1603            aoInfo.setCourseOfferingId(courseOffering.getId());
1604            aoInfo.setStateKey(LuiServiceConstants.LUI_AO_STATE_DRAFT_KEY);
1605            aoInfo.setTermId(courseOffering.getTermId());
1606            aoInfo.setTermCode(termcode);
1607            aoInfo.setFormatOfferingName(formatOfferingInfo.getName());
1608            aoInfo.setCourseOfferingTitle(courseOffering.getCourseOfferingTitle());
1609            aoInfo.setCourseOfferingCode(courseOffering.getCourseOfferingCode());
1610
1611            ActivityOfferingInfo activityOfferingInfo;
1612            try {
1613                //Temp solution here: if there is cluster(s) assocaited with the given FO, call createAO method in adapter
1614                //It will associate created AOs with the first cluster of the given FO
1615                if (clusterId != null && !clusterId.isEmpty()) {
1616                    activityOfferingInfo = ARGUtil.getArgServiceAdapter().createActivityOffering(aoInfo, clusterId, contextInfo).getCreatedActivityOffering();
1617                } else {
1618                    activityOfferingInfo = _getCourseOfferingService().createActivityOffering(formatOfferingInfo.getId(), activityId, activityOfferingType.getKey(), aoInfo, contextInfo);
1619                }
1620                ActivityOfferingWrapper wrapper = new ActivityOfferingWrapper(activityOfferingInfo);
1621                StateInfo state = getStateService().getState(wrapper.getAoInfo().getStateKey(), contextInfo);
1622                wrapper.setStateName(state.getName());
1623                TypeInfo typeInfo = getTypeInfo(wrapper.getAoInfo().getTypeKey());
1624                wrapper.setTypeName(typeInfo.getName());
1625                form.getActivityWrapperList().add(wrapper);
1626            } catch (Exception e) {
1627                throw new RuntimeException(e);
1628            }
1629        }
1630
1631        ARGToolbarUtil.processAoToolbarForUser(form.getActivityWrapperList(), form);
1632        if (noOfActivityOfferings == 1) {
1633            KSUifUtils.addGrowlMessageIcon(GrowlIcon.INFORMATION, CourseOfferingConstants.ACTIVITYOFFERING_TOOLBAR_ADD_1_SUCCESS);
1634        } else {
1635            KSUifUtils.addGrowlMessageIcon(GrowlIcon.INFORMATION, CourseOfferingConstants.ACTIVITYOFFERING_TOOLBAR_ADD_N_SUCCESS);
1636        }
1637    }
1638
1639    public void loadActivityOfferingsByCourseOffering(CourseOfferingInfo theCourseOfferingInfo, ARGCourseOfferingManagementForm form) throws Exception {
1640        String courseOfferingId = theCourseOfferingInfo.getId();
1641        List<ActivityOfferingInfo> activityOfferingInfoList;
1642        List<ActivityOfferingWrapper> activityOfferingWrapperList;
1643
1644        try {
1645            activityOfferingInfoList = _getCourseOfferingService().getActivityOfferingsByCourseOffering(courseOfferingId, createContextInfo());
1646            activityOfferingWrapperList = new ArrayList<ActivityOfferingWrapper>(activityOfferingInfoList.size());
1647
1648            for (ActivityOfferingInfo info : activityOfferingInfoList) {
1649                ActivityOfferingWrapper aoWrapper = convertAOInfoToWrapper(info);
1650                activityOfferingWrapperList.add(aoWrapper);
1651            }
1652        } catch (Exception e) {
1653            throw new RuntimeException(String.format("Could not load AOs for course offering [%s].", courseOfferingId), e);
1654        }
1655        form.setActivityWrapperList(activityOfferingWrapperList);
1656    }
1657
1658    public List<ActivityOfferingWrapper> getActivityOfferingsByCourseOfferingId(String courseOfferingId, ARGCourseOfferingManagementForm form) throws Exception {
1659        List<ActivityOfferingInfo> activityOfferingInfoList;
1660        List<ActivityOfferingWrapper> activityOfferingWrapperList;
1661
1662        try {
1663            activityOfferingInfoList = _getCourseOfferingService().getActivityOfferingsByCourseOffering(courseOfferingId, createContextInfo());
1664            activityOfferingWrapperList = new ArrayList<ActivityOfferingWrapper>(activityOfferingInfoList.size());
1665
1666            for (ActivityOfferingInfo info : activityOfferingInfoList) {
1667                ActivityOfferingWrapper aoWrapper = new ActivityOfferingWrapper(info);
1668                activityOfferingWrapperList.add(aoWrapper);
1669            }
1670        } catch (Exception e) {
1671            throw new RuntimeException(String.format("Could not load AOs for course offering [%s].", courseOfferingId), e);
1672        }
1673        return activityOfferingWrapperList;
1674    }
1675
1676    public void approveCourseOfferings(ARGCourseOfferingManagementForm form) throws Exception {
1677        List<CourseOfferingListSectionWrapper> coList = form.getCourseOfferingResultList();
1678        ContextInfo contextInfo = createContextInfo();
1679        int checked = 0;
1680        int enabled = 0;
1681        for (CourseOfferingListSectionWrapper co : coList) {
1682            if (co.getIsChecked()) {
1683                checked++;
1684                if (co.isEnableApproveButton()) {
1685                    enabled++;
1686                    List<ActivityOfferingWrapper> aos = getActivityOfferingsByCourseOfferingId(co.getCourseOfferingId(), form);
1687                    if (aos != null && !aos.isEmpty()) {
1688                        ARGToolbarUtil.processAoToolbarForUser(aos, form);
1689                        for (ActivityOfferingWrapper ao : aos) {
1690                            if (ao.isEnableApproveButton()) {
1691                                getCourseOfferingService().changeActivityOfferingState(ao.getAoInfo().getId(), LuiServiceConstants.LUI_AO_STATE_APPROVED_KEY, contextInfo);
1692                            }
1693                        }
1694                    }
1695                }
1696            }
1697        }
1698
1699        if (checked > enabled) {
1700            KSUifUtils.addGrowlMessageIcon(GrowlIcon.WARNING, CourseOfferingConstants.COURSEOFFERING_TOOLBAR_APPROVED);
1701        } else {
1702            if (enabled == 1) {
1703                KSUifUtils.addGrowlMessageIcon(GrowlIcon.INFORMATION, CourseOfferingConstants.COURSEOFFERING_TOOLBAR_APPROVED_1_SUCCESS);
1704            } else {
1705                KSUifUtils.addGrowlMessageIcon(GrowlIcon.INFORMATION, CourseOfferingConstants.COURSEOFFERING_TOOLBAR_APPROVED_N_SUCCESS);
1706            }
1707        }
1708    }
1709
1710    public void deleteCourseOfferings(ARGCourseOfferingManagementForm form) throws Exception {
1711        List<CourseOfferingListSectionWrapper> coList = form.getCourseOfferingResultList();
1712        int totalCosToDelete = 0;
1713        int totalColocatedCos = 0;
1714        int totalColocatedAos = 0;
1715        boolean iscolocated;
1716        int totalCrossListedCosToDelete = 0;
1717        int totalJointDefinedCosToDelete = 0;
1718
1719        form.setNumOfColocatedCosToDelete(0);
1720        form.setNumOfColocatedAosToDelete(0);
1721        form.setNumOfCrossListedCosToDelete(0);
1722        form.setNumOfJointDefinedCosToDelete(0);
1723
1724        List<CourseOfferingListSectionWrapper> qualifiedToDeleteList = form.getSelectedCoToDeleteList();
1725        qualifiedToDeleteList.clear();
1726        ContextInfo contextInfo = ContextUtils.createDefaultContextInfo();
1727
1728        int totalAos = 0;
1729        form.setCrossListedCO(false);
1730        form.setColocatedCO(false);
1731        for (CourseOfferingListSectionWrapper co : coList) {
1732            if (co.getIsChecked()) {
1733                totalCosToDelete++;
1734                iscolocated = false;
1735
1736                List<ActivityOfferingDisplayInfo> aoDisplayInfoList = getCourseOfferingService().getActivityOfferingDisplaysForCourseOffering(co.getCourseOfferingId(), contextInfo);
1737                List<ActivityOfferingInfo> aoInfoList = getCourseOfferingService().getActivityOfferingsByCourseOffering(co.getCourseOfferingId(), contextInfo);
1738
1739                co.setCoHasAoToDelete(false);
1740
1741                co.setColocated(false);
1742
1743                if (aoDisplayInfoList != null && !aoDisplayInfoList.isEmpty()) {
1744                    co.setCoHasAoToDelete(true);
1745                    for (ActivityOfferingDisplayInfo aoDisplayInfo : aoDisplayInfoList) {
1746
1747                        ActivityOfferingDisplayWrapper aoDisplayWrapper = new ActivityOfferingDisplayWrapper();
1748                        aoDisplayWrapper.setAoDisplayInfo(aoDisplayInfo);
1749                        aoDisplayWrapper.setActivityOfferingCode(aoDisplayInfo.getActivityOfferingCode());
1750                        // Adding Information (icons)
1751                        String information = "";
1752                        if (aoDisplayInfo.getIsHonorsOffering() != null && aoDisplayInfo.getIsHonorsOffering()) {
1753                            information = "<img src=" + ScheduleOfClassesConstants.SOC_RESULT_PAGE_HONORS_COURSE_IMG + " title=\"" + ScheduleOfClassesConstants.SOC_RESULT_PAGE_HELP_HONORS_ACTIVITY + "\"> ";
1754                        }
1755                        aoDisplayWrapper.setInformation(information);
1756
1757                        if (aoDisplayInfo.getScheduleDisplay() != null && !aoDisplayInfo.getScheduleDisplay().getScheduleComponentDisplays().isEmpty()) {
1758                            //TODO handle TBA state
1759                            List<? extends ScheduleComponentDisplay> scheduleComponentDisplays = aoDisplayInfo.getScheduleDisplay().getScheduleComponentDisplays();
1760                            for (ScheduleComponentDisplay scheduleComponentDisplay : scheduleComponentDisplays) {
1761                                if (scheduleComponentDisplay.getBuilding() != null) {
1762                                    aoDisplayWrapper.setBuildingName(scheduleComponentDisplay.getBuilding().getBuildingCode(), true);
1763                                }
1764                                if (scheduleComponentDisplay.getRoom() != null) {
1765                                    aoDisplayWrapper.setRoomName(scheduleComponentDisplay.getRoom().getRoomCode(), true);
1766                                }
1767                                if (!scheduleComponentDisplay.getTimeSlots().isEmpty()) {
1768                                    if (scheduleComponentDisplay.getTimeSlots().get(0).getStartTime() != null) {
1769                                        aoDisplayWrapper.setStartTimeDisplay(millisToTime(scheduleComponentDisplay.getTimeSlots().get(0).getStartTime().getMilliSeconds()), true);
1770                                    }
1771                                    if (scheduleComponentDisplay.getTimeSlots().get(0).getEndTime() != null) {
1772                                        aoDisplayWrapper.setEndTimeDisplay(millisToTime(scheduleComponentDisplay.getTimeSlots().get(0).getEndTime().getMilliSeconds()), true);
1773                                    }
1774                                    aoDisplayWrapper.setDaysDisplayName(getDays(scheduleComponentDisplay.getTimeSlots().get(0).getWeekdays()), true);
1775                                }
1776                            }
1777                        }
1778                        // if ao is colocated AO add colocated info
1779                        if (isColocatedAo(aoDisplayInfo.getActivityOfferingCode(), aoInfoList)) {
1780                            String colocateInfo = CourseOfferingViewHelperUtil.createColocatedDisplayData(getAoInfo(aoDisplayInfo.getActivityOfferingCode(), aoInfoList), contextInfo);
1781                            aoDisplayWrapper.setColocatedAoInfo(colocateInfo);
1782                            co.setColocated(true);
1783                            co.setColocatedCoCode(colocateInfo);
1784                            form.setColocatedCO(true);
1785                            totalColocatedAos++;
1786                            iscolocated = true;
1787                        }
1788                        co.getAoToBeDeletedList().add(aoDisplayWrapper);
1789                    }
1790
1791                    totalAos = totalAos + co.getAoToBeDeletedList().size();
1792                    co.setCrossListed(false);
1793                    if (co.getAlternateCOCodes() != null && co.getAlternateCOCodes().size() > 0) {
1794                        co.setCrossListed(true);
1795                        form.setCrossListedCO(true);
1796                        totalCrossListedCosToDelete++;
1797                    }
1798                }
1799                co.setJointDefined(false);
1800
1801                if (iscolocated) {
1802                    totalColocatedCos++;
1803                } else {
1804                    // verify whether this CO is joint-defined or not
1805                    String jointDefinedCodes = getJointDefinedInfo(co);
1806                    if (!jointDefinedCodes.isEmpty()) {
1807                        co.setJointDefinedCoCode(jointDefinedCodes);
1808                        co.setJointDefined(true);
1809                        totalJointDefinedCosToDelete++;
1810                    }
1811                }
1812                qualifiedToDeleteList.add(co);
1813            }
1814        }
1815        form.setNumOfCrossListedCosToDelete(totalCrossListedCosToDelete);
1816        form.setNumOfJointDefinedCosToDelete(totalJointDefinedCosToDelete);
1817        if (totalColocatedCos == totalCosToDelete) {
1818            form.setColocatedCoOnly(true);
1819            form.setNumOfColocatedCosToDelete(totalColocatedCos);
1820        }
1821        if (totalJointDefinedCosToDelete >= 1) {
1822            form.setJointDefinedCo(true);
1823        }
1824        form.setNumOfColocatedAosToDelete(totalColocatedAos);
1825        form.setTotalAOsToBeDeleted(totalAos);
1826    }
1827
1828    public void approveActivityOfferings(ARGCourseOfferingManagementForm form) throws Exception {
1829        List<ActivityOfferingWrapper> aoList = form.getActivityWrapperList();
1830        ContextInfo contextInfo = createContextInfo();
1831        int checked = 0;
1832        int enabled = 0;
1833        for (ActivityOfferingWrapper ao : aoList) {
1834            if ((!"0".equals(form.getSelectedTabId())) && ao.getIsChecked() ||
1835                    ("0".equals(form.getSelectedTabId())) && ao.getIsCheckedByCluster()) {
1836                checked++;
1837                if (ao.isEnableApproveButton()) {
1838                    enabled++;
1839                    getCourseOfferingService().changeActivityOfferingState(ao.getAoInfo().getId(), LuiServiceConstants.LUI_AO_STATE_APPROVED_KEY, contextInfo);
1840                }
1841            }
1842        }
1843
1844        if (checked > enabled) {
1845            KSUifUtils.addGrowlMessageIcon(GrowlIcon.WARNING, CourseOfferingConstants.ACTIVITYOFFERING_TOOLBAR_APPROVED);
1846        } else {
1847            if (enabled == 1) {
1848                KSUifUtils.addGrowlMessageIcon(GrowlIcon.INFORMATION, CourseOfferingConstants.ACTIVITYOFFERING_TOOLBAR_APPROVED_1_SUCCESS);
1849            } else {
1850                KSUifUtils.addGrowlMessageIcon(GrowlIcon.INFORMATION, CourseOfferingConstants.ACTIVITYOFFERING_TOOLBAR_APPROVED_N_SUCCESS);
1851            }
1852        }
1853    }
1854
1855    public void draftActivityOfferings(ARGCourseOfferingManagementForm form) throws Exception {
1856        List<ActivityOfferingWrapper> aoList = form.getActivityWrapperList();
1857        ContextInfo contextInfo = createContextInfo();
1858        int checked = 0;
1859        int enabled = 0;
1860        for (ActivityOfferingWrapper ao : aoList) {
1861            if ((!"0".equals(form.getSelectedTabId())) && ao.getIsChecked() ||
1862                    ("0".equals(form.getSelectedTabId())) && ao.getIsCheckedByCluster()) {
1863                checked++;
1864                if (ao.isEnableDraftButton()) {
1865                    enabled++;
1866                    getCourseOfferingService().changeActivityOfferingState(ao.getAoInfo().getId(), LuiServiceConstants.LUI_AO_STATE_DRAFT_KEY, contextInfo);
1867                }
1868            }
1869        }
1870
1871        if (checked > enabled) {
1872            KSUifUtils.addGrowlMessageIcon(GrowlIcon.WARNING, CourseOfferingConstants.ACTIVITYOFFERING_TOOLBAR_DRAFT);
1873        } else {
1874            if (enabled == 1) {
1875                KSUifUtils.addGrowlMessageIcon(GrowlIcon.INFORMATION, CourseOfferingConstants.ACTIVITYOFFERING_TOOLBAR_DRAFT_1_SUCCESS);
1876            } else {
1877                KSUifUtils.addGrowlMessageIcon(GrowlIcon.INFORMATION, CourseOfferingConstants.ACTIVITYOFFERING_TOOLBAR_DRAFT_N_SUCCESS);
1878            }
1879        }
1880
1881    }
1882
1883    /**
1884     * Notes by Bonnie: The following implementation tries to support "Approve for Scheduling" in two cases historically:
1885     * 1)when some COs are checked and approve for scheduling link or button is clicked
1886     * vs.
1887     * 2)when a user simply click "Approve Subject Code for Scheduling" link with/without selecting any CO
1888     * case 1) has been implemented by  approveCourseOfferings method -- invoked by Approve button in toolbar
1889     * case 2) is the only use case when markCourseOfferingsForScheduling is invoked. Therefore the parameter
1890     * checkedOnly and related checking for each CO seem not needed anymore...
1891     * TODO: code cleanup??
1892     * <p/>
1893     * Examines a List of CourseOffering wrappers and changes the state of each "checked" AO (meaning the
1894     * CO was selected on the UI) from "Draft" to "Approved". If the AO has a state other than "Draft" the AO is ignored.
1895     * Also, changes the state of the CourseOffering if appropriate.
1896     *
1897     * @param coWrappers, viewId, socStateKey provides list of CourseOfferings, viewId, socState.
1898     * @param checkedOnly True if the CO wrapper isChecked() flag should be respected.
1899     */
1900    public void markCourseOfferingsForScheduling(List<CourseOfferingListSectionWrapper> coWrappers,
1901                                                 String viewId, String socStateKey, boolean checkedOnly) throws Exception {
1902        boolean hasAOWarning = false, hasStateChangedAO = false, hasOrgWarning = false;
1903        ContextInfo contextInfo = createContextInfo();
1904        for (CourseOfferingListSectionWrapper coWrapper : coWrappers) {
1905            if (coWrapper.getIsChecked() || !checkedOnly) {
1906                // Checking if the person is authorized to approve
1907                Map<String, String> permissionDetails = new HashMap<String, String>();
1908                Map<String, String> roleQualifications = new HashMap<String, String>();
1909                CourseOfferingInfo coInfo = getCourseOfferingService().getCourseOffering(coWrapper.getCourseOfferingId(), contextInfo);
1910                List<String> orgIds = coInfo.getUnitsDeploymentOrgIds();
1911                StringBuilder orgIDs = new StringBuilder();
1912                if (orgIds != null && !orgIds.isEmpty()) {
1913                    for (String orgId : orgIds) {
1914                        orgIDs.append(orgId + ",");
1915                    }
1916                }
1917                boolean canApproveAOs = true;
1918                if (orgIDs.length() > 0) {
1919                    String principalId = GlobalVariables.getUserSession().getPerson().getPrincipalId();
1920
1921                    roleQualifications.put("offeringAdminOrgId", orgIDs.substring(0, orgIDs.length() - 1));
1922
1923                    permissionDetails.put(KimConstants.AttributeConstants.VIEW_ID, viewId);
1924                    permissionDetails.put(KimConstants.AttributeConstants.ACTION_EVENT, "approveSubj");
1925
1926                    String socState = socStateKey == null ? null : socStateKey.substring(socStateKey.lastIndexOf('.') + 1);
1927                    permissionDetails.put("socState", socState);
1928
1929                    canApproveAOs = getPermissionService().isAuthorizedByTemplate(principalId, "KS-ENR", KimConstants.PermissionTemplateNames.PERFORM_ACTION, permissionDetails, roleQualifications);
1930                }
1931
1932                if (!canApproveAOs) {  // if can't approve AOs for all COs (because they are in a different org)
1933                    if (!hasOrgWarning) hasOrgWarning = true;
1934                    continue;
1935                } else {
1936
1937                    List<ActivityOfferingInfo> activityOfferingInfos = getCourseOfferingService().getActivityOfferingsByCourseOffering(coWrapper.getCourseOfferingId(), contextInfo);
1938                    if (activityOfferingInfos.size() == 0) {
1939                        if (!hasAOWarning) hasAOWarning = true;
1940                        continue;
1941                    }
1942                    // Iterate through the AOs and state change Draft -> Approved.
1943                    for (ActivityOfferingInfo activityOfferingInfo : activityOfferingInfos) {
1944                        boolean isAOStateDraft = StringUtils.equals(activityOfferingInfo.getStateKey(), LuiServiceConstants.LUI_AO_STATE_DRAFT_KEY);
1945                        if (isAOStateDraft) {
1946                            StatusInfo statusInfo = getCourseOfferingService().changeActivityOfferingState(activityOfferingInfo.getId(), LuiServiceConstants.LUI_AO_STATE_APPROVED_KEY, contextInfo);
1947                            if (!statusInfo.getIsSuccess()) {
1948                                GlobalVariables.getMessageMap().putError("manageCourseOfferingsPage", CourseOfferingConstants.COURSE_OFFERING_STATE_CHANGE_ERROR, coWrapper.getCourseOfferingCode(), statusInfo.getMessage());
1949                            }
1950                            //  Flag if any AOs can be state changed. This affects the error message whi.
1951                            if (statusInfo.getIsSuccess()) {
1952                                hasStateChangedAO = true;
1953                            }
1954                        } else {
1955                            //  Flag if any AOs are not in a valid state for approval.
1956                            if (!hasAOWarning) hasAOWarning = true;
1957                        }
1958                    }
1959                }
1960            }
1961        }
1962        //  Set feedback messages.
1963        if (!hasStateChangedAO) {
1964            GlobalVariables.getMessageMap().putError("manageCourseOfferingsPage", CourseOfferingConstants.COURSEOFFERING_NONE_APPROVED);
1965        } else {
1966            if (hasAOWarning) {
1967                GlobalVariables.getMessageMap().putWarning("manageCourseOfferingsPage", CourseOfferingConstants.COURSEOFFERING_WITH_AO_DRAFT_APPROVED_ONLY);
1968            }
1969            if (hasOrgWarning) {
1970                GlobalVariables.getMessageMap().putWarning("manageCourseOfferingsPage", CourseOfferingConstants.COURSEOFFERING_WITH_AO_ORG_APPROVED_ONLY);
1971            }
1972        }
1973    }
1974
1975    private void setSocStateKeys(ARGCourseOfferingManagementForm form, List<String> socIds) throws Exception {
1976        if (socIds != null && !socIds.isEmpty()) {
1977            List<SocInfo> targetSocs = this.getSocService().getSocsByIds(socIds, createContextInfo());
1978            for (SocInfo soc : targetSocs) {
1979                if (soc.getTypeKey().equals(CourseOfferingSetServiceConstants.MAIN_SOC_TYPE_KEY)) {
1980                    form.setSocStateKey(soc.getStateKey());
1981                    form.setSocSchedulingStateKey(soc.getSchedulingStateKey());
1982
1983                    //TODO: Set SOC State - temporary display, to be removed after testing is finished
1984
1985                    if (StringUtils.isNotBlank(soc.getStateKey())) {
1986                        String socState = getStateService().getState(soc.getStateKey(), createContextInfo()).getName();
1987                        socState = (socState.substring(0, 1)).toUpperCase() + socState.substring(1, socState.length());
1988                        form.setSocState(socState);
1989                    }
1990
1991                    return;
1992                }
1993            }
1994        }
1995    }
1996
1997    private boolean isColocatedAo(String aoCode, List<ActivityOfferingInfo> aoList) {
1998        for(ActivityOfferingInfo ao : aoList) {
1999            if(StringUtils.equals(aoCode, ao.getActivityCode())) {
2000                if(ao.getIsColocated()) {
2001                    return true;
2002                }
2003            }
2004        }
2005        return false;
2006    }
2007
2008    private ActivityOfferingInfo getAoInfo(String aoCode, List<ActivityOfferingInfo> aoList) {
2009        for(ActivityOfferingInfo ao : aoList) {
2010            if(StringUtils.equals(aoCode, ao.getActivityCode())) {
2011                if(ao.getIsColocated()) {
2012                    return ao;
2013                }
2014            }
2015        }
2016        return null;
2017    }
2018
2019    private String getJointDefinedInfo(CourseOfferingListSectionWrapper co) {
2020        if (co == null) return null;
2021
2022        List<CourseInfo> coInfoList = CourseOfferingViewHelperUtil.getMatchingCoursesFromClu(co.getCourseOfferingCode());
2023        StringBuffer jointDefinedCodes = new StringBuffer();
2024
2025        for (CourseInfo coInfo : coInfoList) {
2026            List<CourseJointInfo> jointList = coInfo.getJoints();
2027            if (!jointList.isEmpty() && jointList.size() >= 1) {
2028                for (CourseJointInfo jointInfo : jointList) {
2029                    jointDefinedCodes.append(jointInfo.getSubjectArea());
2030                    jointDefinedCodes.append(jointInfo.getCourseNumberSuffix());
2031                    jointDefinedCodes.append(" ");
2032                }
2033
2034            }
2035        }
2036
2037        return jointDefinedCodes.toString();
2038    }
2039
2040    private CourseOfferingService _getCourseOfferingService() {
2041        if (coService == null) {
2042            coService = (CourseOfferingService) GlobalResourceLoader.getService(new QName(CourseOfferingServiceConstants.NAMESPACE,
2043                    CourseOfferingServiceConstants.SERVICE_NAME_LOCAL_PART));
2044        }
2045        return coService;
2046    }
2047
2048    private AcademicCalendarService getAcalService() {
2049        if (acalService == null) {
2050            acalService = (AcademicCalendarService) GlobalResourceLoader.getService(new QName(AcademicCalendarServiceConstants.NAMESPACE,
2051                    AcademicCalendarServiceConstants.SERVICE_NAME_LOCAL_PART));
2052        }
2053        return acalService;
2054    }
2055
2056    private String millisToTime(Long milliseconds) {
2057        if (milliseconds == null) {
2058            return null;
2059        }
2060        final Calendar cal = Calendar.getInstance();
2061        cal.setTimeInMillis(milliseconds);
2062        return DateFormatters.HOUR_MINUTE_AM_PM_TIME_FORMATTER.format(cal.getTime());
2063
2064    }
2065
2066    private String convertIntoDaysDisplay(int day) {
2067        String dayOfWeek;
2068        switch (day) {
2069            case 1:
2070                dayOfWeek = SchedulingServiceConstants.SUNDAY_TIMESLOT_DISPLAY_DAY_CODE;
2071                break;
2072            case 2:
2073                dayOfWeek = SchedulingServiceConstants.MONDAY_TIMESLOT_DISPLAY_DAY_CODE;
2074                break;
2075            case 3:
2076                dayOfWeek = SchedulingServiceConstants.TUESDAY_TIMESLOT_DISPLAY_DAY_CODE;
2077                break;
2078            case 4:
2079                dayOfWeek = SchedulingServiceConstants.WEDNESDAY_TIMESLOT_DISPLAY_DAY_CODE;
2080                break;
2081            case 5:
2082                dayOfWeek = SchedulingServiceConstants.THURSDAY_TIMESLOT_DISPLAY_DAY_CODE;
2083                break;
2084            case 6:
2085                dayOfWeek = SchedulingServiceConstants.FRIDAY_TIMESLOT_DISPLAY_DAY_CODE;
2086                break;
2087            case 7:
2088                dayOfWeek = SchedulingServiceConstants.SATURDAY_TIMESLOT_DISPLAY_DAY_CODE;
2089                break;
2090            default:
2091                dayOfWeek = StringUtils.EMPTY;
2092        }
2093        // TODO implement TBA when service stores it.
2094        return dayOfWeek;
2095    }
2096
2097    private String getDays(List<Integer> intList) {
2098
2099        StringBuilder sb = new StringBuilder();
2100        if (intList == null) {
2101            return sb.toString();
2102        }
2103
2104        for (Integer d : intList) {
2105            sb.append(convertIntoDaysDisplay(d));
2106        }
2107
2108        return sb.toString();
2109    }
2110
2111    public CourseOfferingService getCourseOfferingService() {
2112        return CourseOfferingResourceLoader.loadCourseOfferingService();
2113    }
2114
2115    public CourseService getCourseService() {
2116        if (courseService == null) {
2117            courseService = CourseOfferingResourceLoader.loadCourseService();
2118        }
2119        return courseService;
2120    }
2121
2122
2123    protected LRCService getLrcService() {
2124        if (lrcService == null) {
2125            lrcService = (LRCService) GlobalResourceLoader.getService(new QName("http://student.kuali.org/wsdl/lrc", "LrcService"));
2126        }
2127        return this.lrcService;
2128    }
2129
2130    public SearchService getSearchService() {
2131        if (searchService == null) {
2132            searchService = (SearchService) GlobalResourceLoader.getService(new QName(CommonServiceConstants.REF_OBJECT_URI_GLOBAL_PREFIX + "search", SearchService.class.getSimpleName()));
2133        }
2134        return searchService;
2135    }
2136
2137    public AtpService getAtpService() {
2138        if (atpService == null) {
2139            atpService = CourseOfferingResourceLoader.loadAtpService();
2140        }
2141        return atpService;
2142    }
2143
2144    public CourseOfferingSetService getSocService() {
2145        // If it hasn't been set by Spring, then look it up by GlobalResourceLoader
2146        if (socService == null) {
2147            socService = (CourseOfferingSetService) GlobalResourceLoader.getService(new QName(CourseOfferingSetServiceConstants.NAMESPACE,
2148                    CourseOfferingSetServiceConstants.SERVICE_NAME_LOCAL_PART));
2149        }
2150        return socService;
2151    }
2152
2153    private static PermissionService getPermissionService() {
2154        if (permissionService == null) {
2155            permissionService = KimApiServiceLocator.getPermissionService();
2156        }
2157        return permissionService;
2158    }
2159
2160    public static IdentityService getIdentityService() {
2161        if (identityService == null) {
2162            identityService = KimApiServiceLocator.getIdentityService();
2163        }
2164        return identityService;
2165    }
2166
2167    public static LprService getLprService() {
2168        if (lprService == null) {
2169            lprService = (LprService) GlobalResourceLoader.getService(new QName(LprServiceConstants.NAMESPACE,
2170                    LprServiceConstants.SERVICE_NAME_LOCAL_PART));
2171        }
2172        return lprService;
2173    }
2174
2175
2176}