// ---------------------------------------------------------------------------
// - Strvec.cpp                                                              -
// - standard object library - string vector class implementation            -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - This program  is  distributed in  the hope  that it will be useful, but -
// - without  any  warranty;  without  even   the   implied    warranty   of -
// - merchantability or fitness for a particular purpose.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2023 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "Stdsid.hxx"
#include "Strvec.hpp"
#include "Vector.hpp"
#include "Unicode.hpp"
#include "Utility.hpp"
#include "Boolean.hpp"
#include "Integer.hpp"
#include "Exception.hpp"
#include "QuarkZone.hpp"
#include "InputStream.hpp"
#include "OutputStream.hpp"
#include "array.tcc"
#include "transient.tcc"

namespace afnix {

  // -------------------------------------------------------------------------
  // - private section                                                       -
  // -------------------------------------------------------------------------

  // check that a character is in the c-string. 
  static bool match_break_sequence (const t_quad c, const t_quad* str) {
    long size = Unicode::strlen (str);
    // loop and compare
    for (long i = 0; i < size; i++)
      if (c == str[i]) return true;
    return false;
  }

  // -------------------------------------------------------------------------
  // - public section                                                        -
  // -------------------------------------------------------------------------

  // split this string with a sequence of characters

  Strvec Strvec::split (const String& name, const String& sbrk, 
			const bool    uniq, const bool    zlen) {
    // preset buffer and result
    Strvec result (uniq); result.setzlen (zlen);
    Buffer buffer (Encoding::EMOD_UTF8);
    // first thing first - do we have a nil string
    if (name.isnil () == true) return result;
    // get a unicode string representation
    t_quad* data = name.toquad ();
    t_transient<t_quad, true> cptr = data;
    // fix the break sequence in case it is nil
    t_transient<const t_quad, true> cbrk =
      sbrk.isnil () ? Unicode::strdup (" \t\n") : sbrk.toquad ();
    // loop and accumulate - if a character match the break sequence
    // the buffer is inserted into the vector
    t_quad c = nilq;
    buffer.reset ();
    while ((c = *data++) != nilc) {
      if (match_break_sequence (c, *cbrk) == true) {
	result.add (buffer.tostring());
	buffer.reset ();
	continue;
      }
      buffer.add (c);
    }
    // check if the buffer is not empty
    if (buffer.empty () == false) result.add (buffer.tostring());
    return result;
  }

  // split this string with a break sequence and unique flag
  
  Strvec Strvec::split (const String& name, const String& sbrk, 
			const bool    uniq) {
    return Strvec::split (name, sbrk, uniq, true);
  }
  
  // split this string with a break sequence
  
  Strvec Strvec::split (const String& name, const String& sbrk) {
    return Strvec::split (name, sbrk, false, true);
  }
  
  // split this string with a default break sequence
  
  Strvec Strvec::split (const String& name) {
    return Strvec::split (name, "", false, false);
  }

  // -------------------------------------------------------------------------
  // - class section                                                         -
  // -------------------------------------------------------------------------

  // create an empty string vector

  Strvec::Strvec (void) {
    d_uniq = false;
    d_zlen = true;
    p_sary = nullptr;
  }

  // create a string vector with an original size

  Strvec::Strvec (const long size) {
    if (size < 0L) {
      throw Exception ("size-error","negative string vector size");
    }
    d_uniq = false;
    d_zlen = true;
    p_sary = (size == 0L) ? nullptr : new t_array<String>(size);
  }

  // create a string vector with a uniq flag

  Strvec::Strvec (const bool uniq) {
    d_uniq = uniq;
    d_zlen = true;
    p_sary = nullptr;
  }

  // create a string vector with an original size and flag

  Strvec::Strvec (const long size, const bool uniq) {
    if (size < 0L) {
      throw Exception ("size-error","negative string vector size");
    }
    d_uniq = uniq;
    d_zlen = true;
    p_sary = (size == 0L) ? nullptr : new t_array<String>(size);
  }

  // copy construct this string vector

  Strvec::Strvec (const Strvec& that) {
    that.rdlock ();
    try {
      // copy arguments
      d_uniq = that.d_uniq;
      d_zlen = that.d_zlen;
      p_sary = nullptr;
      if (that.p_sary != nullptr) p_sary = new t_array<String>(*that.p_sary);
      that.unlock ();
    } catch (...) {
      that.unlock ();
      throw;
    }
  }

