/*
 * @(#)DataWebBeanImpl.java
 *
 * Copyright 1999-2002 by Oracle Corporation,
 * 500 Oracle Parkway, Redwood Shores, California, 94065, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information
 * of Oracle Corporation.
 */

package oracle.jdeveloper.html;

import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import oracle.jbo.ApplicationModule;
import oracle.jbo.AttributeDef;
import oracle.jbo.Key;
import oracle.jbo.Row;
import oracle.jbo.RowSet;
import oracle.jbo.Transaction;
import oracle.jbo.ViewObject;
import oracle.jbo.common.Diagnostic;
import oracle.jbo.common.ampool.SessionCookie;
import oracle.jbo.html.DataSource;
import oracle.jbo.html.DataSourceImpl;
import oracle.jbo.html.jsp.JSPApplicationRegistry;
import oracle.jbo.http.HttpContainer;
import oracle.jbo.http.HttpSessionCookieFactory;

/**
 * Implements the base methods for a Data Web Bean. Extend this class when you
 * define a Web Bean that must access a data source and that does not need to
 * extend another class.
 */
public class DataWebBeanImpl extends WebBeanImpl implements DataWebBean
{
   protected   DataSource  ds = null;
   protected   Vector      dispAttrs = null;
   protected   boolean     bReleaseApplication = false;
   protected   String      amName;

   
   public DataWebBeanImpl()
	{
	}

    /**
    * Returns the name of the Application Module's property file. The
    * property file typically contains this information:
    * <ul>
    *  <li>database connection mode and password</li>
    *  <li>name and path of the cascading style sheet associated with the
           application</li>
    *  <li>name and path of the directory where images associated with the
    *      application can be stored</li>
    *  <li>Application Module's name</li>
    *  <li>whether the Application Module is stateful</li>
    *  <li>JNDI path</li>
    *  </ul>
    * <p>
    * @return the name of the Application Module's property file.
    */
    public String getApplicationName()
    {
        return amName;
    }

	 /**
    *   Returns the name of the View Object to which this Data Web Bean is bound.
    * <p>
    * @return name of the View Object.
    */
    public String getViewObjectName()
    {
        return ds.getViewObjectName();
    }

	/**
	*	Clears the list of display attributes specified for the RowSet. The result
	* is that the Web Bean will now display all
	* of the RowSet's attributes.
	*/

   public void clearDisplayAttributes()
   {
      dispAttrs = null;
   }

   /**
   * Sets the list of the RowSet's attributes that will be displayed. This is
   * useful for limiting the set of attributes that are
   * to be displayed by the Web Bean. If this function is not called, all attributes are
   * displayed, except those of type <tt>Row</tt> and <tt>RowSet</tt>.
   * <p>
   * Specify the attributes as a comma-delimited list. For example, to display the
   * attributes EmpNo, Ename, and Job, enter <tt>setDisplayAttributes("EmpNo,
   * Ename, Job")</tt>.
   * <p>
   * @param sAttrs names of the attributes to be displayed.
   */
   public void setDisplayAttributes(String sAttrs)
   {
     StringTokenizer strtok = new StringTokenizer(sAttrs , ",");
     String sCol;

	  dispAttrs = new Vector();

     while(strtok.hasMoreTokens())
     {
        sCol = strtok.nextToken();
        sCol = sCol.trim();
        dispAttrs.insertElementAt(sCol, dispAttrs.size());
     }
   }

   /**
   * Returns a Hashtable containing the names of the attributes to be displayed. This
   * function can return <tt>null</tt>.
   *	<p>
   * @return a Hashtable containing the names of the attributes to be displayed.
   */
   public Vector getDisplayAttributes()
   {
      return dispAttrs;
   }

   /** returns the array of attributes to be displayed, if the set is not constrained , it will
   * return all of the attribute defs.
   */
   public AttributeDef[]  getDisplayAttributeDefs()
   {
      Vector      displayAttributes = this.getDisplayAttributes();
      ViewObject  vo = ds.getRowSet().getViewObject();

      if(displayAttributes == null || displayAttributes.size() <= 0)
         return vo.getAttributeDefs();

      Enumeration e = displayAttributes.elements();

      AttributeDef retList[] = new AttributeDef[displayAttributes.size()];
      int nIndex = 0;
      AttributeDef  aDef;

      while(e.hasMoreElements())
      {
         String sName = (String)e.nextElement();

         aDef = vo.findAttributeDef(sName);

         retList[nIndex++] = aDef;
      }
      return retList;
   }

