/**
 * Copyright (c) 2008-2012 EBM WebSourcing, 2012-2024 Linagora
 * 
 * This program/library 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/library 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.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program/library; If not, see http://www.gnu.org/licenses/
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.binding.soap.util;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.jbi.messaging.MessagingException;
import javax.xml.namespace.QName;

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMAttribute;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMInformationItem;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.soap.SOAP11Constants;
import org.apache.axiom.soap.SOAP12Constants;
import org.apache.axiom.soap.SOAPCloneOptions;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.i18n.Messages;

/**
 * An utility class to manipulate AXIOM Elements.
 * @author Christophe Hamerling - EBM WebSourcing
 */
public class AxiomUtils {

	private static final QName REFID = QName.valueOf("id");

	private static final QName HREF = QName.valueOf("href");

	private static final String MULTIREF = "multiRef";

	/**
	 * Creates a new instance of AxiomUtils
	 */
	private AxiomUtils() {
	}

    /**
     * Create the SOAP factory
     * 
     * @param msgContext
     *            the message context
     * @return the SOAP factory
     * @throws MessagingException
     *             if it is not possible to determine the SOAP version
     */
	public static SOAPFactory getSOAPFactory(final MessageContext msgContext)
			throws MessagingException {
		return getSOAPFactory(msgContext.getEnvelope());
	}

    /**
     * Get the SOAP factory from the SOAP envelope version
     * 
     * @param envelope
     *            the SOAP envelope
     * @return the SOAP factory
     * @throws MessagingException
     *             if it is not possible to determine the SOAP version
     * 
     */
	public static SOAPFactory getSOAPFactory(SOAPEnvelope envelope)
			throws MessagingException {
		final String nsURI = envelope.getNamespace().getNamespaceURI();
		if (SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(nsURI)) {
			return OMAbstractFactory.getSOAP12Factory();
		} else if (SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(nsURI)) {
			return OMAbstractFactory.getSOAP11Factory();
		} else {
			throw new MessagingException(Messages.getMessage("invalidSOAPversion"));
		}
	}

	/**
	 * Get all the HREF elements which are children of the input root element
	 * 
	 * @param element
	 * @return a map of &lt;HREF ID, MULTIREF {@link OMElement}&gt;
	 */
	public static Map<String, OMElement> getHrefElements(OMElement element) {
		Map<String, OMElement> result = new HashMap<String, OMElement>();

		if (element.getQName().getLocalPart().equals(MULTIREF)
				&& (element.getAttribute(REFID) != null)) {
			result.put(element.getAttributeValue(REFID), element);
		}

		// go into chidlren
		Iterator<?> iter = element.getChildElements();
		while (iter.hasNext()) {
			OMElement child = (OMElement) iter.next();
			Map<String, OMElement> tmp = getHrefElements(child);
			result.putAll(tmp);
		}
		return result;
	}

	/**
	 * Get the HREF ID from the givem element if exists.
	 * 
	 * @param element
	 *            to get HREF id from
	 * @return id or empty string if not found
	 */
	public static String getHrefId(OMElement element) {
		return ((element != null) && (element.getAttribute(HREF) != null)) ? element
				.getAttributeValue(HREF).substring(1)
				: "";
	}

	/**
	 * Let's look if the element is a multiref one ie
	 * 
	 * <pre>
	 * {@code
	 * <multiRef ... >
	 * }
	 * </pre>
	 * 
	 * @param element
	 * @return
	 */
	public static boolean isMultirefElement(OMElement element) {
		return (element != null) && element.getLocalName().equals(MULTIREF);
	}

	/**
	 * Let's look if the given element contains a HFRE attribute
	 * 
	 * @param element
	 * @return
	 */
	public static boolean isHrefRedirect(OMElement element) {
		return ((element != null) && (element.getAttribute(HREF) != null));
	}

    /**
     * <p>
     * Create a copy of the given {@code sourceNode}.
     * </p>
     * <p>
     * This method is based on {@link org.apache.axiom.om.util.CopyUtils#copy(SOAPEnvelope)} available in
     * org.apache.ws.commons.axiom:axiom-compat.
     * </p>
     */
    public static <T extends OMInformationItem> T copy(final T sourceNode) {

        final SOAPCloneOptions options = new SOAPCloneOptions();

        options.setPreserveModel(true);
        options.setCopyOMDataSources(true);
        options.setFetchDataHandlers(true);

        return (T) sourceNode.clone(options);
	}

	/**
	 * Copy the tag data (attributes and namespaces) from the source element to
	 * the target element.
	 * 
	 * @param sourceElement
	 * @param targetElement
	 */
	public static void copyTagData(OMElement sourceElement,
			OMElement targetElement) {
		for (Iterator<?> i = sourceElement.getAllDeclaredNamespaces(); i
				.hasNext();) {
			OMNamespace ns = (OMNamespace) i.next();
			targetElement.declareNamespace(ns);
		}

		for (Iterator<?> i = sourceElement.getAllAttributes(); i.hasNext();) {
			OMAttribute attr = (OMAttribute) i.next();
			targetElement.addAttribute(attr);
		}
	}

}