  // copy move this string vector

  Strvec::Strvec (Strvec&& that) noexcept {
    that.wrlock ();
    try {
      // move base object
      Object::operator = (static_cast<Object&&>(that));
      // copy arguments
      d_uniq = that.d_uniq; that.d_uniq = false;
      d_zlen = that.d_zlen; that.d_zlen = true;
      p_sary = that.p_sary; that.p_sary = nullptr;
    } catch (...) {
      d_uniq = false;
      d_zlen = true;
      p_sary = nullptr;
    }
    that.unlock ();
  }
  
  // destroy this string vector
  
  Strvec::~Strvec (void) {
    delete p_sary;
  }

  // assign a string vector to this one
  
  Strvec& Strvec::operator = (const Strvec& that) {
    // check for self-assignation
    if (this == &that) return *this;
    // lock everything
    wrlock ();
    that.rdlock ();
    try {
      // delete old array
      delete p_sary; p_sary = nullptr;
      // copy arguments
      d_uniq = that.d_uniq;
      d_zlen = that.d_zlen;
      p_sary = nullptr;
      if (that.p_sary != nullptr) p_sary = new t_array<String>(*that.p_sary);
      that.unlock ();
      unlock ();
      return *this;
    } catch (...) {
      that.unlock ();
      unlock ();
      throw;
    }
  }

  // move a string vector to this one
  
  Strvec& Strvec::operator = (Strvec&& that) noexcept {
    // check for self-assignation
    if (this == &that) return *this;
    // lock and assign
    wrlock ();
    that.wrlock ();
    try {
      // delete old array
      delete p_sary; p_sary = nullptr;
      // move base object
      Object::operator = (static_cast<Object&&>(that));
      // copy arguments
      d_uniq = that.d_uniq; that.d_uniq = false;
      d_zlen = that.d_zlen; that.d_zlen = true;
      p_sary = that.p_sary; that.p_sary = nullptr;
    } catch (...) {
      d_uniq = false;
      d_zlen = true;
      p_sary = nullptr;
    }
    unlock ();
    that.unlock ();
    return *this;
  }
  
  // get the string by index

