/***********************************************************************
 * 
 *  Copyright (C) 2006 Novell, Inc. All Rights Reserved.
 *
 *  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; version 2.1
 *  of the License.
 *
 *  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
 *  Library 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, Novell, Inc.
 * 
 *  To contact Novell about this file by physical or electronic mail, 
 *  you may find current contact information at www.novell.com.
 * 
 *  Author: Juan Carlos Luciani <jluciani@novell.com>
 *  
 ***********************************************************************/

package com.novell.casa.authtoksvc;

import java.io.InputStream;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import org.apache.log4j.Logger;

/**
 * GetAuthPolicyReqMsg Class.
 * <p>
 * This class deals with the message sent by Casa Client when requesting
 * authenication policy to authenticate an entity to a particular service.
 * The format of the the message is as follows:
 * <p>
 *    <?xml version="1.0" encoding="UTF-8"?>
 *    <get_auth_policy_req>
 *    <service>service name</service>
 *    <host>host name</host>
 *    </get_auth_policy_req>
 * 
 */
public final class GetAuthPolicyReqMsg
{
   private static final Logger m_log = Logger.getLogger(GetAuthPolicyReqMsg.class);

   protected String  m_serviceName = null;
   protected String  m_hostName = null;

   /*
    * Class for handling GetAuthPolicyReq msg parsing events.
    */
   private final class SAXHandler extends org.xml.sax.helpers.DefaultHandler
   {
      private final static int AWAITING_ROOT_ELEMENT_START = 0;
      private final static int AWAITING_ROOT_ELEMENT_END = 1;
      private final static int AWAITING_SERVICE_ELEMENT_START = 2;
      private final static int AWAITING_SERVICE_ELEMENT_END = 3;
      private final static int AWAITING_SERVICE_DATA = 4;
      private final static int AWAITING_HOST_ELEMENT_START = 5;
      private final static int AWAITING_HOST_ELEMENT_END = 6;
      private final static int AWAITING_HOST_DATA = 7;
      private final static int DONE_PARSING = 8;

      private final GetAuthPolicyReqMsg   m_GetAuthPolicyReqMsg;
      private int             m_state;

      /**
       * Constructor.
       *
       * @param getAuthPolicyReqMsg Get authentication policy request message object.
       */
      public SAXHandler (GetAuthPolicyReqMsg getAuthPolicyReqMsg)
      {
         super();

         // Initialize our members
         m_GetAuthPolicyReqMsg = getAuthPolicyReqMsg;
         m_state = AWAITING_ROOT_ELEMENT_START;
      }

      /**
       * endDocument() implementation.
       *
       * @throws SAXException
       */
      public final void endDocument () throws SAXException
      {
         // Verify that we obtained all of the required elements
         if (m_state != DONE_PARSING)
         {
            m_log.error("SAXHandler.endDocument()- Missing element");
            throw new SAXException("Missing element");
         }
      }

      /*
       * startElement() implementation.
       *
       * @param uri     Uri.
       * @param name    Local name.
       * @param qName   Qualified name.
       * @param atts    Attributes.
       * @throws SAXException
       */
      public final void startElement (String uri, String name, String qName, org.xml.sax.Attributes atts) throws SAXException
      {
         // Proceed based on our state
         switch (m_state)
         {
            case AWAITING_ROOT_ELEMENT_START:
               // Verify that we are processing the expected tag
               if (ProtoDefs.getAuthPolicyRequestElementName.equalsIgnoreCase(qName))
               {
                  // Advance to the next state
                  m_state = AWAITING_SERVICE_ELEMENT_START;
               }
               else
               {
                  m_log.error("SAXHandler.startElement()- Un-expected element");
                  throw new SAXException("Un-expected element");
               }
               break;

            case AWAITING_SERVICE_ELEMENT_START:
               // Verify that we are processing the expected tag
               if (ProtoDefs.serviceElementName.equalsIgnoreCase(qName))
               {
                  // Advance to the next state
                  m_state = AWAITING_SERVICE_DATA;
               }
               else
               {
                  m_log.error("SAXHandler.startElement()- Un-expected element");
                  throw new SAXException("Un-expected element");
               }
               break;

            case AWAITING_HOST_ELEMENT_START:
               // Verify that we are processing the expected tag
               if (ProtoDefs.hostElementName.equalsIgnoreCase(qName))
               {
                  // Advance to the next state
                  m_state = AWAITING_HOST_DATA;
               }
               else
               {
                  m_log.error("SAXHandler.startElement()- Un-expected element");
                  throw new SAXException("Un-expected element");
               }

               break;

            default:
               m_log.error("SAXHandler.startElement()- State error");
               throw new SAXException("State error");
         }
      }

