/********************************************************************
 *  Oracle JDeveloper
 *  Copyright (c) 1997, 1999, Oracle Corporation. All rights reserved.
 ********************************************************************/


package oracle.jdeveloper.html;

import java.io.*;
import java.util.*;
import oracle.jdeveloper.html.*;
import oracle.jbo.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import java.sql.Types;

import oracle.jbo.html.jsp.JSPApplicationRegistry;

import oracle.jbo.http.HttpContainer;
import oracle.jbo.http.ApplicationBindingListener;

import oracle.jbo.common.Configuration;

/**
 * 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   RowSet      qView;
	protected   Vector      dispAttrs = null;
   protected   boolean     bReleaseApplication = false;
   protected   String      sApplication = "";
   protected   String		sRowSetName = "";
   protected   Hashtable   attributeRenderers = new Hashtable(10);

	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 sApplication;
    }

	 /**
    *   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 sRowSetName;
    }

	/**
	*	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();

      if(displayAttributes == null || displayAttributes.size() <= 0)
         return qView.getViewObject().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 = qView.getViewObject().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.
	*/
   public void setRowSet(RowSet aQuery)
   {
	   this.qView = aQuery;
   }

   /**
   *	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(attrDef.getJavaType().isAssignableFrom(oracle.jbo.Row.class) ||
          attrDef.getJavaType().isAssignableFrom(oracle.jbo.RowSet.class))
           return false;

       if(attrDef.getColumnName() != null)
       {
        if(attrDef.getColumnName().equalsIgnoreCase("ROWID")
          || attrDef.getColumnName().equalsIgnoreCase("ROW_ID"))
           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 this.qView;
	}

  /**
   * 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(sApplication);

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


   /**
    * @deprecated Use JSPApplicationRegistry.getAppModuleFromContexts
    */
   protected ApplicationModule getApplicationFromContext()
   {
      return JSPApplicationRegistry
         .getInstance()
         .getAppModuleFromContexts(sApplication, session, null);
   }

    /**
    *  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
   {
      releaseApplicationResources(JSPApplicationRegistry
         .getInstance()
         .getReleaseMode(sApplication));

   }

   /**
    * Release the data web beans application module with the specified release
    * mode.
    */
   public void releaseApplicationResources(String releaseMode) throws Exception
   {
      if (bReleaseApplication)
      {
         JSPApplicationRegistry
            .getInstance()
            .releaseAppModuleInstance(sApplication, session, response, releaseMode);
      }
   }

  /**
   * 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 sRowSet) throws Exception
	{
      // Initialize the member variables (needed by internalInitialize)
      int nIndex = sRowSet.lastIndexOf('.');
		sApplication = sRowSet.substring(0, nIndex);
		sRowSetName = sRowSet.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();

		oracle.jbo.html.jsp.JSPApplicationRegistry appRegistry =
         oracle.jbo.html.jsp.JSPApplicationRegistry.getInstance();

      appRegistry.registerApplicationFromPropertyFile(session, sApplication);

      ApplicationModule am = appRegistry.getAppModuleInstance(
         sApplication
         , request
         , session);

		RowSet rs = am.findViewObject(sRowSetName);

		if (rs == null)
		{
			throw new RuntimeException("DataWebBean: Could not locate View Object: " + sRowSetName + " in application " + sApplication);
		}

		if(rs.getRangeSize() < this.DEFAULT_RANGE_SIZE && rs.getRangeSize() != -1)
		{
		 rs.setRangeSize(this.DEFAULT_RANGE_SIZE);
		}

		setRowSet(rs);

	}

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

   public Row getRowFromKey(RowSet rs , String sKey)
   {
      try
      {
         Key key = new Key(sKey, rs.getViewObject().getKeyAttributeDefs());


         Row rows[] = qView.findByKey(key,1);


         if(rows.length <= 0)
          return null;

         return rows[0];
      }
      catch(Exception ex)
      {
         ex.printStackTrace();
         return null;
      }
   }

   public void handleCommit() throws Exception
   {
      String releaseMode = JSPApplicationRegistry
         .getInstance()
         .getReleaseMode(getApplicationName());

      if(JSPApplicationRegistry.RESERVED.equals(releaseMode))
      {
         // only post, the user will click on commit button to commit
         // any changes
         qView.getApplicationModule().getTransaction().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.
         qView.getApplicationModule().getTransaction().commit();
      }

		if(qView.getCurrentRow() == null)
      {
        qView.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)
   {
       HTMLFieldRenderer rField = null;
       boolean           isReadOnly = false;
       String            sClassRenderer = null;

       // check for instance-level renderer first
       if(attributeRenderers.get(attrDef.getIndex() + "_EditRenderer") != null)
       {
         rField = (HTMLFieldRenderer)attributeRenderers.get(attrDef.getIndex() + "_EditRenderer");
         return rField;
       }

       if(attrDef.getProperty("EditRenderer") != null)
       {
         sClassRenderer = attrDef.getProperty("EditRenderer").toString();
       }

       if(sClassRenderer == null)
       {
         String sAttrType = attrDef.getJavaType().getName();

         sAttrType = sAttrType.replace('.','_');

         if(session.getValue( sAttrType + "_EditRenderer") != null)
            sClassRenderer = session.getValue(sAttrType + "_EditRenderer").toString();
       }

       try
       {
         if(sClassRenderer != null)
         {
           Class cls = Class.forName(sClassRenderer);

           rField = (HTMLFieldRenderer)cls.newInstance();
         }
       }
       catch(Exception ex)
       {
         ex.printStackTrace();
         rField = null;
       }

       if(row != null)
       {
           isReadOnly = !row.isAttributeUpdateable(attrDef.getIndex());
       }

       boolean isViewReadOnly = qView.getViewObject().isReadOnly();

       if(rField == null && isViewReadOnly)
       {
           rField =  new ReadOnlyField();
       }

       if(rField == null)
       {
           // check if it's read-only, if the view is read-only then every field is read-only
           if(row != null && !row.isAttributeUpdateable(attrDef.getIndex()))
           {
               rField =  new ReadOnlyField();
           }
           else if(attrDef.getPrecision() > 30)
           {
               rField =  new TextArea();
           }
           else if (attrDef.getSQLType() == Types.DATE
              || attrDef.getSQLType() == Types.TIMESTAMP)
           {
               rField = new DateField();
           }
           else
           {
               rField = new TextField();
           }
       }

       rField.setFieldName(attrDef.getName());

       if(attrDef.getPrecision() == 0)
       {
           // it's probably a date field
           rField.setMaxDataLength(10);
           rField.setDisplayWidth(11);
       }
       else
       {
           int nLength = attrDef.getPrecision();

           if(attrDef.getScale() > 0)
               nLength += 1;

           rField.setMaxDataLength(nLength);

           if(attrDef.getPrecision() > 70)
           {
               rField.setDisplayWidth(70);
           }
           else
           {
               rField.setDisplayWidth(attrDef.getPrecision() + 5);
           }
       }

       rField.setDisplayHeight(10);
       rField.setPromptText(attrDef.getName());
       rField.setCSSClassName("cls" + attrDef.getName());

       return rField;
   }


   /**
   * 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)
   {
       HTMLFieldRenderer rField = null;
       boolean           isReadOnly = false;
       String            sClassRenderer = null;

       // check for instance-level renderer first
       if(attributeRenderers.get(attrDef.getIndex() + "_Renderer") != null)
       {
         rField = (HTMLFieldRenderer)attributeRenderers.get(attrDef.getIndex() + "_Renderer");
         return rField;
       }

       if(attrDef.getProperty("Renderer") != null)
       {
         sClassRenderer = attrDef.getProperty("Renderer").toString();
       }

       String sAttrType = attrDef.getJavaType().getName();
       sAttrType = sAttrType.replace('.','_');

       if(sClassRenderer == null && session.getValue( sAttrType + "_Renderer") != null)
       {
           sClassRenderer = session.getValue(sAttrType + "_Renderer").toString();
       }

       try
       {
         if(sClassRenderer != null)
         {
            Class cls = Class.forName(sClassRenderer);

            rField = (HTMLFieldRenderer)cls.newInstance();
         }
       }
       catch(Exception ex)
       {
          ex.printStackTrace();
          rField = null;
       }

       if(rField == null)
       {
           rField =  new ReadOnlyField();
       }

       rField.setFieldName(attrDef.getName());

       if(attrDef.getPrecision() == 0)
       {
           // it's probably a date field
           rField.setMaxDataLength(10);
           rField.setDisplayWidth(11);
       }
       else
       {
           int nLength = attrDef.getPrecision();

           if(attrDef.getScale() > 0)
               nLength += 1;

           rField.setMaxDataLength(nLength);

           if(attrDef.getPrecision() > 70)
           {
               rField.setDisplayWidth(70);
           }
           else
           {
               rField.setDisplayWidth(attrDef.getPrecision() + 5);
           }
       }

       rField.setDisplayHeight(10);
       rField.setPromptText(attrDef.getName());
       rField.setCSSClassName("cls" + attrDef.getName());

       return rField;
   }

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

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

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

   /**
   **  Assigns an instance-level edit field renderer to be used in the DataWebBean
   **/
   public void setEditFieldRenderer(int nIndex, HTMLFieldRenderer rdr)
   {
      attributeRenderers.put(nIndex + "_EditRenderer", 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)
   {
      String sLabel = aDef.getName();

      if (aDef.getProperty("LABEL") != null)
      {
         sLabel = aDef.getProperty("LABEL").toString();
      }
      else
      {
         String sPropName = aDef.getName() + "_LABEL";
         sPropName = sPropName.toUpperCase();
         if(rs.getProperties().get(sPropName) != null)
         {
            sLabel = rs.getViewObject().getProperties().get(sPropName).toString();
         }
      }

      return sLabel;
   }
}
