protected boolean _validateTermRecursive(
      String termId, Set<String> processedTermIds, ContextInfo context)
      throws PermissionDeniedException, MissingParameterException, InvalidParameterException,
          OperationFailedException, DoesNotExistException {
    List<TermInfo> childTerms = acalService.getIncludedTermsInTerm(termId, context);
    TermInfo term = acalService.getTerm(termId, context);

    if (AtpServiceConstants.ATP_DRAFT_STATE_KEY.equals(term.getStateKey())) {
      for (TermInfo subterm : childTerms) {
        // Worth checking all subterms regardless of whether it's been processed or not
        if (AtpServiceConstants.ATP_OFFICIAL_STATE_KEY.equals(subterm.getStateKey())) {
          return false; // Automatically false
        }
      }
    }
    // Otherwise recurse
    for (TermInfo subterm : childTerms) {
      if (processedTermIds.contains(subterm.getId())) { // To prevent accidental infinite recursion
        continue;
      }
      processedTermIds.add(subterm.getId()); // Add this to processed
      boolean result = _validateTermRecursive(subterm.getId(), processedTermIds, context);
      if (!result) {
        return result; // Validation failed, so return false
      }
    }
    // If we got here, then must be true
    return true;
  }
  private static void setTermPropertiesOnFormObject(
      CourseOfferingEditWrapper formObject, String termId, ContextInfo contextInfo)
      throws Exception {

    TermInfo termInfo =
        CourseOfferingManagementUtil.getAcademicCalendarService().getTerm(termId, contextInfo);
    formObject.setTerm(termInfo);
    formObject.setTermName(termInfo.getName());

    // Setting term string: Fall 2012 (09/28/2012 to 12/15/2012)
    String termStartDate =
        DateFormatters.MONTH_DAY_YEAR_DATE_FORMATTER.format(termInfo.getStartDate());
    String termEndDate = DateFormatters.MONTH_DAY_YEAR_DATE_FORMATTER.format(termInfo.getEndDate());
    StringBuilder termStartEnd = new StringBuilder();
    termStartEnd.append(termInfo.getName());
    termStartEnd.append(" (");
    termStartEnd.append(termStartDate);
    termStartEnd.append(" to ");
    termStartEnd.append(termEndDate);
    termStartEnd.append(")");
    formObject.setTermStartEnd(termStartEnd.toString());
  }
 @Override
 public boolean validateCalendar(String acalId, ContextInfo context)
     throws PermissionDeniedException, MissingParameterException, InvalidParameterException,
         OperationFailedException, DoesNotExistException {
   AcademicCalendarInfo cal = acalService.getAcademicCalendar(acalId, context);
   List<TermInfo> parentTerms = acalService.getTermsForAcademicCalendar(acalId, context);
   if (AtpServiceConstants.ATP_DRAFT_STATE_KEY.equals(cal.getStateKey())) {
     for (TermInfo term : parentTerms) {
       if (AtpServiceConstants.ATP_OFFICIAL_STATE_KEY.equals(term.getStateKey())) {
         return false;
       }
     }
   }
   // If we get here, then recursively validate each term
   for (TermInfo term : parentTerms) {
     boolean result = validateTerm(term.getId(), context);
     if (!result) {
       return false;
     }
   }
   return true;
 }
  private CourseOfferingInfo createCourseOffering(
      boolean shouldThrowPermissionDenied, CourseInfo course, String principalId)
      throws DoesNotExistException, DataValidationErrorException, InvalidParameterException,
          MissingParameterException, OperationFailedException, PermissionDeniedException,
          ReadOnlyException, DependentObjectsExistException {
    ContextInfo context = this.getContext(principalId);
    List<String> optionKeys = new ArrayList<String>();
    CourseOfferingInfo sourceCo =
        CourseOfferingServiceTestDataUtils.createCourseOffering(
            course, FA2011_TERM.getId(), LuiServiceConstants.LUI_CO_STATE_OFFERED_KEY);

    try {
      sourceCo =
          courseOfferingService.createCourseOffering(
              sourceCo.getCourseId(),
              sourceCo.getTermId(),
              sourceCo.getTypeKey(),
              sourceCo,
              optionKeys,
              context);
    } catch (PermissionDeniedException ex) {
      if (!shouldThrowPermissionDenied) {
        fail(
            "Create should not have thrown permission denied but did "
                + course.getCode()
                + " "
                + principalId);
        return null;
      }
      return null;
    }
    if (shouldThrowPermissionDenied) {
      fail(
          "Create should have thrown permission denied but did not "
              + course.getCode()
              + " "
              + principalId);
      return null;
    }
    return sourceCo;
  }
  protected static void continueFromCreateCopyCourseOfferingInfo(
      CourseOfferingCreateWrapper coWrapper, CourseInfo course, TermInfo term) {

    ContextInfo contextInfo = ContextUtils.createDefaultContextInfo();
    int firstValue = 0;

    try {
      List<CourseOfferingInfo> courseOfferingInfos =
          CourseOfferingManagementUtil.getCourseOfferingService()
              .getCourseOfferingsByCourseAndTerm(course.getId(), term.getId(), contextInfo);

      coWrapper.setCourse(course);
      coWrapper.setCreditCount(
          CourseOfferingViewHelperUtil.trimTrailing0(
              CourseOfferingManagementUtil.getLrcService()
                  .getResultValue(
                      course
                          .getCreditOptions()
                          .get(firstValue)
                          .getResultValueKeys()
                          .get(firstValue),
                      contextInfo)
                  .getValue()));
      coWrapper.setShowAllSections(true);
      coWrapper.setShowCopyCourseOffering(false);
      coWrapper.setShowTermOfferingLink(true);

      coWrapper.setContextBar(
          CourseOfferingContextBar.NEW_INSTANCE(
              coWrapper.getTerm(),
              coWrapper.getSocInfo(),
              CourseOfferingManagementUtil.getStateService(),
              CourseOfferingManagementUtil.getAcademicCalendarService(),
              contextInfo));

      coWrapper.getExistingTermOfferings().clear();
      coWrapper.getExistingOfferingsInCurrentTerm().clear();

      for (CourseOfferingInfo courseOfferingInfo : courseOfferingInfos) {
        if (StringUtils.equals(
            courseOfferingInfo.getStateKey(), LuiServiceConstants.LUI_CO_STATE_OFFERED_KEY)) {
          CourseOfferingEditWrapper co = new CourseOfferingEditWrapper(courseOfferingInfo);
          co.setGradingOption(
              CourseOfferingManagementUtil.getGradingOption(
                  courseOfferingInfo.getGradingOptionId()));
          coWrapper.getExistingOfferingsInCurrentTerm().add(co);
        }
      }

      // Get past 5 years CO
      Calendar termStart = Calendar.getInstance();
      termStart.setTime(term.getStartDate());
      String termYear = Integer.toString(termStart.get(Calendar.YEAR));
      String termMonth = Integer.toString((termStart.get(Calendar.MONTH) + 1));
      String termDayOfMonth = Integer.toString((termStart.getActualMaximum(Calendar.DAY_OF_MONTH)));

      org.kuali.student.r2.core.search.dto.SearchRequestInfo searchRequest =
          new org.kuali.student.r2.core.search.dto.SearchRequestInfo(
              CourseOfferingHistorySearchImpl.PAST_CO_SEARCH.getKey());
      searchRequest.addParam(
          CourseOfferingHistorySearchImpl.COURSE_ID, coWrapper.getCourse().getId());

      searchRequest.addParam(
          CourseOfferingHistorySearchImpl.TARGET_DAY_OF_MONTH_PARAM, termDayOfMonth);
      searchRequest.addParam(CourseOfferingHistorySearchImpl.TARGET_MONTH_PARAM, termMonth);
      searchRequest.addParam(CourseOfferingHistorySearchImpl.TARGET_YEAR_PARAM, termYear);
      searchRequest.addParam(
          CourseOfferingHistorySearchImpl.SearchParameters.CROSS_LIST_SEARCH_ENABLED,
          BooleanUtils.toStringTrueFalse(true));
      org.kuali.student.r2.core.search.dto.SearchResultInfo searchResult =
          CourseOfferingManagementUtil.getSearchService().search(searchRequest, null);

      List<String> courseOfferingIds = new ArrayList<String>(searchResult.getTotalResults());

      /* Checks whether the course is cross-listed and Set the codes that are cross listed to the cross-listed list */

      for (org.kuali.student.r2.core.search.dto.SearchResultRowInfo row : searchResult.getRows()) {
        for (SearchResultCellInfo cellInfo : row.getCells()) {
          String value = StringUtils.EMPTY;
          if (cellInfo.getValue() != null) {
            value = cellInfo.getValue();
          }
          if (CourseOfferingHistorySearchImpl.SearchResultColumns.CO_ID.equals(cellInfo.getKey())) {
            courseOfferingIds.add(value);
          } else if (CourseOfferingHistorySearchImpl.SearchResultColumns.IS_CROSS_LISTED.equals(
              cellInfo.getValue())) {
            coWrapper.setCrossListedCo(BooleanUtils.toBoolean(value));
          } else if (CourseOfferingHistorySearchImpl.SearchResultColumns.CROSS_LISTED_COURSES
              .equals(cellInfo.getKey())) {
            coWrapper.setAlternateCOCodes(Arrays.asList(StringUtils.split(value, ",")));
          }
        }
      }

      /*
       * Avoid duplicates in case there is a cross Listed
       */
      HashSet hs = new HashSet();
      hs.addAll(courseOfferingIds);
      courseOfferingIds.clear();
      courseOfferingIds.addAll(hs);

      courseOfferingInfos =
          CourseOfferingManagementUtil.getCourseOfferingService()
              .getCourseOfferingsByIds(courseOfferingIds, contextInfo);

      for (CourseOfferingInfo courseOfferingInfo : courseOfferingInfos) {
        CourseOfferingEditWrapper co = new CourseOfferingEditWrapper(courseOfferingInfo);
        TermInfo termInfo =
            CourseOfferingManagementUtil.getAcademicCalendarService()
                .getTerm(courseOfferingInfo.getTermId(), contextInfo);
        co.setTerm(termInfo);
        co.setGradingOption(
            CourseOfferingManagementUtil.getGradingOption(courseOfferingInfo.getGradingOptionId()));
        co.setAlternateCOCodes(coWrapper.getAlternateCOCodes());
        coWrapper.getExistingTermOfferings().add(co);
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
  @Override
  public StatusInfo makeTermOfficialCascaded(String termId, ContextInfo contextInfo)
      throws PermissionDeniedException, MissingParameterException, InvalidParameterException,
          OperationFailedException, DoesNotExistException {
    StatusInfo statusInfo = new StatusInfo();

    // KSENROLL-7251 Implement a new servies process ot change the state of the Academic Calendar
    // from draft to official
    TermInfo termInfo = acalService.getTerm(termId, contextInfo);
    if (AtpServiceConstants.ATP_OFFICIAL_STATE_KEY.equals(termInfo.getStateKey())) {
      // If official, then should have already cascaded.
      statusInfo.setSuccess(Boolean.TRUE);
      return statusInfo;
    }
    // Assumes state propagation not wired in yet.
    Map<String, TermInfo> termIdToTermInfoProcessed = new HashMap<String, TermInfo>();
    Map<String, TermInfo> termIdToTermInfoToBeProcessed = new HashMap<String, TermInfo>();
    Set<String> parentTermIds = new HashSet<String>();
    termIdToTermInfoToBeProcessed.put(termId, termInfo); // Put initial term
    while (!termIdToTermInfoToBeProcessed.keySet().isEmpty()) {
      String nextTermId = termIdToTermInfoToBeProcessed.keySet().iterator().next();
      TermInfo nextTerm = termIdToTermInfoToBeProcessed.get(nextTermId);
      if (termIdToTermInfoProcessed.keySet().contains(nextTermId)) {
        // Skip over ones we've seen
        continue;
      }
      // Change the state
      acalService.changeTermState(
          nextTermId, AtpServiceConstants.ATP_OFFICIAL_STATE_KEY, contextInfo);
      // Change the state of the associated exam period
      changeExamPeriodStateByTermId(
          nextTermId, AtpServiceConstants.ATP_OFFICIAL_STATE_KEY, contextInfo);
      termIdToTermInfoProcessed.put(nextTermId, nextTerm); // Add to processed
      termIdToTermInfoToBeProcessed.remove(nextTermId); // No longer needs processing, so remove
      // Now visit all parents
      List<TermInfo> terms = acalService.getContainingTerms(nextTermId, contextInfo);
      if (terms.isEmpty()) {
        // Assume only parent terms are connected to calendars
        // Given a tree like structure, there should only ever be one parentTermId in the list
        parentTermIds.add(nextTermId);
      } else {
        for (TermInfo term : terms) {
          if (!termIdToTermInfoProcessed.keySet().contains(term.getId())
              && AtpServiceConstants.ATP_DRAFT_STATE_KEY.equals(term.getStateKey())) {
            // Only add if still draft and not yet processed
            termIdToTermInfoToBeProcessed.put(term.getId(), term);
          }
        }
      }
    }
    // Access calendar
    Map<String, AcademicCalendarInfo> idToCalendar = new HashMap<String, AcademicCalendarInfo>();
    for (String parentTermId : parentTermIds) {
      List<AcademicCalendarInfo> cals =
          acalService.getAcademicCalendarsForTerm(parentTermId, contextInfo);
      for (AcademicCalendarInfo cal : cals) {
        idToCalendar.put(cal.getId(), cal);
      }
    }
    // Now iterate over all calendars and make them official
    for (Map.Entry<String, AcademicCalendarInfo> entry : idToCalendar.entrySet()) {
      if (AtpServiceConstants.ATP_DRAFT_STATE_KEY.equals(entry.getValue().getStateKey())) {
        // Only set it if it's still draft
        acalService.changeAcademicCalendarState(
            entry.getKey(), AtpServiceConstants.ATP_OFFICIAL_STATE_KEY, contextInfo);
      }
    }
    statusInfo.setSuccess(Boolean.TRUE);
    return statusInfo;
  }