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.net.URISyntaxException; import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; 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; import net.fortuna.ical4j.util.UidGenerator; /** * http://www.kanzaki.com/docs/ical/ * * @author Henco Appel * @author Christopher Baines * */ public class SotonCalendarParser implements CalendarParser { private boolean MERGE_DUPLICATE_EVENTS_WITH_SEPARATE_LOCATIONS = true; private boolean SHOW_SEMESTER_WEEKS = true; private boolean SHOW_SEMESTERS = true; private boolean SHOW_TERMS = true; private boolean MERGE_HEADERS = false; 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 IOException * @throws ParserException */ public void parse(BufferedReader in, ContentHandler handler) throws IOException, 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.endProperty(Property.VERSION); handler.startProperty(Property.PRODID); handler.propertyValue("-//Chris Baines + Henco Appel//SouthamptonUniversityOrganiser//EN"); handler.endProperty(Property.NAME); Calendar startOfTerm = UniTermDates.getStartOf( UniTermDates.SEMESTER_1, currentYear - 1); 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 && MERGE_DUPLICATE_EVENTS_WITH_SEPARATE_LOCATIONS) { 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); 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); 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.ORGANIZER); // handler.propertyValue("Organiser"); // handler.endProperty(Property.ORGANIZER); String[] people = event.lecturer.split(","); for (String person : people) { // log("Person: " + person); } String[] locationBits = event.location.split("/"); 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 (MERGE_HEADERS) { for (int year = UniTermDates.YEAR_BEGINING_2011; year < UniTermDates.YEAR_BEGINING_2019; year++) { if (SHOW_SEMESTER_WEEKS) { } } } else { ArrayList periods = new ArrayList(5); String[] periodNames = { "Autumn Term", "Spring Term", "Summer Term", "Semester 1", "Semester 2" }; if (SHOW_SEMESTERS) { periods.add(UniTermDates.SEMESTER_1); periods.add(UniTermDates.SEMESTER_2); } if (SHOW_TERMS) { periods.add(UniTermDates.AUTUMN_TERM); periods.add(UniTermDates.SPRING_TERM); periods.add(UniTermDates.SUMMER_TERM); } for (int period : periods) { for (int year = UniTermDates.YEAR_BEGINING_2011; 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 (SHOW_SEMESTER_WEEKS) { for (int period : periods) { for (int year = UniTermDates.YEAR_BEGINING_2011; 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); } } } } handler.endCalendar(); } catch (ParseException e) { e.printStackTrace(); } catch (URISyntaxException e) { e.printStackTrace(); } catch (NumberFormatException e) { e.printStackTrace(); } } private static void logCalendar(String prefix, Calendar cal) { 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 + " " + 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); } private class Event { String dayOfWeek; String startTime; String endTime; String name; String lecturer; String location; String weeks; public Event() { } public Event(String[] info) { dayOfWeek = info[0]; startTime = info[1]; endTime = info[2]; name = info[3]; 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; } } }