package eva.io;
import java.io.*;
import java.util.*;
import org.joda.time.*;
import org.joda.time.format.*;
import org.jdom.*;
import org.jdom.input.*;
import org.jdom.xpath.*;
import eva.schedule.*;
import eva.schedule.Parent;
import eva.tools.EVADateTimeFormat;
public class WishesReader {
InputStream in;
Document doc;
EveningSchedule eveningSchedule;
DateTimeFormatter dateTimeFormat;
//the XPaths to the nodes i need
final static String eveningSettingsPath = "/evening/evening-settings/setting";
final static String eventsPath = "/evening/events/event";
final static String teachersPath = "/evening/teachers/teacher";
final static String teacherVisitsPath = "./visit";
final static String parentVisitsPath = "./visit";
final static String parentsPath = "/evening/parents/parent";
public WishesReader(InputStream in) {
this.in = in;
/*
* i didn't find the right DateTimeFormatter for what i want,
* so i created my own
*/
dateTimeFormat = EVADateTimeFormat.EVAXMLDateTimeFormat();
}
public EveningSchedule getEveningSchedule() {
if(eveningSchedule == null)
throw new IllegalStateException("you must call read() before this");
return eveningSchedule;
}
public void read() throws InvalidWishesFile {
if(doc != null) {
throw new IllegalStateException("read() was already executed");
}
try {
doc = new SAXBuilder().build(in);
}
catch(Exception ex) {
throw new InvalidWishesFile("could not parse XML document", ex);
}
/*
* NOTE: we must read the document in the following way
* 1. <evening-settings>
* 2. <events>
* 3. <teachers>
* 4. <parents>
*
* because there are dependencies
*/
eveningSchedule = new EveningSchedule();
readEveningSettings();
readEvents();
readTeachers();
readParents();
}
private void readEveningSettings() throws InvalidWishesFile {
/*
* we read a XML document part that should look like this:
*
* <evening-settings>
* <setting key="slot-duration" value="slot duration in minutes" />
* <setting key="evening-start" value="start time in ISO format" />
* <setting key="evening-end" value="end time in ISO format" />
* <setting key="parent-buffer-slots" value="number of buffer slots for parents" />
* </evening-settings>
*/
Duration slotDuration = null;
int slotDurationInMinutes;
DateTime eveningStart = null;
DateTime eveningEnd = null;
Interval eveningInterval;
int parentsBufferSlots = -1;
List settingsNode = null;
Iterator nodeIter;
Element node;
try {
settingsNode = XPath.selectNodes(doc, eveningSettingsPath);
} catch (JDOMException e) {
throw new InvalidWishesFile("could not find the settings node in the XML file (" + eveningSettingsPath + ")", e);
}
nodeIter = settingsNode.iterator();
String key;
String value;
while(nodeIter.hasNext()) {
node = (Element) nodeIter.next();
key = node.getAttributeValue("key");
value = node.getAttributeValue("value");
if(key.equals("slot-duration")) {
slotDurationInMinutes = Integer.parseInt(value);
slotDuration = new Duration(slotDurationInMinutes*60*1000);
}
else if(key.equals("evening-start")) {
eveningStart = dateTimeFormat.parseDateTime(value);
}
else if(key.equals("evening-end")) {
eveningEnd = dateTimeFormat.parseDateTime(value);
}
else if(key.equals("parent-buffer-slots")) {
parentsBufferSlots = Integer.parseInt(value);
}
else {
throw new InvalidWishesFile("invalid setting key found in the XML file");
}
}
if(slotDuration != null && eveningEnd != null && eveningStart != null && parentsBufferSlots != -1) {
eveningInterval = new Interval(eveningStart, eveningEnd);
Evening.createEvening(eveningInterval, slotDuration, parentsBufferSlots);
}
else {
throw new InvalidWishesFile("could not find all needed settings value in the XML file");
}
}
private void readEvents() throws InvalidWishesFile {
/*
* we read the events from the XML file. The nodes should look like this
* <events>
* <event id="1" description="an event" begin="ISO datetime" slots="<duration in slots>" />
* </events>
*/
List nodeList = null;
Iterator nodeIter;
Element node;
try {
nodeList = XPath.selectNodes(doc, eventsPath);
} catch (JDOMException e) {
throw new InvalidWishesFile("could not find the events node in the XML file (" + eventsPath + ")", e);
}
nodeIter = nodeList.iterator();
int id;
String description;
DateTime begin;
int slots;
Event event;
while(nodeIter.hasNext()) {
node = (Element) nodeIter.next();
id = Integer.parseInt(node.getAttributeValue("id"));
description = node.getAttributeValue("description");
begin = dateTimeFormat.parseDateTime(node.getAttributeValue("begin"));
slots = Integer.parseInt(node.getAttributeValue("slots"));
if(slots > 0 && begin != null && description != null && id > 0) {
event = new Event(id, description, begin, slots);
event.setSchedule(new EventSchedule(event, new VisitsChecklist()));
eveningSchedule.add(event);
}
else {
throw new InvalidWishesFile("invalid event found in XML file");
}
}
}
private void readTeachers() throws InvalidWishesFile {
/*
* we read the teachers from the XML file. The nodes should look like this
* <teachers>
* <teacher id="1" firstname="A" lastname="teacher">
* <visit host-type="event" id="1" slots="1" />
* <!-- more visit of the teachers to events -->
* </teacher>
* </teachers>
*/
List nodeList = null;
Iterator nodeIter;
Element node;
try {
nodeList = XPath.selectNodes(doc, teachersPath);
} catch (JDOMException e) {
throw new InvalidWishesFile("could not find the events node in the XML file (" + teachersPath + ")", e);
}
nodeIter = nodeList.iterator();
Teacher teacher;
int id = 0;
String firstName = null;
String lastName = null;
while(nodeIter.hasNext()) {
node = (Element) nodeIter.next();
id = Integer.parseInt(node.getAttributeValue("id"));
firstName = node.getAttributeValue("firstname");
lastName = node.getAttributeValue("lastname");
if(firstName != null && lastName != null && id > 0) {
teacher = new Teacher(id, firstName, lastName);
eveningSchedule.add(teacher);
}
else {
throw new InvalidWishesFile("invalid teacher found in XML file");
}
readTeacherVisits(teacher, node);
}
}
private void readTeacherVisits(Teacher teacher, Element rootNode) throws InvalidWishesFile {
/*
* we read the visits of a teacher from the XML file. The nodes should look like this
*
* <visit host-type="event" id="1" slots="1" />
*
* node is the teacher node
*/
List nodeList = null;
Iterator nodeIter;
Element node;
try {
nodeList = XPath.selectNodes(rootNode, teacherVisitsPath);
} catch (JDOMException e) {
throw new InvalidWishesFile("could not find the events node in the XML file (" + teacherVisitsPath + ")", e);
}
nodeIter = nodeList.iterator();
List<VisitWish> wishes = new LinkedList<VisitWish>();
String hostType = null;
int hostId = 0;
int slotsCount = 0;
Event partner;
while(nodeIter.hasNext()) {
node = (Element) nodeIter.next();
hostType = node.getAttributeValue("host-type");
hostId = Integer.parseInt(node.getAttributeValue("id"));
slotsCount = Integer.parseInt(node.getAttributeValue("slots"));
if(hostType != null && hostId > 0 && slotsCount > 0) {
if(!hostType.equals("event")) {
throw new InvalidWishesFile("a teacher can only visit events, but a teacher in the XML file visits something else");
}
partner = eveningSchedule.getEvent(hostId);
wishes.add(new VisitWish(partner, slotsCount));
partner.getSchedule().getVisitsChecklist().add(new VisitWish(teacher, slotsCount));
}
}
VisitsChecklist visitsChecklist = new VisitsChecklist(wishes);
//TODO maybe teacher want also some buffer
PersonSchedule schedule = new PersonSchedule(teacher, visitsChecklist, 0);
teacher.setSchedule(schedule);
}
private void readParents() throws InvalidWishesFile {
/*
* we read the parents from the XML file. The nodes should look like this
* <parents>
* <parent id="1" firstname="A" lastname="parent" email="a_parent@parents.org" use-email="true or false">
* <visit host-type="teacher or event" id="1" slots="1" />
* <!-- more visit of the teachers to events -->
* </parent>
* </parents>
*/
List nodeList = null;
Iterator nodeIter;
Element node;
try {
nodeList = XPath.selectNodes(doc, parentsPath);
} catch (JDOMException e) {
throw new InvalidWishesFile("could not find the events node in the XML file (" + parentsPath + ")", e);
}
nodeIter = nodeList.iterator();
Parent parent;
int id = 0;
String firstName = null;
String lastName = null;
String eMail = null;
String useEMail = null;
while(nodeIter.hasNext()) {
node = (Element) nodeIter.next();
id = Integer.parseInt(node.getAttributeValue("id"));
firstName = node.getAttributeValue("firstname");
lastName = node.getAttributeValue("lastname");
useEMail = node.getAttributeValue("use-email");
if(useEMail.equals("true")) {
eMail = node.getAttributeValue("email");
}
else {
eMail = null;
}
if(firstName != null && lastName != null && id > 0) {
parent = new Parent(id, firstName, lastName, eMail);
eveningSchedule.add(parent);
readParentVisits(parent, node);
}
else {
throw new InvalidWishesFile("invalid parent found in XML file");
}
}
}
private void readParentVisits(Parent parent, Element rootNode) throws InvalidWishesFile {
/*
* we read the visits of a parent from the XML file. The nodes should look like this
*
* <visit host-type="event or teacher" id="1" slots="1" />
*
* node is the parent node
*/
List nodeList = null;
Iterator nodeIter;
Element node;
try {
nodeList = XPath.selectNodes(rootNode, parentVisitsPath);
} catch (JDOMException e) {
throw new InvalidWishesFile("could not find the events node in the XML file (" + parentVisitsPath + ")", e);
}
nodeIter = nodeList.iterator();
List<VisitWish> wishes = new LinkedList<VisitWish>();
String hostType = null;
int hostId = 0;
int slotsCount = 0;
int bufferSlotsCount = Evening.getEvening().getParentBufferSlots();
Appointable partner;
while(nodeIter.hasNext()) {
node = (Element) nodeIter.next();
hostType = node.getAttributeValue("host-type");
hostId = Integer.parseInt(node.getAttributeValue("id"));
slotsCount = Integer.parseInt(node.getAttributeValue("slots"));
if(hostType != null && hostId > 0 && slotsCount > 0) {
if(hostType.equals("event")) {
partner = eveningSchedule.getEvent(hostId);
}
else if(hostType.equals("teacher")) {
partner = eveningSchedule.getTeacher(hostId);
}
else {
throw new InvalidWishesFile("a parent can only visit events or teachers, but a parent in the XML file visits something else");
}
wishes.add(new VisitWish(partner, slotsCount));
partner.getSchedule().getVisitsChecklist().add(new VisitWish(parent, slotsCount));
}
}
VisitsChecklist visitsChecklist = new VisitsChecklist(wishes);
PersonSchedule schedule = new PersonSchedule(parent, visitsChecklist, bufferSlotsCount);
parent.setSchedule(schedule);
}
}