001package edu.pdx.cs.joy.grader.gradebook; 002 003import org.w3c.dom.*; 004import org.xml.sax.*; 005 006import javax.xml.transform.*; 007import javax.xml.transform.dom.DOMSource; 008import javax.xml.transform.stream.StreamResult; 009import java.io.*; 010import java.net.URL; 011import java.time.format.DateTimeFormatter; 012import java.util.ArrayList; 013import java.util.List; 014 015/** 016 * This class contains fields and methods that are useful when dealing 017 * with XML data. 018 */ 019public class XmlHelper implements EntityResolver, ErrorHandler { 020 021 /** The System ID for the Grade Book DTD */ 022 protected static final String systemID = 023 "http://www.cs.pdx.edu/~whitlock/dtds/gradebook.dtd"; 024 025 /** The Public ID for the Grade Book DTD */ 026 protected static final String publicID = 027 "-//Joy of Coding at PSU//DTD Grade Book//EN"; 028 029 /** The Old Public ID for the Grade Book DTD */ 030 private static final String oldPublicID = 031 "-//Portland State University//DTD CS410J Grade Book//EN"; 032 033 protected static final DateTimeFormatter DATE_TIME_FORMAT = DateTimeFormatter.ISO_LOCAL_DATE_TIME; 034 035 public static byte[] getBytesForXmlDocument(Document xmlDoc) throws TransformerException { 036 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 037 PrintWriter pw = 038 new PrintWriter(new OutputStreamWriter(baos), true); 039 writeXmlToPrintWriter(xmlDoc, pw); 040 return baos.toByteArray(); 041 } 042 043 //////////////////// EntityResolver Methods ////////////////// 044 045 /** 046 * Attempt to resolve the external entity (such as a DTD) described 047 * by the given public and system ID. The external entity is 048 * returned as a <code>InputSource</code> 049 */ 050 @Override 051 public InputSource resolveEntity (String publicId, String systemId) 052 throws SAXException, IOException { 053 054 if (publicId != null && (publicId.equals(XmlHelper.publicID) || publicId.equals(oldPublicID))) { 055 // We're resolving the external entity for the Grade Book's 056 // DTD. Check to see if it's in the jar file. This way we don't 057 // need to go all the way to the website to find the DTD. 058 String location = "/edu/pdx/cs/joy/grader/gradebook.dtd"; 059 InputStream stream = 060 this.getClass().getResourceAsStream(location); 061 if (stream != null) { 062 return new InputSource(stream); 063 } 064 } 065 066 // Try to access the DTD using the URL 067 try { 068 URL url = new URL(systemId); 069 InputStream stream = url.openStream(); 070 return new InputSource(stream); 071 072 } catch (Exception ex) { 073 return null; 074 } 075 } 076 077 ////////////////// ErrorHandler Methods //////////////////////// 078 079 @Override 080 public void warning(SAXParseException ex) throws SAXException { 081 // Most warnings are annoying 082 083// String s = "Warning while parsing XML (" + ex.getLineNumber() + 084// ":" + ex.getColumnNumber() + "): " + ex.getMessage(); 085// System.err.println(s); 086 } 087 088 @Override 089 public void error(SAXParseException ex) throws SAXException { 090 String s = "Error while parsing XML (" + ex.getLineNumber() + 091 ":" + ex.getColumnNumber() + "): " + ex.getMessage(); 092 throw new SAXException(s); 093 } 094 095 @Override 096 public void fatalError(SAXParseException ex) throws SAXException { 097 String s = "Fatal error while parsing XML (" + ex.getLineNumber() 098 + ":" + ex.getColumnNumber() + "): " + ex.getMessage(); 099 throw new SAXException(s); 100 } 101 102 /////////////////// Other Helper Methods /////////////////////// 103 104 /** 105 * Extracts a bunch of notes from an <code>Element</code> 106 */ 107 protected static List<String> extractNotesFrom(Element element) { 108 List<String> list = new ArrayList<>(); 109 110 NodeList children = element.getChildNodes(); 111 for (int i = 0; i < children.getLength(); i++) { 112 Node node = children.item(i); 113 if (!(node instanceof Element)) { 114 continue; 115 } 116 117 Element child = (Element) node; 118 if (child.getTagName().equals("note")) { 119 list.add(extractTextFrom(child)); 120 } 121 } 122 123 return list; 124 } 125 126 /** 127 * Extracts the text from an <code>Element</code>. 128 */ 129 protected static String extractTextFrom(Element element) { 130 Text text = (Text) element.getFirstChild(); 131 return (text == null ? "" : text.getData()); 132 } 133 134 static void writeXmlToPrintWriter(Document doc, PrintWriter pw) throws TransformerException { 135 Source src = new DOMSource(doc); 136 Result res = new StreamResult(pw); 137 138 TransformerFactory xFactory = TransformerFactory.newInstance(); 139 Transformer xform = xFactory.newTransformer(); 140 xform.setOutputProperty(OutputKeys.INDENT, "yes"); 141 xform.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "0"); 142 xform.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, systemID); 143 xform.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, publicID); 144 145 // Suppress warnings about "Declared encoding not matching 146 // actual one 147 xform.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); 148 149 xform.transform(src, res); 150 } 151}