me.vertretungsplan.objects.SubstitutionSchedule Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of parser Show documentation
Show all versions of parser Show documentation
Java library for parsing schools' substitution schedules. Supports multiple different systems mainly used in the German-speaking countries.
/*
* substitution-schedule-parser - Java library for parsing schools' substitution schedules
* Copyright (c) 2016 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.objects;
import com.paour.comparator.NaturalOrderComparator;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.joda.time.LocalDateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import java.util.*;
/**
* Represents a school's substitution schedule
*/
public class SubstitutionSchedule implements Cloneable {
static final DateTimeFormatter DAY_DATE_FORMAT = DateTimeFormat.forPattern("EEEE, dd.MM.yyyy").withLocale(
Locale.GERMAN);
static final DateTimeFormatter LAST_CHANGE_DATE_FORMAT = DateTimeFormat.forPattern("dd.MM.yyyy HH:mm").withLocale(
Locale.GERMAN);
private Type type;
private LocalDateTime lastChange;
private String lastChangeString;
private String website;
private List days;
private List additionalInfos;
private List classes;
private List teachers;
public SubstitutionSchedule() {
days = new ArrayList<>();
additionalInfos = new ArrayList<>();
classes = new ArrayList<>();
teachers = new ArrayList<>();
}
/**
* Creates a new SubstitutionSchedule containing the same data as the given one
*
* @param other the SubstitutionSchedule whose data to use
*/
public SubstitutionSchedule(SubstitutionSchedule other) {
this.type = other.type;
this.lastChange = other.lastChange;
this.lastChangeString = other.lastChangeString;
this.website = other.website;
this.days = other.days;
this.additionalInfos = other.additionalInfos;
this.classes = other.classes;
this.teachers = other.teachers;
}
/**
* Initialize a SubstitutionSchedule with the correct type taken from a {@link SubstitutionScheduleData}
*
* @param scheduleData a SubstitutionScheduleData to create a schedule for
* @return a schedule initialized with the correct type
*/
public static SubstitutionSchedule fromData(SubstitutionScheduleData scheduleData) {
SubstitutionSchedule schedule = new SubstitutionSchedule();
schedule.setType(scheduleData.getType());
return schedule;
}
/**
* Filter a set of substitutions by class
*
* @param theClass the name of a class
* @param substitutions a set of {@link Substitution}s
* @return the substitutions from the set that apply to the specified class
*/
public static Set filterByClass(String theClass, Set substitutions) {
if (theClass == null) return substitutions;
Set classSubstitutions = new HashSet<>();
for (Substitution substitution : substitutions) {
if (substitution.getClasses().contains(theClass)) classSubstitutions.add(substitution);
}
return classSubstitutions;
}
/**
* Filter a set of substitutions by excluding a set of subjects
*
* @param excludedSubjects a set of subjects to exclude
* @param substitutions a set of {@link Substitution}s
* @return the substitutions from the set that are not for one of the specified subjects
*/
public static Set filterBySubject(Set excludedSubjects, Set substitutions) {
if (excludedSubjects == null || excludedSubjects.isEmpty()) return substitutions;
Set filteredSubstitutions = new HashSet<>();
for (Substitution substitution : substitutions) {
if (substitution.getPreviousSubject() != null) {
if (!excludedSubjects.contains(substitution.getPreviousSubject())) {
filteredSubstitutions.add(substitution);
}
} else if (substitution.getSubject() != null) {
if (!excludedSubjects.contains(substitution.getSubject())) {
filteredSubstitutions.add(substitution);
}
} else {
filteredSubstitutions.add(substitution);
}
}
return filteredSubstitutions;
}
/**
* Filter a set of substitutions by teacher
*
* @param teacher a teacher's name/abbreviation
* @param substitutions a set of {@link Substitution}s
* @return the substitutions from the set that apply to the specified teacher
*/
public static Set filterByTeacher(String teacher, Set substitutions) {
if (teacher == null) return substitutions;
Set teacherSubstitutions = new HashSet<>();
for (Substitution substitution : substitutions) {
if (substitution.getTeachers().contains(teacher)
|| substitution.getPreviousTeachers().contains(teacher)) {
teacherSubstitutions.add(substitution);
}
}
return teacherSubstitutions;
}
/**
* Get the type of this schedule
*
* @return the type of this schedule
*/
public Type getType() {
return type;
}
/**
* Set the type of this schedule
*
* @param type the type of this schedule
*/
public void setType(Type type) {
this.type = type;
}
/**
* Get the date and time where this schedule was last updated. If the date could not be parsed, there is only a
* string representation available using {@link #getLastChangeString()}.
*
* @return the date and time where this schedule was last updated
*/
public LocalDateTime getLastChange() {
return lastChange;
}
/**
* Set the date and time where this schedule was last updated. If the date could not be parsed,
* use {@link #setLastChangeString(String)} to specify a string representation. If you used
* {@link SubstitutionScheduleDay#setLastChange(LocalDateTime)}, this will automatically be set
* to the newest date of all the days.
*
* @param lastChange the date and time where this schedule was last updated.
*/
public void setLastChange(LocalDateTime lastChange) {
this.lastChange = lastChange;
}
/**
* Get the date and time where this schedule was last updated as a string representation
*
* @return the date and time where this schedule was last updated, as a string representation
*/
public String getLastChangeString() {
if (lastChangeString != null) {
return lastChangeString;
} else if (lastChange != null) {
return LAST_CHANGE_DATE_FORMAT.print(lastChange);
} else {
return null;
}
}
/**
* Set the date and time where this schedule was last updated as a string representation. If you can parse the
* date, you should use {@link #setLastChange(LocalDateTime)} instead.
*
* @param lastChangeString the date and time where this schedule was last updated, as a string representation
*/
public void setLastChangeString(String lastChangeString) {
this.lastChangeString = lastChangeString;
}
/**
* Get the website where this schedule can be found online
*
* @return the website URL
*/
public String getWebsite() {
return website;
}
/**
* Set the website where this schedule can be found online
*
* @param website the website URl
*/
public void setWebsite(String website) {
this.website = website;
}
/**
* Get the list of days included in this schedule
*
* @return the list of days
*/
public List getDays() {
return days;
}
/**
* Add a day to this substitution schedule
*
* @param newDay the day to add
*/
public void addDay(SubstitutionScheduleDay newDay) {
if (lastChange == null && lastChangeString == null) {
// Read lastChange or lastChangeString from day
if (newDay.getLastChange() != null) {
lastChange = newDay.getLastChange();
} else if (newDay.getLastChangeString() != null) {
lastChangeString = newDay.getLastChangeString();
}
} else if (lastChange != null && newDay.getLastChange() != null && newDay.getLastChange().isAfter(lastChange)) {
// Update lastChange from day
lastChange = newDay.getLastChange();
}
addOrMergeDay(newDay);
Collections.sort(days, new Comparator() {
@Override
public int compare(SubstitutionScheduleDay a, SubstitutionScheduleDay b) {
if (a.getDate() != null && b.getDate() != null) {
return a.getDate().compareTo(b.getDate());
} else if (a.getDateString() != null && b.getDateString() != null) {
NaturalOrderComparator comp = new NaturalOrderComparator();
return comp.compare(a.getDateString(), b.getDateString());
} else if (a.getDateString() != null) {
return 1;
} else if (b.getDateString() != null) {
return -1;
} else {
return 0;
}
}
});
}
private void addOrMergeDay(SubstitutionScheduleDay newDay) {
// Check if day should be merged
for (SubstitutionScheduleDay day : days) {
if (day.equalsByDate(newDay)) {
day.merge(newDay);
return;
}
}
days.add(newDay);
}
/**
* Get the list of additional infos on this schedule
*
* @return the list of additional infos on this schedule
*/
public List getAdditionalInfos() {
return additionalInfos;
}
/**
* Add an additional info to this schedule
*
* @param info the additional info to add
*/
public void addAdditionalInfo(AdditionalInfo info) {
additionalInfos.add(info);
}
/**
* Get the list of classes that can appear on this schedule. May only be empty if this is a {@link Type#TEACHER}
* schedule
*
* @return the list of classes
*/
public List getClasses() {
return classes;
}
/**
* Set the list of classes. Not required if this is a {@link Type#TEACHER} schedule.
*
* @param classes the list of classes to set.
*/
public void setClasses(List classes) {
this.classes = classes;
}
/**
* Get the list of teachers that can appear on this schedule. May be empty even if this is a {@link Type#TEACHER}
* schedule.
*
* @return the list of teachers
*/
public List getTeachers() {
return teachers;
}
/**
* Set the list of teachers. Not required even if this is a {@link Type#TEACHER} schedule.
*
* @param teachers the list of teachers to set.
*/
public void setTeachers(List teachers) {
this.teachers = teachers;
}
public SubstitutionSchedule clone() {
try {
return (SubstitutionSchedule) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
/**
* Get a new schedule that only contains the data that applies to the specified class, excluding substitutions
* for the specified subjects.
*
* @param theClass the class whose substitutions should be included
* @param excludedSubjects the subjects that should be excluded
* @return a new SubstitutionSchedule containing the filtered data
*/
public SubstitutionSchedule filteredByClassAndExcludedSubject(String theClass, Set excludedSubjects) {
SubstitutionSchedule filteredSchedule = this.clone();
filterByClassAndExcludedSubject(filteredSchedule, theClass, excludedSubjects);
return filteredSchedule;
}
protected void filterByClassAndExcludedSubject(SubstitutionSchedule filteredSchedule, String theClass,
Set excludedSubjects) {
for (int i = 0; i < filteredSchedule.getDays().size(); i++) {
SubstitutionScheduleDay day = filteredSchedule.getDays().get(i);
SubstitutionScheduleDay filteredDay = day.clone();
filteredDay.setSubstitutions(day.getSubstitutionsByClassAndExcludedSubject(theClass, excludedSubjects));
filteredSchedule.getDays().set(i, filteredDay);
}
if (theClass != null) {
List classes = new ArrayList<>();
classes.add(theClass);
filteredSchedule.setClasses(classes);
}
}
/**
* Get a new schedule that only contains the data that applies to the specified teacher, excluding substitutions
* for the specified subjects.
*
* @param teacher the teacher whose substitutions should be included
* @param excludedSubjects the subjects that should be excluded
* @return a new SubstitutionSchedule containing the filtered data
*/
public SubstitutionSchedule filteredByTeacherAndExcludedSubject(String teacher, Set excludedSubjects) {
SubstitutionSchedule filteredSchedule = this.clone();
filterByTeacherAndExcludedSubject(filteredSchedule, teacher, excludedSubjects);
return filteredSchedule;
}
protected void filterByTeacherAndExcludedSubject(SubstitutionSchedule filteredSchedule, String teacher,
Set excludedSubjects) {
for (int i = 0; i < filteredSchedule.getDays().size(); i++) {
SubstitutionScheduleDay day = filteredSchedule.getDays().get(i);
SubstitutionScheduleDay filteredDay = day.clone();
filteredDay.setSubstitutions(day.getSubstitutionsByTeacherAndExcludedSubject(teacher, excludedSubjects));
filteredSchedule.getDays().set(i, filteredDay);
}
if (teacher != null) {
List teachers = new ArrayList<>();
teachers.add(teacher);
filteredSchedule.setTeachers(teachers);
}
}
@Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SubstitutionSchedule that = (SubstitutionSchedule) o;
return new EqualsBuilder()
.append(type, that.type)
.append(lastChange, that.lastChange)
.append(lastChangeString, that.lastChangeString)
.append(website, that.website)
.append(days, that.days)
.append(additionalInfos, that.additionalInfos)
.append(classes, that.classes)
.append(teachers, that.teachers)
.isEquals();
}
@Override public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(type)
.append(lastChange)
.append(lastChangeString)
.append(website)
.append(days)
.append(additionalInfos)
.append(classes)
.append(teachers)
.toHashCode();
}
/**
* Represents the type of a substitution schedule
*/
public enum Type {
/**
* Schedules with this type are primarily intended for students. They should contain a list of classes and
* the substitutions may not contain information about the teachers.
*/
STUDENT,
/**
* Schedules with this type are primarily intended for teachers. The substitutions should contain information
* about the teachers.
*/
TEACHER
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("--------------------\n");
builder.append("SubstitutionSchedule\n");
builder.append("--------------------\n\n");
builder.append("type: ").append(type).append("\n");
builder.append("last change: ").append(getLastChangeString()).append("\n");
builder.append("website: ").append(website).append("\n");
if (classes != null) {
builder.append("classes: ").append(classes.toString()).append("\n");
}
if (teachers != null) {
builder.append("teachers: ").append(teachers.toString()).append("\n");
}
builder.append("\n\n");
builder.append("Schedule\n");
builder.append("--------\n\n");
for (SubstitutionScheduleDay day : days) builder.append(day.toString(type)).append("\n\n");
if (additionalInfos.size() > 0) {
builder.append("Additional Infos\n");
builder.append("----------------\n\n");
for (AdditionalInfo info : additionalInfos) {
builder.append(info.getTitle()).append("\n").append(info.getText()).append("\n\n");
}
}
return builder.toString();
}
}