All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javanet.staxutils.events.StartElementEvent Maven / Gradle / Ivy

There is a newer version: 2.2.1
Show newest version
/*
 * $Id: StartElementEvent.java,v 1.5 2004/07/15 02:11:01 cniles Exp $
 * 
 * Copyright (c) 2004, Christian Niles, unit12.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 *		*   Redistributions of source code must retain the above copyright
 *          notice, this list of conditions and the following disclaimer.
 * 
 *	    *	Redistributions in binary form must reproduce the above copyright
 *          notice, this list of conditions and the following disclaimer in the
 *          documentation and/or other materials provided with the distribution.
 * 
 *      *   Neither the name of Christian Niles, Unit12, nor the names of its
 *          contributors may be used to endorse or promote products derived from
 *          this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 */
package javanet.staxutils.events;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javanet.staxutils.NamespaceContextAdapter;
import javanet.staxutils.StaticNamespaceContext;

import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.Namespace;
import javax.xml.stream.events.StartElement;

/**
 * {@link StartElement} event implementation. This event will coalesce its namespaces
 * into an internal {@link NamespaceContext}, available via
 * {@link #getNamespaceContext()}. It will also create any implicit namespaces
 * necessary to satisfy the element's name and those of its attributes.
 * 
 * @author Christian Niles
 * @version $Revision: 1.5 $
 */
public class StartElementEvent extends AbstractXMLEvent implements StartElement {

    /** The qualified element name. */
    protected QName name;

    /** The element attributes. */
    protected Map attributes;

    /** The element namespaces. */
    protected Map namespaces;

    /** The namespace context. */
    protected NamespaceContext namespaceCtx;

    public StartElementEvent(QName name, NamespaceContext namespaceCtx,
            Location location) {

        super(location);
        this.name = name;
        this.namespaceCtx = new StartElementContext(namespaceCtx);

    }

    public StartElementEvent(QName name, Iterator attributes,
            Iterator namespaces, NamespaceContext namespaceCtx,
            Location location, QName schemaType) {

        super(location, schemaType);
        this.namespaceCtx = new StartElementContext(namespaceCtx);

        mergeNamespaces(namespaces);
        mergeAttributes(attributes);

        QName newName = processQName(name);
        this.name = (newName == null ? name : newName);

    }

    public StartElementEvent(StartElement that) {

        this(that.getName(),
                that.getAttributes(),
                that.getNamespaces(),
                that.getNamespaceContext(),
                that.getLocation(),
                that.getSchemaType());

    }

    /** Returns {@link #START_ELEMENT}. */
    public int getEventType() {

        return START_ELEMENT;

    }

    public QName getName() {

        return name;

    }

    public Attribute getAttributeByName(QName name) {

        if (attributes != null) {

            return (Attribute) attributes.get(name);

        } else {

            return null;

        }

    }

    public Iterator getAttributes() {

        if (attributes != null) {

            return attributes.values().iterator();

        } else {

            return Collections.EMPTY_LIST.iterator();

        }

    }

    public NamespaceContext getNamespaceContext() {

        return namespaceCtx;

    }

    public Iterator getNamespaces() {

        if (namespaces != null) {

            return namespaces.values().iterator();

        } else {

            return Collections.EMPTY_LIST.iterator();

        }

    }

    public String getNamespaceURI(String prefix) {

        return getNamespaceContext().getNamespaceURI(prefix);

    }

    /**
     * Performs the task of adding {@link Attribute}s into the internal
     * {@link #attributes} map. Along the way, it will also create the necessary
     * {@link Namespace} events to satisfy attribute namespaces.
     * 
     * @param iter An iterator over a set of {@link Attributes}.
     */
    private void mergeAttributes(Iterator iter) {

        if (iter == null) {

            return;

        }

        while (iter.hasNext()) {

            Attribute attr = (Attribute) iter.next();

            if (attributes == null) {

                attributes = new HashMap();

            }

            // check if the attribute QName has the proper mapping
            QName attrName = attr.getName();
            QName newName = processQName(attrName);
            if (newName != null) {

                // need to generate a new attribute with the new qualified name
                Attribute newAttr = new AttributeEvent(newName, null, attr);
                attributes.put(newName, newAttr);

            } else {

                // the attribute is fine
                attributes.put(attrName, attr);

            }

        }

    }

    /**
     * Performs the task of adding {@link Namespace}s into the internal
     * {@link #namespaces} map.
     * 
     * @param iter An iterator over a set of {@link Namespaces}.
     */
    private void mergeNamespaces(Iterator iter) {

        if (iter == null) {

            return;

        }

        // for each namespace, add it to the context, and place it in the list
        while (iter.hasNext()) {

            Namespace ns = (Namespace) iter.next();
            String prefix = ns.getPrefix();

            if (namespaces == null) {

                namespaces = new HashMap();

            }

            if (!namespaces.containsKey(prefix)) {

                namespaces.put(prefix, ns);

            }

        }

    }

