/*
 * @(#)RowTag.java
 *
 * Copyright 2000-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.jbo.html.jsp.datatags;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;
import oracle.jbo.DMLException;
import oracle.jbo.JboException;
import oracle.jbo.Row;
import oracle.jbo.RowNotFoundException;
import oracle.jbo.RowSet;
import oracle.jbo.common.TransPostControl;
import oracle.jbo.html.DataSource;
import oracle.jbo.html.HtmlServices;
import oracle.jbo.html.RequestParameters;

public class RowTag extends TagSupport implements DataBinder
{
   public static final String ACTION_ROW_CREATE    = "create"; // Create and insert the row
   public static final String ACTION_ROW_FIND      = "find";
   public static final String ACTION_ROW_GET       = "get";
   public static final String ACTION_ROW_UPDATE    = "update";
   public static final String ACTION_ROW_DELETE    = "delete";
   public static final String ACTION_ROW_LOCK      = "lock";
   public static final String ACTION_ROW_CURRENT   = "current";
   public static final String ACTION_ROW_ACTIVE    = "active";
   public static final String ACTION_ROW_CREATENOINSERT = "createonly"; // Create without inserting the row
   public static final String ACTION_ROW_INSERT    = "insert";

   protected String           sDataSource;
   protected String           sRowKey;
   protected String           sRowKeyParam;
   protected String           sAction;   
   protected boolean          bUseRequest;
   protected DataSource       ds;            // required attribute
   protected Row              row;
   protected RowSet           rs;
   private RequestParameters  params;

   /**
    * Constructor
    */
   public RowTag()
   {
      super();
      reset();
   }

   public void setDatasource(String sDataSource)
   {
      this.sDataSource = sDataSource;
   }

   public void setAction(String sValue)
   {
      sAction = sValue;
   }

   public void setRowkeyparam(String rowkey)
   {
      this.sRowKeyParam = rowkey;
   }

   public void setRowkey(String rowkey)
   {
      this.sRowKey = rowkey;
   }

   public void setUserequest(String sUse)
   {
      bUseRequest = Utils.isTrue(sUse);
   }

   public RowSet getRowSet()
   {
      return rs;
   }

   public DataSource getDataSource()
   {
      return ds;
   }

   public String getDataSourceName()
   {
      return sDataSource;
   }
   
   public Row getRow()
   {
      return row;
   }

   /**
     * Process the start tag for this instance.
     *
     * The doStartTag() method assumes that all setter methods have been
     * invoked before.
     *
     * When this method is invoked, the body has not yet been invoked.
     *
     * @returns EVAL_BODY_INCLUDE if the tag wants to process body, SKIP_BODY if it
     * does ont want to process it.
     */
   public int doStartTag() throws JspException
   {
      initialize();

      handleAction();

      pageContext.setAttribute(id , row);
      
      return Tag.EVAL_BODY_INCLUDE;
   }

   protected final void handleAction()
   {
      // Find a row given the row key and set the currency to it
      if (ACTION_ROW_FIND.equalsIgnoreCase(sAction))
      {
         row = getRowFromRequestParameters(ds, params);

         if (row != null)
         {
            rs.setCurrentRow(row);
         }
         else
         {
            throw new JboException(Res.format(Res.ROW_ROW_NOT_FOUND, sRowKey));
         }
      }
      // Find a row given the row key
      else if (ACTION_ROW_GET.equalsIgnoreCase(sAction))
      {
         row = getRowFromRequestParameters(ds, params);
      }
      // Create a new row. The row will be inserted on doEndTag
      else if (ACTION_ROW_CREATE.equalsIgnoreCase(sAction) || ACTION_ROW_CREATENOINSERT.equalsIgnoreCase(sAction))
      {
         row = rs.createRow();
      }
      // Update the row will values from a form submission
      else if (ACTION_ROW_UPDATE.equalsIgnoreCase(sAction))
      {
         row = getRowFromRequestParameters(ds, params);
         updateRowAttributesFromRequestParameters(ds, params);
      }
      else if (ACTION_ROW_DELETE.equalsIgnoreCase(sAction))
      {
         row = getRowFromRequestParameters(ds, params);
         row.remove();
      }
      else if (ACTION_ROW_LOCK.equalsIgnoreCase(sAction))
      {
         row = getRowFromRequestParameters(ds, params);
         row.lock();
      }
      // Set the Row variable to the current row
      else if (ACTION_ROW_CURRENT.equalsIgnoreCase(sAction))
      {
         row = rs.getCurrentRow();
         if (row == null)
         {
            row = rs.first();
            if (row == null)
            {
               rs.executeQuery();
               row = rs.first();
            }
         }
      }
      else if (ACTION_ROW_ACTIVE.equalsIgnoreCase(sAction))
      {
         // Look for the first RowsetIterate tag matching our datasource name
         RowsetIterateTag rowSITag = (RowsetIterateTag) Utils.findAncestorWithClassForDataSource(this, sDataSource, RowsetIterateTag.class);
   
         // Retrieve the row from the RowSetIterate tag if any
         if (rowSITag != null)
         {
            row = rowSITag.getRow();
         }
         else
         {
            throw new JboException(Res.getString(Res.ROW_NEED_ACTIVE));
         }
      }
      else if (ACTION_ROW_INSERT.equalsIgnoreCase(sAction))
      {
         row = getRowFromRequestParameters(ds, params);
         rs.insertRow(row);
      }
   }

   protected final Row getRowFromRequestParameters(DataSource ds, RequestParameters params)
   {
      // find the row using the key
      if (sRowKey == null && sRowKeyParam == null)
      {
         throw new JboException(Res.getString(Res.ROW_NEED_ROWKEY));
      }

      if (sRowKeyParam != null)
         sRowKey = params.getParameter(sRowKeyParam);

      if (sRowKey == null)
      {
         throw new JboException(Res.getString(Res.ROW_ROWKEY_IS_NULL));
      }

      return ds.getRowFromKey(sRowKey);
   }

   protected final void updateRowAttributesFromRequestParameters(DataSource ds, RequestParameters params)
   {
      // If user selects not to use the request object's values return from this call
      if (!bUseRequest)
         return;

      updateRowAttributesFromRequestParameters(ds, row, pageContext, params);
   }

   public static final void updateRowAttributesFromRequestParameters(DataSource ds, Row row, PageContext page, RequestParameters params)
   {
      if (row == null)
      {
         throw new JboException(Res.getString(Res.ROW_NO_ROW_AVAILABLE));
      }
      
      try
      {
         HtmlServices.updateRowAttributesFromRequestParameters(ds, row, params);
      }
      catch (DMLException ex)
      {
         try
         {
            ((TransPostControl) ds.getApplicationModule()).transPostRevert(ex.getEntityRowHandle());
         }
         catch (RowNotFoundException rnfe)
         {
            rnfe.addToDetails(ex);
         }
         
         throw ex;
      }
      catch (java.lang.InstantiationException ex)
      {
         page.getServletContext().log(Res.getString(Res.UPDATE_ERROR), ex);
         throw new JboException(ex.getMessage());
      }
      catch (java.lang.IllegalAccessException ex)
      {
         page.getServletContext().log(Res.getString(Res.UPDATE_ERROR), ex);
         throw new JboException(ex.getMessage());
      }
   }

   protected final void initialize()
   {
      ds = Utils.getDataSourceFromContext(pageContext, sDataSource);
      rs = ds.getRowSet();
      params = HtmlServices.getRequestParameters(pageContext);
   }

   /**
    * doEndTag
    * @return int
    * @exception javax.servlet.jsp.JspException
    */
   public int doEndTag() throws JspException
   {
      if (ACTION_ROW_CREATE.equalsIgnoreCase(sAction))
      {
         if (row != null)
         {
            rs.insertRow(row);
         }
      }

      return super.doEndTag();
   }

   private final void reset()
   {
      //required attribute ds             = null;
      rs             = null;
      row            = null;
      sAction        = null;
      params         = null;
      sDataSource    = null;
      sRowKey        = null;
      bUseRequest    = true;
      sRowKeyParam   = null;
   }
   
   /**
    * release
    */
   public void release()
   {
      reset();
      super.release();
   }
}

