/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */

#include "axiom_element_internal.h"
#include "axiom_node_internal.h"
#include <axiom_attribute.h>
#include <axiom_namespace.h>
#include <axiom_xml_writer.h>
#include <axiom_stax_builder.h>
#include <string.h>
#include <stdio.h>

struct axiom_element
{

    /** Element's namespace */
    axiom_namespace_t *ns;

    /** Element's local name */
    axutil_string_t *localname;

    /** List of attributes */
    axutil_hash_t *attributes;

    /** List of other namespaces */
    axutil_hash_t *namespaces;

    /* denotes whether current element is an empty element. i.e. <element/>
     * Used only when writing the output */
    axis2_bool_t is_empty;

    /* Following members are kept as a result of some operations. Reason for keeping them is,
     * (1) we can free them without memory leak
     * (2) Improve the performance, so that we don't need to re-do the calculation again
     */
    axutil_qname_t *qname;                          /* result of axiom_element_get_qname */
    axiom_child_element_iterator_t *child_ele_iter; /* result of axiom_element_get_child_elements*/
    axiom_children_iterator_t *children_iter;       /* result of axiom_element_get_children */
    axiom_children_qname_iterator_t *children_qname_iter; /*axiom_element_get_children_with_qname */
    axis2_char_t *text_value;                       /* result of axiom_element_get_text */

};

/**
 * Creates an AXIOM element with given local name
 * @param env Environment. MUST NOT be NULL.
 * @param parent parent of the element node to be created. can be NULL.
 * @param localname local name of the element. cannot be NULL.
 * @param ns namespace of the element.  can be NULL.
 *                       If the value of the namespace has not already been declared
 *                       then the namespace structure ns will be cloned and declared and will be
 *                       freed when the tree is freed.
 *                       Caller has to delete the original ns object passed to the method.
 * @param node This is an out parameter. cannot be NULL.
 *                       Returns the node corresponding to the comment created.
 *                       Node type will be set to AXIOM_ELEMENT
 * @return a pointer to the newly created element struct
 */
AXIS2_EXTERN axiom_element_t *AXIS2_CALL
axiom_element_create(
    const axutil_env_t * env,
    axiom_node_t * parent,
    const axis2_char_t * localname,
    axiom_namespace_t * ns,
    axiom_node_t ** node)
{
    axiom_element_t *element;
    AXIS2_ASSERT(localname != NULL);
    AXIS2_ASSERT(node != NULL);
    AXIS2_ASSERT(env != NULL);

    (*node) = axiom_node_create(env);
    if(!(*node))
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to create axiom node needed by element");
        return NULL;
    }

    element = (axiom_element_t *)AXIS2_MALLOC(env->allocator, sizeof(axiom_element_t));
    if(!element)
    {
        axiom_node_free_tree(*node, env);
        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE);
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Insufficient memory to create axiom element");
        return NULL;
    }
    memset(element, 0, sizeof(axiom_element_t));

    element->localname = axutil_string_create(env, localname);
    if(!element->localname)
    {
        AXIS2_FREE(env->allocator, element);/* Still we haven't set the data element. so, we have */
        axiom_node_free_tree(*node, env);   /* to free node and element separately */
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to create string to store local name");
        return NULL;
    }

    if(parent)
    {
        axiom_node_add_child(parent, env, (*node));
    }
    axiom_node_set_node_type((*node), env, AXIOM_ELEMENT);
    axiom_node_set_data_element((*node), env, element);

    if(ns)
    {
        if(axiom_element_set_namespace(element, env, ns, *node) != AXIS2_SUCCESS)
        {
            axiom_node_free_tree(*node, env); /* this will internally free axiom element */
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to set namespace of element");
            return NULL;
        }
    }

    return element;
}

/**
 * Creates an AXIOM element with given qname
 * @param env Environment. MUST NOT be NULL.
 * @param parent parent of the element node to be created. can be NULL.
 * @param qname qname of the elment.cannot be NULL.
 * @param node This is an out parameter. cannot be NULL.
 *                       Returns the node corresponding to the comment created.
 *                       Node type will be set to AXIOM_ELEMENT
 * @return a pointer to the newly created element struct
 */
AXIS2_EXTERN axiom_element_t *AXIS2_CALL
axiom_element_create_with_qname(
    const axutil_env_t * env,
    axiom_node_t * parent,
    const axutil_qname_t * qname,
    axiom_node_t ** node)
{
    axiom_element_t *element;
    axis2_char_t *localpart;
    axis2_char_t *temp_nsuri;
    axis2_char_t *temp_prefix;

    AXIS2_ASSERT(qname != NULL);
    AXIS2_ASSERT(node != NULL);
    AXIS2_ASSERT(env != NULL);

    localpart = axutil_qname_get_localpart(qname, env);
    AXIS2_ASSERT(localpart != NULL);

    element = axiom_element_create(env, parent, localpart, NULL, node);
    if(!element)
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to create element with qname");
        return NULL;
    }

    AXIS2_ASSERT(*node != NULL);

    temp_nsuri = axutil_qname_get_uri(qname, env);
    temp_prefix = axutil_qname_get_prefix(qname, env);

    if((!temp_nsuri) || (axutil_strcmp(temp_nsuri, "") == 0))
    {
        /** no namespace uri is available in given qname no need to bother about it */
        return element;
    }

    element->ns = axiom_element_find_namespace(element, env, (*node), temp_nsuri, temp_prefix);
    if(!element->ns)
    {
        /** could not find a namespace so declare namespace */
        axiom_namespace_t *ns = axiom_namespace_create(env, temp_nsuri, temp_prefix);
        if(!ns)
        {
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to create namespace needed by element");
            axiom_node_free_tree(*node, env);
            *node = NULL;
            return NULL;
        }

        if(axiom_element_declare_namespace(element, env, *node, ns) != AXIS2_SUCCESS)
        {
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to declare namespace needed by element");
            axiom_node_free_tree(*node, env);
            *node = NULL;
            axiom_namespace_free(ns, env);
            return NULL;
        }

        element->ns = ns;
    }
    else
    {
        /* namespace is declared somewhere, but since we are going to keep it, we should
         * increment the reference
         */
        axiom_namespace_increment_ref(element->ns, env);
    }
    return element;
}

/**
 * Frees given element
 * @param element AXIOM element to be freed.
 * @param env Environment. MUST NOT be NULL.
 * @return status of the operation. AXIS2_SUCCESS on success ,AXIS2_FAILURE on error.
 */
