/* **************************************************************************

  $Archive: /njcl_v2/src/com/novell/service/file/nw/calls/EAEnumeratorImpl.java $
  $Revision: 17 $
  $Modtime: 1/28/00 12:46p $
 
  Copyright (c) 1998 Novell, Inc.  All Rights Reserved.

  THIS WORK IS  SUBJECT  TO  U.S.  AND  INTERNATIONAL  COPYRIGHT  LAWS  AND
  TREATIES.   NO  PART  OF  THIS  WORK MAY BE  USED,  PRACTICED,  PERFORMED
  COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED,  ABRIDGED, CONDENSED,
  EXPANDED,  COLLECTED,  COMPILED,  LINKED,  RECAST, TRANSFORMED OR ADAPTED
  WITHOUT THE PRIOR WRITTEN CONSENT OF NOVELL, INC. ANY USE OR EXPLOITATION
  OF THIS WORK WITHOUT AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO
  CRIMINAL AND CIVIL LIABILITY.

****************************************************************************/

package com.novell.service.file.nw.calls;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.NoSuchElementException;
import java.util.Vector;

import java.rmi.RemoteException;

import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.AttributeModificationException;
import javax.naming.directory.InvalidSearchFilterException;

import com.novell.service.session.SessionException;
import com.novell.service.session.xplat.CallsService;

import com.novell.service.jncp.NSIException;
import com.novell.service.jncp.ServerErrors;

import com.novell.utility.naming.directory.StaticAttributeValue;

import com.novell.service.file.nw.ExtendedAttribute;
import com.novell.service.file.nw.EAEnumerator;
import com.novell.service.file.nw.NameSpace;
import com.novell.service.file.nw.DataAccessableParameters;
import com.novell.service.file.nw.naming.ExtendedAttributeLock;

import com.novell.service.file.nw.naming.FSAttribute;
import com.novell.service.file.nw.naming.SchemaAttrDef;
import com.novell.service.file.nw.naming.SchemaSyntaxDef;
import com.novell.service.file.nw.naming.DirEntryDirContext;
import com.novell.service.file.nw.naming.FSEnvironment;

import com.novell.service.rfc1960.SearchStringComponent;

import com.novell.service.file.nw.NFileOutputStream;
import com.novell.service.file.nw.NFileInputStream;

/** @internal
 * Extended Attribute Enumerator Implementation
 *
 * <p>This class is the NWCalls implementation of the Extended Attribute
 * Enumerator.  Using the NWCalls interface, this class will supply the
 * hasMoreElements and next (and nextElement) methods, implementing an
 * enumerator of Extended Attributes for a given node.
 * </p>
 * <p>This class also implements the StaticAttributeValue interface which is
 * an interface used by jndi DSContexts for obtaining, modifing and searching
 * for attribute values.
 * </p>
 * @see com.novell.service.file.nw.EAEnumerator
 * @see com.novell.service.file.nw.naming.StaticAttributeValue
 * @see com.novell.service.file.nw.naming.DirEntryDirContext
 */