   /**
   * <b>Internal:</b> <em>Applications should not use this method.</em>
   * <p>
   *	 Allows the override of the default RowSet that was retrieved by using the
   * <tt>initialize()</tt> function. The setRowSet() function sets up the RowSet
   * by clearing the field renderers if the selected row is null. Otherwise, it
   * populates the row with its field renderers.
   * <p>
   * @param aQuery the RowSet to be selected.
   *
   * @deprecated  Deprecated since 5.0.  RowSet is now defined with the DataSource
   */
   public void setRowSet(RowSet aQuery)
   {
      // no-op
   }

   /**
   *	Checks whether an attribute should be displayed by a Web Bean. This function returns
   * <tt>false</tt> if the attribute:
   * <p>
   * <ul>
   *    <li>of type <tt>Row</tt> or <tt>RowSet</tt>.</li>
   *    <li>is not in the Display Attribute List as configured with the
   *        <tt>setDisplayAttributes()</tt> function.</li>
   * </ul>
   * <p>
   * In either of these cases, the attribute will not be displayed.
   * <p>
   * Note, if the <tt>setDisplayAttributes()</tt> function has not been called, all
   * attributes are displayed
   * except <tt>Row</tt> or <tt>RowSet</tt> attributes.
   */
   public boolean shouldDisplayAttribute(AttributeDef attrDef)
   {
       if (!ds.shouldDisplayAttribute(attrDef))
       {
         return false;
       }

       if(dispAttrs != null)
       {
         int nSize = dispAttrs.size();
         String sAttr;

         for(int i = 0 ; i < nSize ; i++)
         {
            sAttr = (String)dispAttrs.elementAt(i);
            if(sAttr.equals(attrDef.getName()))
               return true;
         }
       }

       return true;
   }


   /**
   * Returns the RowSet of the View Object used by the Web Bean object.
   * <p>
   *	@return the RowSet that the Data Web Bean is using.
   */
   public RowSet getRowSet()
	{
		return ds.getRowSet();
	}

  /**
   * Initializes this Data Web Bean object to:
   * <ul>
   * <li>Access the important objects of the JSP: application, session,
   * request, response, and out.</li>
   *
   * <li>Connect to a database through an Application Module and access a data
   * source represented by one of the Application Module's View Objects. The
   * parameter<TT> sRowSet</TT> provides the necessary information, specifying a
   * properties file generated by the Web Application Wizard and a View Object.
   * The properties file contains the name of an Application Module and a
   * database connnect string. The View Object must be associated
   * with the Application Module.</li>
   * </ul>
   * <p>
   * @param application    the JSP page's ServletContext.
   * @param session        the JSP page's HttpSession.
   * @param request        the JSP page's HttpServletRequest.
   * @param response       the JSP page's HttpServletResponse.
   * @param out            the PrintWriter to render to.
   * @param sRowSet        a string that specifies a Business Component JSP
   *                       Application's properties file and a View Object.
   *                       Its format is <em>properties file</em>.<em>View Object</em>
   *                       where <em>properties file</em> is the name of the
   *                       properties file without the <TT>.properties</TT> extension
   *                       and <em>View Object</em> is the name of a View Object
   *                       associated with the Application Module specified in
   *                       the properties file.
   */
	public void initialize(ServletContext application, HttpSession session , HttpServletRequest request,
                               HttpServletResponse response, JspWriter out, String sRowSet) throws Exception
    {
        initialize(application , session, request,response, new PrintWriter(out), sRowSet);
    }

   /**
    * @deprecated.  Use JSPApplicationRegistry.getReleaseMode() instead.
    */
   public boolean isAppStateful()
   {
      String releaseMode =
         JSPApplicationRegistry
         .getInstance()
         .getReleaseMode(getApplicationName());

      return (JSPApplicationRegistry.RESERVED.equals(releaseMode));
   }


   /**
    * Use JSPApplicationRegistry.getAppModuleFromContexts , removed deprecation since no alternative is suggested
    */
   protected ApplicationModule getApplicationFromContext()
   {
      ApplicationModule appModule = null;
      HttpContainer container = HttpContainer.getInstanceFromSession(session);
      SessionCookie cookie = container.getSessionCookie(getApplicationName());
      if (cookie != null)
      {
         appModule = cookie.useApplicationModule();
      }
      return appModule;
   }

    /**
    *  Enables or disables the release of application resources associated
    *  with this request. Only the last Data Web Bean on
    *  a page should set this value to <tt>true</tt> because it releases the
    *  Application Module that is associated
    *  with the page being processed.
    * <p>
    * @param bRelease <TT>true</TT> to release application resources associated with the
    * request; <TT>false</TT> to retain the resources.
    */
    public void setReleaseApplicationResources(boolean bRelease)
    {
        bReleaseApplication = bRelease;
    }