AXIS2_EXTERN void AXIS2_CALL
axiom_element_free(
    axiom_element_t * om_element,
    const axutil_env_t * env)
{
    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(om_element->localname != NULL);

    axutil_string_free(om_element->localname, env);
    if(om_element->ns)
    {
        axiom_namespace_free(om_element->ns, env);
    }

    if(om_element->attributes)
    {
        axutil_hash_index_t *hi;
        void *val;
        for(hi = axutil_hash_first(om_element->attributes, env); hi; hi = axutil_hash_next(env, hi))
        {
            axutil_hash_this(hi, NULL, NULL, &val);
            AXIS2_ASSERT(val != NULL);
            axiom_attribute_free((axiom_attribute_t *)val, env);
        }
        axutil_hash_free(om_element->attributes, env);
    }

    if(om_element->namespaces)
    {
        axutil_hash_index_t *hi;
        void *val;
        for(hi = axutil_hash_first(om_element->namespaces, env); hi; hi = axutil_hash_next(env, hi))
        {
            axutil_hash_this(hi, NULL, NULL, &val);
            AXIS2_ASSERT(val != NULL);
            axiom_namespace_free((axiom_namespace_t *)val, env);
        }
        axutil_hash_free(om_element->namespaces, env);
    }

    if(om_element->qname)
    {
        axutil_qname_free(om_element->qname, env);
    }
    if(om_element->children_iter)
    {
        axiom_children_iterator_free(om_element->children_iter, env);
    }
    if(om_element->child_ele_iter)
    {
        AXIOM_CHILD_ELEMENT_ITERATOR_FREE(om_element->child_ele_iter, env);
    }
    if(om_element->children_qname_iter)
    {
        axiom_children_qname_iterator_free(om_element->children_qname_iter, env);
    }
    if(om_element->text_value)
    {
        AXIS2_FREE(env->allocator, om_element->text_value);
    }
    AXIS2_FREE(env->allocator, om_element);
}

/**
 * finds a namespace in current element's scope, by uri or prefix or both. Will not check in the
 * parents, so even it is defined in parent nodes, this method will return NULL if it is not defined
 * in element's scope
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param uri namespace uri, may be null
 * @param prefix prefix
 * @return axiom_namespace_t if found, else return NULL
 */
AXIS2_EXTERN axiom_namespace_t *AXIS2_CALL
axiom_element_find_declared_namespace(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    const axis2_char_t * uri,
    const axis2_char_t * prefix)
{
    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(env != NULL);

    if(om_element->namespaces)
    {
        if(uri && (!prefix || axutil_strcmp(prefix, "") == 0))
        {
            /** prefix is null , so iterate the namespaces hash to find the namespace */
            axutil_hash_index_t *hashindex;
            for(hashindex = axutil_hash_first(om_element->namespaces, env); hashindex;
                hashindex = axutil_hash_next(env, hashindex))
            {
                void *ns = NULL;
                axutil_hash_this(hashindex, NULL, NULL, &ns);
                if(ns)
                {
                    axiom_namespace_t *temp_ns = (axiom_namespace_t *)ns;
                    axis2_char_t *temp_nsuri = axiom_namespace_get_uri(temp_ns, env);
                    if(axutil_strcmp(temp_nsuri, uri) == 0)
                    {
                        /** namespace uri matches, so free hash index and return ns*/
                        AXIS2_FREE(env->allocator, hashindex);
                        return temp_ns;
                    }
                }
            }
        }
        else if(prefix)
        {
            /** prefix is not null get namespace directly if exist */
            axiom_namespace_t *ns = (axiom_namespace_t *)axutil_hash_get(
                om_element->namespaces, prefix, AXIS2_HASH_KEY_STRING);
            if(ns)
            {
                /* if uri provided, return found ns only if uri matches */
                if((uri) && (axutil_strcmp(axiom_namespace_get_uri(ns, env), uri) != 0))
                {
                    ns = NULL;
                }
                return ns;
            }
        }
    }
    return NULL;
}

/**
 * Find a namespace in the scope of the document.
 * Start to find from the given node and go up the hierarchy.
 * @param om_element pointer to om_element_struct contained in node ,
 * @param env Environment. MUST NOT be NULL.
 * @param node node containing an instance of an AXIOM element,cannot be NULL.
 * @param uri namespace uri..
 * @param prefix namespace prefix. can be NULL.
 * @return pointer to the namespace, if found, else NULL. On error, returns
 *           NULL and sets error code in environment,s error
 */
AXIS2_EXTERN axiom_namespace_t *AXIS2_CALL
axiom_element_find_namespace(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_node_t * element_node,
    const axis2_char_t * uri,
    const axis2_char_t * prefix)
{
    axiom_node_t *parent;
    axiom_namespace_t *ns;

    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(element_node != NULL);

    /* check whether we can find the namespace in current element scope */
    ns = axiom_element_find_declared_namespace(om_element, env, uri, prefix);
    if(ns)
    {
        return ns;
    }

    /* could not find the namespace in current element scope look in the parent */
    parent = axiom_node_get_parent(element_node, env);
    if((parent) && (axiom_node_get_node_type(parent, env) == AXIOM_ELEMENT))
    {
        axiom_element_t *om_element;
        om_element = (axiom_element_t *)axiom_node_get_data_element(parent, env);
        if(om_element)
        {
            /** parent exist, parent is om element so find in parent*/
            return axiom_element_find_namespace(om_element, env, parent, uri, prefix);
        }
    }
    return NULL;
}

/**
 * Finds a namespace using qname. Start to find from the given node and go up the hierarchy.
 * @param om_element om_element contained in node
 * @param env Environment. MUST NOT be NULL.
 * @param node node containing an instance of an AXIOM element, cannot be NULL.
 * @param qname qname of the namespace to be found. cannot be NULL.
 * @return pointer to the namespace, if found, else NULL. On error, returns
 *           NULL and sets the error code in environment's error struct.
 */
AXIS2_EXTERN axiom_namespace_t *AXIS2_CALL
axiom_element_find_namespace_with_qname(
    axiom_element_t * element,
    const axutil_env_t * env,
    axiom_node_t * node,
    axutil_qname_t * qname)
{
    AXIS2_ASSERT(qname != NULL);
    AXIS2_ASSERT(axutil_qname_get_uri(qname, env) != NULL);

    return axiom_element_find_namespace(element, env, node, axutil_qname_get_uri(qname, env),
            axutil_qname_get_prefix(qname, env));
}

/**
 * Declare a namespace in current element (in the scope of this element ).
 * It checks to see if it is already declared at this level or in its ancestors
 * @param om_element contained in the om node struct
 * @param env Environment. MUST NOT be NULL.
 * @param node node containing an instance of an AXIOM element.
 * @param ns pointer to the namespace struct to be declared. Should not be null
 * @return status of the operation. AXIS2_SUCCESS on success else AXIS2_FAILURE.
 */
AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_element_declare_namespace(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_node_t * node,
    axiom_namespace_t * ns)
{
    axiom_namespace_t *declared_ns;
    axis2_char_t *prefix;
    axis2_char_t *uri;

    AXIS2_ASSERT(node != NULL);
    AXIS2_ASSERT(ns != NULL);
    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(env != NULL);

    uri = axiom_namespace_get_uri(ns, env);
    prefix = axiom_namespace_get_prefix(ns, env);
    declared_ns = axiom_element_find_namespace(om_element, env, node, uri, prefix);
    if(declared_ns)
    {
        /*Namespace already declared, so return */
        return AXIS2_SUCCESS;
    }

    if(!om_element->namespaces)
    {
        om_element->namespaces = axutil_hash_make(env);
        if(!om_element->namespaces)
        {
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to create namespaces hash map");
            return AXIS2_FAILURE;
        }
    }

    if(prefix)
    {
        axutil_hash_set(om_element->namespaces, prefix, AXIS2_HASH_KEY_STRING, ns);
    }
    else
    {
        /* create a key with empty string */
        axis2_char_t *key;
        key = AXIS2_MALLOC(env->allocator, sizeof(char) * 1);
        if(!key)
        {
            AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE);
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                "Insufficient memory to create key to store namespace");
        }
        key[0] = '\0';
        axutil_hash_set(om_element->namespaces, key, AXIS2_HASH_KEY_STRING, ns);
    }
    axiom_namespace_increment_ref(ns, env);
    return AXIS2_SUCCESS;
}

/**
 * retrieves the default namespace of this element
 * @param om_element pointer to om element
 * @param env axutil_environment MUST Not be NULL
 * @param element_node corresponding om element node of this om element
 * @returns pointer to default namespace if available , NULL otherwise
 */
axiom_namespace_t *AXIS2_CALL
axiom_element_get_default_namespace(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_node_t * element_node)
{
    axiom_node_t *parent_node;
    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(element_node != NULL);

    if(om_element->namespaces)
    {
        axiom_namespace_t *default_ns;
        default_ns = axutil_hash_get(om_element->namespaces, "", AXIS2_HASH_KEY_STRING);
        if(default_ns)
        {
            return default_ns;
        }
    }

    parent_node = axiom_node_get_parent(element_node, env);
    if((parent_node) && (axiom_node_get_node_type(parent_node, env) == AXIOM_ELEMENT))
    {
        axiom_element_t *parent_ele;
        parent_ele = (axiom_element_t *)axiom_node_get_data_element(parent_node, env);
        return axiom_element_get_default_namespace(parent_ele, env, parent_node);
    }
    return NULL;
}

/**
 * get the namespace  of om_element
 * @param om_element om_element struct
 * @param env environment, MUST NOT be NULL.
 * @returns pointer to axiom_namespace_t struct
 *          NULL if there is no namespace associated with the element,
 *          NULL on error with error code set to environment's error
 */
AXIS2_EXTERN axiom_namespace_t *AXIS2_CALL
axiom_element_get_namespace(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_node_t * ele_node)
{
    axiom_namespace_t *ns;
    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(ele_node != NULL);

    if(om_element->ns)
    {
        ns = om_element->ns;
    }
    else
    {
        ns = axiom_element_get_default_namespace(om_element, env, ele_node);

    }
    return ns;
}

/**
 * set the namespace of the element
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param ns pointer to namespace. Must not be NULL
 *                       If the value of the namespace has not already been declared
 *                       then the namespace structure ns will be declared and will be
 *                       freed when the tree is freed.
 * @returns status code of the op, with error code
 *                  set to environment's error
 */
AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_element_set_namespace(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_namespace_t * ns,
    axiom_node_t * node)
{
    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(ns != NULL);
    AXIS2_ASSERT(node != NULL);

    if(axiom_element_declare_namespace(om_element, env, node, ns) != AXIS2_SUCCESS)
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to declare namespace given");
        return AXIS2_FAILURE;
    }
    om_element->ns = ns;
    axiom_namespace_increment_ref(ns, env);
    return AXIS2_SUCCESS;
}

/**
 * get the namespace list of the element
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @returns axutil_hash pointer to namespaces hash
 * this hash table is read only
 */
AXIS2_EXTERN axutil_hash_t *AXIS2_CALL
axiom_element_get_namespaces(
    axiom_element_t * om_element,
    const axutil_env_t * env)
{
    return om_element->namespaces;
}

/**
 * Adds an attribute to current element. The current element takes responsibility of the
 * assigned attribute
 * @param om_element element to which the attribute is to be added.cannot be NULL.
 * @param env Environment. MUST NOT be NULL.
 * @param attribute attribute to be added.
 * @param node axiom_node_t node that om_element is contained in
 * @return status of the operation. AXIS2_SUCCESS on success else AXIS2_FAILURE.
 */
AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_element_add_attribute(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_attribute_t * attribute,
    axiom_node_t * element_node)
{
    axutil_qname_t *qname;
    axiom_namespace_t *om_namespace;

    AXIS2_ASSERT(attribute != NULL);
    AXIS2_ASSERT(element_node != NULL);
    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(om_element != NULL);

    om_namespace = axiom_attribute_get_namespace(attribute, env);
    if(om_namespace)
    {
        /* Declare the namespace in element */
        if(axiom_element_declare_namespace(om_element, env, element_node, om_namespace)
            != AXIS2_SUCCESS)
        {
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to declare attribute namespace");
            return AXIS2_FAILURE;
        }
    }

    if(!om_element->attributes)
    {
        om_element->attributes = axutil_hash_make(env);
        if(!om_element->attributes)
        {
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to create hash map to store attributes");
            return AXIS2_FAILURE;
        }
    }

    qname = axiom_attribute_get_qname(attribute, env);
    if(qname)
    {
        axis2_char_t *name = axutil_qname_to_string(qname, env);
        axutil_hash_set(om_element->attributes, name, AXIS2_HASH_KEY_STRING, attribute);
        axiom_attribute_increment_ref(attribute, env);
    }
    else
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to create qname to store attribute");
        return AXIS2_FAILURE;
    }

    return AXIS2_SUCCESS;
}

/**
 * Gets (finds) the attribute with the given qname
 * @param element element whose attribute is to be found.
 * @param env Environment. MUST NOT be NULL.
 * @qname qname qname of the attribute to be found. should not be NULL.
 * @return a pointer to the attribute with given qname if found, else NULL.
 *           On error, returns NULL and sets the error code in environment's error struct.
 */
AXIS2_EXTERN axiom_attribute_t *AXIS2_CALL
axiom_element_get_attribute(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axutil_qname_t * qname)
{
    axis2_char_t *name;
    void *attr;

    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(qname != NULL);
    AXIS2_ASSERT(om_element != NULL);

    /* if there are no attributes, then return NULL */
    if(!om_element->attributes)
    {
        return NULL;
    }

    name = axutil_qname_to_string(qname, env);
    if(!name)
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to get string representation of qname");
        return NULL;
    }

    attr = axutil_hash_get(om_element->attributes, name, AXIS2_HASH_KEY_STRING);
    return (axiom_attribute_t *)attr;
}

