//______________________________________________________________________________
//   |       |
//   | 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 org.w3c.dom.Node;
import org.w3c.dom.Attr;
import org.w3c.dom.DOMException;


/**
 *  exception that is thrown if the parsing of the key-value pair or XML 
 *  response fails for some reason
 *
 *  @version  $Revision: 14936 $
 *  @author   Klaus Malorny
 */

public class ResponseParseException extends Exception
{
  /** the section, if available */
  
  private String section;
  
  
  /** the key, if available */
  
  private String key;
  
  
  /** the value, if available */
  
  private String value;
  
  
  /** the node, if available */
  
  private Node node;
  
  
  /**
   *  constructor
   *
   *  @param message                    the message
   */
  
  public ResponseParseException (String message)
  {
    super (message);
  }
  
  
  /**
   *  constructor
   *
   *  @param message                    the message
   *  @param cause                      the cause
   */
  
  public ResponseParseException (String message, Throwable cause)
  {
    super (message, cause);
  }
  
  
  
  /**
   *  constructor for an exception related to key-value pair parsing
   *
   *  @param message               the message
   *  @param theSection            the section, may be {@code null}
   *  @param theKey                the key, may be {@code null}
   *  @param theValue              the value, may be {@code null}
   *  @param cause                 the cause, may be {@code null}
   */
  
  public ResponseParseException (String message, String theSection, 
    String theKey, String theValue, Throwable cause)
  {
    super (message, cause);
    section = theSection;
    key = theKey;
    value = theValue;
  }
  

  
  /**
   *  constructor for an exception related to key-value pair parsing
   *
   *  @param message               the message
   *  @param theKey                the key, may be {@code null}
   *  @param theValue              the value, may be {@code null}
   *  @param cause                 the cause, may be {@code null}
   */
  
  public ResponseParseException (String message, String theKey, String theValue,
    Throwable cause)
  {
    this (message, null, theKey, theValue, cause);
  }
  
  
  /**
   *  constructor for an exception related to key-value pair parsing
   *
   *  @param message               the message
   *  @param theValue              the value, may be {@code null}
   *  @param cause                 the cause, may be {@code null}
   */
  
  public ResponseParseException (String message, String theValue,
    Throwable cause)
  {
    this (message, null, null, theValue, cause);
  }
  

  
  /**
   *  constructor for an exception related to key-value pair parsing
   *
   *  @param message               the message
   *  @param theSection            the section, may be {@code null}
   *  @param theKey                the key, may be {@code null}
   *  @param theValue              the value, may be {@code null}
   */
  
  public ResponseParseException (String message, String theSection, 
    String theKey, String theValue)
  {
    this (message, theSection, theKey, theValue, null);
    
  }
  

  
  /**
   *  constructor for an exception related to key-value pair parsing
   *
   *  @param message               the message
   *  @param theKey                the key, may be {@code null}
   *  @param theValue              the value, may be {@code null}
   */
  
  public ResponseParseException (String message, String theKey, String theValue)
  {
    this (message, null, theKey, theValue, null);
  }
  
  
  /**
   *  constructor for an exception related to key-value pair parsing
   *
   *  @param message               the message
   *  @param theValue              the value, may be {@code null}
   */
  
  public ResponseParseException (String message, String theValue)
  {
    this (message, null, null, theValue, null);
  }
  
  
  /**
   *  constructor for an exception related to XML parsing
   *
   *  @param message               the message
   *  @param theNode               the causing node, may be {@code null}
   */
  
  public ResponseParseException (String message, Node theNode)
  {
    super (message);
    node = theNode;
  }
  

  /**
   *  constructor for an exception related to XML parsing
   *
   *  @param message               the message
   *  @param theNode               the causing node, may be {@code null}
   *  @param cause                 the cause
   */
  
  public ResponseParseException (String message, Node theNode, Throwable cause)
  {
    super (message, cause);
    node = theNode;
  }
  
  
  
  /**
   *  try to construct a XPath like path that describes the location of
   *  the given node.
   *
   *  @param node                  the node
   *  @param sb                    the string builder to add to
   *  @throws DOMException         on exceptions accessing the DOM
   */
  
