package net.cbaines.suca; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.Map; import net.fortuna.ical4j.data.CalendarParser; import net.fortuna.ical4j.data.ContentHandler; import net.fortuna.ical4j.data.ParserException; import net.fortuna.ical4j.model.Component; import net.fortuna.ical4j.model.Date; import net.fortuna.ical4j.model.DateTime; import net.fortuna.ical4j.model.Property; /** * http://www.kanzaki.com/docs/ical/ * * @author Henco Appel * @author Christopher Baines * */ public class SotonCalendarParser implements CalendarParser { private boolean mergeDuplicateEventsWithSeperateLocations = true; private boolean showSemesterWeeks = true; private boolean showSemesters = true; private boolean showTerms = true; private boolean mergeHeaders = false; // private Calendar startYear = UniTermDates.getStartOf(UniTermDates.SEMESTER_1, // Calendar.getInstance().get(Calendar.YEAR) - 1); // private Calendar endYear = UniTermDates.getEndOf(UniTermDates.SEMESTER_2, UniTermDates.YEAR_BEGINING_2019); // private final static String TAG = "SotonCalendarParser"; public void parse(InputStream in, ContentHandler handler) throws IOException, ParserException { parse(new InputStreamReader(in), handler); } public void parse(Reader in, ContentHandler handler) throws IOException, ParserException { parse(new BufferedReader(in, 3), handler); } /** * Parse the csv file * * Line: "Monday","09:00","13:00","COMP1004 Comp Lab1/01","Weal, M","25 / 1009" ,"1-11, 15" * * @param in * @param handler * @throws ParseException */ public void parse(BufferedReader in, ContentHandler handler) throws ParserException { try { log("Year " + Calendar.getInstance().get(Calendar.YEAR)); int currentYear = Calendar.getInstance().get(Calendar.YEAR); handler.startCalendar(); handler.startProperty(Property.NAME); handler.propertyValue("Timetable"); handler.endProperty(Property.NAME); handler.startProperty(Property.VERSION); handler.propertyValue("2.0"); handler.startProperty(Property.PRODID); handler.propertyValue("-//Chris Baines + Henco Appel//SouthamptonUniversityOrganiser//EN"); handler.endProperty(Property.NAME); Calendar startOfTerm = UniTermDates.getStartOf(UniTermDates.SEMESTER_1, currentYear); logCalendar("Semester 1 start time ", startOfTerm); int day = startOfTerm.get(Calendar.DAY_OF_WEEK); int difference = 0; if (day == Calendar.MONDAY) { difference = 0; } else if (day == Calendar.TUESDAY) { difference = -1; } else if (day == Calendar.WEDNESDAY) { difference = -2; } else if (day == Calendar.THURSDAY) { difference = -3; } else if (day == Calendar.FRIDAY) { difference = -4; } else if (day == Calendar.SATURDAY) { difference = -5; } else if (day == Calendar.SUNDAY) { difference = -6; } else { log("Error calculating difference"); } startOfTerm.add(Calendar.DAY_OF_WEEK, difference); logCalendar("Week 0 start time ", startOfTerm); // UidGenerator ug = new UidGenerator("1"); ArrayList events = new ArrayList(); String line; while ((line = in.readLine()) != null) { if (!line.trim().equals("")) { log(line); String[] lectureInfo = line.split("\",\""); lectureInfo[0] = lectureInfo[0].substring(1, lectureInfo[0].length()); lectureInfo[6] = lectureInfo[6].substring(0, lectureInfo[6].length() - 1); Event event = new Event(lectureInfo); int index; if ((index = events.indexOf(event)) != -1 && mergeDuplicateEventsWithSeperateLocations) { event = events.get(index); if (!event.location.equals(lectureInfo[5])) { event.location = event.location + " and " + lectureInfo[5]; } events.set(index, event); } else { events.add(event); } } } for (Event event : events) { handler.startComponent(Component.VEVENT); // handler.startProperty(Property.UID); // handler.propertyValue(ug.generateUid().toString()); // handler.endProperty(Property.UID); // Summary handler.startProperty(Property.SUMMARY); handler.propertyValue(event.name); handler.endProperty(Property.SUMMARY); String[] people = event.lecturer.split(","); if (people.length > 0) { handler.startProperty(Property.ORGANIZER); handler.parameter("CN", people[0]); handler.endProperty(Property.ORGANIZER); } log("Looking at lecture " + event.name); // Time of creation handler.startProperty(Property.DTSTAMP); handler.propertyValue(new DateTime(java.util.Calendar.getInstance().getTime()).toString()); handler.endProperty(Property.DTSTAMP); // Start time Calendar startTime = (Calendar) startOfTerm.clone(); if (event.dayOfWeek.equals("Monday")) { // Nothing to do } else if (event.dayOfWeek.equals("Tuesday")) { startTime.add(Calendar.DAY_OF_WEEK, 1); } else if (event.dayOfWeek.equals("Wednesday")) { startTime.add(Calendar.DAY_OF_WEEK, 2); } else if (event.dayOfWeek.equals("Thursday")) { startTime.add(Calendar.DAY_OF_WEEK, 3); } else if (event.dayOfWeek.equals("Friday")) { startTime.add(Calendar.DAY_OF_WEEK, 4); } else { log("ERROR!!!"); } String startTimeString = event.startTime; String[] startTimeStringParts = startTimeString.split(":"); int startHour = Integer.valueOf(startTimeStringParts[0]); int startMinute = Integer.valueOf(startTimeStringParts[1]); startTime.set(Calendar.HOUR_OF_DAY, startHour); startTime.set(Calendar.MINUTE, startMinute); String[] weeks = event.weeks.split(","); String startWeekStr = weeks[0].split("-")[0]; int startWeek = Integer.valueOf(startWeekStr); startTime.add(Calendar.WEEK_OF_YEAR, startWeek); handler.startProperty(Property.DTSTART); handler.propertyValue(new DateTime(startTime.getTime()).toString()); handler.endProperty(Property.DTSTART); logCalendar(" Start time (week " + startWeek + ") ", startTime); // End time String endTimeString = event.endTime; String[] endTimeStringParts = endTimeString.split(":"); int endHour = Integer.valueOf(endTimeStringParts[0]); int endMinute = Integer.valueOf(endTimeStringParts[1]); Calendar endTime = (Calendar) startTime.clone(); endTime.set(Calendar.HOUR_OF_DAY, endHour); endTime.set(Calendar.MINUTE, endMinute); handler.startProperty(Property.DTEND); handler.propertyValue(new DateTime(endTime.getTime()).toString()); handler.endProperty(Property.DTEND); logCalendar(" End time ", endTime); // Weeks StringBuilder rDate = new StringBuilder(); for (String rule : weeks) { log(" Rule: " + rule); Calendar recurance = null; if (rule.contains("-")) { String[] parts = rule.split("\\-"); String part1Str = parts[0].trim(); String part2Str = parts[1].trim(); int part1 = Integer.valueOf(part1Str); int part2 = Integer.valueOf(part2Str); for (int i = part1; i <= part2; i++) { recurance = (Calendar) startOfTerm.clone(); if (event.dayOfWeek.equals("Monday")) { // Nothing to do } else if (event.dayOfWeek.equals("Tuesday")) { recurance.add(Calendar.DAY_OF_WEEK, 1); } else if (event.dayOfWeek.equals("Wednesday")) { recurance.add(Calendar.DAY_OF_WEEK, 2); } else if (event.dayOfWeek.equals("Thursday")) { recurance.add(Calendar.DAY_OF_WEEK, 3); } else if (event.dayOfWeek.equals("Friday")) { recurance.add(Calendar.DAY_OF_WEEK, 4); } else { log("ERROR!!!"); } recurance.set(Calendar.HOUR_OF_DAY, startHour); recurance.set(Calendar.MINUTE, startMinute); recurance.add(Calendar.WEEK_OF_YEAR, i); rDate.append(new DateTime(recurance.getTime()).toString()); rDate.append(","); logCalendar(" Recurance (week " + i + ") ", recurance); } } else { int week = Integer.valueOf(rule.trim()); recurance = (Calendar) startOfTerm.clone(); recurance.add(Calendar.WEEK_OF_YEAR, week); if (event.dayOfWeek.equals("Monday")) { // Nothing to do } else if (event.dayOfWeek.equals("Tuesday")) { recurance.add(Calendar.DAY_OF_WEEK, 1); } else if (event.dayOfWeek.equals("Wednesday")) { recurance.add(Calendar.DAY_OF_WEEK, 2); } else if (event.dayOfWeek.equals("Thursday")) { recurance.add(Calendar.DAY_OF_WEEK, 3); } else if (event.dayOfWeek.equals("Friday")) { recurance.add(Calendar.DAY_OF_WEEK, 4); } else { log("ERROR!!!"); } recurance.set(Calendar.HOUR_OF_DAY, startHour); recurance.set(Calendar.MINUTE, startMinute); rDate.append(new DateTime(recurance.getTime()).toString()); rDate.append(","); logCalendar(" Recurance (week " + week + ") ", recurance); } } if (rDate.length() != 0) { rDate.deleteCharAt(rDate.length() - 1); // Delete the last , handler.startProperty(Property.RDATE); handler.propertyValue(rDate.toString()); handler.endProperty(Property.RDATE); } handler.startProperty(Property.LOCATION); handler.propertyValue(event.location); handler.endProperty(Property.LOCATION); // The following infomation should be fetched from the // Southampton University Map App, through the content // provider, or from the data.southampton site via a sparqul // query. // handler.startProperty(Property.GEO); // handler.propertyValue(lat + ":" + lng); // handler.endProperty(Property.GEO); handler.endComponent(Component.VEVENT); } if (mergeHeaders) { for (int year = currentYear; year < UniTermDates.YEAR_BEGINING_2019; year++) { if (showSemesterWeeks) { } } } else { ArrayList periods = new ArrayList(5); String[] periodNames = { "Autumn Term", "Spring Term", "Summer Term", "Semester 1", "Semester 2" }; if (showSemesters) { periods.add(UniTermDates.SEMESTER_1); periods.add(UniTermDates.SEMESTER_2); } if (showTerms) { periods.add(UniTermDates.AUTUMN_TERM); periods.add(UniTermDates.SPRING_TERM); periods.add(UniTermDates.SUMMER_TERM); } for (int period : periods) { for (int year = currentYear; year < UniTermDates.YEAR_BEGINING_2019; year++) { log("Looking at " + periodNames[period]); handler.startComponent(Component.VEVENT); // handler.startProperty(Property.UID); // handler.propertyValue(ug.generateUid().toString()); // handler.endProperty(Property.UID); // Summary handler.startProperty(Property.SUMMARY); handler.propertyValue(periodNames[period]); handler.endProperty(Property.SUMMARY); // Time of creation handler.startProperty(Property.DTSTAMP); handler.propertyValue(new DateTime(java.util.Calendar.getInstance().getTime()).toString()); handler.endProperty(Property.DTSTAMP); // Start time logCalendar(" Start time " + periodNames[period] + " ", UniTermDates.getStartOf(period, year)); handler.startProperty(Property.DTSTART); handler.parameter("VALUE", "DATE"); handler.propertyValue(new Date(UniTermDates.getStartOf(period, year).getTime()).toString()); handler.endProperty(Property.DTSTART); // End time logCalendar(" End time " + periodNames[period] + " ", UniTermDates.getEndOf(period, year)); handler.startProperty(Property.DTEND); handler.parameter("VALUE", "DATE"); handler.propertyValue(new Date(UniTermDates.getEndOf(period, year).getTime()).toString()); handler.endProperty(Property.DTEND); handler.endComponent(Component.VEVENT); } } if (showSemesterWeeks) { for (int year = currentYear; year < UniTermDates.YEAR_BEGINING_2019; year++) { // Semester 1 Calendar semester1StartDate = UniTermDates.getStartOf(UniTermDates.SEMESTER_1, year); Calendar semester1EndDate = UniTermDates.getEndOf(UniTermDates.SEMESTER_1, year); int dayOfTheWeek = semester1StartDate.get(Calendar.DAY_OF_WEEK); log("Semester 1, year " + year + " starts on the " + dayOfTheWeek); int difference2 = 0; if (dayOfTheWeek == Calendar.MONDAY) { difference2 = 7; } else if (dayOfTheWeek == Calendar.TUESDAY) { difference2 = 6; } else if (dayOfTheWeek == Calendar.WEDNESDAY) { difference2 = 5; } else if (dayOfTheWeek == Calendar.THURSDAY) { difference2 = 4; } else if (dayOfTheWeek == Calendar.FRIDAY) { difference2 = 3; } else if (dayOfTheWeek == Calendar.SATURDAY) { difference2 = 2; } else if (dayOfTheWeek == Calendar.SUNDAY) { difference2 = 1; } else { log("Error calculating difference"); } log("Moving forward " + difference2); semester1StartDate.add(Calendar.DAY_OF_WEEK, difference2); int week = 1; while (semester1StartDate.before(semester1EndDate)) { if (semester1StartDate.after(UniTermDates.getEndOf(UniTermDates.AUTUMN_TERM, year)) && semester1StartDate.before(UniTermDates .getStartOf(UniTermDates.SPRING_TERM, year))) { semester1StartDate.add(Calendar.WEEK_OF_YEAR, 1); continue; } log("Semester 1, Week " + week); handler.startComponent(Component.VEVENT); // handler.startProperty(Property.UID); // handler.propertyValue(ug.generateUid().toString()); // handler.endProperty(Property.UID); // Summary handler.startProperty(Property.SUMMARY); handler.propertyValue("Week " + week); handler.endProperty(Property.SUMMARY); // Time of creation handler.startProperty(Property.DTSTAMP); handler.propertyValue(new DateTime(java.util.Calendar.getInstance().getTime()).toString()); handler.endProperty(Property.DTSTAMP); // Start time logCalendar(" Start time ", semester1StartDate); handler.startProperty(Property.DTSTART); handler.parameter("VALUE", "DATE"); // Calendar startCalTemp = ((Calendar) semester1StartDate.clone()); // startCalTemp.add(Calendar.DAY_OF_YEAR, 1); // Date startDate = new Date(startCalTemp.getTime()); // logDate(" Start date " + "Week " + week + " ", startDate); handler.propertyValue(getDate(semester1StartDate)); handler.endProperty(Property.DTSTART); semester1StartDate.add(Calendar.WEEK_OF_YEAR, 1); // End time logCalendar(" End time ", semester1StartDate); handler.startProperty(Property.DTEND); handler.parameter("VALUE", "DATE"); // Calendar endCalTemp = ((Calendar) semester1StartDate.clone()); // endCalTemp.add(Calendar.DAY_OF_YEAR, 1); // Date endDate = new Date(endCalTemp.getTime()); // logDate(" End date " + "Week " + week + " ", endDate); handler.propertyValue(getDate(semester1StartDate)); handler.endProperty(Property.DTEND); handler.endComponent(Component.VEVENT); week++; } // Semester 2 Calendar semester2StartDate = UniTermDates.getStartOf(UniTermDates.SEMESTER_2, year); Calendar semester2EndDate = UniTermDates.getEndOf(UniTermDates.SEMESTER_2, year); week = 1; while (semester2StartDate.before(semester2EndDate)) { if (semester2StartDate.after(UniTermDates.getEndOf(UniTermDates.SPRING_TERM, year)) && semester2StartDate.before(UniTermDates .getStartOf(UniTermDates.SUMMER_TERM, year))) { semester2StartDate.add(Calendar.WEEK_OF_YEAR, 1); continue; } log("Semester 2, Week " + week); handler.startComponent(Component.VEVENT); // handler.startProperty(Property.UID); // handler.propertyValue(ug.generateUid().toString()); // handler.endProperty(Property.UID); // Summary handler.startProperty(Property.SUMMARY); handler.propertyValue("Week " + week); handler.endProperty(Property.SUMMARY); // Time of creation handler.startProperty(Property.DTSTAMP); handler.propertyValue(new DateTime(java.util.Calendar.getInstance().getTime()).toString()); handler.endProperty(Property.DTSTAMP); // Start time logCalendar(" Start time ", semester2StartDate); handler.startProperty(Property.DTSTART); handler.parameter("VALUE", "DATE"); handler.propertyValue(getDate(semester2StartDate)); handler.endProperty(Property.DTSTART); semester2StartDate.add(Calendar.WEEK_OF_YEAR, 1); // End time logCalendar(" End time ", semester2StartDate); handler.startProperty(Property.DTEND); handler.parameter("VALUE", "DATE"); handler.propertyValue(getDate(semester2StartDate)); handler.endProperty(Property.DTEND); handler.endComponent(Component.VEVENT); week++; } } } } handler.endCalendar(); } catch (Exception e) { e.printStackTrace(); throw new ParserException("Error parsing csv", 0); } } private String getDate(Calendar cal) { StringBuilder builder = new StringBuilder(8); builder.append(cal.get(Calendar.YEAR)); builder.append(cal.get(Calendar.MONTH) + 1); if (builder.length() == 5) builder.insert(4, '0'); builder.append(cal.get(Calendar.DAY_OF_MONTH)); if (builder.length() == 7) builder.insert(6, '0'); return builder.toString(); } private static void logCalendar(String prefix, Calendar cal) { Map daysOfTheWeek = new HashMap(7); daysOfTheWeek.put(Calendar.MONDAY, "Monday"); daysOfTheWeek.put(Calendar.TUESDAY, "Tuesday"); daysOfTheWeek.put(Calendar.WEDNESDAY, "Wednesday"); daysOfTheWeek.put(Calendar.THURSDAY, "Thursday"); daysOfTheWeek.put(Calendar.FRIDAY, "Friday"); daysOfTheWeek.put(Calendar.SATURDAY, "Saturday"); daysOfTheWeek.put(Calendar.SUNDAY, "Sunday"); String hour = String.valueOf(cal.get(Calendar.HOUR_OF_DAY)); if (hour.length() == 1) { hour = "0" + hour; } String minute = String.valueOf(cal.get(Calendar.MINUTE)); if (minute.length() == 1) { minute = "0" + minute; } log(prefix + hour + ":" + minute + " " + daysOfTheWeek.get(cal.get(Calendar.DAY_OF_WEEK)) + " " + cal.get(Calendar.DAY_OF_MONTH) + "/" + (cal.get(Calendar.MONTH) + 1) + "/" + cal.get(Calendar.YEAR)); } private static void log(String string) { System.out.println(string); } /** * @return the mergeDuplicateEventsWithSeperateLocations */ public boolean isMergeDuplicateEventsWithSeperateLocations() { return mergeDuplicateEventsWithSeperateLocations; } /** * @param mergeDuplicateEventsWithSeperateLocations * the mergeDuplicateEventsWithSeperateLocations to set */ public void setMergeDuplicateEventsWithSeperateLocations(boolean mergeDuplicateEventsWithSeperateLocations) { this.mergeDuplicateEventsWithSeperateLocations = mergeDuplicateEventsWithSeperateLocations; } /** * @return the showSemesterWeeks */ public boolean isShowSemesterWeeks() { return showSemesterWeeks; } /** * @param showSemesterWeeks * the showSemesterWeeks to set */ public void setShowSemesterWeeks(boolean showSemesterWeeks) { this.showSemesterWeeks = showSemesterWeeks; } /** * @return the showSemesters */ public boolean isShowSemesters() { return showSemesters; } /** * @param showSemesters * the showSemesters to set */ public void setShowSemesters(boolean showSemesters) { this.showSemesters = showSemesters; } /** * @return the showTerms */ public boolean isShowTerms() { return showTerms; } /** * @param showTerms * the showTerms to set */ public void setShowTerms(boolean showTerms) { this.showTerms = showTerms; } /** * @return the mergeHeaders */ public boolean isMergeHeaders() { return mergeHeaders; } /** * @param mergeHeaders * the mergeHeaders to set */ public void setMergeHeaders(boolean mergeHeaders) { this.mergeHeaders = mergeHeaders; } private class Event { String dayOfWeek; String startTime; String endTime; String name; String lecturer; String location; String weeks; public Event(String[] info) { dayOfWeek = info[0]; startTime = info[1]; endTime = info[2]; name = info[3].split(" ")[0]; lecturer = info[4]; location = info[5]; weeks = info[6]; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + getOuterType().hashCode(); result = prime * result + ((dayOfWeek == null) ? 0 : dayOfWeek.hashCode()); result = prime * result + ((endTime == null) ? 0 : endTime.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((startTime == null) ? 0 : startTime.hashCode()); result = prime * result + ((weeks == null) ? 0 : weeks.hashCode()); result = prime * result + ((lecturer == null) ? 0 : lecturer.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Event other = (Event) obj; if (!getOuterType().equals(other.getOuterType())) return false; if (dayOfWeek == null) { if (other.dayOfWeek != null) return false; } else if (!dayOfWeek.equals(other.dayOfWeek)) return false; if (endTime == null) { if (other.endTime != null) return false; } else if (!endTime.equals(other.endTime)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (startTime == null) { if (other.startTime != null) return false; } else if (!startTime.equals(other.startTime)) return false; if (weeks == null) { if (other.weeks != null) return false; } else if (!weeks.equals(other.weeks)) return false; if (lecturer == null) { if (other.lecturer != null) return false; } else if (!lecturer.equals(other.lecturer)) return false; return true; } private SotonCalendarParser getOuterType() { return SotonCalendarParser.this; } } }