View Javadoc
1   /**
2    * Copyright 2012 The Kuali Foundation Licensed under the
3    * Educational Community License, Version 2.0 (the "License"); you may
4    * not use this file except in compliance with the License. You may
5    * obtain a copy of the License at
6    *
7    * http://www.osedu.org/licenses/ECL-2.0
8    *
9    * Unless required by applicable law or agreed to in writing,
10   * software distributed under the License is distributed on an "AS IS"
11   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing
13   * permissions and limitations under the License.
14   *
15   * Created by Daniel on 6/7/12
16   */
17  package org.kuali.student.enrollment.class2.courseoffering.service.impl;
18  
19  import org.apache.commons.lang.StringUtils;
20  import org.kuali.student.enrollment.class2.courseoffering.service.CourseOfferingCodeGenerator;
21  import org.kuali.student.enrollment.courseoffering.dto.CourseOfferingInfo;
22  
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.Comparator;
26  import java.util.List;
27  import java.util.Map;
28  
29  /**
30   * This class provides a default implementation of CourseOfferingCodeGenerator
31   *
32   * @author Kuali Student Team
33   */
34  public class CourseOfferingCodeGeneratorImpl implements CourseOfferingCodeGenerator {
35  
36  
37      @Override
38      public String generateActivityOfferingCode(Map<String, Object> generationProperties) {
39  
40          List<String> existingActivityOfferings = (List<String>)generationProperties.get(CourseOfferingCodeGenerator.ACTIVITY_OFFERING_CODE_LIST_KEY);
41  
42          // If this is the first code, send back "A"
43          if (existingActivityOfferings == null || existingActivityOfferings.isEmpty()) {
44              return "A";
45          }
46  
47          List<String> aoCodes = new ArrayList<String>();
48          for (String aoCode : existingActivityOfferings) {
49              aoCodes.add(aoCode);
50          }
51  
52          return calculateNextCode(aoCodes);
53      }
54  
55      @Override
56      public String generateCourseOfferingInternalCode(List<CourseOfferingInfo> existingCourseOfferings) {
57  
58          //If this is the first code, send back "A"
59          if (existingCourseOfferings == null || existingCourseOfferings.isEmpty()) {
60              return "A";
61          }
62  
63          List<String> internalCodes = new ArrayList<String>();
64          for (CourseOfferingInfo coInfo : existingCourseOfferings) {
65              if (coInfo.getCourseNumberSuffix() != null) {
66                  internalCodes.add(coInfo.getCourseNumberSuffix());
67              }
68          }
69  
70          return calculateNextCode(internalCodes);
71      }
72  
73      public String calculateNextCode(List<String> codes){
74          // Always start with A if it's not there
75          if (!codes.contains("A")) {
76              return "A";
77          }
78  
79          // Sort the list so we can fill in gaps
80          Collections.sort(codes, new Comparator<String>() {
81              @Override
82              public int compare(String o1, String o2) {
83                  if (o1.length() == o2.length()) {
84                      return o1.compareTo(o2);
85                  } else {
86                      return o1.length() - o2.length();
87                  }
88              }
89          });
90  
91          // Fill in the gaps of letters
92          for (String s : codes) {
93              //For each existing code, find the next valid generated code and see if it exists
94              String nextCode = getNextCode(s);
95              if (!codes.contains(nextCode)) {
96                  return nextCode;
97              }
98          }
99  
100         // This should never be reached unless there is an infinite list of strings passed in
101         throw new RuntimeException("Error generating codes");
102     }
103     /**
104      * Gets the next letter of the alphabet in caps in the form A,B...Z,AA,AB...AZ,BA,BB..BZ,CA,CB....ZZ,AAA,AAB...
105      *
106      * Make sure it's public as it's needed for Test class
107      *
108      * @param source source code string
109      * @return next code
110      */
111     public String getNextCode(String source){
112         if (StringUtils.isEmpty(source)) {
113             return "A";
114         } else if (StringUtils.endsWithIgnoreCase(source, "Z")) {
115             return getNextCode(StringUtils.substringBeforeLast(source, "Z")) + "A";
116         } else {
117             char lastLetter = source.charAt(source.length() - 1);
118             return StringUtils.substringBeforeLast(source, "" + lastLetter) + ++lastLetter;
119         }
120     }
121 }