org.orbeon.oxf.xml.dom4j.NonLazyUserDataElement.java Source code

Java tutorial

Introduction

Here is the source code for org.orbeon.oxf.xml.dom4j.NonLazyUserDataElement.java

Source

/**
 *  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;
    }
}