/**
 * get  the attribute list of the element
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @returns axutil_hash pointer to attributes hash
 * This hash table is read only
 */
AXIS2_EXTERN axutil_hash_t *AXIS2_CALL
axiom_element_get_all_attributes(
    axiom_element_t * om_element,
    const axutil_env_t * env)
{

    return om_element->attributes;
}

/**
 * Gets (finds) the attribute value with the given qname
 * @param element element whose attribute is to be found.
 * @param env Environment. MUST NOT be NULL.
 * @qname qname qname of the attribute to be found. should not be NULL.
 * @return the attribute value with given qname if found, else NULL.
 *  On error, returns NULL and sets the error code in environment's error struct.
 */
AXIS2_EXTERN axis2_char_t *AXIS2_CALL
axiom_element_get_attribute_value(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axutil_qname_t * qname)
{
    axiom_attribute_t *attr = axiom_element_get_attribute(om_element, env, qname);
    if(!attr)
    {
        /* cannot find the attribute with given name. But this might not be an error, and a valid
         * case */
        return NULL;
    }

    return axiom_attribute_get_value(attr, env);
}

/**
 *  Extract attributes , returns a clones hash table of attributes,
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param om_node pointer to this element node
 */
AXIS2_EXTERN axutil_hash_t *AXIS2_CALL
axiom_element_extract_attributes(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_node_t * ele_node)
{
    axutil_hash_index_t *hi;
    axutil_hash_t *ht_cloned;

    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(ele_node != NULL);

    if(!om_element->attributes)
    {
        /* no attributes defined */
        return NULL;
    }

    ht_cloned = axutil_hash_make(env);
    if(!ht_cloned)
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to create hashmap to extract attributes");
        return NULL;
    }

    for(hi = axutil_hash_first(om_element->attributes, env); hi; hi = axutil_hash_next(env, hi))
    {
        void *val;
        axiom_attribute_t *cloned_attr;
        axis2_char_t *key = NULL;

        axutil_hash_this(hi, NULL, NULL, &val);
        AXIS2_ASSERT(val != NULL);

        cloned_attr = axiom_attribute_clone((axiom_attribute_t*)val, env);
        if(cloned_attr)
        {
            axutil_qname_t *qn = axiom_attribute_get_qname(cloned_attr, env);
            if(qn)
            {
                key = axutil_qname_to_string(qn, env);
            }
        }

        if(key)
        {
            axutil_hash_set(ht_cloned, key, AXIS2_HASH_KEY_STRING, cloned_attr);
        }
        else
        {
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to clone attribute");
            return NULL;
        }
    }
    return ht_cloned;
}

/**
 * Returns the attribute value as a string for the given element
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param attr_name the attribute name
 * @return the attribute value as a string
 */
AXIS2_EXTERN axis2_char_t *AXIS2_CALL
axiom_element_get_attribute_value_by_name(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axis2_char_t * attr_name)
{
    axutil_hash_index_t *hi;

    AXIS2_ASSERT(attr_name != NULL);
    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(env != NULL);

    if(!om_element->attributes)
    {
        /* no attributes are defined. */
        return NULL;
    }

    for(hi = axutil_hash_first(om_element->attributes, env); hi; hi = axutil_hash_next(env, hi))
    {
        void *attr;
        axis2_char_t *this_attr_name;
        axiom_namespace_t *attr_ns;
        axis2_char_t *prefix;

        axutil_hash_this(hi, NULL, NULL, &attr);
        AXIS2_ASSERT(attr != NULL);

        this_attr_name = axiom_attribute_get_localname((axiom_attribute_t*)attr, env);
        attr_ns = axiom_attribute_get_namespace((axiom_attribute_t*)attr, env);
        if(attr_ns && (prefix = axiom_namespace_get_prefix(attr_ns, env)) &&
            (axutil_strcmp(prefix, "") != 0))
        {
            /* namespace is defined and prefix is not empty. So, prefix:localname should match
             * with given name
             */
            axis2_char_t *attr_qn_str = axutil_strcat(env, prefix, ":", this_attr_name, NULL);
            if(axutil_strcmp(attr_qn_str, attr_name) != 0)
            {
                /* not the attribute we are looking for */
                AXIS2_FREE(env->allocator, attr_qn_str);
                continue;
            }
            AXIS2_FREE(env->allocator, attr_qn_str);
        }
        else
        {
            /* no namespace or no prefix. so compare only local name */
            if(axutil_strcmp(this_attr_name, attr_name) != 0)
            {
                /* not the attribute we are looking for */
                continue;
            }
        }

        /* we found the attribute */
        AXIS2_FREE(env->allocator, hi);
        return axiom_attribute_get_value((axiom_attribute_t*)attr, env);
    }
    return NULL;
}

/**
 * Select all the text children and concatenate them to a single string. The string
 * returned by this method call will be free by axiom when this method is called again.
 * So it is recommended to have a copy of the return value if this method is going to
 * be called more that once and the return values of the earlier calls are important.
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param element node , the container node of this om element
 * @return the concatenated text of all text children text values
 *         return null if no text children is available or on error
 */
AXIS2_EXTERN axis2_char_t *AXIS2_CALL
axiom_element_get_text(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_node_t * element_node)
{
    axiom_node_t *temp_node;
    axis2_char_t *dest = NULL;

    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(element_node != NULL);
    AXIS2_ASSERT(om_element != NULL);

    if(om_element->text_value)
    {
        AXIS2_FREE(env->allocator, om_element->text_value);
        om_element->text_value = NULL;
    }

    temp_node = axiom_node_get_first_child(element_node, env);
    while(temp_node)
    {
        if(axiom_node_get_node_type(temp_node, env) == AXIOM_TEXT)
        {
            const axis2_char_t *temp_text;
            axiom_text_t *text_ele;

            text_ele = (axiom_text_t *)axiom_node_get_data_element(temp_node, env);
            AXIS2_ASSERT(text_ele != NULL);
            temp_text = axiom_text_get_value(text_ele, env);
            if(dest && temp_text && axutil_strcmp(temp_text, "") != 0)
            {
                axis2_char_t *temp_dest = axutil_stracat(env, dest, temp_text);
                AXIS2_FREE(env->allocator, dest);
                dest = temp_dest;
            }
            else if(!dest && temp_text && axutil_strcmp(temp_text, "") != 0)
            {
                dest = axutil_strdup(env, temp_text);
            }
        }
        temp_node = axiom_node_get_next_sibling(temp_node, env);
    }

    om_element->text_value = dest;
    return om_element->text_value;
}