      /**
       * endElement() implementation.
       *
       * @param uri     Uri.
       * @param name    Local name.
       * @param qName   Qualified name.
       * @throws SAXException
       */
      public final void endElement (String uri, String name, String qName) throws SAXException
      {
         // Proceed based on our state
         switch (m_state)
         {
            case AWAITING_ROOT_ELEMENT_END:
               // Verify that we are processing the expected tag
               if (ProtoDefs.getAuthPolicyRequestElementName.equalsIgnoreCase(qName))
               {
                  // Advance to the next state
                  m_state = DONE_PARSING;
               }
               else
               {
                  m_log.error("SAXHandler.endElement()- Un-expected element");
                  throw new SAXException("Un-expected element");
               }
               break;

            case AWAITING_SERVICE_ELEMENT_END:
               // Verify that we are processing the expected tag
               if (ProtoDefs.serviceElementName.equalsIgnoreCase(qName))
               {
                  // Advance to the next state
                  m_state = AWAITING_HOST_ELEMENT_START;
               }
               else
               {
                  m_log.error("SAXHandler.endElement()- Un-expected element");
                  throw new SAXException("Un-expected element");
               }
               break;

            case AWAITING_HOST_ELEMENT_END:
               // Verify that we are processing the expected tag
               if (ProtoDefs.hostElementName.equalsIgnoreCase(qName))
               {
                  // Advance to the next state
                  m_state = AWAITING_ROOT_ELEMENT_END;
               }
               else
               {
                  m_log.error("SAXHandler.endElement()- Un-expected element");
                  throw new SAXException("Un-expected element");
               }
               break;

            default:
               m_log.error("SAXHandler.endElement()- State error");
               throw new SAXException("State error");
         }
      }

      /**
       * character() implementation.
       *
       * @param ch      Characters with element data.
       * @param start   Start position in the character array.
       * @param length  Number of characters to use in the array.
       * @throws SAXException
       */
      public final void characters (char ch[], int start, int length) throws SAXException
      {
         // Proceed based on our state
         switch (m_state)
         {
            case AWAITING_SERVICE_DATA:
               // Consume the data
               m_GetAuthPolicyReqMsg.m_serviceName = new String(ch, start, length);

               // Advance to the next state
               m_state = AWAITING_SERVICE_ELEMENT_END;
               break;

            case AWAITING_SERVICE_ELEMENT_END:
               // Consume the data
               m_GetAuthPolicyReqMsg.m_serviceName = m_GetAuthPolicyReqMsg.m_serviceName.concat(new String(ch, start, length));
               break;

            case AWAITING_HOST_DATA:
               // Consume the data
               m_GetAuthPolicyReqMsg.m_hostName = new String(ch, start, length);

               // Advance to the next state
               m_state = AWAITING_HOST_ELEMENT_END;
               break;

            case AWAITING_HOST_ELEMENT_END:
               // Consume the data
               m_GetAuthPolicyReqMsg.m_hostName = m_GetAuthPolicyReqMsg.m_hostName.concat(new String(ch, start, length));
               break;

            default:
               // Do nothing
               break;
         }
      }
   }

   /**
    * Constructor.
    *
    * @param inStream   Input stream with get authentication policy request message.
    * @throws Exception
    */
   public GetAuthPolicyReqMsg (InputStream inStream) throws Exception
   {
      try
      {
         // Parse the GetAuthPolicyReqMsg
         XMLReader xr = XMLReaderFactory.createXMLReader();
         SAXHandler handler = new SAXHandler(this);
         xr.setContentHandler(handler);
         xr.setErrorHandler(handler);

         InputSource source = new InputSource(inStream);
         xr.parse(source);
      }
      catch (SAXException e)
      {
         m_log.error("Constructor()- Parse exception: " + e.toString());
         throw new Exception("Protocol error", e);
      }
   }

   /**
    * Method to get the service name.
    *
    * @return Service name.
    */
   public String getServiceName()
   {
      return m_serviceName;
   }

   /**
    * Method to get the host name.
    *
    * @return  Host name.
    */
   public String getHostName()
   {
      return m_hostName;
   }
}
