001: package prefuse.data.io;
002:
003: import java.io.InputStream;
004: import java.util.Date;
005:
006: import javax.xml.parsers.SAXParser;
007: import javax.xml.parsers.SAXParserFactory;
008:
009: import org.xml.sax.Attributes;
010: import org.xml.sax.helpers.DefaultHandler;
011:
012: import prefuse.data.Graph;
013: import prefuse.data.Node;
014: import prefuse.data.Table;
015: import prefuse.data.Tree;
016: import prefuse.data.parser.DataParseException;
017: import prefuse.data.parser.DataParser;
018: import prefuse.data.parser.ParserFactory;
019:
020: /**
021: * GraphReader instance that reads in tree-structured data in the
022: * XML-based TreeML format. TreeML is an XML format originally created for
023: * the 2003 InfoVis conference contest. A DTD (Document Type Definition) for
024: * TreeML is
025: * <a href="http://www.nomencurator.org/InfoVis2003/download/treeml.dtd">
026: * available online</a>.
027: *
028: * @author <a href="http://jheer.org">jeffrey heer</a>
029: */
030: public class TreeMLReader extends AbstractGraphReader {
031:
032: private ParserFactory m_pf = ParserFactory.getDefaultFactory();
033:
034: /**
035: * @see prefuse.data.io.GraphReader#readGraph(java.io.InputStream)
036: */
037: public Graph readGraph(InputStream is) throws DataIOException {
038: try {
039: TreeMLHandler handler = new TreeMLHandler();
040: SAXParserFactory factory = SAXParserFactory.newInstance();
041: SAXParser saxParser = factory.newSAXParser();
042: saxParser.parse(is, handler);
043: return handler.getTree();
044: } catch (Exception e) {
045: throw new DataIOException(e);
046: }
047: }
048:
049: /**
050: * String tokens used in the TreeML format.
051: */
052: public static interface Tokens {
053: public static final String TREE = "tree";
054: public static final String BRANCH = "branch";
055: public static final String LEAF = "leaf";
056: public static final String ATTR = "attribute";
057: public static final String NAME = "name";
058: public static final String VALUE = "value";
059: public static final String TYPE = "type";
060:
061: public static final String DECLS = "declarations";
062: public static final String DECL = "attributeDecl";
063:
064: public static final String INT = "Int";
065: public static final String INTEGER = "Integer";
066: public static final String LONG = "Long";
067: public static final String FLOAT = "Float";
068: public static final String REAL = "Real";
069: public static final String STRING = "String";
070: public static final String DATE = "Date";
071: public static final String CATEGORY = "Category";
072:
073: // prefuse-specific allowed types
074: public static final String BOOLEAN = "Boolean";
075: public static final String DOUBLE = "Double";
076: }
077:
078: /**
079: * A SAX Parser for TreeML data files.
080: */
081: public class TreeMLHandler extends DefaultHandler implements Tokens {
082:
083: private Table m_nodes = null;
084: private Tree m_tree = null;
085:
086: private Node m_activeNode = null;
087: private boolean m_inSchema = true;
088:
089: public void startDocument() {
090: m_tree = new Tree();
091: m_nodes = m_tree.getNodeTable();
092: }
093:
094: private void schemaCheck() {
095: if (m_inSchema) {
096: m_inSchema = false;
097: }
098: }
099:
100: public void endElement(String namespaceURI, String localName,
101: String qName) {
102: if (qName.equals(BRANCH) || qName.equals(LEAF)) {
103: m_activeNode = m_activeNode.getParent();
104: }
105: }
106:
107: public void startElement(String namespaceURI, String localName,
108: String qName, Attributes atts) {
109: if (qName.equals(DECL)) {
110: if (!m_inSchema) {
111: throw new RuntimeException(
112: "All declarations must be done "
113: + "before nodes begin");
114: }
115: String name = atts.getValue(NAME);
116: String type = atts.getValue(TYPE);
117: Class t = parseType(type);
118: m_nodes.addColumn(name, t);
119: } else if (qName.equals(BRANCH) || qName.equals(LEAF)) {
120: schemaCheck();
121:
122: // parse a node element
123: Node n;
124: if (m_activeNode == null) {
125: n = m_tree.addRoot();
126: } else {
127: n = m_tree.addChild(m_activeNode);
128: }
129: m_activeNode = n;
130: } else if (qName.equals(ATTR)) {
131: // parse an attribute
132: parseAttribute(atts);
133: }
134: }
135:
136: protected void parseAttribute(Attributes atts) {
137: String alName, name = null, value = null;
138: for (int i = 0; i < atts.getLength(); i++) {
139: alName = atts.getQName(i);
140: if (alName.equals(NAME)) {
141: name = atts.getValue(i);
142: } else if (alName.equals(VALUE)) {
143: value = atts.getValue(i);
144: }
145: }
146: if (name == null || value == null) {
147: System.err.println("Attribute under-specified");
148: return;
149: }
150:
151: try {
152: Object val = parse(value, m_nodes.getColumnType(name));
153: m_activeNode.set(name, val);
154: } catch (Exception e) {
155: throw new RuntimeException(e);
156: }
157: }
158:
159: protected Object parse(String s, Class type)
160: throws DataParseException {
161: DataParser dp = m_pf.getParser(type);
162: return dp.parse(s);
163: }
164:
165: protected Class parseType(String type) {
166: type = Character.toUpperCase(type.charAt(0))
167: + type.substring(1).toLowerCase();
168: if (type.equals(INT) || type.equals(INTEGER)) {
169: return int.class;
170: } else if (type.equals(LONG)) {
171: return long.class;
172: } else if (type.equals(FLOAT)) {
173: return float.class;
174: } else if (type.equals(DOUBLE) || type.equals(REAL)) {
175: return double.class;
176: } else if (type.equals(BOOLEAN)) {
177: return boolean.class;
178: } else if (type.equals(STRING)) {
179: return String.class;
180: } else if (type.equals(DATE)) {
181: return Date.class;
182: } else {
183: throw new RuntimeException("Unrecognized data type: "
184: + type);
185: }
186: }
187:
188: public Tree getTree() {
189: return m_tree;
190: }
191:
192: } // end of inner class TreeMLHandler
193:
194: } // end of class TreeMLTReeReader
|