/*
 * Copyright 2007 Edward Kuns
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * $Id: Element.java 0000 2007-01-11 00:00:00Z ekuns $
 */
package org.castor.xmlctf.xmldiff.xml.nodes;

import java.util.Iterator;
import java.util.LinkedList;

import org.castor.xmlctf.xmldiff.xml.Location;

/**
 * A representation of an Element XML node.
 *
 * @author <a href="mailto:edward.kuns@aspect.com">Edward Kuns</a>
 * @version $Revision: 0000 $ $Date: 2007-01-11 00:00:00 -0600 (Thu, 11 Jan 2007) $
 * @since Castor 1.1
 */
public class Element extends ParentNode {

    /** Serial Version UID. */
    private static final long serialVersionUID = 7995110660306473483L;
    /** Our list of Attributes. */
    private final LinkedList  _attributes      = new LinkedList();
    /** Our list of Namespaces. */
    private final LinkedList  _namespaces      = new LinkedList();
    /** Our location */
    private Location          _location        = null;

    /**
     * Creates a new Element.
     *
     * @param namespace the namespace URI for this node. (May be null.)
     * @param localName the localname of this node. (Cannot be null.)
     */
    public Element(final String namespace, final String localName) {
        super(namespace, localName, XMLNode.ELEMENT);
    }

    /**
     * Returns an Iterator over the List of Attributes.
     * @return an Iterator over the List of Attributes.
     */
    public Iterator getAttributeIterator() {
        return _attributes.iterator();
    }

    /**
     * Returns the value of the named attribute, or null if the node has no such
     * attribute. If the argument <tt>uri</tt> is null, the node's namespace
     * URI will be used.
     *
     * @param uri The attribute's namespace URI, or null
     * @param localName The attribute's local name
     * @return The attribute's value, or null if no such attribute exists
     */
    public String getAttribute(final String uri, final String localName) {
        for (Iterator i = _attributes.iterator(); i.hasNext();) {
            Attribute attr = (Attribute) i.next();
            if (namespacesEqual(uri, attr.getNamespaceURI())
                && attr.getLocalName().equals(localName)) {
                return attr.getStringValue();
            }
        }
        return null;
    }

    /**
     * Returns the namespace URI associated with this namespace prefix, as
     * defined in the context of this node. Returns null if the prefix is
     * undefined. Returns an empty String if the prefix is defined and
     * associated with no namespace.
     *
     * @param prefix The namespace prefix
     * @return The namespace URI, or null.
     */
    public String getNamespaceURI(final String prefix) {
        String desiredPrefix = (prefix == null) ? "" : prefix;

        for (Iterator i = _namespaces.iterator(); i.hasNext(); ) {
            Namespace ns = (Namespace) i.next();
            if (desiredPrefix.equals(ns.getPrefix())) {
                return ns.getNamespaceUri();
            }
        }

        // If we didn't find it, search our parent
        return super.getNamespaceURI(desiredPrefix);
    }

    /**
     * Returns the namespace prefix associated with this namespace URI, as
     * defined in the context of this node. Returns null if no prefix is defined
     * for this namespace URI. Returns an empty string if the default prefix is
     * associated with this namespace URI.
     *
     * @param uri The namespace URI
     * @return The namespace prefix, or null
     */
    public String getNamespacePrefix(final String uri) {
        String desiredUri = (uri == null) ? "" : uri;

        for (Iterator i = _namespaces.iterator(); i.hasNext(); ) {
            Namespace ns = (Namespace) i.next();
            if (desiredUri.equals(ns.getNamespaceUri())) {
                return ns.getPrefix();
            }
        }

        // If we didn't find it locally, search our parent
        ParentNode parent = getParentNode();
        if (parent != null && parent instanceof Element) {
            return ((Element)parent).getNamespacePrefix(desiredUri);
        }

        // If no parent, return null
        return null;
    }

    /**
     * Adds the given Attribute to this Element.
     *
     * @param attr the Attribute to add
     */
    public void addAttribute(final Attribute attr) {
        if (attr == null) {
            return;
        }

        attr.setParent(this);
        _attributes.add(attr);
    }

    /**
     * Adds the given Namespace to this Element.
     *
     * @param namespace the Namespace to add
     */
    public void addNamespace(final Namespace namespace) {
        if (namespace == null) {
            return;
        }

        _namespaces.add(namespace);
    }

    /**
     * Returns true if the given two namespace URI strings are equal.
     *
     * @param ns1 The first namespace URI to compare
     * @param ns2 The second namespace URI to compare
     * @return true if the given two namespace URI strings are equal.
     */
    private boolean namespacesEqual(final String ns1, final String ns2) {
        final String namespace1 = (ns1 == null) ? "" : ns1;
        final String namespace2 = (ns2 == null) ? "" : ns2;

        return namespace1.equals(namespace2);
    }

    /**
     * Sets the location of this Element in the document.
     * @param location the location of this Element in the document.
     */
    public void setLocation(final Location location) {
        _location = location;
    }

    /**
     * Returns the location of this Element in the document.
     * @return the location of this Element in the document.
     */
    public Location getLocation() {
        return _location;
    }

}
