/* * @(#)JavaxDOMInput.java * * Copyright (c) 1996-2010 The authors and contributors of JHotDraw. * You may not use, copy or modify this file, except in compliance with the * accompanying license terms. */ package org.jhotdraw.xml; import java.util.*; import javax.xml.XMLConstants; import javax.xml.parsers.*; import org.w3c.dom.*; import java.io.*; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * DOMInput. * <p> * Design pattern:<br> * Name: Adapter.<br> * Role: Adapter.<br> * Partners: {@link org.w3c.dom.Document} as Adaptee. * * @author Werner Randelshofer * @version $Id$ */ public class JavaxDOMInput implements DOMInput { /** * This map is used to unmarshall references to objects to * the XML DOM. A key in this map is a String representing a marshalled * reference. A value in this map is an unmarshalled Object. */ private HashMap<String, Object> idobjects = new HashMap<String, Object>(); /** * The document used for input. */ private Document document; /** * The current node used for input. */ private Node current; /** * The factory used to create objects from XML tag names. */ private DOMFactory factory; protected static DocumentBuilder documentBuilder; /** * Lazily create the document builder and keep a reference to it for * performance improvement. */ protected static DocumentBuilder getBuilder() throws IOException { if (documentBuilder == null) { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); factory.setValidating(false); factory.setXIncludeAware(false); try { factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); documentBuilder = factory.newDocumentBuilder(); } catch (Exception ex) { InternalError error = new InternalError("Unable to create DocumentBuilder"); error.initCause(ex); throw error; } } return documentBuilder; } public JavaxDOMInput(DOMFactory factory, InputStream in) throws IOException { this.factory = factory; try { document = getBuilder().parse(in); current = document; } catch (SAXException ex) { IOException e = new IOException(ex.getMessage()); e.initCause(ex); throw e; } } public JavaxDOMInput(DOMFactory factory, Reader in) throws IOException { this.factory = factory; try { document = getBuilder().parse(new InputSource(in)); current = document; } catch (SAXException ex) { IOException e = new IOException(ex.getMessage()); e.initCause(ex); throw e; } } /** * Returns the tag name of the current element. */ @Override public String getTagName() { return ((Element) current).getTagName(); } /** * Gets an attribute of the current element of the DOM Document. */ @Override public String getAttribute(String name, String defaultValue) { String value = ((Element) current).getAttribute(name); return (value.length() == 0) ? defaultValue : value; } /** * Gets the text of the current element of the DOM Document. */ @Override public String getText() { return getText(null); } /** * Gets the text of the current element of the DOM Document. */ @Override public String getText(String defaultValue) { if (current.getChildNodes().getLength() == 0) { return defaultValue; } StringBuilder buf = new StringBuilder(); getText(current, buf); return buf.toString(); } private static void getText(Node n, StringBuilder buf) { if (n.getNodeValue() != null) { buf.append(n.getNodeValue()); } NodeList children = n.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { getText(children.item(i), buf); } } /** * Gets an attribute of the current element of the DOM Document and of * all parent DOM elements. */ @Override public java.util.List<String> getInheritedAttribute(String name) { LinkedList<String> values = new LinkedList<String>(); Node node = current; while (node != null) { String value = ((Element) node).getAttribute(name); values.addFirst(value); node = node.getParentNode(); } return values; } /** * Gets an attribute of the current element of the DOM Document. */ @Override public int getAttribute(String name, int defaultValue) { String value = ((Element) current).getAttribute(name); return (value.length() == 0) ? defaultValue : Long.decode(value).intValue(); } /** * Gets an attribute of the current element of the DOM Document. */ @Override public double getAttribute(String name, double defaultValue) { String value = ((Element) current).getAttribute(name); return (value.length() == 0) ? defaultValue : Double.parseDouble(value); } /** * Gets an attribute of the current element of the DOM Document. */ @Override public boolean getAttribute(String name, boolean defaultValue) { String value = ((Element) current).getAttribute(name); return (value.length() == 0) ? defaultValue : Boolean.valueOf(value).booleanValue(); } /** * Returns the number of child elements of the current element. */ @Override public int getElementCount() { int count = 0; NodeList list = current.getChildNodes(); for (int i = 0; i < list.getLength(); i++) { Node node = list.item(i); if ((node instanceof Element)) { count++; } } return count; } /** * Returns the number of child elements with the specified tag name * of the current element. */ @Override public int getElementCount(String tagName) { int count = 0; NodeList list = current.getChildNodes(); for (int i = 0; i < list.getLength(); i++) { Node node = list.item(i); if ((node instanceof Element) && ((Element) node).getTagName().equals(tagName)) { count++; } } return count; } /** * Opens the element with the specified index and makes it the current node. */ @Override public void openElement(int index) { int count = 0; NodeList list = current.getChildNodes(); int len = list.getLength(); for (int i = 0; i < len; i++) { Node node = list.item(i); if ((node instanceof Element)) { if (count++ == index) { current = node; return; } } } } /** * Opens the last element with the specified name and makes it the current node. */ @Override public void openElement(String tagName) { int count = 0; NodeList list = current.getChildNodes(); int len = list.getLength(); for (int i = 0; i < len; i++) { Node node = list.item(i); if ((node instanceof Element) && ((Element) node).getTagName().equals(tagName)) { current = node; return; } } throw new IllegalArgumentException("element not found:" + tagName); } /** * Opens the element with the specified name and index and makes it the * current node. */ @Override public void openElement(String tagName, int index) { int count = 0; NodeList list = current.getChildNodes(); int len = list.getLength(); for (int i = 0; i < len; i++) { Node node = list.item(i); if ((node instanceof Element) && ((Element) node).getTagName().equals(tagName)) { if (count++ == index) { current = node; return; } } } throw new IllegalArgumentException("no such child " + tagName + "[" + index + "]"); } /** * Closes the current element of the DOM Document. * The parent of the current element becomes the current element. * @exception IllegalArgumentException if the provided tagName does * not match the tag name of the element. */ @Override public void closeElement() { /* if (! ((Element) current).getTagName().equals(tagName)) { throw new IllegalArgumentException("Attempt to close wrong element:"+tagName +"!="+((Element) current).getTagName()); }*/ current = current.getParentNode(); } /** * Reads an object from the current element. */ @Override public Object readObject() throws IOException { return readObject(0); } /** * Reads an object from the current element. */ @Override public Object readObject(int index) throws IOException { openElement(index); Object o; String ref = getAttribute("ref", null); String id = getAttribute("id", null); if (ref != null && id != null) { throw new IOException("Element has both an id and a ref attribute: <" + getTagName() + " id=" + id + " ref=" + ref + ">"); } if (id != null && idobjects.containsKey(id)) { throw new IOException("Duplicate id attribute: <" + getTagName() + " id=" + id + ">"); } if (ref != null && !idobjects.containsKey(ref)) { throw new IOException("Illegal ref attribute value: <" + getTagName() + " ref=" + ref + ">"); } // Keep track of objects which have an ID if (ref != null) { o = idobjects.get(ref); } else { o = factory.read(this); if (id != null) { idobjects.put(id, o); } if (o instanceof DOMStorable) { ((DOMStorable) o).read(this); } } closeElement(); return o; } }