  String& Strvec::operator [] (const long vidx) const {
    rdlock ();
    try {
      if (p_sary == nullptr) {
	throw Exception ("index-error","in string vector []");
      }
      String& result = (*p_sary)[vidx];
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // return the class name

  String Strvec::repr (void) const {
    return "Strvec";
  }

  // return the string vector did

  t_word Strvec::getdid (void) const {
    return SRL_DEOD_STD;
  }

  // return the string vector sid

  t_word Strvec::getsid (void) const {
    return SRL_STRV_SID;
  }

  // serialize this string vector

  void Strvec::wrstream (OutputStream& os) const {
    rdlock ();
    try {
      // write the flags
      Serial::wrbool (d_uniq, os);
      Serial::wrbool (d_zlen, os);
      // write the strings
      long alen = (p_sary == nullptr) ? 0L : p_sary->length ();
      Serial::wrlong (alen, os);
      for (long k = 0L; k < alen; k++) (*p_sary)[k].wrstream (os);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // deserialize this string vector

  void Strvec::rdstream (InputStream& is) {
    wrlock ();
    try {
      // reset the string vector
      reset  ();
      // get the flags
      d_uniq = Serial::rdbool (is);
      d_zlen = Serial::rdbool (is);
      // get the strings
      long alen = Serial::rdlong (is);
      if (alen > 0) p_sary = new t_array<String>(alen);
      for (long k = 0L; k < alen; k++) {
	String data;
	data.rdstream (is);
	p_sary->push (data);
      }
      unlock ();
    } catch (...) {
      reset  ();
      unlock ();
      throw;
    }
  }

  // reset this vector

  void Strvec::reset (void) {
    wrlock ();
    try {
      delete p_sary; p_sary = nullptr;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // set the uniq flag

  void Strvec::setuniq (const bool uniq) {
    wrlock ();
    try {
      d_uniq = uniq;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get the uniq flag

  bool Strvec::getuniq (void) const {
    rdlock ();
    try {
      bool result = d_uniq;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }
  // set the zero length flag

  void Strvec::setzlen (const bool zlen) {
    wrlock ();
    try {
      d_zlen = zlen;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get the zero length flag

  bool Strvec::getzlen (void) const {
    rdlock ();
    try {
      bool result = d_zlen;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return true if the vector is empty

  bool Strvec::empty (void) const {
    rdlock ();
    try {
      bool result = (p_sary == nullptr) ? true : p_sary->empty ();
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return true if the vector is active

  bool Strvec::active (void) const {
    rdlock ();
    try {
      bool result = (p_sary == nullptr) ? false : !p_sary->empty ();
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // check that a string exists in this vector

  bool Strvec::exists (const String& name) const {
    rdlock ();
    try {
      if ((p_sary == nullptr) || (p_sary->empty () == true)) {
	unlock ();
	return false;
      }
      long alen = (p_sary == nullptr) ? 0L : p_sary->length ();
      for (long k = 0L; k < alen; k++) {
	if ((*p_sary)[k] == name) {
	  unlock ();
	  return true;
	}
      }
      unlock ();
      return false;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get the number of element in this string vector

  long Strvec::length (void) const {
    rdlock ();
    try {
      long result = (p_sary == nullptr) ? 0L : p_sary->length ();
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a new element in this string vector
  
  void Strvec::add (const String& s) {
    wrlock ();
    try {
      // check if we add zero length string
      if ((d_zlen == false) && (s.isnil () == true)) {
	unlock ();
	return;
      }
      if (p_sary == nullptr) p_sary = new t_array<String>;
      // add the string in the array if unique
      if ((d_uniq == false) || (exists (s) == false)) p_sary->push (s);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // set an string at a certain position in this vector

  void Strvec::set (const long vidx, const String& s) {
    wrlock ();
    try {
      if (p_sary == nullptr) {
	throw Exception ("index-error","in string vector set");
      }
      (*p_sary)[vidx] = s;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // reverse pop a string from this vector

  String Strvec::rpop (void) {
    wrlock ();
    try {
      // check that the vector is not empty
      if ((p_sary == nullptr) || (p_sary->empty () == true)) {
	throw Exception ("strvec-error",
			 "empty vector with reverse pop request");
      }
      // save first string
      String result = p_sary->rpop ();
      // unlock and return
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // pop the last string from this vector

  String Strvec::pop (void) {
    wrlock ();
    try {
      // check that the vector is not empty
      if ((p_sary == nullptr) || (p_sary->empty () == true)) {
	throw Exception ("strvec-error", "empty vector with pop request");
      }
      // save last string
      String result = p_sary->pop ();
      // unlock and return
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get an string by vector index

  String Strvec::get (const long vidx) const {
    rdlock ();
    try {
      // check that the vector is not empty
      if ((p_sary == nullptr) || (p_sary->empty () == true)) {
	throw Exception ("strvec-error", "empty vector with get request");
      }
      String result = p_sary->get(vidx);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the first string in this vector

  String Strvec::first (void) const {
    rdlock ();
    try {
      // check that the vector is not empty
      if ((p_sary == nullptr) || (p_sary->empty () == true)) {
	throw Exception ("strvec-error", "empty vector with get request");
      }
      String result = p_sary->first ();
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the last string in this vector

  String Strvec::last (void) const {
    rdlock ();
    try {
      // check that the vector is not empty
      if ((p_sary == nullptr) || (p_sary->empty () == true)) {
	throw Exception ("strvec-error", "empty vector with get request");
      }
      String result = p_sary->last ();
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the index of a key or -1

  long Strvec::find (const String& key) const {
    rdlock ();
    try {
      long alen = (p_sary == nullptr) ? 0L : p_sary->length ();
      for (long k = 0L; k < alen; k++) {
	if ((*p_sary)[k] == key) {
	  unlock ();
	  return k;
	}
      }
      unlock ();
      return -1;
    } catch (...) {
      unlock () ;
      throw;
    }
  }

  // return the index of a key in this string vector

  long Strvec::lookup (const String& key) const {
    rdlock ();
    try {
      long alen = (p_sary == nullptr) ? 0L : p_sary->length ();
      for (long k = 0L; k < alen; k++) {
	if ((*p_sary)[k] == key) {
	  unlock ();
	  return k;
	}
      }
      throw Exception ("key-error", "key not found", key);
    } catch (...) {
      unlock () ;
      throw;
    }
  }
  
  // remove an entry by index and repack the vector

  void Strvec::remove (const long vidx) {
    wrlock ();
    try {
      // check that the vector is not empty
      if ((p_sary == nullptr) || (p_sary->empty () == true)) {
	throw Exception ("strvec-error", "empty vector with remove request");
      }
      p_sary->remove (vidx);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // remove an entry by value if it exists

  void Strvec::remove (const String& key) {
    wrlock ();
    try {
      // get the key index
      long vidx = find (key);
      // remove the entry
      if (vidx != -1L) remove (vidx);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // return the maximum string length in this vector

  long Strvec::maxlen (void) const {
    rdlock ();
    try {
      long result = 0L;
      long   alen = (p_sary == nullptr) ? 0L : p_sary->length ();
      for (long k = 0L; k < alen; k++) {
	long slen = (*p_sary)[k].length ();
	if (slen > result) result = slen;
      }
      unlock ();
      return result;
    } catch (...) {
      unlock () ;
      throw;
    }
  }

  // return the minimum string length in this vector

  long Strvec::minlen (void) const {
    rdlock ();
    try {
      long result = Utility::maxlong ();
      long   alen = (p_sary == nullptr) ? 0L : p_sary->length ();
      for (long k = 0L; k < alen; k++) {
	long slen = (*p_sary)[k].length ();
	if (slen < result) result = slen;
      }
      unlock ();
      return result;
    } catch (...) {
      unlock () ;
      throw;
    }
  }

  // concatenate the string vector without a separator

  String Strvec::concat (void) const {
    rdlock ();
    try {
      String result = concat (nilq);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // concatenate the string vector with a separator

  String Strvec::concat (const t_quad sc) const {
    rdlock ();
    try {
      long alen = (p_sary == nullptr) ? 0L : p_sary->length ();
      String result = (alen > 0L) ? p_sary->first () : "";
      for (long k = 1L; k < alen; k++) {
	// add the separator
	if (sc != nilq) result += sc;
	// add the string
	result += (*p_sary)[k];
      }
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return an array of quarks for this vector

  t_array<long>* Strvec::toquarks (void) const {
    rdlock ();
    try {
      // check for null
      long alen = (p_sary == nullptr) ? 0L : p_sary->length ();
      if (alen == 0L) {
	unlock ();
	return nullptr;
      }
      // allocate and seta
      t_transient<t_array<long>> result = new t_array<long>(alen);
      for (long k = 0; k < alen; k++) result->push ((*p_sary)[k].toquark ());
      unlock ();
      return result.detach();
    } catch (...) {
      unlock () ;
      throw;
    }
  }

  // return a vector of strings

  Vector* Strvec::tovector (void) const {
    rdlock ();
    try {
      // check for null
      long alen = (p_sary == nullptr) ? 0L : p_sary->length ();
      if (alen == 0L) {
	unlock ();
	return nullptr;
      }
      t_transient<Vector> result = new Vector;
      for (long k = 0L; k < alen; k++) {
	result->add (new String ((*p_sary)[k]));
      }
      unlock ();
      return result.detach ();
    } catch (...) {
      unlock () ;
      throw;
    } 
  }

  // return a new vector iterator

  Iterator* Strvec::makeit (void) {
    rdlock ();
    try {
      Iterator* result = new Strvecit (this);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // -------------------------------------------------------------------------
  // - object section                                                        -
  // -------------------------------------------------------------------------

  // the quark zone
  static const long QUARK_ZONE_LENGTH = 22;
  static QuarkZone  zone (QUARK_ZONE_LENGTH);

  // the object supported quarks
  static const long QUARK_POP    = zone.intern ("pop");
  static const long QUARK_ADD    = zone.intern ("add");
  static const long QUARK_GET    = zone.intern ("get");
  static const long QUARK_SET    = zone.intern ("set");
  static const long QUARK_RPOP   = zone.intern ("reverse-pop");
  static const long QUARK_FIND   = zone.intern ("find");
  static const long QUARK_LAST   = zone.intern ("last");
  static const long QUARK_FIRST  = zone.intern ("first");
  static const long QUARK_RESET  = zone.intern ("reset");
  static const long QUARK_REMOVE = zone.intern ("remove");
  static const long QUARK_LENGTH = zone.intern ("length");
  static const long QUARK_CONCAT = zone.intern ("concat");
  static const long QUARK_MINLEN = zone.intern ("min-length");
  static const long QUARK_MAXLEN = zone.intern ("max-length");
  static const long QUARK_EXISTP = zone.intern ("exists-p");
  static const long QUARK_EMPTYP = zone.intern ("empty-p");
  static const long QUARK_ACTIVP = zone.intern ("active-p");
  static const long QUARK_LOOKUP = zone.intern ("lookup");
  static const long QUARK_SETUNQ = zone.intern ("set-unique");
  static const long QUARK_GETUNQ = zone.intern ("get-unique");
  static const long QUARK_SETZLN = zone.intern ("set-zero-length");
  static const long QUARK_GETZLN = zone.intern ("get-zero-length");

  // create a new object in a generic way

  Object* Strvec::mknew (Vector* argv) {
    long argc = (argv == nullptr) ? 0 : argv->length ();
    
    // check 0 argument
    if (argc == 0) return new Strvec;
    // check 1 argument
    if (argc == 1) {
      Object* obj = argv->get (0);
      // check for an integer
      Integer* iobj = dynamic_cast <Integer*> (obj);
      if (iobj !=nullptr) {
        long size = iobj->tolong ();
        return new Strvec (size);
      }
      // check for a boolean
      Boolean* bobj = dynamic_cast <Boolean*> (obj);
      if (bobj !=nullptr) {
        bool uniq = bobj->tobool ();
        return new Strvec (uniq);
      }
      throw Exception ("type-error", "invalid object with heap",
                       Object::repr (obj));
    }
    // check for 2 arguments
    if (argc == 2) {
      long size = argv->getlong (0);
      bool uniq = argv->getbool (1);
      return new Strvec (size, uniq);
    }
    // check for 3 arguments
    if (argc == 3) {
      String sval = argv->getstring (0);
      String sbrk = argv->getstring (1);
      bool   uniq = argv->getbool   (2);
      return new Strvec (Strvec::split (sval, sbrk, uniq));
    }
    throw Exception ("argument-error", "too many argument for string vector");

  }

  // return true if the given quark is defined

  bool Strvec::isquark (const long quark, const bool hflg) const {
    rdlock ();
    try {
      if (zone.exists (quark) == true) {
	unlock ();
	return true;
      }
      bool result = hflg ? Iterable::isquark (quark, hflg) : false;
      if (result == false) {
	result = hflg ? Serial::isquark (quark, hflg) : false;
      }
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // apply this object with a set of arguments and a quark
  
  Object* Strvec::apply (Evaluable* zobj, Nameset* nset, const long quark,
			 Vector* argv) {
    // get the number of arguments
    long argc = (argv == nullptr) ? 0 : argv->length ();

    // dispatch 0 argument
    if (argc == 0) {
      if (quark == QUARK_POP)    return new String  (pop     ());
      if (quark == QUARK_RPOP)   return new String  (rpop    ());
      if (quark == QUARK_LAST)   return new String  (last    ());
      if (quark == QUARK_FIRST)  return new String  (first   ());
      if (quark == QUARK_CONCAT) return new String  (concat  ());
      if (quark == QUARK_LENGTH) return new Integer (length  ());
      if (quark == QUARK_MAXLEN) return new Integer (maxlen  ());
      if (quark == QUARK_MINLEN) return new Integer (minlen  ());
      if (quark == QUARK_EMPTYP) return new Boolean (empty   ());
      if (quark == QUARK_ACTIVP) return new Boolean (active  ());
      if (quark == QUARK_GETUNQ) return new Boolean (getuniq ());
      if (quark == QUARK_GETZLN) return new Boolean (getzlen ());
      if (quark == QUARK_RESET) {
	reset  ();
	return nullptr;
      }
    }

    // dispatch 1 argument
    if (argc == 1) {
      if (quark == QUARK_GET) {
	long index = argv->getlong (0);
	return new String (get (index));
      }
      if (quark == QUARK_ADD) {
	String s = argv->getstring (0);
	add (s);
	return nullptr;
      }
      if (quark == QUARK_EXISTP) {
	String key = argv->getstring (0);
	bool result = exists (key);
	return new Boolean (result);
      }
      if (quark == QUARK_FIND) {
	String key = argv->getstring (0);
	long index = find (key);
	return new Integer (index);
      }
      if (quark == QUARK_LOOKUP) {
	String key = argv->getstring (0);
	long index = lookup (key);
	return new Integer (index);
      }
      if (quark == QUARK_REMOVE) {
	Object* obj = argv->get (0);
	// check for an integer
	Integer* iobj = dynamic_cast <Integer*> (obj);
	if (iobj != nullptr) {
	  long index = iobj->tolong ();
	  remove (index);
	  return nullptr;
	}
	// check for a string
	String* sobj = dynamic_cast <String*> (obj);
	if (sobj != nullptr) {
	  remove (*sobj);
	  return nullptr;
	}
	throw Exception ("type-error", "invalid object with remove",
			 Object::repr (obj));
      }
      if (quark == QUARK_SETUNQ) {
	bool uniq = argv->getbool (0);
	setuniq (uniq);
	return nullptr;
      }
      if (quark == QUARK_SETZLN) {
	bool zlen = argv->getbool (0);
	setzlen (zlen);
	return nullptr;
      }
      if (quark == QUARK_CONCAT) {
	t_quad sc = argv->getchar (0);
	return new String (concat (sc));
      }
    }

    // dispatch 2 arguments
    if (argc == 2) {
      if (quark == QUARK_SET) {
	long index = argv->getlong (0);
	String   s = argv->getstring (1);
	set (index, s);
	return nullptr;
      }
    }
    // check the iterable method
    if (Iterable::isquark (quark, true) == true) {
      return Iterable::apply (zobj, nset, quark, argv);
    }
    // call the serial method
    return Serial::apply (zobj, nset, quark, argv);
  }

  // -------------------------------------------------------------------------
  // - iterator section                                                      -
  // -------------------------------------------------------------------------

  // create a new string vector iterator

  Strvecit::Strvecit (Strvec* vobj) {
    Object::iref (p_vobj = vobj);
    begin ();
  }

  // destroy this vector iterator

  Strvecit::~Strvecit (void) {
    Object::dref (p_vobj);
  }

  // return the class name

  String Strvecit::repr (void) const {
    return "Strvecit";
  }

  // reset the iterator to the begining

  void Strvecit::begin (void) {
    wrlock ();
    try {
      d_vidx = 0;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // reset the iterator to the end

  void Strvecit::end (void) {
    wrlock ();
    if (p_vobj != nullptr) p_vobj->rdlock ();
    try {
      if ((p_vobj != nullptr) && (p_vobj->p_sary != nullptr)) {
	d_vidx = p_vobj->p_sary->length();
      } else {
	d_vidx = 0;
      }
      if (p_vobj != nullptr) p_vobj->unlock ();
      unlock ();
    } catch (...) {
      if (p_vobj != nullptr) p_vobj->unlock ();
      unlock ();
      throw;
    }
  }

  // go to the next object

  void Strvecit::next (void) {
    wrlock ();
    if (p_vobj != nullptr) p_vobj->rdlock ();
    try {
      if ((p_vobj != nullptr) && (p_vobj->p_sary != nullptr)) {
	if (++d_vidx >= p_vobj->p_sary->length()) {
	  d_vidx = p_vobj->p_sary->length ();
	}
      } else {
	d_vidx = 0;
      }
      if (p_vobj != nullptr) p_vobj->unlock ();
      unlock ();
    } catch (...) {
      if (p_vobj != nullptr) p_vobj->unlock ();
      unlock ();
      throw;
    }
  }

  // go to the previous object

  void Strvecit::prev (void) {
    wrlock ();
    try {
      if (--d_vidx < 0) d_vidx = 0;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get the object at the current position

  Object* Strvecit::getobj (void) const {
    rdlock ();
    if (p_vobj != nullptr) p_vobj->rdlock ();
    try {
      Object* result = nullptr;
      if ((p_vobj != nullptr) && (p_vobj->p_sary != nullptr)) {
	if (d_vidx < p_vobj->p_sary->length ()) {
	  result = new String (p_vobj->get (d_vidx));
	}
      }
      if (p_vobj != nullptr) p_vobj->unlock ();
      unlock ();
      return result;
    } catch (...) {
      if (p_vobj != nullptr) p_vobj->unlock ();
      unlock ();
      throw;
    }
  }

  // return true if the iterator is at the end

  bool Strvecit::isend (void) const {
    rdlock ();
    if (p_vobj != nullptr) p_vobj->rdlock ();
    try {
      bool result = false;
      if ((p_vobj != nullptr) && (p_vobj->p_sary != nullptr)) {
	result = (d_vidx >= p_vobj->p_sary->length ());
      } else {
	result = true;
      }
      if (p_vobj != nullptr) p_vobj->unlock ();
      unlock ();
      return result;
    } catch (...) {
      if (p_vobj != nullptr) p_vobj->unlock ();
      unlock ();
      throw;
    }
  }
}
