package edu.pdx.cs410J.grader; import org.w3c.dom.*; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.time.LocalDateTime; import java.util.Iterator; import java.util.List; import java.util.Objects; import static edu.pdx.cs410J.grader.GradeBook.LetterGradeRanges.LetterGradeRange; /** * This class dumps the contents of a <code>GradeBook</code> to an XML * file. By default, the students in the grade book are dumped to XML * files in the same directory as the grade book's XML file. * * @author David Whitlock * @since Fall 2000 */ public class XmlDumper extends XmlHelper { private File studentDir = null; // Where to dump student XML files PrintWriter pw = null; // Where to dump grade book /** * Creates a new <code>XmlDumper</code> that dumps the contents of a * grade book to a given file. */ public XmlDumper(File xmlFile) throws IOException { this(new PrintWriter(new FileWriter(xmlFile), true)); if (!xmlFile.exists()) { if (!xmlFile.createNewFile()) { throw new IOException("Could not create file " + xmlFile); } } this.setStudentDir(xmlFile.getCanonicalFile().getParentFile()); } /** * Creates a new <code>XmlDumper</code> that dumps the contents of a * grade book to a file of the given name. */ public XmlDumper(String xmlFileName) throws IOException { this(new File(xmlFileName)); } /** * Creates an <code>XmlDumper</code> that dumps the contents of a * grade book to a <code>PrintWriter</code>. The location of the * student files is unspecified. */ private XmlDumper(PrintWriter pw) { this.pw = pw; } /** * Sets the directory in which the XML files for students are * generated. */ public void setStudentDir(File dir) { if (dir.exists() && !dir.isDirectory()) { throw new IllegalArgumentException(dir + " is not a directory"); } this.studentDir = dir; } /** * Dumps the contents of a <code>GradeBook</code> in XML format. */ public void dump(GradeBook book) throws IOException { Document doc = dumpGradeBook(book, this); try { writeXmlToPrintWriter(doc, this.pw); } catch (TransformerException ex) { ex.printStackTrace(System.err); System.exit(1); } dumpDirtyStudents(book); // Mark the grade book as being clean book.makeClean(); } static Document dumpGradeBook(GradeBook book, XmlHelper helper) throws IOException { Document doc = createDocumentForGradeBook(helper); Element root = doc.getDocumentElement(); appendXmlForClassName(book, doc, root); appendXmlForAssignments(book, doc, root); appendXmlForLetterGradeRanges(book, doc, root); appendXmlForStudents(book, doc, root); return doc; } private static void appendXmlForLetterGradeRanges(GradeBook book, Document doc, Element root) { for (Student.Section section : book.getSections()) { Element lgrNode = appendXmlForLetterGradeRange(book, section, doc); root.appendChild(lgrNode); } } private static Element appendXmlForLetterGradeRange(GradeBook book, Student.Section section, Document doc) { Element lgrNode = doc.createElement("letter-grade-ranges"); lgrNode.setAttribute("for-section", getSectionXmlAttributeValue(section)); for (LetterGradeRange range : book.getLetterGradeRanges(section)) { appendXmlForLetterGradeRange(range, doc, lgrNode); } return lgrNode; } private static String getSectionXmlAttributeValue(Student.Section section) { return Objects.toString(section, null); } private static void appendXmlForLetterGradeRange(LetterGradeRange range, Document doc, Element parent) { Element node = doc.createElement("letter-grade-range"); node.setAttribute("letter-grade", range.letterGrade().asString()); node.setAttribute("minimum-score", String.valueOf(range.minimum())); node.setAttribute("maximum-score", String.valueOf(range.maximum())); parent.appendChild(node); } private static Document createDocumentForGradeBook(XmlHelper helper) { Document doc = null; try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(true); DocumentBuilder builder = factory.newDocumentBuilder(); builder.setErrorHandler(helper); builder.setEntityResolver(helper); DOMImplementation dom = builder.getDOMImplementation(); DocumentType docType = dom.createDocumentType("gradebook", publicID, systemID); doc = dom.createDocument(null, "gradebook", docType); } catch (ParserConfigurationException | DOMException ex) { ex.printStackTrace(System.err); System.exit(1); } return doc; } private static void appendXmlForStudents(GradeBook book, Document doc, Element root) { // Students Element studentsNode = doc.createElement("students"); for (String id : book.getStudentIds()) { Element studentNode = doc.createElement("id"); studentNode.appendChild(doc.createTextNode(id)); studentsNode.appendChild(studentNode); } root.appendChild(studentsNode); } private static void appendXmlForClassName(GradeBook book, Document doc, Element root) { // name node Element name = doc.createElement("name"); name.appendChild(doc.createTextNode(book.getClassName())); root.appendChild(name); } private static void appendXmlForAssignments(GradeBook book, Document doc, Element root) { // assignment nodes Element assignments = doc.createElement("assignments"); for (String assignmentName : book.getAssignmentNames()) { Assignment assign = book.getAssignment(assignmentName); Element assignNode = doc.createElement("assignment"); setAssignmentTypeAttribute(assign, assignNode); appendTextElementIfValueIsNotNull(assignNode, "name", assign.getName()); appendTextElementIfValueIsNotNull(assignNode, "description", assign.getDescription()); appendTextElementIfValueIsNotNull(assignNode, "points", String.valueOf(assign.getPoints())); appendTextElementIfValueIsNotNull(assignNode, "due-date", assign.getDueDate()); doNotes(doc, assignNode, assign.getNotes()); assignments.appendChild(assignNode); } root.appendChild(assignments); } private static void appendTextElementIfValueIsNotNull(Element parent, String elementName, LocalDateTime dateTime) { if (dateTime != null) { appendTextElementIfValueIsNotNull(parent, elementName, dateTime.format(DATE_TIME_FORMAT)); } } private static void setAssignmentTypeAttribute(Assignment assign, Element assignNode) { Assignment.AssignmentType type = assign.getType(); switch (type) { case PROJECT: assignNode.setAttribute("type", "PROJECT"); break; case QUIZ: assignNode.setAttribute("type", "QUIZ"); break; case OTHER: assignNode.setAttribute("type", "OTHER"); break; case OPTIONAL: assignNode.setAttribute("type", "OPTIONAL"); break; case POA: assignNode.setAttribute("type", "POA"); break; default: throw new IllegalArgumentException("Can't handle assignment " + "type " + type); } } private void dumpDirtyStudents(GradeBook book) throws IOException { book.forEachStudent(student -> { if (student.isDirty()) { dumpStudent(student); } }); } /** * Creates a <code>notes</code> XML element for a given * <code>List</code> of notes. */ private static void doNotes(Document doc, Element parent, List<String> notes) { Element notesNode = doc.createElement("notes"); for (String note : notes) { Element noteNode = doc.createElement("note"); noteNode.appendChild(doc.createTextNode(note)); notesNode.appendChild(noteNode); } parent.appendChild(notesNode); } /** * Dumps a <code>Student</code> out to an XML file whose name is * based on the student's id and resides in the * <code>studentDir</code>. */ private void dumpStudent(Student student) { Document doc = toXml(student); // Now dump DOM tree to the file File studentFile = new File(studentDir, student.getId() + ".xml"); PrintWriter pw = new PrintWriter(newFileWriter(studentFile), true); try { writeXmlToPrintWriter(doc, pw); } catch (TransformerException ex) { ex.printStackTrace(System.err); System.exit(1); } } private FileWriter newFileWriter(File studentFile) { try { return new FileWriter(studentFile); } catch (IOException ex) { throw new IllegalStateException("Couldn't create FileWriter for " + studentFile, ex); } } /** * Returns a DOM tree that represents a <code>Student</code> */ static Document toXml(Student student) { Document doc = createXmlDocument(); Element root = doc.getDocumentElement(); appendStudentInformation(student, root); appendGradesInformation(student, root); appendLateInformation(student, root); appendResubmittedInformation(student, root); appendNotes(student, root); return doc; } private static void appendNotes(Student student, Element parent) { List<String> notes = student.getNotes(); if (!notes.isEmpty()) { doNotes(parent.getOwnerDocument(), parent, notes); } } private static void appendResubmittedInformation(Student student, Element parent) { Document doc = parent.getOwnerDocument(); List<String> resubmitted = student.getResubmitted(); if (!resubmitted.isEmpty()) { Element resubNode = doc.createElement("resubmitted"); for (String assignmentName : resubmitted) { Element nameNode = doc.createElement("name"); nameNode.appendChild(doc.createTextNode(assignmentName)); resubNode.appendChild(nameNode); } parent.appendChild(resubNode); } } private static void appendLateInformation(Student student, Element parent) { Document doc = parent.getOwnerDocument(); List<String> late = student.getLate(); if (!late.isEmpty()) { Element lateNode = doc.createElement("late"); for (String assignmentName : late) { Element nameNode = doc.createElement("name"); nameNode.appendChild(doc.createTextNode(assignmentName)); lateNode.appendChild(nameNode); } parent.appendChild(lateNode); } } private static void appendGradesInformation(Student student, Element parent) { Document doc = parent.getOwnerDocument(); Iterator gradeNames = student.getGradeNames().iterator(); if (gradeNames.hasNext()) { Element gradesNode = doc.createElement("grades"); while (gradeNames.hasNext()) { String gradeName = (String) gradeNames.next(); Grade grade = student.getGrade(gradeName); Element gradeNode = doc.createElement("grade"); Element nameNode = doc.createElement("name"); nameNode.appendChild(doc.createTextNode(grade.getAssignmentName())); gradeNode.appendChild(nameNode); Element scoreNode = doc.createElement("score"); scoreNode.appendChild(doc.createTextNode(grade.getScore() + "")); gradeNode.appendChild(scoreNode); appendSubmissionTimesInformation(grade.getSubmissionTimes(), gradeNode); doNotes(doc, gradeNode, grade.getNotes()); if (grade.getScore() == Grade.INCOMPLETE) { gradeNode.setAttribute("type", "INCOMPLETE"); } else if (grade.getScore() == Grade.NO_GRADE) { gradeNode.setAttribute("type", "NO_GRADE"); } gradesNode.appendChild(gradeNode); } parent.appendChild(gradesNode); } } private static void appendSubmissionTimesInformation(List<LocalDateTime> submissionTimes, Element parent) { if (!submissionTimes.isEmpty()) { Document doc = parent.getOwnerDocument(); Element submissions = doc.createElement("submissions"); parent.appendChild(submissions); submissionTimes.forEach(submissionTime -> { Element submission = doc.createElement("submission"); submissions.appendChild(submission); submission.appendChild(doc.createTextNode(submissionTime.format(DATE_TIME_FORMAT))); }); } } private static void appendStudentInformation(Student student, Element root) { appendTextElementIfValueIsNotNull(root, "id", student.getId()); appendTextElementIfValueIsNotNull(root, "firstName", student.getFirstName()); appendTextElementIfValueIsNotNull(root, "lastName", student.getLastName()); appendTextElementIfValueIsNotNull(root, "nickName", student.getNickName()); appendTextElementIfValueIsNotNull(root, "email", student.getEmail()); appendTextElementIfValueIsNotNull(root, "ssn", student.getSsn()); appendTextElementIfValueIsNotNull(root, "major", student.getMajor()); appendTextElementIfValueIsNotNull(root, "d2l-id", student.getD2LId()); appendTextElementIfValueIsNotNull(root, "letter-grade", Objects.toString(student.getLetterGrade(), null)); setAttributeIfValueIsNotNull(root, "enrolled-section", getSectionXmlAttributeValue(student.getEnrolledSection())); } private static void setAttributeIfValueIsNotNull(Element root, String name, String value) { if (value != null) { root.setAttribute(name, value); } } private static Document createXmlDocument() { Document doc = null; try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(true); DocumentBuilder builder = factory.newDocumentBuilder(); DOMImplementation dom = builder.getDOMImplementation(); DocumentType docType = dom.createDocumentType("student", publicID, systemID); doc = dom.createDocument(null, "student", docType); } catch (ParserConfigurationException | DOMException ex) { ex.printStackTrace(System.err); System.exit(1); } return doc; } private static void appendTextElementIfValueIsNotNull(Element parent, String elementName, String textValue) { if (textValue != null) { Document doc = parent.getOwnerDocument(); Element id = doc.createElement(elementName); id.appendChild(doc.createTextNode(textValue)); parent.appendChild(id); } } }