  private static void buildPath (Node node, StringBuilder sb)
    throws DOMException
  {
    Node parent;
      
    switch (node.getNodeType ())
    {
      case Node.DOCUMENT_NODE:
        break;
        
      case Node.ATTRIBUTE_NODE:
        parent = ((Attr) node).getOwnerElement ();
        if (parent != null) buildPath (parent, sb);

        sb.append ("/@");
        sb.append (node.getNodeName ());
        sb.append (" = \"");
        sb.append (node.getNodeValue ());
        sb.append ("\"");
        break;
        
      case Node.ELEMENT_NODE:
        parent = node.getParentNode ();
        if (parent != null) buildPath (parent, sb);

        sb.append ("/");
        sb.append (node.getNodeName ());
        
        Node temp = node;
        Node temp2;
        while ((temp2 = temp.getPreviousSibling ()) != null) temp = temp2;
        int pos = 0;
        int count = 0;
        
        while (temp != null)
        {
          if (temp == node ||
            temp.getNodeType () == Node.ELEMENT_NODE &&
            node.getLocalName ().equals (temp.getLocalName ()) &&
            (node.getNamespaceURI () == null ? 
            temp.getNamespaceURI () == null : 
            node.getNamespaceURI ().equals (temp.getNamespaceURI ())))
          {
            count++;
            if (temp == node) pos = count;
          }
          
          temp = temp.getNextSibling ();
        }
        
        if (count != 1)
        {
          sb.append ("[");
          sb.append (pos);
          sb.append ("]");
        }
        
        break;
        
      case Node.TEXT_NODE:
        parent = node.getParentNode ();
        if (parent != null) buildPath (parent, sb);

        sb.append ("/text() = \"");
        sb.append (node.getNodeValue ());
        sb.append ("\"");
        break;
        
      case Node.CDATA_SECTION_NODE:
        parent = node.getParentNode ();
        if (parent != null) buildPath (parent, sb);

        sb.append ("/cdata() = \"");
        sb.append (node.getNodeValue ());
        sb.append ("\"");
        break;
      
      case Node.COMMENT_NODE:
        parent = node.getParentNode ();
        if (parent != null) buildPath (parent, sb);

        sb.append ("/comment() = \"");
        sb.append (node.getNodeValue ());
        sb.append ("\"");
        break;
      
      default:
        parent = node.getParentNode ();
        if (parent != null) buildPath (parent, sb);

        sb.append ("node(");
        sb.append (node.getNodeType ());
        sb.append (")");
        break;
    }
  }
  
  
  
  /**
   *  return a description of the exception
   *
   *  @return           the description
   */

  @Override
  public String getMessage ()
  {
    StringBuilder sb = new StringBuilder (super.getMessage ());

    if (section != null)
    {
      sb.append (", section=\"");
      sb.append (section);
      sb.append ("\"");
    }
    
    if (key != null)
    {
      sb.append (", key=\"");
      sb.append (key);
      sb.append ("\"");
    }
    
    if (value != null)
    {
      sb.append (", value=\"");
      sb.append (value);
      sb.append ("\"");
    }
    
    if (node != null)
    {
      try
      {
        sb.append (", node ");
        buildPath (node, sb);
        
      } catch (DOMException e)
      {
        sb.append ("node <not displayable>");
      }
    }
    
    return sb.toString ();
  }
  
  
  //--- access methods ---------------------------------------------------------
  
  /**
   *  return the section
   *
   *  @return           the section name or {@code null} if not available
   */
  
  public String getSection ()
  {
    return section;
  }
  
  
  
  /**
   *  return the key
   *    
   *  @return           the key name or {@code null} if not available
   */
  
  public String getKey ()
  {
    return key;
  }
  
  
  
  /**
   *  return the value
   *
   *  @return           the value or {@code null} if not available
   */
  
  public String getValue ()
  {
    return value;
  }
  
  
  
  /**
   *  return the XML node
   *
   *  @return          the node or {@code null} if not available
   */
  
  public Node getNode ()
  {
    return node;
  }
}

