//------------------------------------------------------------------------------
// <copyright file="WSDLParser.java" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <summary>
//      Searches for the service metadata based on the location given by Service's WSDL_LOCATION field
//      or in any WSDL files in current directory
// </summary>
//------------------------------------------------------------------------------

package com.microsoft.hpc.scheduler.session;

import java.io.File;
import java.io.FilenameFilter;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.microsoft.hpc.exceptions.SR;

/**
 * Searches for the service metadata based on the location given by Service's WSDL_LOCATION field
 * or in any WSDL files in current directory
 */
public class WSDLParser
{
    private Document wsdl;
    
    Map<String, String> action;
    Map<String, String> responseAction;
    Map<String, String> fault;
    private QName binding; 
    
    {//initializer
        action = new HashMap<String, String>();
        responseAction = new HashMap<String, String>();
        fault = new HashMap<String, String>();
    }
    
    /**
     * Gets the action string for an operation
     * @param operation
     * @return 
     */
    String getAction(String operation) throws SessionException
    { 
        if(!action.containsKey(operation))
            throw new SessionException(SR.ServicesConfigurationMissing + ": " + operation);
        return action.get(operation); 
    }
    
    /**
     * Gets the response action string for an operation
     * @param operation
     * @return
     */
    String getResponseAction(String operation) throws SessionException 
    {
        if(!operation.endsWith("Response"))
        {
            operation += "Response";
        }
        if(!responseAction.containsKey(operation))
            throw new SessionException(SR.ServicesConfigurationMissing + ": " + operation);
        return responseAction.get(operation); 
    }
    
    /**
     * Gets the default binding QName as defined in metadata file
     * @return
     */
    QName getBinding() { return binding; }
    
    /**
     * Gets only wsdl files
     */
    class WSDLFiles implements FilenameFilter
    {
        public boolean accept(File dir, String name)
        {
            return name.endsWith(".wsdl");
        }
    }
    
    /**
     * Searches current folder for all wsdl files
     */
    WSDLParser()
    {
        parseArrayFiles();
    }
    
    /**
     * Parse each wsdl file in current directory and uses it to fill action strings
     * 
     */
    void parseArrayFiles()
    {
        File files[];
        
        File file = new File(".");
        
        files = file.listFiles(new WSDLFiles());
        
        for(int i = 0; i < files.length; i++)
        {
            try
            {
                parseFile(files[i].getCanonicalPath());
            }
            catch(Exception e)
            {
                ;//do nothing if malformed
            }
        }
    }
    
    /**
     * Uses a specific wsdl file as defined in Service Class
     * @param filelocation
     */
    public WSDLParser(String filelocation)
    {
        try
        {
            parseFile(filelocation);
        } catch (Exception e)
        {
            ;//parseArrayFiles();
        }
    }
    
    /**
     * parses a specific file for action strings
     * @param file
     */
    private void parseFile(String filelocation) throws Exception
    {
        try
        {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            InputStream stream = this.getClass().getResourceAsStream(filelocation);

            if(stream == null)
            {
                int fileIndex = filelocation.lastIndexOf("\\"); //windows.
                if(fileIndex == -1)
                {
                    fileIndex = filelocation.lastIndexOf("/"); //unix
                }
                if(fileIndex == -1) //checks twice
                {
                    throw new Exception(); //exception could occur if the directory was root in first place
                }   
                stream = this.getClass().getResourceAsStream(filelocation.substring(fileIndex));
            }    
            wsdl = db.parse(stream);

            
            parseWSDL(action, "wsdl:input", "");
            parseWSDL(responseAction, "wsdl:input", "Response");
            parseWSDL(fault, "wsdl:fault", "");
            
            binding = new QName("http://tempuri.org/", jumpToAttribute("wsdl:binding", "name"));
            if(action.isEmpty() || responseAction.isEmpty())
            {
                throw new Exception();
            }
        }
        catch(Exception e)
        {
            //file not found
            //search current directory
        }
    }
    
    /**
     * parses the WSDL and fills the map with the appropriate wsdl action strings
     * @param map - map to fill 
     * @param tagname - tagname to find 
     * @param actionNameAppend - for responses (used to append Reponse)
     */
          
    private void parseWSDL(Map<String, String> map, String tagname, String actionNameAppend)
    {
        NodeList nodelist = wsdl.getElementsByTagName(tagname);
        for(int i = 0; i < nodelist.getLength(); i++)
        {
            String actionValue = null;
            Node attributes = null;
            Node currentNode = nodelist.item(i);
            if(currentNode.hasAttributes())
            {
                attributes = currentNode.getAttributes().getNamedItem("wsaw:Action");
            }
            if(attributes != null)
            {
                actionValue = attributes.getNodeValue();
            }
            if(actionValue != null)
            {
                String actionName = currentNode.getParentNode().getAttributes().getNamedItem("name").getNodeValue();
                map.put(actionName + actionNameAppend, actionValue);
            }
        }
    }
    
    /**
     * Shortens the call to get value of an attribute at a tag
     * Does not have error handling (throws exception if attribute does not exist)
     * @param tagName
     * @param attribute
     * @return attribute value
     */
    private String jumpToAttribute(String tagName, String attribute)
    {
        return wsdl.getElementsByTagName(tagName).item(0)
        .getAttributes().getNamedItem(attribute).getNodeValue();
    }
    
}