    /**
    * Releases any application resources associated with this request if
    * <tt>setReleaseApplicationResources(true)</tt>. Only the last Data Web Bean on
    * a page should set this value to <tt>true</tt> because it releases the
    * Application Module that is associated with the page being processed.
    */
   public void releaseApplicationResources() throws Exception
   {
      if (!bUsedInTag) // Don't do it when usae from tag because the
                       // releasePageResourceTag will takecare of it.
      {
         releaseApplicationResources(JSPApplicationRegistry
            .getInstance()
            .getReleaseMode(getApplicationName()));
      }
   }

   /**
    * Release the data web beans application module with the specified release
    * mode.
    */
   public void releaseApplicationResources(String releaseMode) throws Exception
   {
      if (bReleaseApplication)
      {
         HttpContainer container = HttpContainer.getInstanceFromSession(session);
         SessionCookie cookie = container.getSessionCookie(getApplicationName());

         if (JSPApplicationRegistry.RESERVED.equals(releaseMode))
         {
            Diagnostic.println("ApplicationModule release mode is:  Reserved");
            cookie.releaseApplicationModule(
               false // checkin
               , true); // manageState
         }
         // If a stateful release mode has been specified then passivate
         // the application module and return it to the application pool.  This
         // mode is not supported if a response object has not been specified.
         else if (JSPApplicationRegistry.STATEFUL.equals(releaseMode))
         {

            Diagnostic.println("ApplicationModule release mode is:  Stateful");

            cookie.releaseApplicationModule(
               true // checkin
               , true); // manageState
            cookie.writeValue(response);
         }
         // If the release mode is stateless then simply return the application
         // module to the pool.
         else
         {
            Diagnostic.println("ApplicationModule release mode is:  Stateless");
            cookie.releaseApplicationModule(
               true // checkin
               , false); // manageState
            cookie.writeValue(response);
         }
      }
   }

  /**
   * Initializes this Data Web Bean object to:
   * <ul>
   * <li>Access the important objects of the JSP: application, session,
   * request, response, and out.</li>
   *
   * <li>Connect to a database through an Application Module and access a data
   * source represented by one of the Application Module's View Objects. The
   * parameter<TT> sRowSet</TT> provides the necessary information, specifying a
   * properties file generated by the Web Application Wizard and a View Object.
   * The properties file contains the name of an Application Module and a
   * database connnect string. The View Object must be associated
   * with the Application Module.</li>
   * </ul>
   * <p>
   * @param application    the JSP page's ServletContext.
   * @param session        the JSP page's HttpSession.
   * @param request        the JSP page's HttpServletRequest.
   * @param response       the JSP page's HttpServletResponse.
   * @param out            the PrintWriter to render to.
   * @param sRowSet        a string that specifies a Business Component JSP
   *                       Application's properties file and a View Object.
   *                       Its format is <em>properties file</em>.<em>View Object</em>
   *                       where <em>properties file</em> is the name of the
   *                       properties file without the <TT>.properties</TT> extension
   *                       and <em>View Object</em> is the name of a View Object
   *                       associated with the Application Module specified in
   *                       the properties file.
   *
   */
	public void initialize(ServletContext application, HttpSession session , HttpServletRequest request, HttpServletResponse response, PrintWriter out, String viewObjectFullName) throws Exception
	{
      // Initialize the member variables (needed by internalInitialize)
      int nIndex = viewObjectFullName.lastIndexOf('.');
      amName = viewObjectFullName.substring(0, nIndex);
      ds = new DataSourceImpl(request, viewObjectFullName.substring(nIndex + 1));

      super.initialize(application, session, request, response, out);
	}

  /**
   * Initializes this Data Web Bean object to:
   * <ul>
   * <li>Access the page object of the JSP.</li>
   *
   * <li>Connect to a database through an Application Module and access a data
   * source represented by one of the Application Module's View Objects. The
   * parameter <TT>sRowSet</TT> provides the necessary information, specifying a
   * properties file generated by the Web Application Wizard and a View Object.
   * The properties file contains the name of an Application Module and a
   * database connnect string. The View Object must be one of those associated
   * with the Application Module.</li>
   * </ul>
   *
   * @param page           the JSP page's PageContext.
   * @param sRowSet        a string that specifies a Business Component JSP
   *                       Application's properties file and a View Object.
   *                       Its format is <em>properties file</em>.<em>View Object</em>
   *                       where <em>properties file</em> is the name of the
   *                       properties file without the <TT>.properties</TT> extension
   *                       and <em>View Object</em> is the name of a View Object
   *                       associated with the Application Module specified in
   *                       the properties file.
   */
   public void initialize(PageContext page, String sRowSet) throws Exception
	{
      this.page = page;
      initialize(page.getServletContext(), page.getSession(), (HttpServletRequest) page.getRequest(),
            (HttpServletResponse) page.getResponse(), new PrintWriter(page.getOut()), sRowSet);
   }

