/* * XMLObject.java * * Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard * * This file is part of BEAST. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership and licensing. * * BEAST is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * BEAST is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with BEAST; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ package dr.xml; import dr.inference.model.AbstractModel; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import java.util.Vector; /** * This class wraps a DOM Element for the purposes of parsing. * * @author Alexei Drummond * @version $Id: XMLObject.java,v 1.30 2005/07/11 14:06:25 rambaut Exp $ */ public class XMLObject { public static final String missingValue = "NA"; /** * @param e the element the construct this XML object from */ public XMLObject(Element e, XMLObject parent) { this.element = e; this.parent = parent; } public XMLObject(XMLObject obj, int index) { this(obj.element, null); nativeObject = ((List)obj.getNativeObject()).get(index); } /** * @return the number of children this XMLObject has. */ public final int getChildCount() { return children.size(); } /** * @param i the index of the child to return * @return the ith child in native format if available, otherwise as * an XMLObject. */ public Object getChild(int i) { Object obj = getRawChild(i); XMLObject xo = null; if( obj instanceof XMLObject ) { xo = (XMLObject) obj; } else if( obj instanceof Reference ) { xo = ((Reference) obj).getReferenceObject(); } if( xo != null && xo.hasNativeObject() ) { return xo.getNativeObject(); } return obj; } /** * @param c the class of the child to return * @return the first child with a native format of the given class, or null if no such child exists. */ public Object getChild(Class c) { for (int i = 0; i < getChildCount(); i++) { Object child = getChild(i); if( c.isInstance(child) ) { return child; } } return null; } /** * @return all children with or empty list if no children. */ public List<Object> getChildren() { List<Object> allChildren = new ArrayList<Object>(); for (int i = 0; i < getChildCount(); i++) { Object child = getChild(i); allChildren.add(child); } return allChildren; } /** * @param c the class of the children to return * @return all children with a native format of the given class, or null if no such child exists. */ public <T> List<T> getAllChildren(Class<T> c) { List<T> allChildren = null; for (int i = 0; i < getChildCount(); i++) { Object child = getChild(i); if( c.isInstance(child) ) { if (allChildren == null) { allChildren = new ArrayList<T>(); } allChildren.add(c.cast(child)); } } return allChildren; } /** * @param name the name of the child to return * @return the first child of type XMLObject with a given name, or null if no such child exists. */ public XMLObject getChild(String name) { for (int i = 0; i < getChildCount(); i++) { Object child = getChild(i); if( child instanceof XMLObject ) { if( ((XMLObject) child).getName().equals(name) ) { return (XMLObject) child; } } } return null; } /** * @param name the name of the children * @return all children with a given name. */ public List<XMLObject> getAllChildren(String name) { List<XMLObject> allChildren = new ArrayList<XMLObject>(); for (int i = 0; i < getChildCount(); i++) { Object child = getChild(i); if( child instanceof XMLObject && ((XMLObject)child).getName().equals(name)) { allChildren.add((XMLObject)child); } } return allChildren; } /** * @param elementName the name of the XML wrapper element the child resides in * @return the first child element out of a named XMLObject element * @throws XMLParseException if no wrapper element exists, or it the child in * wrapper element is not an XMLObject */ public Object getElementFirstChild(String elementName) throws XMLParseException { Object child = getChild(elementName); if (child == null) throw new XMLParseException("Child element called " + elementName + " does not exist inside element " + getName()); if (!(child instanceof XMLObject)) throw new XMLParseException("Child element called " + elementName + " inside element " + getName() + " is not an XMLObject."); return ((XMLObject) child).getChild(0); } public String getChildName(int i) { Object obj = getRawChild(i); XMLObject xo; if (obj instanceof XMLObject) { xo = (XMLObject) obj; } else if (obj instanceof Reference) { xo = ((Reference) obj).getReferenceObject(); } else { return ""; } return xo.getName(); } /** * @param name the name of the child element being tested for. * @return true if a child element of the given name exists. */ public boolean hasChildNamed(String name) { final Object child = getChild(name); return (child != null) && (child instanceof XMLObject); } /** * @return all attributes */ public NamedNodeMap getAttributes() { return element.getAttributes(); } /** * @param i the index of the child to return * @return the ith child as a boolean. * @throws XMLParseException if getChild(i) would */ public boolean getBooleanChild(int i) throws XMLParseException { return getBoolean(getChild(i)); } /** * @param i the index of the child to return * @return the ith child as a double. * @throws XMLParseException if getChild(i) would */ public double getDoubleChild(int i) throws XMLParseException { return getDouble(getChild(i)); } /** * @param i the index of the child to return * @return the ith child as a double[]. * @throws XMLParseException if getChild(i) would */ public double[] getDoubleArrayChild(int i) throws XMLParseException { return getDoubleArray(getChild(i)); } /** * @param i the index of the child to return * @return the ith child as an integer. * @throws XMLParseException if getChild(i) would */ public int getIntegerChild(int i) throws XMLParseException { return getInteger(getChild(i)); } /** * @param i the index of the child to return * @return the ith child as a string. * @throws XMLParseException if getChild(i) would */ public String getStringChild(int i) throws XMLParseException { return getString(getChild(i)); } /** * @param i the index of the child to return * @return the ith child as a String[]. * @throws XMLParseException if getChild(i) would */ public String[] getStringArrayChild(int i) throws XMLParseException { return getStringArray(getChild(i)); } /** * Attribute value, if present - default otherwise. * * @param name attribute name * @param defaultValue the default value * @return the given attribute if it exists, otherwise the default value * @throws XMLParseException if attribute can't be converted to desired type */ public <T> T getAttribute(String name, T defaultValue) throws XMLParseException { if (element.hasAttribute(name)) { final String s = element.getAttribute(name); for (Constructor c : defaultValue.getClass().getConstructors()) { final Class[] classes = c.getParameterTypes(); if (classes.length == 1 && classes[0].equals(String.class)) { try { return (T) c.newInstance(s); } catch (Exception e) { throw new XMLParseException(" conversion of '" + s + "' to " + defaultValue.getClass().getName() + " failed"); } } } } return defaultValue; } /** * @return the named attribute */ public Object getAttribute(String name) throws XMLParseException { return getAndTest(name); } /** * @return the named attribute as a boolean. */ public boolean getBooleanAttribute(String name) throws XMLParseException { return getBoolean(getAndTest(name)); } /** * @return the named attribute as a double. */ public double getDoubleAttribute(String name) throws XMLParseException { return getDouble(getAndTest(name)); } /** * @return the named attribute as a double[]. */ public double[] getDoubleArrayAttribute(String name) throws XMLParseException { return getDoubleArray(getAndTest(name)); } /** * @return the named attribute as a double[]. */ public int[] getIntegerArrayAttribute(String name) throws XMLParseException { return getIntegerArray(getAndTest(name)); } /** * @return the named attribute as an integer. */ public int getIntegerAttribute(String name) throws XMLParseException { return getInteger(getAndTest(name)); } /** * @return the named attribute as a long integer. */ public long getLongIntegerAttribute(String name) throws XMLParseException { return getLongInteger(getAndTest(name)); } /** * @return the named attribute as a string. */ public String getStringAttribute(String name) throws XMLParseException { return getString(getAndTest(name)); } /** * @return the named attribute as a String[]. */ public String[] getStringArrayAttribute(String name) throws XMLParseException { return getStringArray(getAndTest(name)); } /** * @param valueList if this ArrayList is not null it is populated by the double array * that the given string represents. * @return true if the given string represents a whitespaced-delimited array of doubles. */ public static boolean isDoubleArray(String s, List<Double> valueList) { try { StringTokenizer st = new StringTokenizer(s); while (st.hasMoreTokens()) { String token = st.nextToken(); Double d; if (token.compareToIgnoreCase(missingValue) == 0) d = Double.NaN; else d = new Double(token); if (valueList != null) valueList.add(d); } return true; } catch (NumberFormatException e) { return false; } } /** * @param valueList if this ArrayList is not null it is populated by the integer array * that the given string represents. * @return true if the given string represents a whitespaced-delimited array of integers. */ public static boolean isIntegerArray(String s, List<Integer> valueList) { try { StringTokenizer st = new StringTokenizer(s); while (st.hasMoreTokens()) { Integer d = new Integer(st.nextToken()); if (valueList != null) valueList.add(d); } return true; } catch (NumberFormatException e) { return false; } } public final static String ID = "id"; public boolean hasId() { return hasAttribute(ID); } public String getId() throws XMLParseException { return getStringAttribute(ID); } /** * @return true if either an attribute exists. */ public boolean hasAttribute(String name) { return (element.hasAttribute(name)); } public String getName() { return element.getTagName(); } public Object getNativeObject() { return nativeObject; } public boolean hasNativeObject() { return nativeObject != null; } public String toString() { String prefix = getName(); if (hasId()) { try { prefix += ":" + getId(); } catch (XMLParseException e) { // this shouldn't happen assert false; } } //if (nativeObject != null) return prefix + ":" + nativeObject.toString(); return prefix; } public String content() { if (nativeObject != null) { if (nativeObject instanceof dr.util.XHTMLable) { return ((dr.util.XHTMLable) nativeObject).toXHTML(); } else { return nativeObject.toString(); } } return ""; } //********************************************************************* // Package functions //********************************************************************* /** * Adds a child. */ void addChild(Object child) { if (child instanceof XMLObject || child instanceof Reference || child instanceof String) { children.add(child); } else throw new IllegalArgumentException(); } /** * @return the ith child of this XMLObject, without processing. */ public Object getRawChild(int i) { return children.get(i); } /** * Sets the native object represented by this XMLObject. */ public void setNativeObject(Object obj) { nativeObject = obj; } boolean isReference(int child) { return (getRawChild(child) instanceof Reference); } //********************************************************************* // Static members //********************************************************************* //********************************************************************* // Private methods //********************************************************************* /** * @return the object as a boolean if possible */ private boolean getBoolean(Object obj) throws XMLParseException { if (obj instanceof Boolean) return (Boolean) obj; if (obj instanceof String) { if (obj.equals("true")) return true; if (obj.equals("false")) return false; } throw new XMLParseException("Expected a boolean (true|false), but got " + obj); } /** * @return the object as an double if possible */ private double getDouble(Object obj) throws XMLParseException { try { if (obj instanceof Number) { return ((Number) obj).doubleValue(); } if (obj instanceof String) { return Double.parseDouble((String) obj); } } catch (NumberFormatException nfe) { throw new XMLParseException("Expected double precision number, but got " + obj); } throw new XMLParseException("Expected double precision number, but got " + obj); } /** * @return the object as an double[] if possible */ private double[] getDoubleArray(Object obj) throws XMLParseException { if (obj instanceof Number) return new double[]{((Number) obj).doubleValue()}; if (obj instanceof double[]) return (double[]) obj; if (obj instanceof String) { List<Double> valueList = new ArrayList<Double>(); if (isDoubleArray((String) obj, valueList)) { double[] values = new double[valueList.size()]; for (int i = 0; i < values.length; i++) { values[i] = valueList.get(i); } return values; } else { throw new XMLParseException("Expected array of double precision numbers, but got " + obj); } } throw new XMLParseException("Expected array of double precision numbers, but got " + obj); } /** * @return the object as an double[] if possible */ private int[] getIntegerArray(Object obj) throws XMLParseException { if (obj instanceof Number) return new int[]{((Number) obj).intValue()}; if (obj instanceof int[]) return (int[]) obj; if (obj instanceof String) { ArrayList<Integer> valueList = new ArrayList<Integer>(); if (isIntegerArray((String) obj, valueList)) { int[] values = new int[valueList.size()]; for (int i = 0; i < values.length; i++) { values[i] = valueList.get(i); } return values; } else { throw new XMLParseException("Expected array of integers, but got " + obj); } } throw new XMLParseException("Expected array of integers, but got " + obj); } /** * @return the object as an integer if possible */ private int getInteger(Object obj) throws XMLParseException { if (obj instanceof Number) return ((Number) obj).intValue(); try { return Integer.parseInt((String) obj); } catch (NumberFormatException e) { throw new XMLParseException("Expected integer, got " + obj); } } /** * @return the object as an integer if possible */ private long getLongInteger(Object obj) throws XMLParseException { if (obj instanceof Number) return ((Number) obj).longValue(); try { return Long.parseLong((String) obj); } catch (NumberFormatException e) { throw new XMLParseException("Expected long integer, got " + obj); } } /** * @return the object as a string if possible */ private String getString(Object obj) throws XMLParseException { if (obj instanceof String) return (String) obj; throw new XMLParseException("Expected string, but got " + obj); } /** * @return the object as an double[] if possible */ private String[] getStringArray(Object obj) throws XMLParseException { if (obj instanceof String[]) return (String[]) obj; if (obj instanceof String) { ArrayList<String> stringList = new ArrayList<String>(); StringTokenizer st = new StringTokenizer((String) obj); while (st.hasMoreTokens()) { stringList.add(st.nextToken()); } String[] strings = new String[stringList.size()]; for (int i = 0; i < strings.length; i++) { strings[i] = stringList.get(i); } return strings; } throw new XMLParseException("Expected array of strings, but got " + obj); } /** * @return the named attribute if it exists, throws XMLParseException otherwise. */ private Object getAndTest(String name) throws XMLParseException { if (element.hasAttribute(name)) { return element.getAttribute(name); } throw new XMLParseException("'" + name + "' attribute was not found in " + element.getTagName() + " element."); } public XMLObject getParent() { return parent; } //********************************************************************* // Private instance variables //********************************************************************* private final Vector<Object> children = new Vector<Object>(); private final Element element; private final XMLObject parent; private Object nativeObject; // The objectStore representing the local scope of this element. // private ObjectStore store; }