package edu.pdx.cs410J.grader; import edu.pdx.cs410J.ParserException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.Reader; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; /** * This class parses XML data that describes a <code>Student</code>. */ class XmlStudentParser extends XmlHelper { /** The Reader from which the XML Data is read */ private Reader reader; /////////////////////// Constructors //////////////////////// /** * Creates a new <code>XmlStudentParser</code> that reads XML data * from a given <code>Reader</code> */ public XmlStudentParser(Reader reader) { this.reader = reader; } /** * Creates a new <code>XmlStudentParser</code> that reads XML data * from a given <code>File</code> */ public XmlStudentParser(File file) throws IOException { this(new FileReader(file)); } ///////////////////// Instance Methods ////////////////////// /** * Extracts a <code>Grade</code> from a chuck of a DOM tree */ private Grade extractGradeFrom(Element root) throws ParserException { String name = null; String score = null; List<String> notes = null; List<LocalDateTime> submissions = null; NodeList kids = root.getChildNodes(); for (int j = 0; j < kids.getLength(); j++) { Node kidNode = kids.item(j); if (!(kidNode instanceof Element)) { continue; } Element kid = (Element) kidNode; if (kid.getTagName().equals("name")) { name = extractTextFrom(kid); } else if (kid.getTagName().equals("score")) { score = extractTextFrom(kid); } else if (kid.getTagName().equals("notes")) { notes = extractNotesFrom(kid); } else if (kid.getTagName().equals("submissions")) { submissions = extractSubmissionsFrom(kid); } } if (name == null || score == null) { throw new ParserException("Malformed grade"); } try { double s = Double.parseDouble(score); Grade grade = new Grade(name, s); if (notes != null) { notes.forEach(grade::addNote); } if (submissions != null) { submissions.forEach(grade::addSubmissionTime); } return grade; } catch (NumberFormatException ex) { throw new ParserException("Malformatted number: " + score); } } private static List<LocalDateTime> extractSubmissionsFrom(Element parent) { List<LocalDateTime> list = new ArrayList<>(); NodeList children = parent.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node node = children.item(i); if (!(node instanceof Element)) { continue; } Element child = (Element) node; if (child.getTagName().equals("submission")) { String text = extractTextFrom(child); list.add(LocalDateTime.parse(text, DATE_TIME_FORMAT)); } } return list; } /** * Creates a new <code>Student</code> from the contents of a file. */ public Student parseStudent() throws ParserException { Document doc = parseXml(); Element root = getRootElement(doc); Student student = null; NodeList children = root.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node node = children.item(i); if (!(node instanceof Element)) { continue; } Element child = (Element) node; if (child.getTagName().equals("id")) { String idFromFile = extractTextFrom(child); student = new Student(idFromFile); } else if (child.getTagName().equals("firstName")) { String firstName = extractTextFrom(child); student.setFirstName(firstName); } else if (child.getTagName().equals("lastName")) { String lastName = extractTextFrom(child); student.setLastName(lastName); } else if (child.getTagName().equals("nickName")) { String nickName = extractTextFrom(child); student.setNickName(nickName); } else if (child.getTagName().equals("email")) { String email = extractTextFrom(child); student.setEmail(email); } else if (child.getTagName().equals("ssn")) { String ssn = extractTextFrom(child); student.setSsn(ssn); } else if (child.getTagName().equals("major")) { String major = extractTextFrom(child); student.setMajor(major); } else if (child.getTagName().equals("d2l-id")) { String d2lId = extractTextFrom(child); student.setD2LId(d2lId); } else if (child.getTagName().equals("letter-grade")) { String letterGrade = extractTextFrom(child); student.setLetterGrade(LetterGrade.fromString(letterGrade)); } else if (child.getTagName().equals("grades")) { NodeList kids = child.getChildNodes(); for (int j = 0; j < kids.getLength(); j++) { Node kidNode = kids.item(j); if (!(kidNode instanceof Element)) { continue; } Element kid = (Element) kidNode; if (kid.getTagName().equals("grade")) { Grade grade = extractGradeFrom(kid); student.setGrade(grade.getAssignmentName(), grade); } } } else if (child.getTagName().equals("late")) { NodeList kids = child.getChildNodes(); for (int j = 0; j < kids.getLength(); j++) { Node kidNode = kids.item(j); if (!(kidNode instanceof Element)) { continue; } Element kid = (Element) kidNode; if (kid.getTagName().equals("name")) { student.addLate(extractTextFrom(kid)); } } } else if (child.getTagName().equals("resubmitted")) { NodeList kids = child.getChildNodes(); for (int j = 0; j < kids.getLength(); j++) { Node kidNode = kids.item(j); if (!(kidNode instanceof Element)) { continue; } Element kid = (Element) kidNode; if (kid.getTagName().equals("name")) { student.addResubmitted(extractTextFrom(kid)); } } } else if (child.getTagName().equals("notes")) { for (String note : extractNotesFrom(child)) { student.addNote(note); } } } if (student != null) { if (root.hasAttribute("enrolled-section")) { String section = root.getAttribute("enrolled-section"); student.setEnrolledSection(Student.Section.fromString(section)); } // Students are initially clean student.makeClean(); } return student; } private Element getRootElement(Document doc) throws ParserException { Element root = null; if (doc != null) { root = doc.getDocumentElement(); } if (doc == null || root == null) { throw new ParserException("Document parsing failed"); } if (!root.getTagName().equals("student")) { String s = "XML data does not contain a student"; throw new ParserException(s); } return root; } private Document parseXml() throws ParserException { Document doc = null; try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(true); DocumentBuilder builder = factory.newDocumentBuilder(); builder.setErrorHandler(this); builder.setEntityResolver(this); doc = builder.parse(new InputSource(this.reader)); } catch (ParserConfigurationException | IOException | SAXException ex) { throw new ParserException("While parsing XML source: " + ex); } return doc; } }