/**
 * Sets the text of the given element.
 * caution - This method will wipe out all the text elements (and hence any mixed content)
 * before setting the text
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param text text to set.
 * @param element_node node of element.
 * @return AXIS2_SUCCESS if attribute was found and removed, else
 *           AXIS2_FAILURE
 */
AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_element_set_text(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    const axis2_char_t * text,
    axiom_node_t * element_node)
{
    axiom_node_t *temp_node, *next_node;

    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(text != NULL);
    AXIS2_ASSERT(element_node != NULL);

    next_node = axiom_node_get_first_child(element_node, env);
    while(next_node)
    {
        temp_node = next_node;
        next_node = axiom_node_get_next_sibling(temp_node, env);
        if(axiom_node_get_node_type(temp_node, env) == AXIOM_TEXT)
        {
            axiom_node_free_tree(temp_node, env);
        }
    }

    if(!axiom_text_create(env, element_node, text, &temp_node))
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to set text to element");
        return AXIS2_FAILURE;
    }
    return AXIS2_SUCCESS;
}

/**
 * returns the localname of this element
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @returns localname of element, returns NULL on error.
 */
AXIS2_EXTERN axis2_char_t *AXIS2_CALL
axiom_element_get_localname(
    axiom_element_t * om_element,
    const axutil_env_t * env)
{
    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(om_element->localname != NULL);

    return (axis2_char_t *)axutil_string_get_buffer(om_element->localname, env);
}

/**
 * set the localname of this element
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @localname text value to be set as localname
 * @returns status code of operation, AXIS2_SUCCESS on success, AXIS2_FAILURE on error.
 */
AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_element_set_localname(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    const axis2_char_t * localname)
{
    axutil_string_t *new_name;

    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(om_element->localname != NULL);
    AXIS2_ASSERT(localname != NULL);
    AXIS2_ASSERT(env != NULL);

    new_name = axutil_string_create(env, localname);
    if(!new_name)
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to set local name of element");
        return AXIS2_FAILURE;
    }

    axutil_string_free(om_element->localname, env);
    om_element->localname = new_name;
    return AXIS2_SUCCESS;
}

/**
 * return qname of this element. The returned qname should not be freed by the caller.
 * It will be freed when om_element struct is freed
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param ele_node pointer to this element node
 * @returns axutil_qname_t struct , NULL on failure
 */
AXIS2_EXTERN axutil_qname_t *AXIS2_CALL
axiom_element_get_qname(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_node_t * ele_node)
{
    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(ele_node != NULL);

    if(!om_element->qname)
    {
        axiom_namespace_t *ns = axiom_element_get_namespace(om_element, env, ele_node);
        const axis2_char_t *localname = axutil_string_get_buffer(om_element->localname, env);
        axis2_char_t *prefix = NULL;
        axis2_char_t *uri = NULL;

        if(ns)
        {
            prefix = axiom_namespace_get_prefix(ns, env);
            uri = axiom_namespace_get_uri(ns, env);
        }

        om_element->qname = axutil_qname_create(env, localname, uri, prefix);
    }
    return om_element->qname;
}

/**
 * returns a list of children iterator. Returned iterator is freed when om_element struct
 * is freed
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param element_node pointer to this element node
 */
AXIS2_EXTERN axiom_children_iterator_t *AXIS2_CALL
axiom_element_get_children(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_node_t * element_node)
{
    AXIS2_ASSERT(element_node != NULL);
    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(env != NULL);

    if(!om_element->children_iter)
    {
        om_element->children_iter = axiom_children_iterator_create(env,
            axiom_node_get_first_child(element_node, env));
    }
    else
    {
        axiom_children_iterator_reset(om_element->children_iter, env);
    }
    return om_element->children_iter;
}

/**
 * returns a list of children iterator with qname. Returned iterator is freed when om element
 * struct is freed
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param element_node pointer to this element node
 * @returns children qname iterator struct
 */
AXIS2_EXTERN axiom_children_qname_iterator_t *AXIS2_CALL
axiom_element_get_children_with_qname(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axutil_qname_t * element_qname,
    axiom_node_t * element_node)
{
    AXIS2_ASSERT(element_node != NULL);
    AXIS2_ASSERT(element_qname != NULL);
    AXIS2_ASSERT(om_element != NULL);

    if(om_element->children_qname_iter)
    {
        axiom_children_qname_iterator_free(om_element->children_qname_iter, env);
    }
    om_element->children_qname_iter = axiom_children_qname_iterator_create(env,
        axiom_node_get_first_child(element_node, env), element_qname);
    return om_element->children_qname_iter;
}

/**
 * Returns the first om_element corresponding to element_qname
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param element_qname qname of the element
 * @param om_node pointer to this element node
 * @param element_node
 * @param child_node
 * @returns children qname iterator struct
 */
AXIS2_EXTERN axiom_element_t *AXIS2_CALL
axiom_element_get_first_child_with_qname(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axutil_qname_t * qname,
    axiom_node_t * element_node,
    axiom_node_t ** child_node)
{
    axiom_children_qname_iterator_t *children_iterator;
    children_iterator = axiom_element_get_children_with_qname(om_element, env, qname, element_node);
    if(!children_iterator)
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Could not get children qname iterator");
        return NULL;
    }

    if(axiom_children_qname_iterator_has_next(children_iterator, env))
    {
        axiom_node_t *om_node = axiom_children_qname_iterator_next(children_iterator, env);
        AXIS2_ASSERT(om_node != NULL);
        AXIS2_ASSERT(axiom_node_get_node_type(om_node, env) == AXIOM_ELEMENT);

        if(child_node)
        {
            *child_node = om_node;
        }
        return (axiom_element_t *)axiom_node_get_data_element(om_node, env);
    }

    return NULL;
}

/**
 * returns the first child om element of this om element node
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param om_node pointer to this element node
 * @return om_element if one is available otherwise return NULL
 */
AXIS2_EXTERN axiom_element_t *AXIS2_CALL
axiom_element_get_first_element(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_node_t * element_node,
    axiom_node_t ** first_ele_node)
{
    axiom_node_t *temp_node;

    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(element_node != NULL);

    temp_node = axiom_node_get_first_child(element_node, env);
    while(temp_node)
    {
        if(axiom_node_get_node_type(temp_node, env) == AXIOM_ELEMENT)
        {
            if(first_ele_node)
            {
                *first_ele_node = temp_node;
            }
            return (axiom_element_t *)axiom_node_get_data_element(temp_node, env);
        }
        else
        {
            temp_node = axiom_node_get_next_sibling(temp_node, env);
        }
    }
    return NULL;
}

