/*
 * (c) Copyright IBM Corp 2001, 2005 
 */

package edu.uga.cs.lsdis.meteors.wadls;

import java.net.URI;
import java.util.*;

import javax.wadls.*;
import javax.wadls.extensions.*;
import javax.wadls.extensions.ExtensionRegistry;
import javax.xml.namespace.*;
import org.w3c.dom.*;

/**
 * This class represents a WSDLS definition.
 *
 * @author Zixin Wu (wuzixin@uga.edu)
 * @author Paul Fremantle
 * @author Nirmal Mukhi
 * @author Matthew J. Duftler
 * @author namelessNerd
 */
public class ApplicationImpl implements Application
{
  protected String documentBaseURI = null;
  protected QName name = null;
  protected String targetNamespace = null;
  protected Map namespaces = new HashMap();
  protected Map includes = new HashMap();
  protected Params params = null;
  protected Grammars grammars = null;
  protected Map resources = new HashMap();
  protected Element docEl = null;
  protected String resourcespath=null;
  protected List extElements = new Vector();
  
  // added by namelessNerd
  protected List modelReferenceList= new ArrayList();
  protected List ontologyNamespaces= new ArrayList();
  // end namelessNerd additions
  
  protected ExtensionRegistry extReg = null;

  public static final long serialVersionUID = 1;

  	/**
  	 * Get all the Parts which has a modelReference with the specified namespace and concept.
  	 * @param namespace The namespace of the modelReference
  	 * @param concept The concept of the modelReference
  	 * @return All the Parts which has a modelReference with the specified namespace and concept.
  	 */

  	/**
  	 * Get all the Operations which has a modelReference with the specified namespace and concept.
  	 * @param namespace The namespace of the modelReference
  	 * @param concept The concept of the modelReference
  	 * @return All the Operations which has a modelReference with the specified namespace and concept.
  	 */
  	public List getMethodsByModelReference(URI namespace, String concept){
		//TODO not implemented yet.
		return null;
	}
  	

  	/**
  	 * Get all the XSD Elements which has a modelReference with the specified namespace and concept.
  	 * @param namespace The namespace of the modelReference
  	 * @param concept The concept of the modelReference
  	 * @return All the XSD Elements which has a modelReference with the specified namespace and concept.
  	 */
  	public List getXSDElementsByModelReference(URI namespace, String concept){
		//TODO not implemented yet.
		return null;
	}

  	/**
  	 * Get all the XSD ComplexTypes which has a modelReference with the specified namespace and concept.
  	 * @param namespace The namespace of the modelReference
  	 * @param concept The concept of the modelReference
  	 * @return All the XSD ComplexTypes which has a modelReference with the specified namespace and concept.
  	 */
  	public List getXSDComplexTypeByModelReference(URI namespace, String concept){
		//TODO not implemented yet.
		return null;
	}


  /**
   * Set the document base URI of this definition. Can be used to
   * represent the origin of the Definition, and can be exploited
   * when resolving relative URIs (e.g. in &lt;import&gt;s).
   *
   * @param documentBaseURI the document base URI of this definition
   */
  
  /**
   * Create a new modelReference.
   *
   * @return the newly created modelReference.
   */
  public ModelReference createModelReference(){
	  return new ModelReferenceImpl();
  }



  /**
   * Create a new precondition.
   *
   * @return the newly created precondition
   */
  public PreCondition createPreCondition(){
  	return new OperationAddOnImpl();
  }

  /**
   * Create a new effect.
   *
   * @return the newly created effect
   */
  public Effect createEffect(){
  	return new OperationAddOnImpl();
  }
  
  public void setDocumentBaseURI(String documentBaseURI)
  {
    this.documentBaseURI = documentBaseURI;
  }

  
  /**
   * Get the document base URI of this definition.
   *
   * @return the document base URI
   */
  public String getDocumentBaseURI()
  {
    return documentBaseURI;
  }
  
  public void setResourcesPath(String resourcespath)
  {
     this.resourcespath = resourcespath;
  }

  
  /**
   * Get the document base URI of this definition.
   *
   * @return the document base URI
   */
  public String getResourcesPath()
  {
    return resourcespath;
  }

  /**
   * Set the name of this definition.
   *
   * @param name the desired name
   */
  public void setQName(QName name)
  {
    this.name = name;
  }

  /**
   * Get the name of this definition.
   *
   * @return the definition name
   */
  public QName getQName()
  {
    return name;
  }