    /**
     * Processes a {@link QName}, possibly rewriting it to match the current
     * namespace context. If necessary, a new {@link Namespace} will be added to
     * support the name's prefix.
     * 
     * @param name The {@link QName} to process.
     * @return The new name, or null if no changes were necessary.
     */
    private QName processQName(QName name) {

        String nsURI = name.getNamespaceURI();
        String prefix = name.getPrefix();

        if (nsURI == null || nsURI.length() == 0) {

            // name belongs to no namespace. This can only be okay if the name is
            // an attribute name, or the default namespace hasn't been overridden.
            // either way, no prefix should be allowed on the name, so the best we
            // can do is rewrite the name to make sure no prefix is present

            // clear any prefix from the name
            if (prefix != null && prefix.length() > 0) {

                return new QName(name.getLocalPart());

            } else {

                return name;

            }

        }

        // namespace uri is non-null after this point

        String resolvedNS = namespaceCtx.getNamespaceURI(prefix);
        if (resolvedNS == null) {

            // if the prefix is not empty, then we should default the prefix
            if (prefix != null && prefix.length() > 0) {

                if (namespaces == null) {

                    namespaces = new HashMap();

                }
                namespaces.put(prefix, new NamespaceEvent(prefix, nsURI));

            }

            return null;

        } else if (!resolvedNS.equals(nsURI)) {

            // The prefix is bound to a different namespace, so we'll have to
            // search for existing prefixes bound to the namespace uri, or
            // generate a new namespace binding.
            String newPrefix = namespaceCtx.getPrefix(nsURI);
            if (newPrefix == null) {

                // no existing prefix; need to generate a new prefix
                newPrefix = generatePrefix(nsURI);

            }

            // return the newly prefixed name
            return new QName(nsURI, name.getLocalPart(), newPrefix);

        } else {

            // prefix has already been bound to the namespace; nothing to do
            return null;

        }

    }

    /**
     * Generates a new namespace prefix for the specified namespace URI that
     * doesn't collide with any existing prefix.
     * 
     * @param nsURI The URI for which to generate a prefix.
     * @return The new prefix.
     */
    private String generatePrefix(String nsURI) {

        String newPrefix;
        int nsCount = 0;
        do {

            newPrefix = "ns" + nsCount;
            nsCount++;

        } while (namespaceCtx.getNamespaceURI(newPrefix) != null);

        if (namespaces == null) {

            namespaces = new HashMap();

        }
        namespaces.put(newPrefix, new NamespaceEvent(newPrefix, nsURI));

        return newPrefix;

    }

    /**
     * Adapts another {@link NamespaceContext} to expose this tag's declared
     * namespaces.
     *
     * @author Christian Niles
     * @version $Revision: 1.5 $
     */
    private final class StartElementContext extends NamespaceContextAdapter
            implements
                StaticNamespaceContext {

        public StartElementContext(NamespaceContext namespaceCtx) {

            super(namespaceCtx);

        }

        public String getNamespaceURI(String prefix) {

            if (namespaces != null && namespaces.containsKey(prefix)) {

                Namespace namespace = (Namespace) namespaces.get(prefix);
                return namespace.getNamespaceURI();

            } else {

                return super.getNamespaceURI(prefix);

            }

        }

        public String getPrefix(String nsURI) {

            for (Iterator i = getNamespaces(); i.hasNext();) {

                Namespace ns = (Namespace) i.next();
                if (ns.getNamespaceURI().equals(nsURI)) {

                    return ns.getPrefix();

                }

            }

            return super.getPrefix(nsURI);

        }

        public Iterator getPrefixes(String nsURI) {

            // lazily-loaded set to store found prefixes
            List prefixes = null;

            // add our prefixes first
            if (namespaces != null) {

                for (Iterator i = namespaces.values().iterator(); i.hasNext();) {

                    Namespace ns = (Namespace) i.next();
                    if (ns.getNamespaceURI().equals(nsURI)) {

                        if (prefixes == null) {

                            prefixes = new ArrayList();

                        }

                        String prefix = ns.getPrefix();
                        prefixes.add(prefix);

                    }

                }

            }

            // copy parent prefixes that aren't redefined by this context
            Iterator parentPrefixes = super.getPrefixes(nsURI);
            while (parentPrefixes.hasNext()) {

                String prefix = (String) parentPrefixes.next();

                // only add the prefix if we haven't redefined it
                if (namespaces != null && !namespaces.containsKey(prefix)) {

                    if (prefixes == null) {

                        prefixes = new ArrayList();

                    }
                    prefixes.add(prefix);

                }

            }

            return prefixes == null
                    ? Collections.EMPTY_LIST.iterator()
                    : prefixes.iterator();

        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy