package de.ix.jspTutorial.taglib;

import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.*;

import java.lang.reflect.Method ;

/**
 * Iteration über eine Liste von Objekten mit der Ausgabe des Body's
 *
 * Beispiel der Verwendung:
 * <xmp>
 * <%@ taglib uri="/ixtaglib" prefix="jsptut" %>
 * <%@ page import="java.util.*" %>
 * ...
 *   <% Vector theVector = new Vector() ;
 *     theVector.addElement("Hello" );
 *     theVector.addElement("World" );
 *  %>
 *  <jsptut:foreach item="element"  list="<%= theVector.elements() %>" >
 *  <%= element %>
 *  </jsptut:foreach> 
 * ...
 * </xmp>
 *
 * @author Peter Rossbach (<a href="mailto://pr@webapp.de">pr@webapp.de</a>)
 * @version $Id:$
 */
public class ForEachTag extends BodyTagSupport
{
   /**
    * Source-Version
    */
   public static String vcid = "$Id:$";

    /**
     * Key im PageContext
     */
    private String myItem;
    /**
     * Objekt das auf eine Liste andere Objekt verweist
     * Unterstütze typen sind Enumeration,Iteration,Object[],ResultSet
     */
    private Object myList;
    
    /**
     * Index des nächsten Elements in einem Object Array
     */
    private int index;
    
    /**
     * Speichern der Eval Method für schnellere Ausführung
     */
    private Method myMethod;
    
    /**
     * Konstruktion eines leer Tag die eigentliche Initialisierng
     * finde in doInifBodyTag statt.
     */
    public ForEachTag()
    {
        index = 0;
        myMethod = null;
		 }

    /**
     * Initialisierung des Bodys
     */
    public void doInitBody()
        throws JspTagException
    {
    }

    /**
     * Es wird die Evaluation des Body auf Basis des ersten Elements in der For Schleife gestartet.
     *
     * @see de.ix.jspTutorial.taglib.ForEachTag#checkIterationConstraint()
     */
    public int doAfterBody()
        throws JspTagException
    {
       return checkIterationConstraint() ;
    }

    /**
     * Ausgabe des aktuellen Body Content. Es werden alle Ressourcen nun wieder feigegeben
     *
     * @exception JspTagException
     */
    public int doEndTag()
        throws JspTagException
    {
        try
        {
            if(getBodyContent() != null)
                getBodyContent().writeOut(pageContext.getOut());
        }
        catch(IOException ioe)
        {
            throw new JspTagException(ioe.getMessage());
        }
        finally
        {
            release();
			       if(myItem != null)
     		       pageContext.removeAttribute(myItem);
        }
        return EVAL_PAGE;
    }

    /**
     * Auswahl des nächsten Elements der <CODE>Enumeration</CODE>.
     * Falls es noch Element gibt wird das Element im Attribute <CODE>myItem</CODE>
     * hintelegt und der Body des Tags wird zur Ausführung gebracht.
     * Wenn kein Element mehr vorhanden wird die Ausgabe des Body`s gestoppt.
     * Das Attribute <CODE>myItem</CODE> wird durch die Methode {@link de.ix.jspTutorial.taglib.ForEachTag#doEndTag() doEndTag} aus
     * dem Pagecontext gelöscht.
     * @param aEnum
     * @exception JspTagException
     * @return EVAL_BODY_TAG mind. ein Element noch vorhanden) oder SKIP_BODY (kein Element mehr vorhanden)
     */
    public int eval(Enumeration aEnum)
    	 throws JspTagException
    {
        if(aEnum.hasMoreElements())
        {
            setItemAttribute(aEnum.nextElement());
            return EVAL_BODY_TAG;
        } else
        {
            return SKIP_BODY;
        }
    }
    
    /**
     * Auswahl des nächsten Elements der <CODE>Iterators</CODE>.
     * Falls es noch Element gibt wird das Element im Attribute <CODE>myItem</CODE>
     * hintelegt und der Body des Tags wird zur Ausführung gebracht.
     * Wenn kein Element mehr vorhanden wird die Ausgabe des Body`s gestoppt.
     * Das Attribute <CODE>myItem</CODE> wird durch die Methode {@link de.ix.jspTutorial.taglib.ForEachTag#doEndTag() doEndTag} aus
     * dem Pagecontext gelöscht.
     * @param aIterator Iterator einer Collection (Java 2 )
     * @exception JspTagException
     * @return EVAL_BODY_TAG mind. ein Element noch vorhanden) oder SKIP_BODY (kein Element mehr vorhanden)
     */
    public int eval(Iterator aIterator)
    	 throws JspTagException
    	 {
        if(aIterator.hasNext())
        {
            setItemAttribute(aIterator.next());
            return EVAL_BODY_TAG;
        } else
        {
            return SKIP_BODY;
        }
    	 }

    /**
     * Auswahl des nächsten Elements der <CODE>Enumeration</CODE>.
     * Falls es noch Element gibt wird das Element im Attribute <CODE>myItem</CODE>
     * hintelegt und der Body des Tags wird zur Ausführung gebracht.
     * Wenn kein Element mehr vorhanden wird die Ausgabe des Body`s gestoppt.
     * Das Attribute <CODE>myItem</CODE> wird durch die Methode {@link de.ix.jspTutorial.taglib.ForEachTag#doEndTag() doEndTag} aus
     * dem Pagecontext gelöscht.
     * @param aArray Ein Arry von Objekten
     * @exception JspTagException
     * @return EVAL_BODY_TAG mind. ein Element noch vorhanden) oder SKIP_BODY (kein Element mehr vorhanden)
     */
    public int eval(Object[] aArray)
    	 throws JspTagException
    	 {
 
        if(index < aArray.length)
        {
            setItemAttribute(aArray[index]);
            index++;
            return EVAL_BODY_TAG;
        } else
        {
            return SKIP_BODY;
        }
    	 }
    
    /**
     * Set Object to PageContext
     * @param aObject new value
     */
    protected void setItemAttribute(Object aObject)
    {
    	 if(aObject instanceof Map.Entry)
    	 	  aObject = ((Map.Entry)aObject).getValue() ;
      pageContext.setAttribute(myItem, aObject);
    }
    
    	 
    /**
     * Auswahl des nächsten Elements der <CODE>ResultSet</CODE>.
     * Das nächste Element kann nun aus dem ResultSet gelesen werden.
     * @param aResultset Ein JDBC {@link java.sql.ResultSet ResultSet}
     * @exception JspTagException
     * @return EVAL_BODY_TAG mind. ein Element noch vorhanden) oder SKIP_BODY (kein Element mehr vorhanden)
     */
     public int eval(ResultSet aResultset)
    	 throws JspTagException
    {
        try
        {
            if(aResultset.next())
                return EVAL_BODY_TAG;
            try
            {
                aResultset.close();
            }
            catch(Exception _ex) { }
            return SKIP_BODY;
        }
        catch(SQLException sqlexception)
        {
            try
            {
                aResultset.close();
            }
            catch(Exception _ex) { }
            finally
            {
                release();
            }
            throw new JspTagException(sqlexception.getMessage());
        }
		}
		
   /**
    * Finde zu dem Objekttyp in dem Attribute {@link de.ix.jspTutorial.taglib.ForEachTag#myList myList} die
    * Eval Methode. Eine Subklasse kann einfach weitere evel(typ) realisieren
    * um die bestehenden Typen zu erweitern.
    * @exception JspTagException
    * @return EVAL_BODY_TAG mind. ein Element noch vorhanden) oder SKIP_BODY (kein Element mehr vorhanden)
    */
		public int eval()
    	  throws JspTagException
    {
          if(myList instanceof Enumeration)
          	 return eval((Enumeration) myList) ;
          if(myList instanceof Iterator)
          	 return eval((Iterator) myList) ;
          if(myList instanceof ResultSet)
          	 return eval((ResultSet) myList) ;
          return eval((Object[]) myList) ;
    }
    
    
    /**
     * Starte Ausführung des ForEach tags. Es muß ein Objekt {@link de.ix.jspTutorial.taglib#myList myList} vorliegen
     * damit es zur Ausführung kommt.
     * @exception JspTagException
     * @see de.ix.jspTutorial.taglib.ForEachTag#checkIterationConstraint()
     */
    public int doStartTag()
        throws JspTagException
    {
       return checkIterationConstraint() ;
    }

  
    /**
     * Ist Ausführung eines Body noch möglich
     * @see de.ix.jspTutorial.taglib.ForEachTag#eval()
     */
    protected int checkIterationConstraint()
        throws JspTagException
    {
	    if(myList == null)
        {
            release();
            throw new JspTagException("Enumeration object is required.");
        }
        try {
            return eval() ;
        } catch (JspTagException ex) {
            release();
            throw ex ;
        }
    }
    
    /**
     * Zurücksetzen aller notwendigen init Werte damit diese
     * Instanz wiederverwertet werden kann.
     */
    public void release()
    {
        myItem = null;
        myList = null;
        myMethod = null;
        index = 0;
        super.release();
    }

    /**
     * setze Collection Objekt
     *
     * @param obj neue Liste
     */
    public void setList(Object obj)
    {
    	  if(obj instanceof Map)
    	  	 myList = ((Map)obj).entrySet().iterator() ;
    	  else
    	  if(obj instanceof Collection)
    	  	 myList = ((Collection)obj).iterator() ;
    	  else
    	  if(obj instanceof Vector)
    	  	 myList = ((Vector)obj).elements() ;
       else
    	  	 myList = obj;
    }

    /**
     * setze Collection Objekt
     *
     * @param obj neue Liste als Array
     */
    public void setList(Object aobj[])
    {
       myList = ((Object) (aobj));
    }

    public Object getList()
    {
       return myList ;
    }
   /**
     * Setze Name der Variablen in der JSP.
     *
     * @param aItem Name der Variablen
     */
    public void setItem(String aItem)
    {
        myItem = aItem;
    }

    /**
     * Hote Namen der Variablen.
     * @return Name der JSP Variablen
     */
    public String getItem()
    {
        return myItem ;
    }
        

} // end of class

//
// History
//
// $Log:$
//
//