/**
 * returns an iterator with child elements of type AXIOM_ELEMENT
 * iterator is freed when om_element node is freed
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param element_node
 * @returns axiom_child_element_iterator_t , NULL on error
 */
AXIS2_EXTERN axiom_child_element_iterator_t *AXIS2_CALL
axiom_element_get_child_elements(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_node_t * element_node)
{
    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(element_node != NULL);

    if(om_element->child_ele_iter)
    {
        return om_element->child_ele_iter;
    }
    else
    {
        axiom_node_t *first_node;
        axiom_element_t *ele;
        ele = axiom_element_get_first_element(om_element, env, element_node, &first_node);
        if(ele)
        {
            AXIS2_ASSERT(first_node != NULL);
            om_element->child_ele_iter = axiom_child_element_iterator_create(env, first_node);
            return om_element->child_ele_iter;
        }
    }
    return NULL;
}



/**
 * Collect all the namespaces with distinct prefixes in the parents of the given element.
 * Effectively this is the set of namespaces declared above this element and might be used by it
 * or its children. Output of this will be used later by axiom_element_redeclare_parent_namespaces
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param om_node pointer to this element node
 * @returns pointer to hash of relevant namespaces
 */
axutil_hash_t * AXIS2_CALL
axiom_element_gather_parent_namespaces(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_node_t * om_node)
{
    axutil_hash_t *inscope_namespaces;
    axiom_node_t *parent_node = om_node;

    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(om_node != NULL);

    inscope_namespaces = axutil_hash_make(env);
    if(!inscope_namespaces)
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
            "Unable to create hashmap needed to gather parent namespace");
        return NULL;
    }

    while((parent_node = axiom_node_get_parent(parent_node, env)) &&
        (axiom_node_get_node_type(parent_node, env) == AXIOM_ELEMENT))
    {
        axiom_element_t *parent_element;
        axutil_hash_t *parent_namespaces;
        axutil_hash_index_t *hi;

        parent_element = (axiom_element_t *)axiom_node_get_data_element(parent_node, env);
        parent_namespaces = axiom_element_get_namespaces(parent_element, env);
        if(!parent_namespaces)
        {
            /* no namespaces are declared. So, continue without processing */
            continue;
        }

        for(hi = axutil_hash_first(parent_namespaces, env); hi; hi = axutil_hash_next(env, hi))
        {
            axis2_char_t *key;
            void *val;
            axutil_hash_this(hi, NULL, NULL, &val);
            AXIS2_ASSERT(val != NULL);

            key = axiom_namespace_get_prefix((axiom_namespace_t *)val, env);
            if(!key)
            {
                key = "";
            }

            /* Check if prefix already associated with some namespace in a parent node */
            if(!axutil_hash_get(inscope_namespaces, key, AXIS2_HASH_KEY_STRING))
            {
                /* Remember this namespace as needing to be declared, if used */
                axutil_hash_set(inscope_namespaces, key, AXIS2_HASH_KEY_STRING, val);
            }
        }
    }

    return inscope_namespaces;
}

/**
 * If the provided namespace used by the provided element is one of the namespaces from the
 * parent of the detached node, redeclares that namespace at the element level and removes it
 * from the hash of parent namespaces
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param om_node pointer to this element node
 * @param ns pointer to namespace to redeclare
 * @param inscope_namespaces pointer to hash of parent namespaces
 */
void AXIS2_CALL
axiom_element_use_parent_namespace(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_node_t * om_node,
    axiom_namespace_t *ns,
    axutil_hash_t *inscope_namespaces)
{
    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(om_node != NULL);

    if(ns && inscope_namespaces)
    {
        axiom_namespace_t *parent_ns;
        axis2_char_t *key = axiom_namespace_get_prefix(ns, env);
        if(!key)
        {
            key = "";
        }

        parent_ns = axutil_hash_get(inscope_namespaces, key, AXIS2_HASH_KEY_STRING);
        /* Check if namespace is a namespace declared in a parent and not also declared at an
         * intermediate level */
        if(parent_ns)
        {
            /* declare the namespace. If it is already declared in intermediate level,
             * axiom_elment_declare_namespace will handle it
             */
            axiom_element_declare_namespace(om_element, env, om_node, parent_ns);
            /* Remove the namespace from the inscope parent namespaces now that it has
             been redeclared. */
            axutil_hash_set(inscope_namespaces, key, AXIS2_HASH_KEY_STRING, NULL);
        }
    }
}

/**
 * Examines the subtree beginning at the provided element for each element or attribute,
 * if it refers to a namespace declared in a parent of the subtree root element, if not already
 * declared, redeclares that namespace at the level of the subtree root and removes
 * it from the set of parent inscope_namespaces. inscope_namespaces contains all the parent
 * namespaces which should be redeclared at some point.
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param om_node pointer to this element node
 * @param inscope_namespaces pointer to hash of parent namespaces
 */
void AXIS2_CALL
axiom_element_redeclare_parent_namespaces(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_node_t * om_node,
    axutil_hash_t *inscope_namespaces)
{
    axiom_node_t *child_node;
    axutil_hash_t * attributes;
    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(om_node != NULL);

    /* ensure the element's namespace is declared */
    axiom_element_use_parent_namespace(om_element, env, om_node, om_element->ns, inscope_namespaces);

    /* for each attribute, ensure the attribute's namespace is declared */
    attributes = om_element->attributes;
    if(attributes)
    {
        axutil_hash_index_t *hi;
        for(hi = axutil_hash_first(attributes, env); hi; hi = axutil_hash_next(env, hi))
        {
            void *val;
            axiom_namespace_t* ns;

            axutil_hash_this(hi, NULL, NULL, &val);
            AXIS2_ASSERT(val != NULL);

            ns = axiom_attribute_get_namespace((axiom_attribute_t*)val, env);
            axiom_element_use_parent_namespace(om_element, env, om_node,ns, inscope_namespaces);
        }
    }

    /* ensure the namespaces in all the children are declared */
    child_node = axiom_node_get_first_child(om_node, env);
    while(child_node && (axutil_hash_count(inscope_namespaces) > 0))
    {
        if(axiom_node_get_node_type(child_node, env) == AXIOM_ELEMENT)
        {
            axiom_element_redeclare_parent_namespaces(axiom_node_get_data_element(child_node, env),
                env, child_node, inscope_namespaces);
        }
        child_node = axiom_node_get_next_sibling(child_node, env);
    }
}

/**
 * Serializes the start part of the given element
 * @param element element to be serialized.
 * @param env Environment. MUST NOT be NULL.
 * @param om_output AXIOM output handler to be used in serializing
 * @return status of the operation. AXIS2_SUCCESS on success else AXIS2_FAILURE
 */
