//______________________________________________________________________________
//   |       |
//   | knipp |           Knipp Medien und Kommunikation GmbH
//    -------
//
//
//     Knipp RRI Toolkit
//
//
//     Copyright (C) 2005 by Knipp Medien und Kommunikation GmbH
//
//     This 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 any later version.
// 
//     This 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 library; if not, write to the 
//     Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
//     Boston, MA 02111-1307 USA 
//
//
//     initial author:    Klaus Malorny
//
//     version:           1.3 (15112), 2011/05/27 16:00:14
//                        $Revision: 14936 $ 
//                        $Date: 2010-11-05 15:57:40 +0100 (Fri, 05 Nov 2010) $
//                        $Author: Klaus.Malorny $
//

package de.knipp.rritoolkit;

import java.util.List;
import java.util.ArrayList;
import java.util.Collections;

import javax.xml.namespace.QName;

import org.w3c.dom.Node;
import org.w3c.dom.Element;
import org.w3c.dom.DOMException;

import static de.knipp.rritoolkit.DenicConstants.NS_XSI;

/**
 *  helper class that simplifies the parsing of DOM trees
 *
 *  @version  $Revision: 14936 $
 *  @author   Klaus Malorny
 */

public class XmlHelper
{

  /**
   *  private constructor to prevent instantiation
   */

  private XmlHelper ()
  {
    // empty
  }


  /**
   *  return a list of all child elements of the given node
   *
   *  @param node                     the parent node
   *  @return                         a list containing the elements
   *  @throws DOMException            on DOM exceptison
   */
  
  public static List<Element> getElements (Node node)
    throws DOMException
  {
    List<Element> result = null;
    Node child = node.getFirstChild ();
    
    while (child != null)
    {
      if (child instanceof Element)
      {
        if (result == null) result = new ArrayList<Element> ();
        result.add ((Element) child);
      }
      
      child = child.getNextSibling ();
    }
    
    if (result == null) return Collections.emptyList ();
    return result;
  }
  
  
  
  /**
   *  return a list of all child elements of the given node which
   *  match the given local name. The functionality is similar to
   *  {@code Element.getElementsByTagName}, but it returns a list,
   *  which is iterable.
   *
   *  @param node                    the parent node
   *  @param localName               the element's local name
   *  @return                        a list containing the elements
   *  @throws DOMException           on DOM exceptions
   */
  
  public static List<Element> getElements (Node node, String localName)
    throws DOMException
  {
    List<Element> result = null;
    Node child = node.getFirstChild ();
    
    while (child != null)
    {
      if (child instanceof Element)
      {
        Element el = (Element) child;
        
        if (localName.equals (el.getLocalName ()))
        {        
          if (result == null) result = new ArrayList<Element> ();
          result.add (el);
        }
      }
      
      child = child.getNextSibling ();
    }
    
    if (result == null) return Collections.emptyList ();
    return result;
  }

  
  
  /**
   *  return a list of all child elements of the given node which
   *  match the given namespace and local name. Please note that the prefix
   *  of the elements are not tested. The functionality is similar to
   *  {@code Element.getElementsByTagName}, but it returns a list,
   *  which is iterable.
   *
   *  @param node                    the parent node
   *  @param uri                     the namespace URI
   *  @param localName               the element's local name. If 
   *                                 {@code null}, all elements matching
   *                                 the given namespace are returned
   *  @return                        a list containing the elements
   *  @throws DOMException           on DOM exceptions
   */
  
  public static List<Element> getElements (Node node, String uri, 
    String localName)
    throws DOMException
  {
    List<Element> result = null;
    Node child = node.getFirstChild ();
    
    while (child != null)
    {
      if (child instanceof Element)
      {
        Element el = (Element) child;
        
        if (uri.equals (el.getNamespaceURI ()) &&
          (localName == null || localName.equals (el.getLocalName ())))
        {        
          if (result == null) result = new ArrayList<Element> ();
          result.add (el);
        }
      }
      
      child = child.getNextSibling ();
    }
    
    if (result == null) return Collections.emptyList ();
    return result;
  }

  
  
  /**
   *  return a list of all child elements of the given node which
   *  match the given namespace and local name. Please note that the prefix
   *  of the elements are not tested. The functionality is similar to
   *  {@code Element.getElementsByTagName}, but it returns a list,
   *  which is iterable.
   *
   *  @param node                    the parent node
   *  @param qName                   the qualified name
   *  @return                        a list containing the elements
   *  @throws DOMException           on DOM exceptions
   */
  
  public static List<Element> getElements (Node node, QName qName)
    throws DOMException
  {
    return getElements (node, qName.getNamespaceURI (), qName.getLocalPart ());
  }
  

  
  
  /**
   *  return the first element
   *
   *  @param node                    the parent node
   *  @return                        the first element or {@code null}
   *                                 if there is no matching element
   *  @throws DOMException           on DOM exceptions
   */
  
  public static Element getFirstElement (Node node)
    throws DOMException
  {
    Node child = node.getFirstChild ();

    while (child != null)
    {
      if (child instanceof Element) return (Element) child;
      child = child.getNextSibling ();
    }
    
    return null;
  }
  
  
  /**
   *  return the next sibling element
   *
   *  @param el                      the element
   *  @return                        the next element or {@code null} if
   *                                 there is no next sibling element
   */
  
