/*
 * @(#)AttributeIterateTag.java
 *
 * Copyright 2001-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 com.sun.java.util.collections.ArrayList;
import java.util.StringTokenizer;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;
import oracle.jbo.AttributeDef;
import oracle.jbo.ViewObject;
import oracle.jbo.html.DataSource;

/**
 *
 * @author Charles Gayraud
 */
public class AttributeIterateTag extends BodyTagSupport implements DataBinder
{
   protected String         sDataSource;
   protected AttributeDef[] dattrs;
   protected int            attr;
   protected DataSource     ds;
   protected boolean        queriableOnly;
   protected ArrayList      dispAttrs;
   protected ArrayList      hideAttrs;

   public AttributeIterateTag()
   {
      reset();
   }

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

   public void setQueriableonly(String sValue)
   {
      this.queriableOnly = Utils.isTrue(sValue);
   }

   public void setDisplayattributes(String sValue)
   {
      StringTokenizer strtok = new StringTokenizer(sValue , ",");
      String sCol;
 
      dispAttrs = new ArrayList();
 
      while (strtok.hasMoreTokens())
      {
         sCol = strtok.nextToken();
         sCol = sCol.trim();
         dispAttrs.add(sCol);
      }
   }
   
   public void setHideattributes(String sValue)
   {
      StringTokenizer strtok = new StringTokenizer(sValue , ",");
      String sCol;
 
      hideAttrs = new ArrayList();
 
      while (strtok.hasMoreTokens())
      {
         sCol = strtok.nextToken();
         sCol = sCol.trim();
         hideAttrs.add(sCol);
      }
   }

   // DataBinder interface implementation
   public String getDataSourceName()
   {
      return sDataSource;
   }

   public DataSource getDataSource()
   {
      return ds;
   }
   
   private AttributeDef[] getDisplayAttributeDefs()
   {
      ViewObject vo = ds.getRowSet().getViewObject();

      if (dispAttrs != null && dispAttrs.size() > 0)
      {
         AttributeDef retList[] = new AttributeDef[dispAttrs.size()];
   
         for (int i = 0; i < retList.length; i++)
         {
            retList[i] = vo.findAttributeDef((String) dispAttrs.get(i));
         }
   
         return retList;
      }
      else if (hideAttrs != null && hideAttrs.size() > 0)
      {
         AttributeDef attr[] = vo.getAttributeDefs();
         ArrayList tmpList = new ArrayList(attr.length - hideAttrs.size());
   
         boolean addIt;

         for (int i = 0; i < attr.length; i++)
         {
            addIt = true;

            for (int j = 0; j < hideAttrs.size(); j++)
            {
               if (((String)hideAttrs.get(j)).equalsIgnoreCase(attr[i].getName()))
               {
                  hideAttrs.remove(j);
                  addIt = false;
                  break;
               }
            }

            if (addIt)
            {
               tmpList.add(attr[i]);
            }
         }
   
         return (AttributeDef[])tmpList.toArray(new AttributeDef[tmpList.size()]);
      }
      else
      {
         return vo.getAttributeDefs();
      }
   }
   
   public AttributeDef getAttributeDef()
   {
      if (dattrs == null || attr >= dattrs.length)
      {
         return null;
      }

      return dattrs[attr];
   }

   /**
    * 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
   {
      ds = Utils.getDataSourceFromContext(pageContext, sDataSource);
         
      dattrs = getDisplayAttributeDefs();
      if (dattrs == null || dattrs.length == 0)
      {
         return SKIP_BODY;
      }

      return gotoNextValidIndex(ds);
   }

   /**
    * Actions after some body has been evaluated.
    *
    * Not invoked in empty tags or in tags returning SKIP_BODY in doStartTag()
    * This method is invoked after every body evaluation.
    * The pair "BODY -- doAfterBody()" is invoked initially if doStartTag()
    * returned EVAL_BODY_TAG, and it is repeated as long
    * as the doAfterBody() evaluation returns EVAL_BODY_TAG
    * <p>
    * The method re-invocations may be lead to different actions because
    * there might have been some changes to shared state, or because
    * of external computation.
    *
    * @returns whether additional evaluations of the body are desired
    * @seealso #doInitBody
    */
   public int doAfterBody() throws JspException
   {
      BodyContent body = getBodyContent();
      
      try
      {
         // Flush content and reset
         body.writeOut(getPreviousOut());
         body.clearBody();
         
         attr++;
         return gotoNextValidIndex(ds);
      }
      catch (java.io.IOException ex)
      {
         pageContext.getServletContext().log(Res.getString(Res.IO_ERROR), ex);
         throw new JspTagException(ex.getMessage());
      }
      
   }

   private int gotoNextValidIndex(DataSource ds)
   {
      // Skip non-displayable attribute
      while (attr < dattrs.length &&
             (!ds.shouldDisplayAttribute(dattrs[attr]) ||
             (queriableOnly && !dattrs[attr].isQueriable())))
      {
         attr++;
      }

      if (attr < dattrs.length)
      {
         // Scriptable variable of type AttributeDef
         pageContext.setAttribute(id, getAttributeDef());
         return EVAL_BODY_AGAIN;
      }
      else
      {
         return SKIP_BODY;
      }
   }

   // Use by the constructor and the release method to reset the member variable values
   private void reset()
   {
      sDataSource = null;
      dattrs = null;
      attr = 0;
      ds = null;
      queriableOnly = false;
      dispAttrs = null;
   }

    /**
     * reset the state of the Tag
     */
   public void release()
   {
      reset();
      super.release();
   }
}