axis2_status_t AXIS2_CALL
axiom_element_serialize_start_part(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_output_t * om_output,
    axiom_node_t * ele_node)
{
    axis2_status_t status = AXIS2_SUCCESS;

    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(om_output != NULL);
    AXIS2_ASSERT(ele_node != NULL);

    if(om_element->is_empty)
    {
        if(om_element->ns)
        {
            axis2_char_t *uri = axiom_namespace_get_uri(om_element->ns, env);
            axis2_char_t *prefix = axiom_namespace_get_prefix(om_element->ns, env);
            AXIS2_ASSERT(uri != NULL);

            if(prefix && (axutil_strcmp(prefix, "") != 0))
            {
                status = axiom_output_write(om_output, env, AXIOM_ELEMENT, 4,
                    axutil_string_get_buffer(om_element-> localname, env), uri, prefix, NULL);
            }
            else
            {
                status = axiom_output_write(om_output, env, AXIOM_ELEMENT, 4,
                    axutil_string_get_buffer(om_element-> localname, env), uri, NULL, NULL);
            }
        }
        else
        {
            status = axiom_output_write(om_output, env, AXIOM_ELEMENT, 4,
                axutil_string_get_buffer(om_element-> localname, env), NULL, NULL, NULL);
        }
    }
    else
    {
        if(om_element->ns)
        {
            axis2_char_t *uri = axiom_namespace_get_uri(om_element->ns, env);
            axis2_char_t *prefix = axiom_namespace_get_prefix(om_element->ns, env);
            AXIS2_ASSERT(uri != NULL);

            if(prefix && (axutil_strcmp(prefix, "") != 0))
            {
                status = axiom_output_write(om_output, env, AXIOM_ELEMENT, 3,
                    axutil_string_get_buffer(om_element-> localname, env), uri, prefix);
            }
            else
            {
                status = axiom_output_write(om_output, env, AXIOM_ELEMENT, 2,
                    axutil_string_get_buffer(om_element-> localname, env), uri);
            }
        }
        else
        {
            status = axiom_output_write(om_output, env, AXIOM_ELEMENT, 1,
                axutil_string_get_buffer(om_element-> localname, env));
        }
    }

    if(status != AXIS2_SUCCESS)
    {
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "element serialized failed");
        return AXIS2_FAILURE;
    }

    if(om_element->attributes)
    {
        axutil_hash_index_t *hi;
        void *val;
        for(hi = axutil_hash_first(om_element->attributes, env); hi; hi = axutil_hash_next(env, hi))
        {
            axutil_hash_this(hi, NULL, NULL, &val);
            AXIS2_ASSERT(val != NULL);

            if(axiom_attribute_serialize((axiom_attribute_t *)val, env, om_output) != AXIS2_SUCCESS)
            {
                AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "element attribute serialize failed");
                AXIS2_FREE(env->allocator, hi);
                return AXIS2_FAILURE;
            }
        }
    }

    if(om_element->namespaces)
    {
        axutil_hash_index_t *hi;
        void *val;
        for(hi = axutil_hash_first(om_element->namespaces, env); hi; hi = axutil_hash_next(env, hi))
        {
            axutil_hash_this(hi, NULL, NULL, &val);
            AXIS2_ASSERT(val != NULL);

            if(axiom_namespace_serialize((axiom_namespace_t *)val, env, om_output) != AXIS2_SUCCESS)
            {
                AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "element namespace serialize failed");
                AXIS2_FREE(env->allocator, hi);
                return AXIS2_FAILURE;
            }
        }
    }

    return AXIS2_SUCCESS;
}

/**
 * Serializes the end part of the given element. serialize_start_part must
 *     have been called before calling this method.
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param om_node pointer to this element node
 * @param om_output AXIOM output handler to be used in serializing
 * @return status of the operation. AXIS2_SUCCESS on success else AXIS2_FAILURE
 */
axis2_status_t AXIS2_CALL
axiom_element_serialize_end_part(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_output_t * om_output)
{
    AXIS2_ASSERT(env != NULL);
    AXIS2_ASSERT(om_element != NULL);
    AXIS2_ASSERT(om_output != NULL);

    return axiom_output_write(om_output, env, AXIOM_ELEMENT, 0);
}

/**
 * Set whether the element is empty or not
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param is_empty AXIS2_TRUE if empty AXIS2_FALSE if not empty
 * @return VOID
 */
void AXIS2_CALL
axiom_element_set_is_empty(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axis2_bool_t is_empty)
{
    om_element->is_empty = is_empty;
}

/**
 * This method will declare the namespace without checking whether it is already declared. 
 * (This method is only used by codegen. We have to remove this method in future)
 * @param om_element pointer to om_element
 * @param env environment MUST not be NULL
 * @param om_node pointer to this element node
 * @return satus of the op. AXIS2_SUCCESS on success else AXIS2_FAILURE.
 *
 */
AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_element_declare_namespace_assume_param_ownership(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_namespace_t * ns)
{
    axis2_char_t *prefix = NULL;

    if(!ns || !om_element)
    {
        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_INVALID_NULL_PARAM, AXIS2_FAILURE);
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "namespace or om_element is NULL");
        return AXIS2_FAILURE;
    }

    if(!(om_element->namespaces))
    {
        om_element->namespaces = axutil_hash_make(env);
        if(!(om_element->namespaces))
        {
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to create namespaces hash map");
            return AXIS2_FAILURE;
        }
    }
	
    prefix = axiom_namespace_get_prefix(ns, env);
    if(prefix)
    {
        axutil_hash_set(om_element->namespaces, prefix, AXIS2_HASH_KEY_STRING, ns);
    }
    else
    {
        /* create a key with empty string */
        axis2_char_t *key;
        key = AXIS2_MALLOC(env->allocator, sizeof(char) * 1);
        if(!key)
        {
            AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE);
            AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
                "Insufficient memory to create key to store namespace");
        }
        key[0] = '\0';
        axutil_hash_set(om_element->namespaces, key, AXIS2_HASH_KEY_STRING, ns);
    }
    axiom_namespace_increment_ref(ns, env);
    return AXIS2_SUCCESS;
}

#if 0
AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_element_build(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_node_t * om_ele_node)
{
    axiom_stax_builder_t *builder = NULL;
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);

    AXIS2_PARAM_CHECK(env->error, om_ele_node, AXIS2_FAILURE);
    if(axiom_node_get_node_type(om_ele_node, env) != AXIOM_ELEMENT)
    {
        return AXIS2_FAILURE;
    }

    builder = axiom_node_get_builder(om_ele_node, env);
    if(!builder)
    {
        return AXIS2_FAILURE;
    }
    while(!axiom_node_is_complete(om_ele_node, env)
        && !axiom_stax_builder_is_complete(builder, env))
    {
        void *value = NULL;
        value = axiom_stax_builder_next(builder, env);
        if(!value)
        {
            return AXIS2_FAILURE;
        }
    }
    return AXIS2_SUCCESS;
}