   public void internalInitialize() throws Exception
   {
      super.internalInitialize();

      // If the webBean is being used from a tag, the applicationModule has already
      // been locked by the datasource tag.
      if (!bUsedInTag)
      {

         JSPApplicationRegistry appRegistry = JSPApplicationRegistry.getInstance();

         appRegistry.registerApplicationFromPropertyFile(session, getApplicationName());

         Properties props = new Properties();
         props.put(HttpSessionCookieFactory.HTTP_SERVLET_REQUEST, request);
         SessionCookie cookie = HttpContainer.findSessionCookie(
            session
            , getApplicationName()
            , getApplicationName()
            , (String)null
            , (String)null
            , (Properties)null
            , props);
            

         ds.setApplicationModule(
            getApplicationName()
            , cookie.useApplicationModule());
      }

      // The default range size for a VO is 1, which is not appropriate for
      // html rendering.
      if (ds.getRowSet().getRangeSize() == 1)
      {
         ds.getRowSet().setRangeSize(DataSource.DEFAULT_RANGE_SIZE);
      }
	}

   public String getRowKey(Row row)
   {
      Key rowKey = row.getKey();
      return rowKey.toStringFormat(true);
   }

   public void handleCommit() throws Exception
   {
      String releaseMode;
       
      if (!bUsedInTag)
      {
         releaseMode = JSPApplicationRegistry.getInstance().getReleaseMode(getApplicationName());
      }
      else //$$$ until we define the reelasemode in the applicationmodule tag
           //$$$ we will always autocommit
      {
         releaseMode = JSPApplicationRegistry.STATEFUL;
      }

      RowSet      rs = ds.getRowSet();
      Transaction trans = rs.getApplicationModule().getTransaction();
      if(JSPApplicationRegistry.RESERVED.equals(releaseMode))
      {
         // only post, the user will click on commit button to commit
         // any changes
         trans.postChanges();
      }
      else if (JSPApplicationRegistry.STATELESS.equals(releaseMode))
      {
         // autocommit if we are stateless.  This is necessary
         // because the application module will be rolled back when it is
         // checked into the pool.
         trans.commit();
      }

		if (rs.getCurrentRow() == null)
      {
         rs.first();
      }
   }

  /**
   * Retrieves the default field renderer for a specified attribute definition
   * (that is, the attribute metadata).
   * <p>
   * @param attrDef an attribute definition.
   * @return default field renderer for the attribute definition.
   */
   public HTMLFieldRenderer getEditFieldRenderer(Row row, AttributeDef attrDef)
   {
      return ds.getEditFieldRenderer(page, session, row, attrDef); 
   }

   /**
   * Retrieves the default field renderer for a specified attribute definition
   * (that is, the attribute metadata).
   * <p>
   * @param attrDef an attribute definition.
   * @return default field renderer for the attribute definition.
   */
   public HTMLFieldRenderer getDisplayFieldRenderer(Row row, AttributeDef attrDef)
   {
      return ds.getDisplayFieldRenderer(page, session, row, attrDef); 
   }

    /**
   **  Assigns an instance-level display field renderer to be used in the DataWebBean
   **/
   public void setDisplayFieldRenderer(AttributeDef attrDef, HTMLFieldRenderer rdr)
   {
      ds.setDisplayFieldRenderer(attrDef, rdr); 
   }

   /**
   **  Assigns an instance-level edit field renderer to be used in the DataWebBean
   **/
   public void setEditFieldRenderer(AttributeDef attrDef, HTMLFieldRenderer rdr)
   {
      ds.setEditFieldRenderer(attrDef, rdr); 
   }

    /**
   **  Assigns an instance-level display field renderer to be used in the DataWebBean
   **/
   public void setDisplayFieldRenderer(int nIndex, HTMLFieldRenderer rdr)
   {
      ds.setDisplayFieldRenderer(nIndex, rdr); 
   }

   /**
   **  Assigns an instance-level edit field renderer to be used in the DataWebBean
   **/
   public void setEditFieldRenderer(int nIndex, HTMLFieldRenderer rdr)
   {
      ds.setEditFieldRenderer(nIndex, rdr); 
   }

   /**
   **  Utility method for getting the attribute's label , the label is first located in the
   **  AttributeDEf and then in the RowSet VO's attribute list. If no label is provided, the attribute
   **  name is used.
   **/
   public String getAttributeLabel(RowSet rs, AttributeDef aDef)
   {
      return DataSourceImpl.getAttributeLabel(rs, aDef); 
   }
}
