Coverage Report - org.kuali.student.enrollment.acal.AcalReferenceDataParser
 
Classes in this File Line Coverage Branch Coverage Complexity
AcalReferenceDataParser
0%
0/625
0%
0/272
7.963
 
 1  
 package org.kuali.student.enrollment.acal;
 2  
 
 3  
 
 4  
 import java.io.BufferedReader;
 5  
 import java.io.BufferedWriter;
 6  
 import java.io.DataInputStream;
 7  
 import java.io.File;
 8  
 import java.io.FileInputStream;
 9  
 import java.io.FileWriter;
 10  
 import java.io.IOException;
 11  
 import java.io.InputStreamReader;
 12  
 import java.text.SimpleDateFormat;
 13  
 import java.util.ArrayList;
 14  
 import java.util.Arrays;
 15  
 import java.util.Calendar;
 16  
 import java.util.Date;
 17  
 import java.util.GregorianCalendar;
 18  
 import java.util.HashMap;
 19  
 import java.util.HashSet;
 20  
 import java.util.LinkedHashMap;
 21  
 import java.util.List;
 22  
 import java.util.Map;
 23  
 import java.util.Set;
 24  
 
 25  0
 public class AcalReferenceDataParser {
 26  0
     private List<Map<String, String>> dataSet = new ArrayList<Map<String, String>>();
 27  0
     private Map<String, Atp> allAcals = new LinkedHashMap<String, Atp>();
 28  0
     private Map<String, Atp> allHolidayCalendars = new LinkedHashMap<String, Atp>();
 29  
 
 30  
     private static final String FALL_ATP_TYPE = "Fall";
 31  
     private static final String WINTER_ATP_TYPE = "Winter";
 32  
     private static final String SPRING_ATP_TYPE = "Spring";
 33  
     private static final String SUMMER_ATP_TYPE = "Summer";
 34  
     private static final String SESSION1_ATP_TYPE = "Session 1";
 35  
     private static final String SESSION2_ATP_TYPE = "Session 2";
 36  
     private static final String FALL_ATP_TYPE_KEY = "kuali.atp.type.Fall";
 37  
     private static final String WINTER_ATP_TYPE_KEY = "kuali.atp.type.Winter";
 38  
     private static final String SPRING_ATP_TYPE_KEY = "kuali.atp.type.Spring";
 39  
     private static final String SUMMER_ATP_TYPE_KEY = "kuali.atp.type.Summer";
 40  
     private static final String SESSION1_ATP_TYPE_KEY = "kuali.atp.type.Session1";
 41  
     private static final String SESSION2_ATP_TYPE_KEY = "kuali.atp.type.Session2";
 42  
 
 43  
     private static final String RECORD_KEY = "table_key";
 44  
 
 45  
     private static final String RECORD_FIRST_DAY = "first_day";
 46  
     private static final String RECORD_CENSUS_DAY = "tenth_day";
 47  
     private static final String RECORD_LAST_DAY_ADD = "last_day_add";
 48  
     private static final String RECORD_LAST_DAY_DROP_NOT = "last_drop_not";
 49  
     private static final String RECORD_DROP_UG = "last_drop_ug";
 50  
     private static final String RECORD_DROP_GRAD = "last_drop_grad";
 51  
     private static final String RECORD_LAST_DAY_CLASSES = "last_day_classes";
 52  
     private static final String RECORD_LAST_DAY_EXAMS = "last_day_exams";
 53  
     private static final String RECORD_GRADE_SUBMIT_DEADLINE_DATE = "grade_submit_ddln";
 54  
     private static final String RECORD_GRADE_SUBMIT_DEADLINE_TIME = "grade_submit_ddtm";
 55  
 
 56  
     private static final String RECORD_SUB_1_FIRST_DAY = "first_day";
 57  
     private static final String RECORD_SUB_1_CENSUS_DAY = "tenth_day";
 58  
     private static final String RECORD_SUB_1_LAST_DAY_ADD = "last_day_add_a";
 59  
     private static final String RECORD_SUB_1_LAST_DAY_DROP_NOT = "last_drop_not_a";
 60  
     private static final String RECORD_SUB_1_DROP_UG = "last_a_drop_ug";
 61  
     private static final String RECORD_SUB_1_DROP_GRAD = "last_a_drop_grad";
 62  
     private static final String RECORD_SUB_1_LAST_DAY_CLASSES = "last_day_class_a";
 63  
     private static final String RECORD_SUB_1_GRADE_SUBMIT_DEADLINE_DATE = "grade_submit_ddln";
 64  
     private static final String RECORD_SUB_1_GRADE_SUBMIT_DEADLINE_TIME = "grade_submit_ddtm";
 65  
 
 66  
     private static final String RECORD_SUB_2_FIRST_DAY = "first_day_b";
 67  
     private static final String RECORD_SUB_2_LAST_DAY_ADD = "last_day_add_b";
 68  
     private static final String RECORD_SUB_2_LAST_DAY_DROP_NOT = "last_drop_not_b";
 69  
     private static final String RECORD_SUB_2_DROP_UG = "last_b_drop_ug";
 70  
     private static final String RECORD_SUB_2_DROP_GRAD = "last_b_drop_grad";
 71  
     private static final String RECORD_SUB_2_LAST_DAY_CLASSES = "last_day_classes";
 72  
     private static final String RECORD_SUB_2_LAST_DAY_EXAMS = "last_day_exams";
 73  
     private static final String RECORD_SUB_2_GRADE_SUBMIT_DEADLINE_DATE = "grade_submit_ddln";
 74  
     private static final String RECORD_SUB_2_GRADE_SUBMIT_DEADLINE_TIME = "grade_submit_ddtm";
 75  
 
 76  
 
 77  
     private static final String KEYDATE_INSTRUCTIONAL_PERIOD_KEY = "kuali.atp.milestone.InstructionalPeriod";
 78  
     private static final String KEYDATE_FINAL_EXAM_PERIOD_KEY = "kuali.atp.milestone.FinalExamPeriod";
 79  
     private static final String KEYDATE_CENSUS_KEY = "kuali.atp.milestone.FinancialAidCensus";
 80  
     private static final String KEYDATE_DROP_DEADLINE_WITHOUT_RECORD_KEY = "kuali.atp.milestone.DropDeadlineWithoutRecord";
 81  
     private static final String KEYDATE_DROP_DEADLINE_KEY = "kuali.atp.milestone.DropDate";
 82  
     private static final String KEYDATE_GRADES_DUE_KEY = "kuali.atp.milestone.GradesDue";
 83  
     private static final String KEYDATE_COURSE_SELECTION_PERIOD_END_KEY = "kuali.atp.milestone.CourseSelectionPeriodEnd";
 84  
 
 85  0
     private static final Map<String, String> keydateNames = new LinkedHashMap<String, String>();
 86  
     {
 87  0
         keydateNames.put(KEYDATE_INSTRUCTIONAL_PERIOD_KEY, "Instructional Period");
 88  0
         keydateNames.put(KEYDATE_COURSE_SELECTION_PERIOD_END_KEY, "Course Selection Period End");
 89  0
         keydateNames.put(KEYDATE_CENSUS_KEY, "Census");
 90  0
         keydateNames.put(KEYDATE_DROP_DEADLINE_WITHOUT_RECORD_KEY, "Drop Deadline Without Record");
 91  0
         keydateNames.put(KEYDATE_DROP_DEADLINE_KEY, "Drop Deadline");
 92  0
         keydateNames.put(KEYDATE_FINAL_EXAM_PERIOD_KEY, "Final Exam Period");
 93  0
         keydateNames.put(KEYDATE_GRADES_DUE_KEY, "Grades Due");
 94  
     }
 95  
 
 96  0
     private static final Set<String> relativeToInstructionPeriod = new HashSet<String>();
 97  
     {
 98  0
         relativeToInstructionPeriod.add(KEYDATE_CENSUS_KEY);
 99  0
         relativeToInstructionPeriod.add(KEYDATE_COURSE_SELECTION_PERIOD_END_KEY);
 100  0
         relativeToInstructionPeriod.add(KEYDATE_FINAL_EXAM_PERIOD_KEY);
 101  
     }
 102  
 
 103  
     private static final String HOLIDAY_NEWYEARSDAY_KEY = "kuali.atp.milestone.NewYearsDay";
 104  
     private static final String HOLIDAY_MLKDAY_KEY = "kuali.atp.milestone.MLKDay";
 105  
     private static final String HOLIDAY_PRESIDENTSDAY_KEY = "kuali.atp.milestone.PresidentsDay";
 106  
     private static final String HOLIDAY_MEMORIALDAY_KEY = "kuali.atp.milestone.MemorialDay";
 107  
     private static final String HOLIDAY_INDEPENDENCEDAY_KEY = "kuali.atp.milestone.IndependenceDay";
 108  
     private static final String HOLIDAY_LABORDAY_KEY = "kuali.atp.milestone.LaborDay";
 109  
     private static final String HOLIDAY_VETERANSDAY_KEY = "kuali.atp.milestone.VeteransDay";
 110  
     private static final String HOLIDAY_THANKSGIVINGBREAK_KEY = "kuali.atp.milestone.ThanksgivingBreak";
 111  
     private static final String HOLIDAY_CHRISTMAS_KEY = "kuali.atp.milestone.Christmas";
 112  
     private static final String HOLIDAY_OTHERNONINSTRUCTIONALHOLIDAY_KEY = "kuali.atp.milestone.OtherNonInstructionalHoliday";
 113  
     private static final String HOLIDAY_OTHERNONINSTRUCTIONALDAY_KEY = "kuali.atp.milestone.OtherNonInstructionalDay";
 114  
 
 115  
     private static final String HOLIDAY_NEWYEARSDAYOBSERVED_KEY = "kuali.atp.milestone.NewYearsDayObserved";
 116  
     private static final String HOLIDAY_INDEPENDENCEDAYOBSERVED_KEY = "kuali.atp.milestone.IndependenceDayObserved";
 117  
     private static final String HOLIDAY_VETERANSDAYOBSERVED_KEY = "kuali.atp.milestone.VeteransDayObserved";
 118  
     private static final String HOLIDAY_CHRISTMASOBSERVED_KEY = "kuali.atp.milestone.ChristmasObserved";
 119  
 
 120  
 
 121  0
     private static final Map<String, String> holidayNames = new LinkedHashMap<String, String>();
 122  
     {
 123  0
         holidayNames.put(HOLIDAY_NEWYEARSDAY_KEY, "New Years Day");
 124  0
         holidayNames.put(HOLIDAY_MLKDAY_KEY, "Martin Luther King, Jr. Day");
 125  0
         holidayNames.put(HOLIDAY_PRESIDENTSDAY_KEY, "Presidents Day");
 126  0
         holidayNames.put(HOLIDAY_MEMORIALDAY_KEY, "Memorial Day");
 127  0
         holidayNames.put(HOLIDAY_INDEPENDENCEDAY_KEY, "Independence Day");
 128  0
         holidayNames.put(HOLIDAY_LABORDAY_KEY, "Labor Day");
 129  0
         holidayNames.put(HOLIDAY_VETERANSDAY_KEY, "Veterans Day");
 130  0
         holidayNames.put(HOLIDAY_THANKSGIVINGBREAK_KEY, "Thanksgiving Break");
 131  0
         holidayNames.put(HOLIDAY_CHRISTMAS_KEY, "Christmas Day");
 132  0
         holidayNames.put(HOLIDAY_NEWYEARSDAYOBSERVED_KEY, "New Years Day Observed");
 133  0
         holidayNames.put(HOLIDAY_INDEPENDENCEDAYOBSERVED_KEY, "Independence Day Observed");
 134  0
         holidayNames.put(HOLIDAY_VETERANSDAYOBSERVED_KEY, "Veterans Day Observed");
 135  0
         holidayNames.put(HOLIDAY_CHRISTMASOBSERVED_KEY, "Christmas Day Observed");
 136  
     }
 137  
 
 138  
 
 139  0
     private static final Map<String, String> observedHolidays = new LinkedHashMap<String, String>();
 140  
     {
 141  0
         observedHolidays.put(HOLIDAY_NEWYEARSDAY_KEY, HOLIDAY_NEWYEARSDAYOBSERVED_KEY);
 142  0
         observedHolidays.put(HOLIDAY_MLKDAY_KEY, null);
 143  0
         observedHolidays.put(HOLIDAY_PRESIDENTSDAY_KEY, null);
 144  0
         observedHolidays.put(HOLIDAY_MEMORIALDAY_KEY, null);
 145  0
         observedHolidays.put(HOLIDAY_INDEPENDENCEDAY_KEY, HOLIDAY_INDEPENDENCEDAYOBSERVED_KEY);
 146  0
         observedHolidays.put(HOLIDAY_LABORDAY_KEY, null);
 147  0
         observedHolidays.put(HOLIDAY_VETERANSDAY_KEY, HOLIDAY_VETERANSDAYOBSERVED_KEY);
 148  0
         observedHolidays.put(HOLIDAY_THANKSGIVINGBREAK_KEY, null);
 149  0
         observedHolidays.put(HOLIDAY_CHRISTMAS_KEY, HOLIDAY_CHRISTMASOBSERVED_KEY);
 150  0
     }
 151  
 
 152  
 
 153  
     public static void main(String[] args) {
 154  0
         AcalReferenceDataParser parser = new AcalReferenceDataParser();
 155  0
         File inputFile = new File("/kuali/temp/Reference Insitution Data_ Terms - Data.tsv");
 156  0
         File outputFile = new File("/kuali/temp/output.sql");
 157  
 
 158  0
         parser.loadDataSet(inputFile);
 159  
 
 160  0
         int[] records = new int[] {35};
 161  0
         for (int record : records) {
 162  
 //            System.out.println("Record " + record + ": " + parser.dataSet.get(record));
 163  
         }
 164  
 
 165  0
         parser.parse();
 166  0
         SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
 167  0
         for (Atp acal : parser.allAcals.values()) {
 168  0
             int startYear = Integer.parseInt(yearFormat.format(acal.getStartDate()));
 169  0
             int endYear = Integer.parseInt(yearFormat.format(acal.getEndDate()));
 170  0
             for (int year = startYear; year <= endYear; year++) {
 171  0
                 Atp holiCal = parser.getHolidayCalendar(Integer.toString(year));
 172  0
                 acal.getHolidayCalendars().add(holiCal);
 173  
             }
 174  0
         }
 175  
 
 176  0
         parser.alterEndDates(new ArrayList<Atp>(parser.allAcals.values()));
 177  
 
 178  
 //        System.out.println(parser.getReport(Arrays.asList(new String[]{"1978","2011"})));
 179  
 //        System.out.println(parser.getReport(Arrays.asList(new String[]{"1979"})));
 180  
 
 181  0
         SqlGenerator sqlGenerator = new SqlGenerator();
 182  0
         StringBuilder sql = new StringBuilder();
 183  0
         for (Atp acal : parser.allAcals.values()) {
 184  0
             sqlGenerator.getSqlForAcademicCalendar(sql, acal);
 185  
         }
 186  0
         parser.writeToFile(outputFile, sql.toString());
 187  
 
 188  
 
 189  0
     }
 190  
 
 191  
     private void parse() {
 192  0
         int i = 0;
 193  0
         for (Map<String, String> record : dataSet) {
 194  0
             i++;
 195  
 //            System.out.println("parsing record [" + i + "]");
 196  
             // create term
 197  0
             Atp term = createTerm(record);
 198  
 
 199  
             // add term to acal
 200  0
             Atp acal = getAcalForTermId(record.get(RECORD_KEY));
 201  0
             List<Atp> terms = acal.getTerms();
 202  0
             terms.add(term);
 203  0
         }
 204  0
         for (Atp acal : allAcals.values()) {
 205  0
             List<Atp> terms = acal.getTerms();
 206  0
             Atp firstTerm = terms.get(0);
 207  0
             Atp lastTerm = terms.get(terms.size() - 1);
 208  0
             acal.setStartDate(firstTerm.getStartDate());
 209  0
             acal.setEndDate(lastTerm.getEndDate());
 210  0
         }
 211  0
     }
 212  
 
 213  
     private Atp createTerm(Map<String, String> record) {
 214  0
         String recordKey = record.get(RECORD_KEY);
 215  0
         int year = getYear(recordKey);
 216  0
         int quarter = getQuarter(recordKey);
 217  0
         String termName = getAtpTypeName(quarter);
 218  
 
 219  0
         Atp term = new Atp();
 220  
 
 221  0
         term.setId((year + "" + termName).replaceAll(" ", "").toUpperCase());
 222  0
         term.setName(termName + " " + year);
 223  0
         term.setDescriptionPlain(termName + " " + year);
 224  0
         term.setDescriptionFormatted("<p>" + term.getDescriptionPlain() + "</p>");
 225  0
         term.setStartDate(parseDate(record.get(RECORD_FIRST_DAY)));
 226  0
         Date termEnd = parseDate(record.get(RECORD_LAST_DAY_EXAMS));
 227  0
         term.setEndDate(termEnd);
 228  0
         term.setType(getAtpTypeKey(quarter));
 229  0
         term.setAtpCode(termName.substring(0,3).toUpperCase() + year);
 230  
 
 231  0
         for (String keydateType : keydateNames.keySet()) {
 232  0
             term.addMilestone(getKeydateForTerm(keydateType, record));
 233  
         }
 234  
 
 235  
         // create subterm 1
 236  0
         if (3 == quarter) {
 237  0
             String subtermName = SESSION1_ATP_TYPE;
 238  0
             Atp subterm = new Atp();
 239  
 
 240  0
             subterm.setId((year + "" + subtermName).replaceAll(" ", "").toUpperCase());
 241  0
             subterm.setName(subtermName + " " + year);
 242  0
             subterm.setDescriptionPlain(subtermName + " " + year);
 243  0
             subterm.setDescriptionFormatted("<p>" + subterm.getDescriptionPlain() + "</p>");
 244  0
             subterm.setStartDate(parseDate(record.get(RECORD_SUB_1_FIRST_DAY)));
 245  0
             Date subTermEnd = parseDate(record.get(RECORD_SUB_1_LAST_DAY_CLASSES));
 246  0
             subterm.setEndDate(subTermEnd);
 247  0
             subterm.setType(SESSION1_ATP_TYPE_KEY);
 248  0
             subterm.setAtpCode(termName.substring(0, 3).toUpperCase() + "A" + year);
 249  
 
 250  0
             for (String keydateType : keydateNames.keySet()) {
 251  0
                 subterm.addMilestone(getKeydateForSubterm1(keydateType, record));
 252  
             }
 253  
 
 254  0
             term.getTerms().add(subterm);
 255  
         }
 256  
 
 257  
 
 258  
 
 259  
         // create subterm 2
 260  0
         if (3 == quarter) {
 261  0
             String subtermName = SESSION2_ATP_TYPE;
 262  0
             Atp subterm = new Atp();
 263  
 
 264  0
             subterm.setId((year + "" + subtermName).replaceAll(" ", "").toUpperCase());
 265  0
             subterm.setName(subtermName + " " + year);
 266  0
             subterm.setDescriptionPlain(subtermName + " " + year);
 267  0
             subterm.setDescriptionFormatted("<p>" + subterm.getDescriptionPlain() + "</p>");
 268  0
             subterm.setStartDate(parseDate(record.get(RECORD_SUB_2_FIRST_DAY)));
 269  0
             Date subTermEnd = parseDate(record.get(RECORD_SUB_2_LAST_DAY_EXAMS));
 270  0
             subterm.setEndDate(subTermEnd);
 271  0
             subterm.setType(SESSION2_ATP_TYPE_KEY);
 272  0
             subterm.setAtpCode(termName.substring(0, 3).toUpperCase() + "A" + year);
 273  
 
 274  0
             for (String keydateType : keydateNames.keySet()) {
 275  0
                 subterm.addMilestone(getKeydateForSubterm2(keydateType, record));
 276  
             }
 277  
 
 278  0
             term.getTerms().add(subterm);
 279  
         }
 280  
 
 281  0
         addMilestoneRelations(term);
 282  0
         return term;
 283  
     }
 284  
 
 285  
     private Milestone getKeydateForTerm(String keydateType, Map<String, String> record) {
 286  0
         String recordKey = record.get(RECORD_KEY);
 287  0
         int termYear = getYear(recordKey);
 288  0
         String termName = getAtpTypeName(getQuarter(recordKey));
 289  0
         String keydateName = keydateNames.get(keydateType);
 290  
 
 291  0
         String id = (termYear + termName + keydateName).replaceAll(" ", "").toUpperCase();
 292  0
         String name = termName + " " + termYear + " " + keydateName;
 293  0
         String descriptionPlain = keydateName + " for " + termName + " " + termYear;
 294  0
         String descriptionFormatted = "<p>" + descriptionPlain + "<p>";
 295  
 
 296  0
         Date start = null;
 297  0
         Date end = null;
 298  
         boolean allDay;
 299  
         boolean range;
 300  
 
 301  0
         if (KEYDATE_COURSE_SELECTION_PERIOD_END_KEY.equals(keydateType)) {
 302  0
             start = parseDate(record.get(RECORD_LAST_DAY_ADD));
 303  0
             if (start == null) {
 304  
 //                throw new RuntimeException(recordKey);
 305  0
                 return null; // TODO Some records don't have selection period end. Should this be calculated?
 306  
             }
 307  0
             allDay = true;
 308  0
             range = false;
 309  
 
 310  0
         } else if (KEYDATE_INSTRUCTIONAL_PERIOD_KEY.equals(keydateType)) {
 311  0
             start = parseDate(record.get(RECORD_FIRST_DAY));
 312  0
             if (start == null) {
 313  0
                 throw new RuntimeException(recordKey);
 314  
             }
 315  0
             end = parseDate(record.get(RECORD_LAST_DAY_CLASSES));
 316  0
             if (end == null) {
 317  0
                 throw new RuntimeException(recordKey);
 318  
             }
 319  0
             allDay = true;
 320  0
             range = true;
 321  
 
 322  0
         } else if (KEYDATE_FINAL_EXAM_PERIOD_KEY.equals(keydateType)) {
 323  0
             if (3 == getQuarter(record.get(RECORD_KEY))) {
 324  0
                 return null;
 325  
             }
 326  0
             start = parseDate(record.get(RECORD_LAST_DAY_CLASSES));
 327  0
             if (start == null) {
 328  0
                 throw new RuntimeException(recordKey);
 329  
             }
 330  0
             start = addToDate(start, Calendar.DATE, 1);
 331  0
             end = parseDate(record.get(RECORD_LAST_DAY_EXAMS));
 332  0
             if (end == null) {
 333  0
                 throw new RuntimeException(recordKey);
 334  
             }
 335  0
             allDay    = true;
 336  0
             range = true;
 337  
 
 338  0
         } else if (KEYDATE_CENSUS_KEY.equals(keydateType)) {
 339  0
             start = parseDate(record.get(RECORD_CENSUS_DAY));
 340  0
             if (start == null) {
 341  0
                 throw new RuntimeException(recordKey);
 342  
             }
 343  0
             allDay = true;
 344  0
             range = false;
 345  
 
 346  0
         } else if (KEYDATE_DROP_DEADLINE_WITHOUT_RECORD_KEY.equals(keydateType)) {
 347  0
             start = parseDate(record.get(RECORD_LAST_DAY_DROP_NOT));
 348  0
             if (start == null) {
 349  0
                 throw new RuntimeException(recordKey);
 350  
             }
 351  0
             allDay = true;
 352  0
             range = false;
 353  
 
 354  0
         } else if (KEYDATE_DROP_DEADLINE_KEY.equals(keydateType)) {
 355  0
             start = parseDate(record.get(RECORD_DROP_UG));
 356  0
             if (start == null) {
 357  0
                 throw new RuntimeException(recordKey);
 358  
             }
 359  0
             allDay = true;
 360  0
             range = false;
 361  
 
 362  0
         } else if (KEYDATE_GRADES_DUE_KEY.equals(keydateType)) {
 363  0
             start = parseDate(record.get(RECORD_GRADE_SUBMIT_DEADLINE_DATE), record.get(RECORD_GRADE_SUBMIT_DEADLINE_TIME));
 364  0
             if (start == null) {
 365  0
                 return null;
 366  
             }
 367  0
             allDay = false;
 368  0
             range = false;
 369  
 
 370  
         } else {
 371  0
             throw new RuntimeException(recordKey + " Keydate type not known.");
 372  
         }
 373  
 
 374  0
         Milestone milestone = new Milestone(id, name, keydateType, descriptionPlain, descriptionFormatted, start, end, allDay, range);
 375  0
         return milestone;
 376  
     }
 377  
 
 378  
     private Milestone getKeydateForSubterm1(String keydateType, Map<String, String> record) {
 379  0
         String recordKey = record.get(RECORD_KEY);
 380  0
         int termYear = getYear(recordKey);
 381  0
         String termName = SESSION1_ATP_TYPE;
 382  0
         String keydateName = keydateNames.get(keydateType);
 383  
 
 384  0
         String id = (termYear + termName + keydateName).replaceAll(" ", "").toUpperCase();
 385  0
         String name = termName + " " + termYear + " " + keydateName;
 386  0
         String descriptionPlain = keydateName + " for " + termName + " " + termYear;
 387  0
         String descriptionFormatted = "<p>" + descriptionPlain + "<p>";
 388  
 
 389  0
         Date start = null;
 390  0
         Date end = null;
 391  
         boolean allDay;
 392  
         boolean range;
 393  
 
 394  0
         if (KEYDATE_COURSE_SELECTION_PERIOD_END_KEY.equals(keydateType)) {
 395  0
             start = parseDate(record.get(RECORD_SUB_1_LAST_DAY_ADD));
 396  0
             if (start == null) {
 397  
 //                throw new RuntimeException(recordKey);
 398  0
                 return null;  // TODO Some records don't have selection period end
 399  
             }
 400  0
             allDay = true;
 401  0
             range = false;
 402  
 
 403  0
         } else if (KEYDATE_INSTRUCTIONAL_PERIOD_KEY.equals(keydateType)) {
 404  0
             start = parseDate(record.get(RECORD_SUB_1_FIRST_DAY));
 405  0
             if (start == null) {
 406  0
                 throw new RuntimeException(recordKey);
 407  
             }
 408  0
             end = parseDate(record.get(RECORD_SUB_1_LAST_DAY_CLASSES));
 409  0
             if (end == null) {
 410  0
                 end = parseDate(record.get(RECORD_SUB_2_FIRST_DAY));
 411  0
                 end = addToDate(end, Calendar.DATE, -1);
 412  0
                 if (end == null) {
 413  0
                     throw new RuntimeException(record.get(RECORD_KEY));
 414  
                 }
 415  
             }
 416  0
             allDay = true;
 417  0
             range = true;
 418  
 
 419  0
         } else if (KEYDATE_FINAL_EXAM_PERIOD_KEY.equals(keydateType)) {
 420  0
             return null;
 421  
 
 422  0
         } else if (KEYDATE_CENSUS_KEY.equals(keydateType)) {
 423  0
             start = parseDate(record.get(RECORD_SUB_1_CENSUS_DAY));
 424  0
             if (start == null) {
 425  0
                 throw new RuntimeException(recordKey);
 426  
             }
 427  0
             allDay = true;
 428  0
             range = false;
 429  
 
 430  0
         } else if (KEYDATE_DROP_DEADLINE_WITHOUT_RECORD_KEY.equals(keydateType)) {
 431  0
             start = parseDate(record.get(RECORD_SUB_1_LAST_DAY_DROP_NOT));
 432  0
             if (start == null) {
 433  0
                 throw new RuntimeException(recordKey);
 434  
             }
 435  0
             allDay = true;
 436  0
             range = false;
 437  
 
 438  0
         } else if (KEYDATE_DROP_DEADLINE_KEY.equals(keydateType)) {
 439  0
             start = parseDate(record.get(RECORD_SUB_1_DROP_UG));
 440  0
             if (start == null) {
 441  0
                 throw new RuntimeException(recordKey);
 442  
             }
 443  0
             allDay = true;
 444  0
             range = false;
 445  
 
 446  0
         } else if (KEYDATE_GRADES_DUE_KEY.equals(keydateType)) {
 447  0
             start = parseDate(record.get(RECORD_SUB_1_GRADE_SUBMIT_DEADLINE_DATE), record.get(RECORD_SUB_1_GRADE_SUBMIT_DEADLINE_TIME));
 448  0
             if (start == null) {
 449  0
                 return null;
 450  
             }
 451  0
             allDay = false;
 452  0
             range = false;
 453  
 
 454  
         } else {
 455  0
             throw new RuntimeException(recordKey + " Keydate type not known.");
 456  
         }
 457  
 
 458  0
         Milestone milestone = new Milestone(id, name, keydateType, descriptionPlain, descriptionFormatted, start, end, allDay, range);
 459  0
         return milestone;
 460  
     }
 461  
 
 462  
     private Milestone getKeydateForSubterm2(String keydateType, Map<String, String> record) {
 463  0
         String recordKey = record.get(RECORD_KEY);
 464  0
         int termYear = getYear(recordKey);
 465  0
         String termName = SESSION2_ATP_TYPE;
 466  0
         String keydateName = keydateNames.get(keydateType);
 467  
 
 468  0
         String id = (termYear + termName + keydateName).replaceAll(" ", "").toUpperCase();
 469  0
         String name = termName + " " + termYear + " " + keydateName;
 470  0
         String descriptionPlain = keydateName + " for " + termName + " " + termYear;
 471  0
         String descriptionFormatted = "<p>" + descriptionPlain + "<p>";
 472  
 
 473  0
         Date start = null;
 474  0
         Date end = null;
 475  
         boolean allDay;
 476  
         boolean range;
 477  
 
 478  0
         if (KEYDATE_COURSE_SELECTION_PERIOD_END_KEY.equals(keydateType)) {
 479  0
             start = parseDate(record.get(RECORD_SUB_2_LAST_DAY_ADD));
 480  0
             if (start == null) {
 481  
 //                throw new RuntimeException(recordKey);
 482  0
                 return null; // TODO Some records don't have selection period end
 483  
             }
 484  0
             allDay = true;
 485  0
             range = false;
 486  
 
 487  0
         } else if (KEYDATE_INSTRUCTIONAL_PERIOD_KEY.equals(keydateType)) {
 488  0
             start = parseDate(record.get(RECORD_SUB_2_FIRST_DAY));
 489  0
             if (start == null) {
 490  0
                 throw new RuntimeException(recordKey);
 491  
             }
 492  0
             end = parseDate(record.get(RECORD_SUB_2_LAST_DAY_CLASSES));
 493  0
             if (end == null) {
 494  0
                 throw new RuntimeException(recordKey);
 495  
             }
 496  0
             allDay = true;
 497  0
             range = true;
 498  
 
 499  0
         } else if (KEYDATE_FINAL_EXAM_PERIOD_KEY.equals(keydateType)) {
 500  0
             return null;
 501  
 
 502  0
         } else if (KEYDATE_CENSUS_KEY.equals(keydateType)) {
 503  0
             return null;
 504  
 
 505  0
         } else if (KEYDATE_DROP_DEADLINE_WITHOUT_RECORD_KEY.equals(keydateType)) {
 506  0
             start = parseDate(record.get(RECORD_SUB_2_LAST_DAY_DROP_NOT));
 507  0
             if (start == null) {
 508  0
                 throw new RuntimeException(recordKey);
 509  
             }
 510  0
             allDay = true;
 511  0
             range = false;
 512  
 
 513  0
         } else if (KEYDATE_DROP_DEADLINE_KEY.equals(keydateType)) {
 514  0
             start = parseDate(record.get(RECORD_SUB_2_DROP_UG));
 515  0
             if (start == null) {
 516  0
                 throw new RuntimeException(recordKey);
 517  
             }
 518  0
             allDay = true;
 519  0
             range = false;
 520  
 
 521  0
         } else if (KEYDATE_GRADES_DUE_KEY.equals(keydateType)) {
 522  0
             start = parseDate(record.get(RECORD_SUB_2_GRADE_SUBMIT_DEADLINE_DATE), record.get(RECORD_SUB_2_GRADE_SUBMIT_DEADLINE_TIME));
 523  0
             if (start == null) {
 524  0
                 return null;
 525  
             }
 526  0
             allDay = false;
 527  0
             range = false;
 528  
 
 529  
         } else {
 530  0
             throw new RuntimeException("Keydate type not known.");
 531  
         }
 532  
 
 533  0
         Milestone milestone = new Milestone(id, name, keydateType, descriptionPlain, descriptionFormatted, start, end, allDay, range);
 534  0
         return milestone;
 535  
     }
 536  
 
 537  
     private Date addToDate(Date date, int field, int amount) {
 538  0
         if (date == null) {
 539  0
             return null;
 540  
         }
 541  0
         Calendar cal = new GregorianCalendar();
 542  0
         cal.setTime(date);
 543  0
         cal.add(field, amount);
 544  0
         return cal.getTime();
 545  
     }
 546  
     private Date parseDate(String dateString) {
 547  0
         Date date = null;
 548  0
         if (dateString != null && dateString.length() > 0) {
 549  0
             String[] dateSegments = dateString.split("/");
 550  0
             int day = Integer.parseInt(dateSegments[1]);
 551  0
             int month = Integer.parseInt(dateSegments[0]) - 1;
 552  0
             int year = Integer.parseInt(dateSegments[2]);
 553  0
             if (year > 99) {
 554  0
                 throw new RuntimeException("Unexpected year format: " + dateString);
 555  
             }
 556  0
             if (year > 20) {
 557  0
                 year = 1900 + year;
 558  
             } else {
 559  0
                 year = 2000 + year;
 560  
             }
 561  0
             Calendar cal = new GregorianCalendar(year, month, day);
 562  0
             date = cal.getTime();
 563  
         }
 564  0
         return date;
 565  
     }
 566  
 
 567  
     private Date parseDate(String dateString, String timeString) {
 568  0
         Date date = null;
 569  0
         if (dateString != null && dateString.length() > 0 && timeString != null && timeString.length() > 0) {
 570  0
             while (timeString.length() < 6) {
 571  0
                 timeString = "0" + timeString;
 572  
             }
 573  
 
 574  0
             int hour = Integer.parseInt(timeString.substring(0,2));
 575  0
             int minute = Integer.parseInt(timeString.substring(2,4));
 576  0
             int second = Integer.parseInt(timeString.substring(4,6));
 577  
 
 578  0
             Calendar cal = new GregorianCalendar();
 579  0
             cal.setTime(parseDate(dateString));
 580  0
             cal.set(Calendar.HOUR_OF_DAY, hour);
 581  0
             cal.set(Calendar.MINUTE, minute);
 582  0
             cal.set(Calendar.SECOND, second);
 583  0
             date = cal.getTime();
 584  
         }
 585  0
         return date;
 586  
     }
 587  
 
 588  
     int getQuarter(String recordKey) {
 589  0
         if (recordKey.length() != 5) {
 590  0
             throw new IllegalArgumentException("recordKey: " + recordKey);
 591  
         }
 592  0
         int quater = Integer.parseInt(recordKey.substring(4, 5));
 593  0
         return quater;
 594  
     }
 595  
     int getYear(String recordKey) {
 596  0
         if (recordKey.length() != 5) {
 597  0
             throw new IllegalArgumentException("recordKey: " + recordKey);
 598  
         }
 599  0
         int year = Integer.parseInt(recordKey.substring(0, 4));
 600  0
         return year;
 601  
     }
 602  
     String getAtpTypeName(int quarter) {
 603  0
         String name = null;
 604  0
         switch (quarter) {
 605  0
             case 1: name = WINTER_ATP_TYPE; break;
 606  0
             case 2: name = SPRING_ATP_TYPE; break;
 607  0
             case 3: name = SUMMER_ATP_TYPE; break;
 608  0
             case 4: name = FALL_ATP_TYPE; break;
 609  
         }
 610  0
         return name;
 611  
     }
 612  
     String getAtpTypeKey(int quarter) {
 613  0
         String name = null;
 614  0
         switch (quarter) {
 615  0
             case 1: name = WINTER_ATP_TYPE_KEY; break;
 616  0
             case 2: name = SPRING_ATP_TYPE_KEY; break;
 617  0
             case 3: name = SUMMER_ATP_TYPE_KEY; break;
 618  0
             case 4: name = FALL_ATP_TYPE_KEY; break;
 619  
         }
 620  0
         return name;
 621  
     }
 622  
 
 623  
     private Atp getAcalForTermId(String termId) {
 624  0
         if (termId.length() != 5) {
 625  0
             throw new IllegalArgumentException("termId: " + termId);
 626  
         }
 627  0
         int year = getYear(termId);
 628  0
         int quater = getQuarter(termId);
 629  0
         if (quater < 3) {
 630  0
             year--;
 631  
         }
 632  0
         Atp acal = allAcals.get(String.valueOf(year));
 633  0
         if (acal == null) {
 634  0
             acal = new Atp();
 635  0
             allAcals.put(String.valueOf(year), acal);
 636  0
             acal.setId(year + "" + (year + 1) + "ACADEMICCALENDAR");
 637  0
             acal.setType("kuali.atp.type.AcademicCalendar");
 638  0
             acal.setAdminOrgId("102"); // Registrar code
 639  0
             acal.setAtpCode("ACAL"+year);
 640  0
             acal.setName(year + "/" + (year + 1) + " Academic Calendar");
 641  0
             acal.setDescriptionPlain("Academic Calendar for " + year + "/" + (year + 1));
 642  0
             acal.setDescriptionFormatted("<p>" + acal.getDescriptionPlain() + "</p>");
 643  
         }
 644  0
         return acal;
 645  
     }
 646  
 
 647  
     private void loadDataSet(File file) {
 648  0
         DataInputStream in = null;
 649  
         try {
 650  0
             FileInputStream fstream = new FileInputStream(file);
 651  0
             in = new DataInputStream(fstream);
 652  0
             BufferedReader br = new BufferedReader(new InputStreamReader(in));
 653  
 
 654  0
             String[] headers = null;
 655  
             String line;
 656  0
             int row = 0;
 657  0
             while ((line = br.readLine()) != null) {
 658  0
                 String[] columnValues = line.split("\t");
 659  0
                 if (0 == row) {
 660  0
                     headers = columnValues;
 661  
                 } else {
 662  0
                     Map<String, String> map = new LinkedHashMap<String, String>();
 663  0
                     for (int i = 0; i < headers.length; i++) {
 664  0
                         map.put(headers[i], columnValues[i]);
 665  
                     }
 666  0
                     dataSet.add(map);
 667  
                 }
 668  0
                 row++;
 669  0
             }
 670  0
         } catch (Exception e) {
 671  0
             e.printStackTrace();
 672  
         } finally {
 673  0
             if (in != null) {
 674  
                 try {
 675  0
                     in.close();
 676  0
                 } catch (IOException e) {
 677  0
                     e.printStackTrace();
 678  0
                 }
 679  
             }
 680  
         }
 681  0
     }
 682  
 
 683  
     private void writeToFile(File file, String content) {
 684  
         try{
 685  0
             FileWriter fstream = new FileWriter(file);
 686  0
             BufferedWriter out = new BufferedWriter(fstream);
 687  0
             out.write(content);
 688  0
             out.close();
 689  0
         } catch (Exception e){
 690  0
             e.printStackTrace();
 691  0
         }
 692  0
     }
 693  
 
 694  
     public String getReport(List<String> years) {
 695  0
         StringBuilder builder = new StringBuilder();
 696  0
         String tab = "\t";
 697  0
         String line = "\n";
 698  
 
 699  
 
 700  0
         for (String year : years) {
 701  0
             Atp acal = allAcals.get(year);
 702  0
             addReportLineForAtp(builder, acal);
 703  0
             for (Atp holiCal : acal.getHolidayCalendars()) {
 704  0
                 addReportLineForAtp(builder, holiCal);
 705  0
                 for (Milestone holiday : holiCal.getMilestones()) {
 706  0
                     addReportLineForMilestone(builder, holiday);
 707  
                 }
 708  
             }
 709  0
             for (Atp term : acal.getTerms()) {
 710  0
                 addReportLineForAtp(builder, term);
 711  0
                 for (Milestone milestone : term.getMilestones()) {
 712  0
                     addReportLineForMilestone(builder, milestone);
 713  
                 }
 714  0
                 for (Atp subTerm : term.getTerms()) {
 715  0
                     addReportLineForAtp(builder, subTerm);
 716  0
                     for (Milestone milestone : subTerm.getMilestones()) {
 717  0
                         addReportLineForMilestone(builder, milestone);
 718  
                     }
 719  
 
 720  
                 }
 721  
             }
 722  0
         }
 723  0
         return builder.toString();
 724  
     }
 725  
 
 726  
 
 727  
     private StringBuilder addReportLineForAtp(StringBuilder builder, Atp atp) {
 728  0
         String tab = "\t";
 729  0
         String line = "\n";
 730  
 
 731  0
         if (builder == null) {
 732  0
             builder = new StringBuilder();
 733  
         }
 734  
 
 735  0
         builder.append(atp.getName()).append(tab);
 736  
 
 737  0
         Date startDate = atp.getStartDate();
 738  0
         builder.append(getDate(startDate)).append(tab);
 739  0
         builder.append(tab);
 740  
 
 741  0
         Date endDate = atp.getEndDate();
 742  0
         builder.append(getDate(endDate)).append(tab);
 743  0
         builder.append(tab);
 744  
 
 745  0
         builder.append(atp.getType());
 746  
 
 747  0
         builder.append(line);
 748  0
         return builder;
 749  
     }
 750  
 
 751  
     private StringBuilder addReportLineForMilestone(StringBuilder builder, Milestone milestone) {
 752  0
         String tab = "\t";
 753  0
         String line = "\n";
 754  
 
 755  0
         if (builder == null) {
 756  0
             builder = new StringBuilder();
 757  
         }
 758  
 
 759  0
         builder.append(milestone.getName()).append(tab);
 760  
 
 761  0
         Date startDate = milestone.getStartDate();
 762  0
         builder.append(getDate(startDate)).append(tab);
 763  0
         if (!milestone.isAllDay()) {
 764  0
             builder.append(getTime(startDate));
 765  
         }
 766  0
         builder.append(tab);
 767  
 
 768  0
         Date endDate = milestone.getEndDate();
 769  0
         if (milestone.isDateRange()) {
 770  0
             if (endDate == null) {
 771  0
                 throw new RuntimeException("End date not supplied for range. " + milestone.getId());
 772  
             }
 773  0
             builder.append(getDate(endDate)).append(tab);
 774  0
             if (!milestone.isAllDay()) {
 775  0
                 builder.append(getTime(endDate));
 776  
             }
 777  
         } else {
 778  0
             builder.append(tab);
 779  0
             if (endDate != null) {
 780  0
                 throw new RuntimeException("End date not expected for non range. " + milestone.getId());
 781  
             }
 782  
         }
 783  0
         builder.append(tab);
 784  0
         builder.append(milestone.getType());
 785  
 
 786  0
         builder.append(line);
 787  0
         return builder;
 788  
     }
 789  
 
 790  
     private String getDate(Date date) {
 791  0
         SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-yyyy");
 792  0
         if (date == null) {
 793  0
             return "";
 794  
         }
 795  0
         return dateFormat.format(date);
 796  
     }
 797  
     private String getTime(Date date) {
 798  0
         SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
 799  0
         return timeFormat.format(date);
 800  
     }
 801  
 
 802  
     public Atp getHolidayCalendar(String yearString) {
 803  0
         Atp holiCal = allHolidayCalendars.get(yearString);
 804  0
         int year = Integer.parseInt(yearString);
 805  0
         if (null == holiCal) {
 806  0
             holiCal = new Atp();
 807  0
             holiCal.setId(year + "HOLIDAYCALENDAR");
 808  0
             holiCal.setName(year + " Holiday Calendar");
 809  0
             holiCal.setStartDate(new GregorianCalendar(year, Calendar.JANUARY, 1).getTime());
 810  0
             holiCal.setEndDate(new GregorianCalendar(year, Calendar.DECEMBER, 31).getTime());
 811  0
             holiCal.setDescriptionPlain("Holiday Calendar for " + year);
 812  0
             holiCal.setDescriptionFormatted("<p>" + holiCal.getDescriptionPlain() + "</p>");
 813  0
             holiCal.setType("kuali.atp.type.HolidayCalendar");
 814  0
             holiCal.setAtpCode("HCAL" + year);
 815  0
             holiCal.setAdminOrgId("102"); // Registrar code
 816  
 
 817  0
             for (String holidayTypeKey : observedHolidays.keySet()) {
 818  0
                 Milestone holiday = getHoliday(holidayTypeKey, year);
 819  0
                 holiCal.addMilestone(holiday);
 820  0
                 Milestone observedholiday = getHoliday(observedHolidays.get(holidayTypeKey), year);
 821  0
                 if (null != observedholiday) {
 822  0
                     holiCal.addMilestone(observedholiday);
 823  
                 }
 824  0
             }
 825  0
             allHolidayCalendars.put(yearString, holiCal);
 826  
         }
 827  0
         return holiCal;
 828  
     }
 829  
 
 830  
     public Milestone getHoliday(String holidayType, int year) {
 831  0
         if (null == holidayType) {
 832  0
             return null;
 833  
         }
 834  
 
 835  0
         String[] typeSplit = holidayType.split("\\.");
 836  
 
 837  0
         String id = (year + typeSplit[typeSplit.length-1]).toUpperCase();
 838  0
         String name = holidayNames.get(holidayType);
 839  0
         String descriptionPlain = name + " for " + year;
 840  0
         String descriptionFormatted = "<p>" + descriptionPlain + "<p>";
 841  
 
 842  0
         Date start = null;
 843  0
         Date end = null;
 844  0
         boolean allDay = true;
 845  0
         boolean range = false;
 846  
 
 847  0
         if (HOLIDAY_NEWYEARSDAY_KEY.equals(holidayType)) {
 848  0
             Calendar cal = new GregorianCalendar(year, Calendar.JANUARY, 1);
 849  0
             start = cal.getTime();
 850  0
         } else if (HOLIDAY_NEWYEARSDAYOBSERVED_KEY.equals(holidayType)) {
 851  0
             Milestone milestone = getHoliday(HOLIDAY_NEWYEARSDAY_KEY, year);
 852  0
             int offset = getWeekendOffset(milestone.getStartDate());
 853  0
             if (0 == offset) {
 854  0
                 return null;
 855  
             } else {
 856  0
                 start = addToDate(milestone.getStartDate(), Calendar.DATE, offset);
 857  0
                 end = addToDate(milestone.getEndDate(), Calendar.DATE, offset);
 858  
             }
 859  0
         } else if (HOLIDAY_MLKDAY_KEY.equals(holidayType)) {
 860  0
             start = getNthDayOfWeekInMonth(3, Calendar.MONDAY, Calendar.JANUARY, year);
 861  0
         } else if (HOLIDAY_PRESIDENTSDAY_KEY.equals(holidayType)) {
 862  0
             start = getNthDayOfWeekInMonth(3, Calendar.MONDAY, Calendar.FEBRUARY, year);
 863  0
         } else if (HOLIDAY_MEMORIALDAY_KEY.equals(holidayType)) {
 864  0
             start = getNthDayOfWeekInMonth(1, Calendar.MONDAY, Calendar.JUNE, year);
 865  0
             start = addToDate(start, Calendar.DATE, -7);
 866  0
         } else if (HOLIDAY_INDEPENDENCEDAY_KEY.equals(holidayType)) {
 867  0
             start = new GregorianCalendar(year, Calendar.JULY, 4).getTime();
 868  0
         } else if (HOLIDAY_INDEPENDENCEDAYOBSERVED_KEY.equals(holidayType)) {
 869  0
             Milestone milestone = getHoliday(HOLIDAY_INDEPENDENCEDAY_KEY, year);
 870  0
             int offset = getWeekendOffset(milestone.getStartDate());
 871  0
             if (0 == offset) {
 872  0
                 return null;
 873  
             } else {
 874  0
                 start = addToDate(milestone.getStartDate(), Calendar.DATE, offset);
 875  0
                 end = addToDate(milestone.getEndDate(), Calendar.DATE, offset);
 876  
             }
 877  0
         } else if (HOLIDAY_LABORDAY_KEY.equals(holidayType)) {
 878  0
             start = getNthDayOfWeekInMonth(1, Calendar.MONDAY, Calendar.SEPTEMBER, year);
 879  0
         } else if (HOLIDAY_VETERANSDAY_KEY.equals(holidayType)) {
 880  0
             start = new GregorianCalendar(year, Calendar.NOVEMBER, 11).getTime();
 881  0
         } else if (HOLIDAY_VETERANSDAYOBSERVED_KEY.equals(holidayType)) {
 882  0
             Milestone milestone = getHoliday(HOLIDAY_VETERANSDAY_KEY, year);
 883  0
             int offset = getWeekendOffset(milestone.getStartDate());
 884  0
             if (0 == offset) {
 885  0
                 return null;
 886  
             } else {
 887  0
                 start = addToDate(milestone.getStartDate(), Calendar.DATE, offset);
 888  0
                 end = addToDate(milestone.getEndDate(), Calendar.DATE, offset);
 889  
             }
 890  0
         } else if (HOLIDAY_THANKSGIVINGBREAK_KEY.equals(holidayType)) {
 891  0
             start = getNthDayOfWeekInMonth(4, Calendar.THURSDAY, Calendar.NOVEMBER, year);
 892  0
             end = addToDate(start, Calendar.DATE, 1);
 893  0
             range = true;
 894  0
         } else if (HOLIDAY_CHRISTMAS_KEY.equals(holidayType)) {
 895  0
             start = new GregorianCalendar(year, Calendar.DECEMBER, 25).getTime();
 896  0
         } else if (HOLIDAY_CHRISTMASOBSERVED_KEY.equals(holidayType)) {
 897  0
             Milestone milestone = getHoliday(HOLIDAY_CHRISTMAS_KEY, year);
 898  0
             int offset = getWeekendOffset(milestone.getStartDate());
 899  0
             if (0 == offset) {
 900  0
                 return null;
 901  
             } else {
 902  0
                 start = addToDate(milestone.getStartDate(), Calendar.DATE, offset);
 903  0
                 end = addToDate(milestone.getEndDate(), Calendar.DATE, offset);
 904  
             }
 905  0
         } else {
 906  0
             throw new RuntimeException("Holiday type not known. " + holidayType);
 907  
         }
 908  
 
 909  0
         Milestone milestone = new Milestone(id, name, holidayType, descriptionPlain, descriptionFormatted, start, end, allDay, range);
 910  0
         return milestone;
 911  
     }
 912  
 
 913  
     private void addMilestoneRelations(Atp term) {
 914  0
         List<Milestone> keyDates = term.getMilestones();
 915  0
         Milestone instructionperiod = null;
 916  0
         for (Milestone keyDate : keyDates) {
 917  0
             if (KEYDATE_INSTRUCTIONAL_PERIOD_KEY.equals(keyDate.getType())) {
 918  0
                 instructionperiod = keyDate;
 919  0
                 break;
 920  
             }
 921  
         }
 922  0
         if (null == instructionperiod) {
 923  0
             throw new RuntimeException("Could not find instruction period for term: " + term.getId());
 924  
         }
 925  0
         for (Milestone keyDate : keyDates) {
 926  0
             if (relativeToInstructionPeriod.contains(keyDate.getType())) {
 927  0
                 keyDate.setRelative(true);
 928  0
                 keyDate.setRelativeMilestoneId(instructionperiod.getId());
 929  
             } else {
 930  0
                 keyDate.setRelative(false);
 931  
             }
 932  
         }
 933  
 
 934  0
         for (Atp atp : term.getTerms()) {
 935  0
             addMilestoneRelations(atp);
 936  
         }
 937  0
     }
 938  
 
 939  
     private void alterEndDates(List<Atp> acals) {
 940  0
         for (int i = acals.size()-1; i >= 0 ; i--) {
 941  0
             Atp acal = acals.get(i);
 942  0
             if (i > 0) {
 943  0
                 Atp prevAcal = acals.get(i-1);
 944  0
                 Date start = acal.getStartDate();
 945  0
                 Date prevEndDate = addToDate(start, Calendar.DATE, -1);
 946  0
                 prevAcal.setEndDate(prevEndDate);
 947  0
                 List<Atp> prevTerms = prevAcal.getTerms();
 948  0
                 if (!prevTerms.isEmpty()) {
 949  0
                     Atp lastPrevTerm = prevTerms.get(prevTerms.size()-1);
 950  0
                     lastPrevTerm.setEndDate(prevEndDate);
 951  0
                     List<Atp> prevSubTerms = lastPrevTerm.getTerms();
 952  0
                     if (!prevSubTerms.isEmpty()) {
 953  0
                         Atp lastPrevTermSubTerm = prevSubTerms.get(prevSubTerms.size()-1);
 954  0
                         lastPrevTermSubTerm.setEndDate(prevEndDate);
 955  
                     }
 956  
                 }
 957  
             }
 958  
 
 959  0
             List<Atp> terms = acal.getTerms();
 960  0
             for (int j = terms.size()-1; j >= 0; j--) {
 961  0
                 Atp term = terms.get(j);
 962  0
                 if (j > 0) {
 963  0
                     Atp prevTerm = terms.get(j-1);
 964  0
                     Date prevEndDate = addToDate(term.getStartDate(), Calendar.DATE, -1);
 965  0
                     prevTerm.setEndDate(prevEndDate);
 966  0
                     List<Atp> prevSubTerms = prevTerm.getTerms();
 967  0
                     if (!prevSubTerms.isEmpty()) {
 968  0
                         Atp lastPrevSubTerm = prevSubTerms.get(prevSubTerms.size()-1);
 969  0
                         lastPrevSubTerm.setEndDate(prevEndDate);
 970  
                     }
 971  
                 }
 972  
             }
 973  
 
 974  0
             for (int j = terms.size()-1; j >= 0; j--) {
 975  0
                 Atp term = terms.get(j);
 976  0
                 if (j > 0) {
 977  0
                     Atp prevTerm = terms.get(j-1);
 978  0
                     Date start = term.getStartDate();
 979  0
                     prevTerm.setEndDate(addToDate(start, Calendar.DATE, -1));
 980  
                 }
 981  0
                 List<Atp> subTerms = term.getTerms();
 982  0
                 for (int k = subTerms.size()-1; k >= 0; k--) {
 983  0
                     Atp subTerm = subTerms.get(k);
 984  0
                     if (k > 0) {
 985  0
                         Atp prevSubTerm = subTerms.get(k-1);
 986  0
                         Date start = subTerm.getStartDate();
 987  0
                         prevSubTerm.setEndDate(addToDate(start, Calendar.DATE, -1));
 988  
                     }
 989  
                 }
 990  
             }
 991  
         }
 992  0
     }
 993  
 
 994  
     private int getWeekendOffset(Date date) {
 995  0
         Calendar cal = new GregorianCalendar();
 996  0
         cal.setTime(date);
 997  0
         int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
 998  0
         if (Calendar.SATURDAY == dayOfWeek) {
 999  0
             return -1;
 1000  0
         } else if (Calendar.SUNDAY == dayOfWeek) {
 1001  0
             return 1;
 1002  
         } else {
 1003  0
             return 0;
 1004  
         }
 1005  
     }
 1006  
 
 1007  
     private Date getNthDayOfWeekInMonth(int n, int dayOfWeek, int month, int year) {
 1008  0
         Calendar cal = new GregorianCalendar(year, month, 1);
 1009  0
         if (dayOfWeek != cal.get(Calendar.DAY_OF_WEEK)) {
 1010  0
             int daysUntil = (dayOfWeek - cal.get(Calendar.DAY_OF_WEEK) + 7) % 7;
 1011  0
             cal.add(Calendar.DATE, daysUntil);
 1012  
         }
 1013  0
         cal.add(Calendar.DATE, (n-1) * 7);
 1014  0
         return cal.getTime();
 1015  
     }
 1016  
 
 1017  
 }