Java tutorial
/** * Copyright (C) 2005 Orbeon, Inc. * * This program 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.1 of the License, or (at your option) any later version. * * This program 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. * * The full text of the license is available at http://www.gnu.org/copyleft/lesser.html */ package org.orbeon.oxf.xml.dom4j; import org.dom4j.DocumentFactory; import org.dom4j.util.NodeComparator; import org.dom4j.util.NonLazyElement; /** * 4/7/2005 d : Under JDK 1.5 the fact that dom4j isn't thread safe by default became apparent. * In particular DefaultElement ( and sub-classes thereof ) are not thread safe because of the * following : * o DefaultElement has a single field, private Object content, by which it refers to all of its * child nodes. If there is a single child node then content points to it. If there are more * then content points to a java.util.List which in turns points to all the children. * o However, if you do almost anything with an instance of DefaultElement, i.e. iterate over * children, it will first create and fill a list before completing the operation. This even * if there was only a single child. * The consequence of the above is that DefaultElement and its sub-classes aren't thread safe, * even if all of the threads are just readers. * * The 'usual' solution is to use dom4j's NonLazyElement and NonLazyElementDocumentFactory. * However in our case we were using a sub-class of DefaultElement, UserDataElement, whose * functionality is unmatched by NonLazyElement. Hence this class, a subclass of NonLazyElement * with the safe functionality as UserDataElement. * * Btw NonLazyUserDataElement also tries to be smart wrt to cloning and parent specifying. That * is, if you clone the clone will have parent == null but will have all of the requisite * namespace declarations and if you setParent( notNull ) then any redundant namespace declarations * are removed. * */ public class NonLazyUserDataElement extends NonLazyElement { private Object data; public NonLazyUserDataElement(final String name) { super(name); } public NonLazyUserDataElement(final org.dom4j.QName qname) { super(qname); } public NonLazyUserDataElement(final String nm, final org.dom4j.Namespace ns) { super(nm, ns); } /** * Doesn't try to grab name space decls from ancestors. * @return a clone. May be missing some necessary namespace decls. * @see #clone */ private NonLazyUserDataElement cloneInternal() { final NonLazyUserDataElement ret = (NonLazyUserDataElement) super.clone(); if (ret != this) { ret.content = null; ret.attributes = null; ret.appendAttributes(this); ret.appendContent(this); ret.data = getCopyOfUserData(); } return ret; } protected java.util.List createContentList() { return createContentList(2); } protected Object getCopyOfUserData() { return data; } protected org.dom4j.Element createElement(final String name) { final org.dom4j.DocumentFactory factory = getDocumentFactory(); final org.dom4j.QName qnam = factory.createQName(name); return createElement(qnam); } protected org.dom4j.Element createElement(final org.dom4j.QName qName) { final DocumentFactory factory = NonLazyUserDataDocumentFactory.getInstance(); final org.dom4j.Element ret = factory.createElement(qName); final Object dta = getCopyOfUserData(); ret.setData(dta); return ret; } protected org.dom4j.DocumentFactory getDocumentFactory() { return NonLazyUserDataDocumentFactory.getInstance(); } public Object getData() { return data; } public void setData(final Object d) { data = d; } public String toString() { return super.toString() + " userData: " + data; } public void appendAttributes(final org.dom4j.Element src) { for (int i = 0, size = src.attributeCount(); i < size; i++) { final org.dom4j.Attribute att = src.attribute(i); final org.dom4j.Attribute attCln = (org.dom4j.Attribute) att.clone(); add(attCln); } } public void appendContent(final org.dom4j.Branch branch) { final int size = branch.nodeCount(); for (int i = 0; i < size; i++) { final org.dom4j.Node node = branch.node(i); final org.dom4j.Node cln; if (node.getNodeType() == org.dom4j.Node.ELEMENT_NODE) { cln = ((NonLazyUserDataElement) node).cloneInternal(); } else { cln = (org.dom4j.Node) node.clone(); } add(cln); } } /** * @return A clone. The clone will have parent == null but will have any necessary namespace * declarations this element's ancestors. */ public Object clone() { final NonLazyUserDataElement ret = cloneInternal(); org.dom4j.Element anstr = getParent(); done: if (anstr != null) { final NodeComparator nc = new NodeComparator(); final java.util.TreeSet nsSet = new java.util.TreeSet(nc); do { final java.util.List sibs = anstr.content(); for (final java.util.Iterator itr = sibs.iterator(); itr.hasNext();) { final org.dom4j.Node sib = (org.dom4j.Node) itr.next(); if (sib.getNodeType() != org.dom4j.Node.NAMESPACE_NODE) continue; nsSet.add(sib); } anstr = anstr.getParent(); } while (anstr != null); if (nsSet.isEmpty()) break done; for (final java.util.Iterator itr = nsSet.iterator(); itr.hasNext();) { final org.dom4j.Namespace ns = (org.dom4j.Namespace) itr.next(); final String pfx = ns.getPrefix(); if (ret.getNamespaceForPrefix(pfx) != null) continue; ret.add(ns); } } return ret; } /** * If parent != null checks with ancestors and removes any redundant namespace declarations. */ public void setParent(final org.dom4j.Element prnt) { super.setParent(prnt); done: if (prnt != null) { final org.dom4j.Namespace myNs = getNamespace(); if (myNs != org.dom4j.Namespace.NO_NAMESPACE) { final String myPfx = myNs.getPrefix(); final org.dom4j.Namespace prntNs = prnt.getNamespaceForPrefix(myPfx); if (myPfx.equals(prntNs)) { final String myNm = myNs.getName(); final org.dom4j.QName newNm = new org.dom4j.QName(myNm); setQName(newNm); } } if (content == null) break done; for (final java.util.Iterator itr = content.iterator(); itr.hasNext();) { final org.dom4j.Node chld = (org.dom4j.Node) itr.next(); if (chld.getNodeType() != org.dom4j.Node.NAMESPACE_NODE) continue; final org.dom4j.Namespace ns = (org.dom4j.Namespace) chld; final String pfx = ns.getPrefix(); final org.dom4j.Namespace prntNs = prnt.getNamespaceForPrefix(pfx); if (ns.equals(prntNs)) itr.remove(); } } } public org.dom4j.Element createCopy() { final NonLazyUserDataElement ret = (NonLazyUserDataElement) super.createCopy(); ret.data = getCopyOfUserData(); return ret; } public org.dom4j.Element createCopy(final org.dom4j.QName qName) { final NonLazyUserDataElement ret = (NonLazyUserDataElement) super.createCopy(qName); ret.data = getCopyOfUserData(); return ret; } public org.dom4j.Element createCopy(final String name) { final NonLazyUserDataElement ret = (NonLazyUserDataElement) super.createCopy(name); ret.data = getCopyOfUserData(); return ret; } }