  /**
   * Set the target namespace in which WSDL elements are defined.
   *
   * @param targetNamespace the target namespace
   */
  public void setTargetNamespace(String targetNamespace)
  {
    this.targetNamespace = targetNamespace;
  }

  /**
   * Get the target namespace in which the WSDL elements
   * are defined.
   *
   * @return the target namespace
   */
  public String getTargetNamespace()
  {
    return targetNamespace;
  }
  
  /**
   * Get the target namespace in which the WSDL elements
   * are defined.
   *
   * @return the target namespace
   */
  public List getAllModelReference()
  {
	// grabModel
    return modelReferenceList;
  }

  /**
   * This is a way to add a namespace association to a definition.
   * It is similar to adding a namespace prefix declaration to the
   * top of a &lt;wsdl:definition&gt; element. This has nothing to do
   * with the &lt;wsdl:import&gt; element; there are separate methods for
   * dealing with information described by &lt;wsdl:import&gt; elements.
   *
   * @param prefix the prefix to use for this namespace (when
   * rendering this information as XML). Use null or an empty string
   * to describe the default namespace (i.e. xmlns="...").
   * @param namespaceURI the namespace URI to associate the prefix
   * with. If you use null, the namespace association will be removed.
   */
   public void addNamespace(String prefix, String namespaceURI)
   {
     if (prefix == null)
     {
       prefix = "";
     }

     if (namespaceURI != null)
     {
       namespaces.put(prefix, namespaceURI);
     }
     else
     {
       namespaces.remove(prefix);
     }
   }

   /**
    * Get the namespace URI associated with this prefix. Or null if
    * there is no namespace URI associated with this prefix. This is
    * unrelated to the &lt;wsdl:import&gt; element.
    *
    * @see #addNamespace(String, String)
    * @see #getPrefix(String)
    */
   public String getNamespace(String prefix)
   {
     if (prefix == null)
     {
       prefix = "";
     }

     return (String)namespaces.get(prefix);
   }

   /**
    * Get a prefix associated with this namespace URI. Or null if
    * there are no prefixes associated with this namespace URI. This is
    * unrelated to the &lt;wsdl:import&gt; element.
    *
    * @see #addNamespace(String, String)
    * @see #getNamespace(String)
    */
   public String getPrefix(String namespaceURI)
   {
     if (namespaceURI == null)
     {
       return null;
     }

     Iterator entryIterator = namespaces.entrySet().iterator();

     while (entryIterator.hasNext())
     {
       Map.Entry entry = (Map.Entry)entryIterator.next();
       String prefix = (String)entry.getKey();
       String assocNamespaceURI = (String)entry.getValue();

       if (namespaceURI.equals(assocNamespaceURI))
       {
         return prefix;
       }
     }

     return null;
   }

   /**
    * Get all namespace associations in this definition. The keys are
    * the prefixes, and the namespace URIs are the values. This is
    * unrelated to the &lt;wsdl:import&gt; element.
    *
    * @see #addNamespace(String, String)
    */
   public Map getNamespaces()
   {
     return namespaces;
   }

  /**
   * Set the types section.
   */
  public void setParams(Params params)
  {
    this.params = params;
  }

  public void setGrammars(Grammars grammars)
  {
    this.grammars = grammars;
  }
  /**
   * Get the types section.
   *
   * @return the types section
   */
  public Params getParams()
  {
    return params;
  }
  
  public Grammars getGrammars()
  {
    return grammars;
  }

  /**
   * Add an import to this WADL description.
   *
   * @param importDef the import to be added
   */
  public void addInclude(Include includeApp)
  {
    String namespaceURI = includeApp.getNamespaceURI();
    List includeList = (List)includes.get(namespaceURI);

    if (includeList == null)
    {
      includeList = new Vector();

      includes.put(namespaceURI, includeList);
    }

    includeList.add(includeApp);
  }

  /**
   * Get the list of imports for the specified namespaceURI.
   *
   * @param namespaceURI the namespaceURI associated with the
   * desired imports.
   * @return a list of the corresponding imports, or null if
   * there weren't any matching imports
   */
  public List getIncludes(String namespaceURI)
  {
    return (List)includes.get(namespaceURI);
  }

