/* Author: Jeff Dalton * Updated: Fri Nov 30 13:38:07 2001 by Jeff Dalton * Copyright: (c) 2001, AIAI, University of Edinburgh */ package ix.iface.util; import java.util.*; import java.io.IOException; import java.io.StringReader; // Imports for using JDOM // import java.io.IOException; import org.jdom.Document; import org.jdom.Element; import org.jdom.Attribute; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.jdom.output.XMLOutputter; import ix.icore.Issue; import ix.icore.BasicIssue; import ix.icore.Report; import ix.icore.process.StatusValues; import ix.ichat.ChatMessage; import ix.util.lisp.*; import ix.util.*; /** * A class that encapsulates the XML knowledge needed for * sending and receiving Issues and Reports and that contains * other useful static XML utilities.

* * Example issue: *

 *    <issue sender-id="ILEED" ref="ILEED-IDEEL-1" report-back="no">
 *       <priority>high</priority>
 *       <statement>avoid elephants laki_safari_park</statement>
 *    </issue>
 * 
* * Example report: *
 *    <report sender-id="MCA" ref="MCA-IDEEL-1">
 *       done - no conflicts
 *    </report>
 * 
*/ /* Those examples again: * * * high * avoid elephants laki_safari_park * * * * done - no conflicts * */ public class XML { private XML() { } // don't allow instantiation /** * Thrown to indicate a problem converting to or from XML. */ public static class XMLException extends RuntimeException { public XMLException(String message) { super(message); } } /* * XML Parsing */ /** * Converts a string of XML to an object such as an Issue or Report. */ public static Object objectFromXML(String text) { Document doc = parseXML(text); Element root = doc.getRootElement(); String rootName = root.getName(); if (rootName.equals("issue")) return issueFromXML(root); else if (rootName.equals("report")) return reportFromXML(root); else if (rootName.equals("service-address")) return serviceAddressFromXML(root); else if (rootName.equals("chat-message")) return chatMessageFromXML(root); else throw new XMLException("Can't handle " + root); } /** * A Set containing the attribute names that are valid in "issue" * elements. */ public static Set issueAttributes = new HashSet(Lisp.list("sender-id", "ref", "report-back")); /** * Converts a JDOM Element to an Issue. */ public static Issue issueFromXML(Element issueElt) { int priority = -1; String statement = null; for (Iterator i = issueElt.getChildren().iterator(); i.hasNext();) { Element child = (Element)i.next(); if (child.getName().equals("priority")) priority = priorityFromXML(child); else if (child.getName().equals("statement")) statement = statementFromXML(child); else throw new XMLException("Illegal within issue: " + child); } if (statement == null) throw new XMLException("Need a statement in " + issueElt); Issue issue = new BasicIssue(statement); if (priority != -1) issue.setPriority(priority); issue.setProperties (propertiesFromAttributes(issueElt, issueAttributes)); return issue; } public static int priorityFromXML(Element priorityElt) { String name = priorityElt.getText(); return ViewColor.priorityValue(name); } public static String statementFromXML(Element statementElt) { return statementElt.getText(); } /** * A Set containing the attribute names that are valid in "report" * elements. */ // report-type = success, failure, or progress public static Set reportAttributes = new HashSet(Lisp.list("sender-id", "ref", "report-type")); /** * Converts a JDOM Element to a Report. */ public static Report reportFromXML(Element reportElt) { Report report = new Report(reportElt.getText()); report.setProperties (propertiesFromAttributes(reportElt, reportAttributes)); return report; } /** * Converts a JDOM Element to an IPC.ServiceAddress. */ public static IPC.ServiceAddress serviceAddressFromXML(Element addrElt) { // This is messy but should work. /\/ Map props = propertiesFromAttributes(addrElt, serviceAddressAttributes); String host = (String)props.get("host"); Long port = (Long)Lisp.readFromString((String)props.get("port")); return new IPC.ServiceAddress(host, port.intValue()); } public static Set serviceAddressAttributes = new HashSet(Lisp.list("host", "port")); /** * Converts a JDOM Element to a ChatMessage */ public static ChatMessage chatMessageFromXML(Element elt) { Map props = propertiesFromAttributes(elt, chatMessageAttributes); String senderId = (String)props.get("sender-id"); String text = elt.getText(); return new ChatMessage(text, senderId); } public static Set chatMessageAttributes = new HashSet(Lisp.list("sender-id")); /** * Converts XML attributes to a Map. */ public static Map propertiesFromAttributes(Element elt, Set validNames) { Map map = new HashMap(5); List attributes = elt.getAttributes(); for (Iterator i = attributes.iterator(); i.hasNext();) { Attribute attr = (Attribute)i.next(); String name = attr.getName(); if (validNames.contains(name)) map.put(name, attr.getValue()); else throw new XMLException ("Invalid attribute " + Util.quote(name) + " in " + elt); } return map; } /** * The class of the SAX parser used by the parseXML * method. */ static String SAXDriverClass = "org.apache.xerces.parsers.SAXParser"; /** * Converts a string of XML to a JDOM Document. */ public static Document parseXML(String text) { StringReader reader = new StringReader(text); try { SAXBuilder builder = new SAXBuilder(SAXDriverClass); Document doc = builder.build(reader); return doc; // for now /\/ } catch (Exception e) { Debug.noteln("While parsing", e); // Debug.noteException(e); // Throw because we have nothing to return // and stray nulls are a pain. throw new XMLException("Can't parse because " + e); } } /* * XML Generation */ /** * Converts an object, such as an Issue or Report, to a string of XML. */ public static String objectToXML(Object contents) { if (contents instanceof Issue) { return issueToXML((Issue)contents); } else if (contents instanceof Report) { return reportToXML((Report)contents); } else if (contents instanceof IPC.ServiceAddress) { return serviceAddressToXML((IPC.ServiceAddress)contents); } else if (contents instanceof ChatMessage) { return chatMessageToXML((ChatMessage)contents); } else { throw new XMLException("Can't convert to XML " + contents); } } public static String issueToXML(Issue issue) { // Issues should make it easier to get a description by // providing an appropriate method. /\/ return "" + priorityToXML(issue.getPriority()) + statementToXML(issueDescription(issue)) + ""; } public static String priorityToXML(int priority) { if (priority == StatusValues.PRIORITY_NONE) return ""; else { String p = ViewColor.priorityName[priority].toLowerCase(); return "" + p + ""; } } public static String statementToXML(String statement) { return "" + statement + ""; } public static String issueDescription(Issue i) { LList pattern = Lisp.cons(i.getVerb(), (LList)i.getParameters()); return Lisp.elementsToString(pattern); } public static String reportToXML(Report report) { return "" + report.getText() + ""; } public static String propertiesToAttributes(Map map, Set validNames) { StringBuffer result = new StringBuffer(64); for (Iterator i = map.entrySet().iterator(); i.hasNext();) { Map.Entry e = (Map.Entry)i.next(); String name = e.getKey().toString(); String value = e.getValue().toString(); if (validNames.contains(name)) result .append(" ") .append(name).append("=\"").append(value).append("\""); else ; // ignore } return result.toString(); } public static String serviceAddressToXML(IPC.ServiceAddress addr) { return ""; } public static String chatMessageToXML(ChatMessage message) { return "" + message.getText() + ""; } /** * An XML outputter created by makePrettyXMLOutputter(). */ public static XMLOutputter prettyXMLOutputter = makePrettyXMLOutputter(); /** * Constructs a JDOM XMLOutputter that outputs a JDOM Document * in a nicely indented fashion. For example: *
     *    XMLOutputter outputter = makePrettyXMLOutputter()
     *    outputter.output(doc, System.out);
     *    System.out.flush();
     * 
*/ public static XMLOutputter makePrettyXMLOutputter() { // Create an outputter with 3 space indent and newlines=true XMLOutputter outputter = new XMLOutputter(" ", true); outputter.setTrimText(true); return outputter; } }