me.vertretungsplan.additionalinfo.BaseIcalParser Maven / Gradle / Ivy
/*
* substitution-schedule-parser - Java library for parsing schools' substitution schedules
* Copyright (c) 2017 Johan v. Forstner
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package me.vertretungsplan.additionalinfo;
import biweekly.Biweekly;
import biweekly.ICalendar;
import biweekly.component.VEvent;
import biweekly.io.TimezoneAssignment;
import biweekly.io.TimezoneInfo;
import biweekly.util.com.google.ical.compat.javautil.DateIterator;
import me.vertretungsplan.objects.AdditionalInfo;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import java.io.IOException;
import java.util.*;
import java.util.regex.Pattern;
public abstract class BaseIcalParser extends BaseAdditionalInfoParser {
protected abstract String getIcalUrl();
protected String getTitle() {
return "Termine";
}
protected int getMaxItemsCount() {
return 4;
}
@Override
public AdditionalInfo getAdditionalInfo() throws IOException {
AdditionalInfo info = new AdditionalInfo();
info.setTitle(getTitle());
String rawdata = httpGet(getIcalUrl(), "UTF-8");
if (shouldStripTimezoneInfo()) {
Pattern pattern = Pattern.compile("BEGIN:VTIMEZONE.*END:VTIMEZONE", Pattern.DOTALL);
rawdata = pattern.matcher(rawdata).replaceAll("");
}
DateTime now = DateTime.now().withTimeAtStartOfDay();
List icals = Biweekly.parse(rawdata).all();
List events = new ArrayList<>();
for (ICalendar ical : icals) {
for (VEvent event : ical.getEvents()) {
Event item = new Event();
TimeZone timezoneStart = getTimeZoneStart(ical, event);
if (event.getDescription() != null) {
item.description = event.getDescription().getValue();
}
if (event.getSummary() != null) {
item.summary = event.getSummary().getValue();
}
if (event.getDateStart() != null) {
item.startDate = new DateTime(event.getDateStart().getValue());
item.startHasTime = event.getDateStart().getValue().hasTime();
} else {
continue;
}
if (event.getDateEnd() != null) {
item.endDate = new DateTime(event.getDateEnd().getValue());
item.endHasTime = event.getDateEnd().getValue().hasTime();
}
if (event.getLocation() != null) {
item.location = event.getLocation().getValue();
}
if (event.getUrl() != null) {
item.url = event.getUrl().getValue();
}
if (event.getRecurrenceRule() == null
&& item.endDate != null
&& (item.endDate.compareTo(now) < 0)) {
continue;
} else if (event.getRecurrenceRule() == null
&& (item.startDate.compareTo(now) < 0)) {
continue;
}
if (event.getRecurrenceRule() != null
&& event.getRecurrenceRule().getValue().getUntil() != null
&& event.getRecurrenceRule().getValue().getUntil()
.compareTo(now.toDate()) < 0) {
continue;
}
if (event.getRecurrenceRule() != null) {
Duration duration = null;
if (event.getDateEnd() != null) {
duration = new Duration(new DateTime(event.getDateStart().getValue()), new DateTime(event
.getDateEnd().getValue()));
}
DateIterator iterator = event.getDateIterator(timezoneStart);
while (iterator.hasNext()) {
Date date = iterator.next();
Event reccitem = item.clone();
reccitem.startDate = new DateTime(date);
reccitem.endDate = reccitem.startDate.plus(duration);
if (item.startDate.equals(reccitem.startDate)) continue;
if (item.endDate != null
&& (item.endDate.compareTo(now) < 0)) {
continue;
} else if (item.endDate == null
&& (item.startDate.compareTo(now) < 0)) {
continue;
}
events.add(reccitem);
}
}
if (item.endDate != null
&& (item.endDate.compareTo(now) < 0)) {
continue;
} else if (item.endDate == null
&& (item.startDate.compareTo(now) < 0)) {
continue;
}
events.add(item);
}
}
Collections.sort(events, new Comparator() {
@Override public int compare(Event o1, Event o2) {
return o1.startDate.compareTo(o2.startDate);
}
});
StringBuilder content = new StringBuilder();
int count = 0;
DateTimeFormatter fmtDt = DateTimeFormat.shortDateTime().withLocale(Locale.GERMANY);
DateTimeFormatter fmtD = DateTimeFormat.shortDate().withLocale(Locale.GERMANY);
DateTimeFormatter fmtT = DateTimeFormat.shortTime().withLocale(Locale.GERMANY);
for (Event item : events) {
if (count >= getMaxItemsCount()) {
break;
} else if (count != 0) {
content.append("
\n\n");
}
DateTime start = item.startDate;
if (item.endDate != null) {
DateTime end = item.endDate;
if (!item.endHasTime) {
end = end.minusDays(1);
}
content.append((item.startHasTime ? fmtDt : fmtD).print(start));
if (!end.equals(start)) {
content.append(" - ");
if (item.startHasTime && item.endHasTime && end.toLocalDate().equals(start.toLocalDate())) {
content.append(fmtT.print(end));
} else {
content.append((item.endHasTime ? fmtDt : fmtD).print(end));
}
}
} else {
content.append(fmtDt.print(start));
}
content.append("
\n");
content.append("");
content.append(item.summary);
content.append("");
count++;
}
info.setText(content.toString());
return info;
}
protected boolean shouldStripTimezoneInfo() {
return false;
}
private TimeZone getTimeZoneStart(ICalendar ical, VEvent event) {
if (event.getDateStart() == null) {
return null;
}
TimezoneInfo tzinfo = ical.getTimezoneInfo();
TimeZone timezone;
if (tzinfo.isFloating(event.getDateStart())) {
timezone = TimeZone.getDefault();
} else {
TimezoneAssignment dtstartTimezone = tzinfo.getTimezone(event.getDateStart());
timezone = (dtstartTimezone == null) ? TimeZone.getTimeZone("UTC") : dtstartTimezone.getTimeZone();
}
return timezone;
}
private class Event implements Cloneable {
public String summary;
public String description;
public String location;
public DateTime startDate;
public DateTime endDate;
public String url;
public boolean startHasTime;
public boolean endHasTime;
@Override
protected Event clone() {
Event clone = new Event();
clone.summary = this.summary;
clone.description = this.description;
clone.location = this.location;
clone.startDate = this.startDate;
clone.endDate = this.endDate;
clone.url = this.url;
return clone;
}
}
}