  public static Element getNextElement (Element el)
  {
    Node child = el.getNextSibling ();
    
    while (child != null)
    {
      if (child instanceof Element) return (Element) child;
      child = child.getNextSibling ();
    }
    
    return null;
  }
    
  
  
  /**
   *  return the first element with the given local name
   *
   *  @param node                    the parent node
   *  @param localName               the element's local name
   *  @return                        the first element or {@code null}
   *                                 if there is no matching element
   *  @throws DOMException           on DOM exceptions
   */
  
  public static Element getFirstElement (Node node, String localName)
    throws DOMException
  {
    Node child = node.getFirstChild ();

    while (child != null)
    {
      if (child instanceof Element)
      {
        Element el = (Element) child;
        if (localName.equals (el.getLocalName ())) return el;
      }
      
      child = child.getNextSibling ();
    }
    
    return null;
  }

  
  /**
   *  return the first element with the given namespace/local name
   *
   *  @param node                    the parent node
   *  @param uri                     the namespace URI
   *  @param localName               the element's local name. If 
   *                                 {@code null}, the first element
   *                                 matching the given namespace is returned
   *  @return                        the first element or {@code null}
   *                                 if there is no matching element
   *  @throws DOMException           on DOM exceptions
   */

  public static Element getFirstElement (Node node, String uri, 
    String localName)
    throws DOMException
  {
    Node child = node.getFirstChild ();

    while (child != null)
    {
      if (child instanceof Element)
      {
        Element el = (Element) child;

        if (uri.equals (el.getNamespaceURI ()) &&
          (localName == null || localName.equals (el.getLocalName ())))
          return el;
      }
      
      child = child.getNextSibling ();
    }
    
    return null;
  }


  
  
  /**
   *  return the first element with the given namespace/local name
   *
   *  @param node                    the parent node
   *  @param qName                   the qualified name
   *  @return                        the first element or {@code null}
   *                                 if there is no matching element
   *  @throws DOMException           on DOM exceptions
   */

  public static Element getFirstElement (Node node, QName qName)
    throws DOMException
  {
    return getFirstElement (node, qName.getNamespaceURI (), 
      qName.getLocalPart ());
  }
  
  
  /**
   *  return the text of the first matching element
   *
   *  @param node                    the parent node
   *  @param localName               the element's local name
   *  @return                        the text of the first element or 
   *                                 {@code null} if there is no
   *                                 matching element
   *  @throws DOMException           on DOM exceptions
   */
  
  public static String getFirstElementText (Node node, String localName)
    throws DOMException
  {
    Element el = getFirstElement (node, localName);
    return el == null ? null : el.getTextContent ();
  }

  
  /**
   *  return the the text first element with the given namespace/local name
   *
   *  @param node                    the parent node
   *  @param uri                     the namespace URI
   *  @param localName               the element's local name. If 
   *                                 {@code null}, the first element
   *                                 matching the given namespace is returned
   *  @return                        the text of the first element or 
   *                                 {@code null} if there is no
   *                                 matching element
   *  @throws DOMException           on DOM exceptions
   */

  public static String getFirstElementText (Node node, String uri, 
    String localName)
    throws DOMException
  {
    Element el = getFirstElement (node, uri, localName);
    return el == null ? null : el.getTextContent ();
  }

  
  /**
   *  return the the text first element with the given namespace/local name
   *
   *  @param node                    the parent node
   *  @param qName                   the qualified name
   *  @return                        the text of the first element or 
   *                                 {@code null} if there is no
   *                                 matching element
   *  @throws DOMException           on DOM exceptions
   */

  public static String getFirstElementText (Node node, QName qName)
    throws DOMException
  {
    Element el = getFirstElement (node, qName.getNamespaceURI (), 
      qName.getLocalPart ());
    return el == null ? null : el.getTextContent ();
  }  
  
  
  
  /**
   *  return a boolean value of a given string, 
   *
   *  @param text                     the text, may be {@code null}
   *  @param defaultValue             the default value
   *  @return                         the value of {@code text}, as defined
   *                                  by the {@code boolean} XML Schema
   *                                  datatype, or the default value if
   *                                  {@code text} is null or invalid
   */
  
  public static boolean asBoolean (String text, boolean defaultValue)
  {
    if (text == null) return defaultValue;
    text = text.trim ();
    if (text.equals ("false") || text.equals ("0")) return false;
    if (text.equals ("true") || text.equals ("1")) return true;
    return defaultValue;
  }
  
  
    
  /**
   *  return whether the given element is nilled
   *
   *  @param el                       the element
   *  @return                         {@code true} if nilled, i.e. has
   *                                  the {@code xsi:nil} attribute with
   *                                  {@code true} as a value
   *  @throws DOMException            on DOM exceptions
   */
  
  public static boolean isNilled (Element el)
    throws DOMException
  {
    return asBoolean (el.getAttributeNS (NS_XSI, "nil"), false);
  }
  
  

}