public class EAEnumeratorImpl
   implements StaticAttributeValue, EAEnumerator
{
   private FSEnvironment environment;
   private String fullNodeName;
   private String fullUrlName;
   private String nameSpace;
   private CallsService callsService;
   private DirEntryDirContext dataAccessable;

   private int eaHandle = 0;
   private StringBuffer eaName;
   private int eaFFStruct = 0;
   private boolean foundFirst = false;   // set true when EA scan has been started
   private boolean foundLast = false;    // set true when native EA scan is complete
   private boolean nextOk = false;

   private EAEnumeratorImpl attributeValue = null;

   /**
    * Main constructor
    *
    * <p>Constructs an EAEnumeratorImpl object that is in one of two states.
    * If the realThing parameter is false, the state of the object is setup
    * to support the StaticAttributeValue interface.  If the realThing
    * parameter is true, the state of the object is setup to support the
    * Extended Attribute Enumerator functionality.
    * </p>
    *
    * @param environment         The JNDI DSContext's environment associated
    *                            with this attribute and this static
    *                            attribute value.
    * @param dataAccessable      DataAccessable to use for streams IO.
    * @param realThing           If true, setup as an enumerator of extended
    *                            attribute values.  If false, setup as a
    *                            StaticAttributeValue interface.
    * @exception OutOfMemoryError
    * @exception NSIException    If an error occurs.
    */

   public EAEnumeratorImpl(
      FSEnvironment environment,
      DirEntryDirContext dataAccessable,
      boolean realThing)
      throws NSIException
   {
      this.environment = environment;
      fullNodeName = environment.getFullNodeName();
      fullUrlName = environment.getFullUrlName();
      nameSpace = environment.getNameSpace();
      this.dataAccessable = dataAccessable;

      try
      {
         this.callsService = environment.getCallsService();
      } catch (NamingException e)
      {
         throw new NSIException("" + e, e);
      }

      try
      {
      if (realThing)
      {
         eaHandle = callsService.allocEAHandle();
         eaFFStruct = callsService.allocEAFFStruct();


         if (eaHandle == 0 || eaFFStruct == 0)
            throw new OutOfMemoryError();
      }
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
   }

   /**
    * jndi DSContext constructor
    *
    * <p>This constructor is used by the jndi DSContexts to instantiate a
    * StaticAttributeValue interface.
    * </p>
    *
    * @param environment         The JNDI DSContext's environment associated
    *                            with this attribute and this static
    *                            attribute value.
    *
    * @param dataAccessable      DataAccessable to use for streams IO.
    * @exception OutOfMemoryError
    * @exception NSIException    If an error occurs.
    *
    * @see com.novell.service.file.nw.naming.DirEntryDirContext#DirEntryDirContext
    */

   public EAEnumeratorImpl(
      FSEnvironment environment,
      DirEntryDirContext dataAccessable)
      throws NSIException
   {
      this(environment, dataAccessable, false);
   }

   /**
    * Cleanup method
    *
    * <p>The main constructor uses native methods that allocate native memory
    * that needs to be cleaned up on destruction of this object.
    * </p>
    */

   public void finalize()
   {
      try
      {
      if (eaHandle != 0)
      {
         callsService.freePtr(eaHandle);
         eaHandle = 0;
      }
      }
      catch (SessionException e)
      {
      }
      
      try
      {
      if (eaFFStruct != 0)
      {
         callsService.freePtr(eaFFStruct);
         eaFFStruct = 0;
      }
      }
      catch (SessionException e)
      {
      }
   }

/* **************************************************************************
   Enumeration implementations
****************************************************************************/

   /**
    * Enumerator hasMoreElements
    *
    * <p>This method is used to determine if an extended attribute is
    * available to be returned in a corrisponding nextElement or next method
    * call.  This method must be called before the nextElement (or next)
    * method is called.  However, this method must not be called a second
    * time until the nextElement (or next) method has been called.
    * </p>
    * <p>The StaticAttributeValue interface also declares this method for
    * lazy evaluation of jndi attribute values.  The buildAttribute method
    * will return a FSAttribute which will have a reference to this object.
    * The FSAttribute.getValues method will return an enumerator which will
    * forward the hasMoreElements and nextElement calls back into this
    * object.
    * </p>
    *
    * @return                    true if nextElement or next will return a
    *                            valid value. false if there are no values
    *                            available.
    *
    * @exception                 NSIException if nwFindFirstEA fails
    *
    * @see com.novell.service.file.nw.naming.StaticAttributeValue
    * @see com.novell.service.file.nw.naming.FSAttribute
    * @see com.novell.service.file.nw.calls.EAEnumeratorImpl#next
    * @see com.novell.service.file.nw.calls.EAEnumeratorImpl#nextElement
    * @see com.novell.service.file.nw.calls.EAEnumeratorImpl#buildAttribute
    */

   public boolean hasMoreElements()
   {
      if (nextOk)
         return true;

      if (foundLast)
         return false;

      int ccode;
      eaName = new StringBuffer();

      if (!foundFirst)
      {
         foundFirst = true;
         try
         {
            callsService.findFirstEA(
                           0,
                           fullNodeName,
                           NameSpace.nameToNumber(nameSpace),
                           eaFFStruct,
                           eaHandle,
                           eaName);

         }catch (NSIException nsie)
         {
            finalize();
            ccode = nsie.getCCode();

            // You can't scan EAs without modify rights
            if (ccode == 1 || ccode == ServerErrors.NWE_FILE_NO_MOD_PRIV)
               foundLast = true; // signal no more calls to EA functions
            else
               throw nsie;
         }
         catch (SessionException e)
         {
            finalize();
            throw new NSIException(e.getMessage(), 0, e);
         }
      }else
      {
         try
         {
            callsService.findNextEA(eaFFStruct, eaHandle, eaName);
         }catch (NSIException nsie)
         {
            foundLast = true; // signal no more calls to EA functions
            finalize();
         }
         catch (SessionException e)
         {
            finalize();
            throw new NSIException(e.getMessage(), 0, e);
         }
      }

      if (foundLast)
         return false;

// if you get to here, the eaHandle is pointing at the data to read
      nextOk = true;
      return true;
   }

   public boolean hasMore()
   {
      return hasMoreElements();
   }

   /**
    * Enumerator nextElement
    *
    * <p>This method is used to return the value obtained in the
    * hasMoreElements call.  This method must be called after the
    * hasMoreElements method is called.
    * </p>
    * <p>The StaticAttributeValue interface also declares this method for
    * lazy evaluation of jndi attribute values.  The buildAttribute method
    * will return a FSAttribute which will have a reference to this object.
    * The FSAttribute.getValues method will return an enumerator which will
    * forward the hasMoreElements and nextElement calls back into this
    * object.
    * </p>
    *
    * @return                    The ExtendedAttribute (as an Object) that
    *                            the last call to hasMoreElements obtained.
    *
    * @exception                 NoSuchElementException
    * @exception NSIException    If readExtendedAttribute() fails.
    *
    * @see com.novell.service.file.nw.naming.StaticAttributeValue
    * @see com.novell.service.file.nw.naming.FSAttribute
    * @see com.novell.service.file.nw.calls.EAEnumeratorImpl#hasMoreElements
    * @see com.novell.service.file.nw.calls.EAEnumeratorImpl#next
    * @see com.novell.service.file.nw.calls.EAEnumeratorImpl#buildAttribute
    * @see com.novell.service.file.nw.ExtendedAttribute
    */

   public Object nextElement()
   {
      return next();
   }

   /**
    * Enumerator next (typed nextElement)
    *
    * <p>This method is used to return the value obtained in the
    * hasMoreElements call.  This method must be called after the
    * hasMoreElements method is called.
    * </p>
    *
    * @return                    The ExtendedAttribute that the last call to
    *                            hasMoreElements obtained.
    *
    * @exception                 NoSuchElementException
    * @exception NSIException    If readExtendedAttribute() fails.
    *
    * @see com.novell.service.file.nw.calls.EAEnumeratorImpl#hasMoreElements
    * @see com.novell.service.file.nw.ExtendedAttribute
    */

   public ExtendedAttribute next()
   {
      if (!nextOk)
         if (!hasMoreElements())
            throw new NoSuchElementException();

      if (foundLast)
         throw new NoSuchElementException();

      byte[] buffer = readExtendedAttribute();
      ExtendedAttribute ea = new ExtendedAttribute(
                                    eaName.toString(),
                                    buffer);
      nextOk = false;
      return ea;
   }

/* **************************************************************************
   StaticAttributeValue implementation
****************************************************************************/

   /**
    * Builds and returns a lazy evaluation Attribute object
    *
    * <p>This method returns a FSAttribute object which has a reference to
    * a EAEnumeratorImpl that has been instantiated in the enumerator state
    * (verses the StaticAttributeValue interface state).  The FSAttribute
    * extends javax.naming.directory.Attribute and overrides the getValues method.  It's
    * getValues method returns an enumerator that simply forwards the
    * hasMoreElements and nextElement calls back into the EAEnumeratorImpl
    * reference that the FSAttribute contains.
    * </p>
    *
    * @return                    A FSAttribute object that provides for lazy
    *                            evaluation of all possible
    *                            ExtendedAttributes associated with the file
    *                            this object is associated with.
    *
    * @see com.novell.service.file.nw.naming.FSAttribute
    * @see com.novell.service.file.nw.ExtendedAttribute
    * @see com.novell.service.file.nw.naming.StaticAttributeValue
    * @see com.novell.service.file.nw.calls.EAEnumeratorImpl#hasMoreElements
    * @see com.novell.service.file.nw.calls.EAEnumeratorImpl#nextElement
    * @see com.novell.service.file.nw.naming.DirEntryDirContext#c_getAttributes
    */

   public Attribute buildAttribute()
      throws NamingException
   {
      EAEnumeratorImpl eaEnum = null;
      try
      {
         eaEnum = new EAEnumeratorImpl(
                        environment,
                        dataAccessable,
                        true);
      } catch (NSIException nsie)
      {
         NamingException ne = new NamingException();
         ne.setRootCause(nsie);
         throw ne;
      }

      Object lock = ExtendedAttributeLock.getInstance(
                        fullUrlName, 
                        DirEntryDirContext.JNDIEAName /*"findfirst/next"*/);

      synchronized(lock)
      {
         try
         {
           FSAttribute attr = new 
               FSAttribute(
                  ATTRIBUTE_ID,
                  eaEnum,
                  new SchemaAttrDef(SchemaAttrDef.EA_ATTRDEF, environment),
                  new SchemaSyntaxDef(SchemaAttrDef.EA_ATTRDEF, environment));
            finalize();
            return attr;
         } catch (Exception e)
         {
            NamingException ne = new NamingException("" + e);
            ne.setRootCause(e);
            throw ne;
         }
         finally
         {
            ExtendedAttributeLock.releaseInstance(lock);
         }
      }
   }

   /**
    * Adds an Attribute to the Attribute ID's values.
    *
    * <p>There are some dynamic aspects of the static attribute values.
    * examples of these are in the file system name space; Extended
    * Attributes, Trustee lists, and volume restrictions.  All three of these
    * are dynamic lists of information that will be found under a single
    * attribute ID.  The addAttribute will be used by attribute values that
    * have this dynamic aspect to them.  All others will throw an
    * AttributeModificationException.
    * </p>
    *
    * @param                   The attribute to be added.
    *
    * @exception AttributeModificationException
    *
    * @see com.novell.service.file.nw.naming.StaticAttributeValue
    * @see com.novell.service.file.nw.naming.DirEntryDirContext#c_modifyAttributes
    */

   public void addAttribute(Attribute attr)
      throws NamingException
   {
      modifyAttribute(attr);
   }


   /**
    * Deletes an Attribute from the Attribute ID's values.
    *
    * <p>There are some dynamic aspects of the static attribute values.
    * examples of these are in the file system name space; Extended
    * Attributes, Trustee lists, and volume restrictions.  All three of these
    * are dynamic lists of information that will be found under a single
    * attribute ID.  The delAttribute will be used by attribute values that
    * have this dynamic aspect to them, and allow for deletion of items.  All
    * others will throw an AttributeModificationException.
    * </p>
    *
    * @param                   The attribute to be removed.
    *
    * @exception AttributeModificationException
    *
    * @see com.novell.service.file.nw.naming.StaticAttributeValue
    * @see com.novell.service.file.nw.naming.DirEntryDirContext#c_modifyAttributes
    */

   public void deleteAttribute(Attribute attr)
      throws NamingException
   {
      throw new AttributeModificationException();
   }

   /**
    * Sets the current state to the Attribute values.
    *
    * <p>If this object is a compound Attribute, It is possible
    * for some of the values to be read-only.  Any read-only fields of the
    * attribute value being passed in must match the current state of this
    * object or an AttributeModificationException will be thrown.
    * </p>
    *
    * @exception AttributeModificationException
    *
    * @see com.novell.service.file.nw.naming.StaticAttributeValue
    * @see com.novell.service.file.nw.naming.DirEntryDirContext#c_modifyAttributes
    */

   public void modifyAttribute(Attribute attr)
      throws NamingException
   {
      Enumeration enum = attr.getAll();
      while (enum.hasMoreElements())
      {
         ExtendedAttribute extAttr = (ExtendedAttribute) enum.nextElement();
         synchronized(extAttr)
         {
            writeExtendedAttribute(
               extAttr.getName(),
               extAttr.getData());
         }
      }
   }

   /**
    * Get the Attribute Name.
    *
    * <p>Returns the Attribute name (ATTRIBUTE_ID) of the AttributeInformation
    * implementation object.
    * </p>
    *
    * @return                    The Attribute name (ATTRIBUTE_ID).
    *
    * @see com.novell.service.file.nw.naming.StaticAttributeValue
    */

   public String getID()
   {
      return(ATTRIBUTE_ID);
   }

   /**
    * Does the specified compare operation and returns the result
    *
    * <p>This method will compare the value object against the attributes
    * current value, in the manner specified in the SearchStringComponent,
    * and return the result of this compare.
    * </p>
    *
    * @param ssc                 The SearchStringComponent to use for the
    *                            compare.
    *
    * @return                    true if the operation compares true, false
    *                            otherwise.
    *
    * @exception InvalidSearchFilterException
    *
    * @see com.novell.service.file.nw.naming.StaticAttributeValue
    * @see com.novell.utility.naming.directory.SearchStringComponent
    */

   public boolean compare(SearchStringComponent ssc)
      throws NamingException
   {
      try
      {
         return equals(ssc);
      } catch (IllegalArgumentException e)
      {
         InvalidSearchFilterException isfe =
            new InvalidSearchFilterException();

         isfe.setRootCause(e);
         throw isfe;
      }
   }

   /**
    * Does the specified compare operation and returns the result
    *
    * <p>This method will compare the value object against the attributes
    * current value, in the manner specified in the SearchStringComponent,
    * and return the result of this compare.
    * </p>
    *
    * @param ssc                 The SearchStringComponent to use for the
    *                            compare.
    *
    * @return                    true if the operation compares true, false
    *                            otherwise.
    *
    * @exception IllegalArgumentException
    * @exception OutOfMemoryError
    *
    * @see com.novell.utility.naming.directory.SearchStringComponent
    */

   public boolean equals(SearchStringComponent ssc)
   {
      if (ssc == null)
         return false;

      int type = ssc.getOperationType();

      if (type == ssc.PRESENT)
         return true;      // static attribute values are always present

      if (type != ssc.EQUALS && type != ssc.SUBSTRING)
         return false;

      ExtendedAttribute ea = null;
      boolean nameOnly = true;
      if (ssc.operandReplacement())
      {
         ea = (ExtendedAttribute)ssc.getReplacementObject();
         nameOnly = false;
         if (ea == null)
            throw new IllegalArgumentException(ssc.getOperation());
      }else
      {
         // just match the extended attribute name, ignore the data
         ea = new ExtendedAttribute(ssc.getOperand());
      }

      /*
         if the operation type == equals, try and open and read and compare
         the extended attribute
      */
      if (type == ssc.EQUALS)
      {
         synchronized(ea)
         {
            eaName = new StringBuffer(ea.getName());
         }
         byte[] data = null;
         try
         {
            data = readExtendedAttribute();
         } catch (NSIException rte)
         {
            // name was not found, or could not read, don't match
            return false;
         }

         boolean equals = false;
         if (nameOnly)
            equals = true;    // it found the name or it would not have opened
         else
         {
            try
            {
               synchronized(ea)
               {
                  equals = ea.equals(data);
               }
            }catch(Exception e)
            {
               equals = false;
            }
         }
         return equals;
      }

      if (type == ssc.SUBSTRING)
      {
         /*
            If the type == substring, you need to iterate through all ea's and
            check for the sub-string
         */

         EAEnumeratorImpl eai = null;
         eai = new EAEnumeratorImpl(
                     environment,
                     dataAccessable,
                     true);

         while (eai.hasMoreElements())
         {
            ExtendedAttribute value = eai.next();
            synchronized(ea)
            {
               if (SearchStringComponent.compareSubString(
                                             ea.getName(),
                                             value.getName(),
                                             true))
                  return true;
            }
         }
         return false;
      }
      return false;
   }

   /**
    * Returns the static interface object.
    *
    * <p>This method returns the appropriate object for the static interface
    * to this attribute value.
    * </p>
    *
    * @return                    EAEnumerator
    *
    */
   public Object getStaticInterface()
   {
      EAEnumeratorImpl eaEnum =
         new EAEnumeratorImpl(
               environment,
               dataAccessable,
               true);

      Object lock = ExtendedAttributeLock.getInstance(
                        fullUrlName, 
                        DirEntryDirContext.JNDIEAName /*"findfirst/next"*/);

      LoadedEAEnumerator enum = null;
      synchronized(lock)
      {  
         try
         {
            enum = new LoadedEAEnumerator(eaEnum);
         } catch (RuntimeException e)
         {
            throw e;
         }
         finally
         {
            ExtendedAttributeLock.releaseInstance(lock);
         }
      }
      return enum;
   }

/* **************************************************************************
   Private methods
****************************************************************************/

   private void writeExtendedAttribute(
      String eaName,
      byte[] buffer)
      throws NamingException
   {
      OutputStream os = null;
      boolean open = false;

      try
      {
         // open the ext. attr stream on this context
         os = new NFileOutputStream(
                        eaName,
                        dataAccessable,
                        0,
                        DataAccessableParameters.EA_STREAM_SELECTOR);

         open = true;

         os.write(buffer);
         os.close ();

         open = false;
      }
      // if an exception was thrown, rethrow a NamingException
      catch (IOException e)
      {
         if (open)
         {
            try
            {
               os.close();
            }
            catch (IOException ignore)
            {
            }
         }
         throw new NSIException(e.getMessage(), e);
      }
   }

/* *************************************************************************/

   private byte[] readExtendedAttribute()
   {
      InputStream is = null;  // is only non-null on successful EA open
      boolean open = false;   // only set to true between successful EA open
                              //   and successful EA close

      byte[] buffer = new byte[4096];
      byte[] totalData = null;
      int totalBytes = 0;

      try
      {
         // open the ext. attr stream on this context
         is = new NFileInputStream(
                        eaName.toString(),
                        dataAccessable,
                        0,
                        DataAccessableParameters.EA_STREAM_SELECTOR);

         open = true;   // signals successful EA open
         
         byte[] tmpData = null;
         int bytesRead;
         do
         {
            bytesRead = is.read(buffer);
            if (bytesRead != -1)
            {
               if (bytesRead == buffer.length && totalData == null)
               {
                  /*
                      This is the first pass there might be more data, 
                      need to maintain totalData, alloc the new buffer
                      and copy the last read into it.
                  */
                  totalData = new byte[buffer.length];
                  for (int i=0; i < buffer.length; i++)
                     totalData[i] = buffer[i];
                  continue;
               }

               /*
                  possiblities from this point
                     1. first read, size < buffer.length - next pass -1
                     2. subsequent read, we have more data
               */
               if (totalData != null)
               {
                  // this is a subsequent pass, re-alloc and copy
                  tmpData = totalData;
                  totalData = new byte[tmpData.length + bytesRead];

                  int i = 0;
                  for (; i < tmpData.length; i++)
                     totalData[i] = tmpData[i];

                  for (int j=0; j < bytesRead; j++)
                     totalData[i+j] = buffer[j];
                  continue;
               }
               totalBytes = bytesRead;
            }
         }while(bytesRead != -1);

         is.close ();
         open = false;  // signals successful EA close

      }catch (IOException e)
      {
         // if after successful EA open, an exception is thrown, then we
         //   need to clean up the EA stream so that we don't cause a
         //   memory leak
         if (open)
         {
            try
            {
               is.close ();
            }
            catch (IOException ignore) // ignore and take 'e' as exception
            {
            }
         }
         throw new NSIException(e.getMessage(), e);
      }

      if (totalData != null)
         return totalData;

      totalData = new byte[totalBytes];
      for (int i=0; i < totalBytes; i++)
         totalData[i] = buffer[i];

      return totalData;
   }
}

class LoadedEAEnumerator implements EAEnumerator
{
   Vector data;
   int index = 0;
   
   LoadedEAEnumerator(EAEnumerator unloaded)
   {
      data = new Vector();
      while (unloaded.hasMore())
      {
         data.addElement(unloaded.next());
      }   
   }
   
   public boolean hasMoreElements()
   {
      return hasMore();
   }

   public boolean hasMore()
   {
      if (index < data.size())
         return true;
      return false;
   }

   public Object nextElement()
   {
      return next();
   }

   public ExtendedAttribute next()
   {
      if (index < data.size())      
         return (ExtendedAttribute) data.elementAt(index++);
      throw new NoSuchElementException();
   }
}   
