package prefuse.data.io; import java.io.InputStream; import java.util.Date; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.helpers.DefaultHandler; import prefuse.data.Graph; import prefuse.data.Node; import prefuse.data.Table; import prefuse.data.Tree; import prefuse.data.parser.DataParseException; import prefuse.data.parser.DataParser; import prefuse.data.parser.ParserFactory; /** * GraphReader instance that reads in tree-structured data in the * XML-based TreeML format. TreeML is an XML format originally created for * the 2003 InfoVis conference contest. A DTD (Document Type Definition) for * TreeML is * <a href="http://www.nomencurator.org/InfoVis2003/download/treeml.dtd"> * available online</a>. * * @author <a href="http://jheer.org">jeffrey heer</a> */ public class TreeMLReader extends AbstractGraphReader { private ParserFactory m_pf = ParserFactory.getDefaultFactory(); /** * @see prefuse.data.io.GraphReader#readGraph(java.io.InputStream) */ public Graph readGraph(InputStream is) throws DataIOException { try { TreeMLHandler handler = new TreeMLHandler(); SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser saxParser = factory.newSAXParser(); saxParser.parse(is, handler); return handler.getTree(); } catch ( Exception e ) { throw new DataIOException(e); } } /** * String tokens used in the TreeML format. */ public static interface Tokens { public static final String TREE = "tree"; public static final String BRANCH = "branch"; public static final String LEAF = "leaf"; public static final String ATTR = "attribute"; public static final String NAME = "name"; public static final String VALUE = "value"; public static final String TYPE = "type"; public static final String DECLS = "declarations"; public static final String DECL = "attributeDecl"; public static final String INT = "Int"; public static final String INTEGER = "Integer"; public static final String LONG = "Long"; public static final String FLOAT = "Float"; public static final String REAL = "Real"; public static final String STRING = "String"; public static final String DATE = "Date"; public static final String CATEGORY = "Category"; // prefuse-specific allowed types public static final String BOOLEAN = "Boolean"; public static final String DOUBLE = "Double"; } /** * A SAX Parser for TreeML data files. */ public class TreeMLHandler extends DefaultHandler implements Tokens { private Table m_nodes = null; private Tree m_tree = null; private Node m_activeNode = null; private boolean m_inSchema = true; public void startDocument() { m_tree = new Tree(); m_nodes = m_tree.getNodeTable(); } private void schemaCheck() { if ( m_inSchema ) { m_inSchema = false; } } public void endElement(String namespaceURI, String localName, String qName) { if ( qName.equals(BRANCH) || qName.equals(LEAF) ) { m_activeNode = m_activeNode.getParent(); } } public void startElement(String namespaceURI, String localName, String qName, Attributes atts) { if ( qName.equals(DECL) ) { if ( !m_inSchema ) { throw new RuntimeException("All declarations must be done " + "before nodes begin"); } String name = atts.getValue(NAME); String type = atts.getValue(TYPE); Class t = parseType(type); m_nodes.addColumn(name, t); } else if ( qName.equals(BRANCH) || qName.equals(LEAF) ) { schemaCheck(); // parse a node element Node n; if ( m_activeNode == null ) { n = m_tree.addRoot(); } else { n = m_tree.addChild(m_activeNode); } m_activeNode = n; } else if ( qName.equals(ATTR) ) { // parse an attribute parseAttribute(atts); } } protected void parseAttribute(Attributes atts) { String alName, name = null, value = null; for ( int i = 0; i < atts.getLength(); i++ ) { alName = atts.getQName(i); if ( alName.equals(NAME) ) { name = atts.getValue(i); } else if ( alName.equals(VALUE) ) { value = atts.getValue(i); } } if ( name == null || value == null ) { System.err.println("Attribute under-specified"); return; } try { Object val = parse(value, m_nodes.getColumnType(name)); m_activeNode.set(name, val); } catch ( Exception e ) { throw new RuntimeException(e); } } protected Object parse(String s, Class type) throws DataParseException { DataParser dp = m_pf.getParser(type); return dp.parse(s); } protected Class parseType(String type) { type = Character.toUpperCase(type.charAt(0)) + type.substring(1).toLowerCase(); if ( type.equals(INT) || type.equals(INTEGER) ) { return int.class; } else if ( type.equals(LONG) ) { return long.class; } else if ( type.equals(FLOAT) ) { return float.class; } else if ( type.equals(DOUBLE) || type.equals(REAL)) { return double.class; } else if ( type.equals(BOOLEAN) ) { return boolean.class; } else if ( type.equals(STRING) ) { return String.class; } else if ( type.equals(DATE) ) { return Date.class; } else { throw new RuntimeException("Unrecognized data type: "+type); } } public Tree getTree() { return m_tree; } } // end of inner class TreeMLHandler } // end of class TreeMLTReeReader