  /**
   * Get a map of lists containing all the imports defined here.
   * The map's keys are the namespaceURIs, and the map's values
   * are lists. There is one list for each namespaceURI for which
   * imports have been defined.
   */
  public Map getIncludes()
  {
    return includes;
  }


  /**
   * Add a portType to this WSDL description.
   *
   * @param portType the portType to be added
   */
  public void addResource(Resource portType)
  {
    resources.put(portType.getQName(), portType);
    
  }

  /**
   * Get the specified portType. Also checks imported documents.
   *
   * @param name the name of the desired portType.
   * @return the corresponding portType, or null if there wasn't
   * any matching portType
   */
  public Resource getResource(QName name)
  {
	  Resource resource = (Resource)resources.get(name);

    if (resource == null && name != null)
    {
    	resource = (Resource)getFromIncludes(Constants.ELEM_RESOURCE, name);
    }

    return resource;
  }

  /**
   * Remove the specified portType from this definition.
   *
   * @param name the name of the portType to remove
   * @return the portType previously associated with this qname, if there
   * was one; may return null
   */
  public Resource removeResource(QName name)
  {
    return (Resource)resources.remove(name);
  }

  /**
   * Get all the portTypes defined here.
   */
  public Map getResources()
  {
    return resources;
  }


  /**
   * Set the documentation element for this document. This dependency
   * on org.w3c.dom.Element should eventually be removed when a more
   * appropriate way of representing this information is employed.
   *
   * @param docEl the documentation element
   */
  public void setDocumentationElement(Element docEl)
  {
    this.docEl = docEl;
  }

  /**
   * Get the documentation element. This dependency on org.w3c.dom.Element
   * should eventually be removed when a more appropriate way of
   * representing this information is employed.
   *
   * @return the documentation element
   */
  public Element getDocumentationElement()
  {
    return docEl;
  }

  /**
   * Get all the extensibility elements defined here.
   */
  public List getExtensibilityElements()
  {
    return extElements;
  }


  /**
   * Create a new import.
   *
   * @return the newly created import
   */
  public Include createInclude()
  {
    return new IncludeImpl();
  }

  /**
   * Create a new input.
   *
   * @return the newly created input
   */
  public Request createRequest()
  {
    return new RequestImpl();
  }

  
  /**
   * Create a new operation.
   *
   * @return the newly created operation
   */
  public Method createMethod()
  {
    return new MethodImpl();
  }

  /**
   * Create a new output.
   *
   * @return the newly created output
   */
  public Response createResponse()
  {
    return new ResponseImpl();
  }

  /**
   * Create a new port type.
   *
   * @return the newly created port type
   */
  public Resource createResource()
  {
    return new ResourceImpl();
  }

  /**
  * Create a new types section.
   *
   * @return the newly created types section
   */
  public Params createParams()
  {
    return new ParamsImpl(this);
  }
  public Grammars createGrammars()
  {
    return new GrammarsImpl(this);
  }

  private Object getFromIncludes(String typeOfApplication, QName name)
  {
    Object ret = null;
    List includeList = getIncludes(name.getNamespaceURI());

    if (includeList != null)
    {
      Iterator includeIterator = includeList.iterator();

      while (includeIterator.hasNext())
      {
        Include includeApp = (Include)includeIterator.next();

        if (includeApp != null)
        {
          Application includedApp = includeApp.getApplication();
    
          if (includedApp != null)
          {
            /*
              These object comparisons will work fine because
              this private method is only called from within
              this class, using only the pre-defined constants
              from the Constants class as the typeOfDefinition
              argument.
            */
           if (typeOfApplication == Constants.ELEM_RESOURCE)
            {
              ret = includedApp.getResource(name);
            }

            if (ret != null)
            {
              return ret;
            }
          }
        }
      }
    }

    return ret;
  }

  public String toString()
  {
    StringBuffer strBuf = new StringBuffer();

    strBuf.append("Definition: name=" + name +
                  " targetNamespace=" + targetNamespace);

    if (includes != null)
    {
      Iterator includeIterator = includes.values().iterator();

      while (includeIterator.hasNext())
      {
        strBuf.append("\n" + includeIterator.next());
      }
    }

    if (params != null)
    {
      strBuf.append("\n" + params);
    }



    if (resources != null)
    {
      Iterator resourceIterator = resources.values().iterator();

      while (resourceIterator.hasNext())
      {
        strBuf.append("\n" + resourceIterator.next());
      }
    }


    return strBuf.toString();
  }
  
}