/**
 * checks for the namespace in the context of this element
 * with the given prefix
 */

AXIS2_EXTERN axiom_namespace_t *AXIS2_CALL
axiom_element_find_namespace_uri(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    const axis2_char_t * prefix,
    axiom_node_t * element_node)
{
    axiom_node_t *parent_node = NULL;
    axiom_namespace_t *ns = NULL;

    AXIS2_ENV_CHECK(env, NULL);
    AXIS2_PARAM_CHECK(env->error, element_node, NULL);
    AXIS2_PARAM_CHECK(env->error, prefix, NULL);

    if(om_element->namespaces)
    {
        ns = axutil_hash_get(om_element->namespaces, prefix, AXIS2_HASH_KEY_STRING);
        if(ns)
        {
            return ns;
        }
    }

    parent_node = axiom_node_get_parent(element_node, env);
    if((parent_node) && (axiom_node_get_node_type(parent_node, env) == AXIOM_ELEMENT))
    {
        axiom_element_t *parent_ele = NULL;
        parent_ele = (axiom_element_t *)axiom_node_get_data_element(parent_node, env);
        if(parent_ele)
        {
            return axiom_element_find_namespace_uri(parent_ele, env, prefix, parent_node);
        }
    }
    return NULL;
}

AXIS2_EXTERN axutil_string_t *AXIS2_CALL
axiom_element_get_localname_str(
    axiom_element_t * om_element,
    const axutil_env_t * env)
{
    return om_element->localname;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_element_set_localname_str(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axutil_string_t * localname)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    AXIS2_PARAM_CHECK(env->error, localname, AXIS2_FAILURE);

    if(om_element->localname)
    {
        axutil_string_free(om_element->localname, env);
        om_element->localname = NULL;
    }

    om_element->localname = axutil_string_clone(localname, env);

    if(!(om_element->localname))
    {
        return AXIS2_FAILURE;
    }
    return AXIS2_SUCCESS;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_element_set_namespace_with_no_find_in_current_scope(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_namespace_t * om_ns)
{
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    AXIS2_PARAM_CHECK(env->error, om_ns, AXIS2_FAILURE);
    om_element->ns = om_ns;
    return AXIS2_SUCCESS;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_element_set_namespace_assume_param_ownership(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_namespace_t * ns)
{
    om_element->ns = ns;
    return AXIS2_SUCCESS;
}

/**
 * declared a default namespace explicitly
 */
AXIS2_EXTERN axiom_namespace_t *AXIS2_CALL
axiom_element_declare_default_namespace(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axis2_char_t * uri)
{
    axiom_namespace_t *default_ns = NULL;
    AXIS2_ENV_CHECK(env, NULL);
    AXIS2_PARAM_CHECK(env->error, uri, NULL);

    if(axutil_strcmp(uri, "") == 0)
    {
        return NULL;
    }

    default_ns = axiom_namespace_create(env, uri, "");
    if(!default_ns)
    {
        return NULL;
    }
    if(!om_element->namespaces)
    {
        om_element->namespaces = axutil_hash_make(env);
        if(!(om_element->namespaces))
        {
            axiom_namespace_free(default_ns, env);
            return NULL;
        }
    }

    axutil_hash_set(om_element->namespaces, "", AXIS2_HASH_KEY_STRING, default_ns);
    axiom_namespace_increment_ref(default_ns, env);
    return default_ns;
}

AXIS2_EXTERN axiom_element_t *AXIS2_CALL
axiom_element_create_str(
    const axutil_env_t * env,
    axiom_node_t * parent,
    axutil_string_t * localname,
    axiom_namespace_t * ns,
    axiom_node_t ** node)
{
    axiom_element_t *element;

    if(!localname || !node)
    {
        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_INVALID_NULL_PARAM, AXIS2_FAILURE);
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "localname or node is NULL");
        return NULL;
    }

    (*node) = axiom_node_create(env);
    if(!(*node))
    {
        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE);
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Unable to create axiom node");
        return NULL;
    }

    element = (axiom_element_t *)AXIS2_MALLOC(env->allocator, sizeof(axiom_element_t));
    if(!element)
    {
        AXIS2_FREE(env->allocator, (*node));
        AXIS2_ERROR_SET(env->error, AXIS2_ERROR_NO_MEMORY, AXIS2_FAILURE);
        AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI, "Insufficient memory to create axiom element");
        return NULL;
    }

    memset(element, 0, sizeof(axiom_element_t));
    element->localname = axutil_string_clone(localname, env);
    /* clone can't be null so, no need to check for null validity*/

    if(parent)
    {
        axiom_node_add_child(parent, env, (*node));
    }
    axiom_node_set_node_type((*node), env, AXIOM_ELEMENT);
    axiom_node_set_data_element((*node), env, element);

    if(ns)
    {
        axis2_char_t *uri = NULL;
        axis2_char_t *prefix = NULL;

        uri = axiom_namespace_get_uri(ns, env);
        prefix = axiom_namespace_get_prefix(ns, env);

        element->ns = axiom_element_find_namespace(element, env, *node, uri, prefix);
        if(!(element->ns))
        {
            if(axiom_element_declare_namespace(element, env, *node, ns) == AXIS2_SUCCESS)
            {
                element->ns = ns;
            }
        }
        if(prefix && axutil_strcmp(prefix, "") == 0)
        {
            element->ns = NULL;
        }
    }

    return element;
}

AXIS2_EXTERN axis2_bool_t AXIS2_CALL
axiom_element_get_is_empty(
    axiom_element_t * om_element,
    const axutil_env_t * env)
{
    return om_element->is_empty;
}

AXIS2_EXTERN axis2_status_t AXIS2_CALL
axiom_element_remove_attribute(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_attribute_t * om_attribute)
{
    axutil_qname_t *qname = NULL;
    AXIS2_ENV_CHECK(env, AXIS2_FAILURE);
    AXIS2_PARAM_CHECK(env->error, om_attribute, AXIS2_FAILURE);

    qname = axiom_attribute_get_qname(om_attribute, env);
    if(qname && (om_element->attributes))
    {
        axis2_char_t *name = NULL;
        name = axutil_qname_to_string(qname, env);
        if(name)
        {
            axutil_hash_set(om_element->attributes, name, AXIS2_HASH_KEY_STRING, NULL);
            return AXIS2_SUCCESS;
        }
    }
    return AXIS2_FAILURE;
}

AXIS2_EXTERN axis2_char_t *AXIS2_CALL
axiom_element_to_string(
    axiom_element_t * om_element,
    const axutil_env_t * env,
    axiom_node_t * element_node)
{
    return axiom_node_to_string(element_node, env);
}
#endif