// ---------------------------------------------------------------------------
// - Sheet.cpp                                                               -
// - afnix:sps module - sheet 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-2011 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "Sheet.hpp"
#include "Spssid.hxx"
#include "Boolean.hpp"
#include "Integer.hpp"
#include "Importer.hpp"
#include "Runnable.hpp"
#include "QuarkZone.hpp"
#include "Exception.hpp"
#include "InputStream.hpp"
#include "OutputStream.hpp"

namespace afnix {

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

  class SheetSorter {
  private:
    // the column index
    long d_col;
    // the sorting mode
    bool d_mode;

    // compare two objects for ascending order
    bool cmplth (Object* ref, Object* slv) const {
      // check that we have a object records
      Record* rref = dynamic_cast <Record*> (ref);
      Record* rslv = dynamic_cast <Record*> (slv);
      // map the cell to their object
      Object* robj = nilp;
      try {
	robj = (rref == nilp) ? nilp : rref->map (d_col);
      } catch (...) {
	robj = nilp;
      }
      Object* sobj = nilp;
      try {
	sobj = (rslv == nilp) ? nilp : rslv->map (d_col);
      } catch (...) {
	sobj = nilp;
      }
      // compare with operator
      if (robj == nilp) return true;
      Object*   obj = robj->oper (Object::LTH, sobj);
      Boolean* bobj = dynamic_cast <Boolean*> (obj);
      bool result   = (bobj == nilp) ? false : bobj->tobool ();
      Object::cref (obj);
      return result;
    }

    // compare two objects for descending order
    bool cmpgth (Object* ref, Object* slv) const {
      // check that we have a object records
      Record* rref = dynamic_cast <Record*> (ref);
      Record* rslv = dynamic_cast <Record*> (slv);
      // map the cell to their object
      Object* robj = nilp;
      try {
	robj = (rref == nilp) ? nilp : rref->map (d_col);
      } catch (...) {
	robj = nilp;
      }
      Object* sobj = nilp;
      try {
	sobj = (rslv == nilp) ? nilp : rslv->map (d_col);
      } catch (...) {
	sobj = nilp;
      }
      // compare with operator
      if ((robj == nilp) && (sobj == nilp)) return true;
      if ((robj == nilp) && (sobj != nilp)) return false;
      Object*   obj = robj->oper (Object::GTH, sobj);
      Boolean* bobj = dynamic_cast <Boolean*> (obj);
      bool result   = (bobj == nilp) ? false : bobj->tobool ();
      Object::cref (obj);
      return result;
    }

    // partition the vector and returns the pivot index

    long partition (Vector& argv, const long first, const long last) const {
      // initialize the pivot index, last index and next (unknown) index
      long pidx = first;
      long lidx = first;
      long uidx = lidx + 1;
      // get the pivot object - and protect it
      Object* pvt = Object::iref (argv.get (pidx));
      // swap until we have reached the last element
      while (uidx <= last) {
	Object* ref = Object::iref (argv.get (uidx));
	bool   cflg = d_mode ? cmplth (ref, pvt) : cmpgth (ref, pvt);
	if (cflg == true) {
	  lidx++;
	  argv.set (uidx, argv.get (lidx));
	  argv.set (lidx, ref);
	}
	Object::tref (ref);
	// the intel is in the other region
	uidx++;
      }
      // place the pivot in proper position
      argv.set (first, argv.get (lidx));
      argv.set (lidx,  pvt);
      Object::tref (pvt);
      // the pivot index is now the last index
      return lidx;
    }
    
    // this function performs a recursive quick sort

    void quicksort (Vector& argv, const long first, const long last) const {
      if (first >= last) return;
      long pidx = partition (argv, first, last);
      quicksort (argv, first, pidx - 1);
      quicksort (argv, pidx + 1, last);
    }

  public:
    // create a default sheet sorter
    SheetSorter (void) {
      d_col  = 0;
      d_mode = false;
    }

    // create a sorting object by index
    SheetSorter (const long col, const bool mode) {
      d_col  = col;
      d_mode = mode;
    }

    // sort a vector

    void qsort (Vector& argv) const {
      // sort the vector in place
      argv.wrlock ();
      try {
	long argc = argv.length ();
	quicksort (argv, 0, argc - 1);
	argv.unlock ();
      } catch (...) {
	argv.unlock ();
	throw;
      }
    }
  };

  // this function computes the maximum between two numbers
  static inline long max (const long x, const long y) {
    return (x < y) ? y : x;
  }

  // this procedure returns a new sheet object for deserialization
  static Serial* mksob (void) {
    return new Sheet;
  }
  // register this cell serial id
  static const t_byte SERIAL_ID = Serial::setsid (SERIAL_SHTT_ID, mksob);


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

  // create a nil sheet

  Sheet::Sheet (void) {
    reset ();
  }

  // create a new sheet by name

  Sheet::Sheet (const String& name) {
    d_name = name;
    reset ();
  }

  // create a new sheet by name and info

  Sheet::Sheet (const String& name, const String& info) {
    d_name = name;
    d_info = info;
    reset ();
  }

  // copy construct this sheet

  Sheet::Sheet (const Sheet& that) {
    that.rdlock ();
    try {
      d_name = that.d_name;
      d_info = that.d_info;
      d_tags = that.d_tags;
      d_mark = that.d_mark;
      d_sign = that.d_sign;
      d_head = that.d_head;
      d_foot = that.d_foot;
      long slen = that.length ();
      for (long i = 0; i < slen; i++) {
	Record* rcd = that.get (i);
	add (new Record (*rcd));
      }
      that.unlock ();
    } catch (...) {
      that.unlock ();
      throw;
    }
  }

  // return the object name

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

  // return a clone of this object

  Object* Sheet::clone (void) const {
    return new Sheet (*this);
  }
			       
  // return the sheet serial id

  t_byte Sheet::serialid (void) const {
    return SERIAL_SHTT_ID;
  }

  // serialize a sheet

  void Sheet::wrstream (OutputStream& os) const {
    rdlock ();
    try {
      // save the sheet name
      d_name.wrstream (os);
      // save the sheet info
      d_info.wrstream (os);
      /// save the tags
      d_tags.wrstream (os);
      // save the markers
      d_mark.wrstream (os);
      // save the signature
      d_sign.wrstream (os);
      // save the header
      d_head.wrstream (os);
      // save the footer
      d_foot.wrstream (os);
      // save the vector
      d_body.wrstream (os);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // deserialize this sheet

  void Sheet::rdstream (InputStream& is) {
    wrlock ();
    try {
      // get the sheet name
      d_name.rdstream (is);
      // get the sheet info
      d_info.rdstream (is);
      // get the tags
      d_tags.rdstream (is);
      // get the markers
      d_mark.rdstream (is);
      // get the signature
      d_sign.rdstream (is);
      // get the header
      d_head.rdstream (is);
      // get the footer
      d_foot.rdstream (is);
      // get the vector
      d_body.rdstream (is);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // reset the sheet

  void Sheet::reset (void) {
    wrlock ();
    try {
      d_head.reset  ();
      d_foot.reset  ();
      d_body.reset ();
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the sheet name

  String Sheet::getname (void) const {
    rdlock ();
    try {
      String result = d_name;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set the sheet name

  void Sheet::setname (const String& name) {
    wrlock ();
    try {
      d_name = name;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the sheet info

  String Sheet::getinfo (void) const {
    rdlock ();
    try {
      String result = d_info;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set the sheet info

  void Sheet::setinfo (const String& info) {
    wrlock ();
    try {
      d_info = info;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a sheet tag

  void Sheet::addtag (const String& tag) {
    wrlock ();
    try {
      if (d_tags.exists (tag) == false) d_tags.add (tag);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a vector of literals in the tags descriptor

  void Sheet::addtag (const Vector* argv) {
    long argc = (argv == nilp) ? 0 : argv->length ();
    if (argc == 0) return;
    wrlock ();
    try {
      for (long i = 0; i < argc; i++) {
	Object*   obj = argv->get (i);
	Literal* lobj = dynamic_cast <Literal*> (obj);
	if (lobj == nilp) {
	  throw Exception ("type-error", 
			   "non literal object with sheet add-tag",
			   Object::repr (obj));
	}
	d_tags.add (lobj->tostring ());
      }
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the tag descriptor length

  long Sheet::tagslen (void) const {
    rdlock ();
    try {
      long result = d_tags.length ();
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // check if a tag exists

  bool Sheet::istag (const String& tag) const {
    rdlock ();
    try {
      bool result = d_tags.exists (tag);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a tag by index
  
  String Sheet::gettag (const long index) const {
    wrlock ();
    try {
      String result = d_tags.get (index);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set the sheet tag by index
  
  void Sheet::settag (const long index, Literal* lobj) {
    wrlock ();
    try {
      String value = (lobj == nilp) ? "" : lobj->tostring ();
      d_tags.set (index, value);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // find a tag index by tag

  long Sheet::findtag (const String& tag) const {
    rdlock ();
    try {
      long result = d_tags.lookup (tag);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a literal in the markers descriptor
  
  void Sheet::addmark (const String& mark) {
    wrlock ();
    try {
      d_mark.add (mark);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a vector of objects in the marker record

  void Sheet::addmark (const Vector* argv) {
    long argc = (argv == nilp) ? 0 : argv->length ();
    if (argc == 0) return;
    wrlock ();
    try {
      for (long i = 0; i < argc; i++) {
	Object*   obj = argv->get (i);
	Literal* lobj = dynamic_cast <Literal*> (obj);
	if (lobj == nilp) {
	  throw Exception ("type-error", 
			   "non literal object with sheet add-mark",
			   Object::repr (obj));
	}
	d_mark.add (lobj->tostring ());
      }
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the marker length

  long Sheet::marklen (void) const {
    rdlock ();
    try {
      long result = d_mark.length ();
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // check if a marker exists exists

  bool Sheet::ismark (const String& mark) const {
    rdlock ();
    try {
      bool result = d_mark.exists (mark);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a sheet marker by index
  
  String Sheet::getmark (const long index) const {
    wrlock ();
    try {
      String result = d_mark.get (index);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set the sheet marker by index
  
  void Sheet::setmark (const long index, Literal* lobj) {
    wrlock ();
    try {
      String value = (lobj == nilp) ? "" : lobj->tostring ();
      d_mark.set (index, value);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // find a marker index by mark

  long Sheet::findmark (const String& mark) const {
    rdlock ();
    try {
      long result = d_mark.lookup (mark);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a literal in the signature descriptor
  
  void Sheet::addsign (const String& sign) {
    wrlock ();
    try {
      d_sign.add (sign);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a vector of objects in the signature record

  void Sheet::addsign (const Vector* argv) {
    long argc = (argv == nilp) ? 0 : argv->length ();
    if (argc == 0) return;
    wrlock ();
    try {
      for (long i = 0; i < argc; i++) {
	Object*   obj = argv->get (i);
	Literal* lobj = dynamic_cast <Literal*> (obj);
	if (lobj == nilp) {
	  throw Exception ("type-error", 
			   "non literal object with sheet add-sign",
			   Object::repr (obj));
	}
	d_sign.add (lobj->tostring ());
      }
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the signer length

  long Sheet::signlen (void) const {
    rdlock ();
    try {
      long result = d_sign.length ();
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // check if a signer exists exists

  bool Sheet::issign (const String& sign) const {
    rdlock ();
    try {
      bool result = d_sign.exists (sign);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a sheet signer by index
  
  String Sheet::getsign (const long index) const {
    wrlock ();
    try {
      String result = d_sign.get (index);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set the sheet signer by index
  
  void Sheet::setsign (const long index, Literal* lobj) {
    wrlock ();
    try {
      String value = (lobj == nilp) ? "" : lobj->tostring ();
      d_sign.set (index, value);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // find a signer index by sign

  long Sheet::findsign (const String& sign) const {
    rdlock ();
    try {
      long result = d_sign.lookup (sign);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add an object in the header record
  
  void Sheet::addhead (Object* object) {
    wrlock ();
    try {
      d_head.add (object);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a vector of objects in the header record

  void Sheet::addhead (const Vector* argv) {
    long argc = (argv == nilp) ? 0 : argv->length ();
    if (argc == 0) return;
    wrlock ();
    try {
      for (long i = 0; i < argc; i++) d_head.add (argv->get (i));
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a header cell by index

  Cell* Sheet::gethead (const long index) const {
    rdlock ();
    try {
      Cell* result = dynamic_cast <Cell*> (d_head.get (index));
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // evaluate a header cell by index

  Literal* Sheet::maphead (const long index) const {
    rdlock ();
    try {
      Cell* cell = gethead (index);
      Literal* result = (cell == nilp) ? nilp : cell->get ();
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set a record header by index and literal
  
  void Sheet::sethead (const long index, Literal* lobj) {
    wrlock ();
    try {
      Cell* cell = dynamic_cast <Cell*> (d_head.get (index)); 
      if (cell != nilp) cell->set (lobj);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add an object in the footer record
  
  void Sheet::addfoot (Object* object) {
    wrlock ();
    try {
      d_foot.add (object);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a vector of objects in the footer record

  void Sheet::addfoot (const Vector* argv) {
    long argc = (argv == nilp) ? 0 : argv->length ();
    if (argc == 0) return;
    wrlock ();
    try {
      for (long i = 0; i < argc; i++) d_foot.add (argv->get (i));
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a footer cell by index

  Cell* Sheet::getfoot (const long index) const {
    rdlock ();
    try {
      Cell* result = dynamic_cast <Cell*> (d_foot.get (index));
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // evaluate a footer cell by index

  Literal* Sheet::mapfoot (const long index) const {
    rdlock ();
    try {
      Cell* cell = getfoot (index);
      Literal* result = (cell == nilp) ? nilp : cell->get ();
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set a record footer by index and literal
  
  void Sheet::setfoot (const long index, Literal* lobj) {
    wrlock ();
    try {
      Cell* cell = dynamic_cast <Cell*> (d_foot.get (index)); 
      if (cell != nilp) cell->set (lobj);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the length of the sheet list

  long Sheet::length (void) const {
    rdlock ();
    try {
      long result = d_body.length ();
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the number of columns
  
  long Sheet::getcols (void) const {
    rdlock ();
    try {
      // get the sheet length
      long tlen = length ();
      // compute the maximum columns
      long result = 0;
      for (long i = 0; i < tlen; i++) {
	Record* rcd = get (i);
	if (rcd == nilp) continue;
	result = max (result, rcd->length ());
      }
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a record in this sheet

  void Sheet::add (Record* rcd) {
    if (rcd == nilp) return;
    wrlock ();
    try {
      d_body.add (rcd);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a record by index

  Record* Sheet::get (const long index) const {
    rdlock ();
    try {
      Record* result = dynamic_cast <Record*> (d_body.get (index));
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a cell by row and column

  Cell* Sheet::get (const long row, const long col) const {
    rdlock ();
    try {
      // get the record
      Record* rcd = get (row);
      if (rcd == nilp) {
	unlock ();
	return nilp;
      }
      // get the cell
      Cell* cell = rcd->get (col);
      unlock ();
      return cell;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // map a cell literal by ow and column

  Literal* Sheet::map (const long row, const long col) const {
    rdlock ();
    try {
      Cell* cell = get (row, col);
      Literal* lobj = (cell == nilp) ? nilp : cell->get ();
      unlock ();
      return lobj;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set a record in this sheet by index

  void Sheet::set (const long index, Record* rcd) {
    wrlock ();
    try {
      d_body.set (index, rcd);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set an object in this sheet by row and column

  void Sheet::set (const long row, const long col, Object* object) {
    wrlock ();
    try {
      // check if we have enough record
      long slen = length ();
      if (row >= slen) {
	long delta = row - slen + 1;
	for (long i = 0; i < delta; i++) add (new Record);
      }
      // get the record
      Record* rcd = get (row);
      if (rcd == nilp) {
	unlock ();
	return;
      }
      // set the cell
      rcd->set (col, object);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a vector of literal as a record

  void Sheet::adddata (const Vector* argv) {
    long argc = (argv == nilp) ? 0 : argv->length ();
    if (argc == 0) return;
    wrlock ();
    Record* rcd = new Record;
    try {
      for (long i = 0; i < argc; i++) rcd->add (argv->get (i));
      d_body.add (rcd);
      unlock ();
    } catch (...) {
      Object::cref (rcd);
      unlock ();
      throw;
    }
  }

  // remove a record by index

  void Sheet::remove (const long index) {
    wrlock ();
    try {
      d_body.remove (index);
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // import data in this sheet

  void Sheet::import (InputStream* is) {
    // do nothing if nil
    if (is == nilp) return;
    // protect us during the importation
    wrlock ();
    Object::iref (this);
    // create a new importer
    try {
      Sheeting si (this);
      si.import (is);
      Object::tref (this);
      unlock ();
    } catch (...) {
      Object::tref (this);
      unlock ();
      throw;
    }
  }

  // convert this sheet into a print sheet

  PrintTable* Sheet::convert (long max, long start, bool flag) const {
    rdlock ();
    // get the number of rows
    long rows = length ();
    // check for start index
    if ((rows != 0) && ((start < 0) || (start >= rows))) {
      unlock ();
      throw Exception ("sheet-error", "start index out of range for convert");
    }
    // check for max index
    long tlen = start + ((max == 0) ? rows : max);
    if (tlen > rows) {
      unlock ();
      throw Exception ("sheet-error", "max index is out of range for convert");
    }
    // format result
    long cols = getcols ();
    PrintTable* result = new PrintTable (cols);
    try {
      // iterate through the record
      for (long i = start; i < tlen; i++) {
	Record* rcd = get (i);
	if (rcd == nilp) continue;
	long row = result->add ();
	// get the record length
	long rlen = rcd->length ();
	for (long j = 0; j < rlen; j++) {
	  Literal* lobj = rcd->map (j);
	  if (lobj == nilp) {
	    result->set (row, j, "nil");
	  } else {
	    String data = flag ? lobj->tostring () : lobj->toliteral ();
	    result->set (row, j, data);
	  }
	}
	// fill the rest of the sheet
	for (long j = rlen; j < cols; j++) result->set (row, j, "nil");
      }
      unlock ();
      return result;
    } catch (...) {
      delete result;
      unlock ();
      throw;
    }
  }

  // sort this sheet by column index and mode

  void Sheet::sort (const long col, const bool mode) {
    wrlock ();
    try {
      // build the sorter object
      SheetSorter sorter (col, mode);
      // sort the vector
      sorter.qsort (d_body);
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // link a sheet column into this sheet

  void Sheet::lkcol (const Sheet* sheet, const long col) {
    // check for a sheet and lock
    if ((sheet == nilp) || (sheet == this)) return;
    sheet->rdlock ();
    wrlock ();
    try {
      // get the insert column
      long cidx = getcols ();
      // get the column size
      long size = sheet->length ();
      // copy the column
      for (long i = 0; i < size; i++) {
	Cell* cell = sheet->get (i, col);
	set (i, cidx, cell);
      }
    } catch (...) {
      unlock ();
      sheet->unlock ();
      throw;
    }
  }

  // check if a row exists by column index and literal

  bool Sheet::isrow (const long col, const Literal& lobj) const {
    rdlock ();
    try {
      // get the number of rows
      long rows = length ();
      // initialize result
      bool result = false;
      // loop in the sheet and check
      for (long row = 0; row < rows; row++) {
	Record* rcd = get (row);
	if (rcd == nilp) continue;
	if (rcd->isequal (col, lobj) == true) {
	  result = true;
	  break;
	}
      }
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // find a row by column index and literal

  long Sheet::rfind (const long col, const Literal& lobj) const {
    rdlock ();
    try {
      // get the number of rows
      long rows = length ();
      // initialize result
      long result = -1;
      // loop in the sheet and check
      for (long row = 0; row < rows; row++) {
	Record* rcd = get (row);
	if (rcd == nilp) continue;
	if (rcd->isequal (col, lobj) == true) {
	  result = row;
	  break;
	}
      }
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

 // find a row by column index and literal or throw an exception

  long Sheet::rlookup (const long col, const Literal& lobj) const {
    rdlock ();
    try {
      long result = rfind (col, lobj);
      if (result == -1) {
	throw Exception ("lookup-error", "cannot find row by literal",
			 lobj.tostring ());
      }
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

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

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

  // the sheet supported quarks
  static const long QUARK_ADD     = zone.intern ("add");
  static const long QUARK_GET     = zone.intern ("get");
  static const long QUARK_MAP     = zone.intern ("map");
  static const long QUARK_SET     = zone.intern ("set");
  static const long QUARK_SORT    = zone.intern ("sort");
  static const long QUARK_RESET   = zone.intern ("reset");
  static const long QUARK_RFIND   = zone.intern ("find-row");
  static const long QUARK_REMOVE  = zone.intern ("remove");
  static const long QUARK_ISROWP  = zone.intern ("row-p");
  static const long QUARK_IMPORT  = zone.intern ("import");
  static const long QUARK_LENGTH  = zone.intern ("length");
  static const long QUARK_COLLEN  = zone.intern ("column-length");
  static const long QUARK_ADDTAG  = zone.intern ("add-tag");
  static const long QUARK_TAGLEN  = zone.intern ("tag-length");
  static const long QUARK_ISTAGP  = zone.intern ("tag-p");
  static const long QUARK_GETTAG  = zone.intern ("get-tag");
  static const long QUARK_SETTAG  = zone.intern ("set-tag");
  static const long QUARK_FINDTAG = zone.intern ("find-tag");
  static const long QUARK_CONVERT = zone.intern ("convert");
  static const long QUARK_SETNAME = zone.intern ("set-name");
  static const long QUARK_GETINFO = zone.intern ("get-info");
  static const long QUARK_SETINFO = zone.intern ("set-info");
  static const long QUARK_ADDMARK = zone.intern ("add-marker");
  static const long QUARK_MARKLEN = zone.intern ("marker-length");
  static const long QUARK_ISMARKP = zone.intern ("marker-p");
  static const long QUARK_GETMARK = zone.intern ("get-marker");
  static const long QUARK_SETMARK = zone.intern ("set-marker");
  static const long QUARK_FINDMRK = zone.intern ("find-marker");
  static const long QUARK_ADDSIGN = zone.intern ("add-sign");
  static const long QUARK_SIGNLEN = zone.intern ("signature-length");
  static const long QUARK_ISSIGNP = zone.intern ("sign-p");
  static const long QUARK_GETSIGN = zone.intern ("get-sign");
  static const long QUARK_SETSIGN = zone.intern ("set-sign");
  static const long QUARK_FINDSGN = zone.intern ("find-sign");
  static const long QUARK_ADDHEAD = zone.intern ("add-header");
  static const long QUARK_GETHEAD = zone.intern ("get-header");
  static const long QUARK_MAPHEAD = zone.intern ("map-header");
  static const long QUARK_SETHEAD = zone.intern ("set-header");
  static const long QUARK_ADDFOOT = zone.intern ("add-footer");
  static const long QUARK_GETFOOT = zone.intern ("get-footer");
  static const long QUARK_MAPFOOT = zone.intern ("map-footer");
  static const long QUARK_SETFOOT = zone.intern ("set-footer");
  static const long QUARK_ADDDATA = zone.intern ("add-data");
  static const long QUARK_LINKCOL = zone.intern ("link-column");
  static const long QUARK_RLOOKUP = zone.intern ("lookup-row");

  // create a new object in a generic way

  Object* Sheet::mknew (Vector* argv) {
    // get number of arguments
    long argc = (argv == nilp) ? 0 : argv->length ();
    // check for 0 argument
    if (argc == 0) return new Sheet;
    // check for 1 argument
    if (argc == 1) {
      String name = argv->getstring (0);
      return new Sheet (name);
    }
    // check for 2 arguments
    if (argc == 2) {
      String name = argv->getstring (0);
      String info = argv->getstring (1);
      return new Sheet (name, info);
    }
    throw Exception ("argument-error", "too many argument with sheet");
  }

  // return true if the given quark is defined

  bool Sheet::isquark (const long quark, const bool hflg) const {
    rdlock ();
    if (zone.exists (quark) == true) {
      unlock ();
      return true;
    }
    bool result = hflg ? Persist::isquark (quark, hflg) : false;
    unlock ();
    return result;
  }

  // apply this object with a set of arguments and a quark

  Object* Sheet::apply (Runnable* robj, Nameset* nset, const long quark,
			Vector* argv) {
    // check for generic quark
    if (quark == QUARK_ADDDATA) {
      adddata (argv);
      return nilp;
    }
    if (quark == QUARK_ADDTAG) {
      addtag (argv);
      return nilp;
    }
    if (quark == QUARK_ADDMARK) {
      addmark (argv);
      return nilp;
    }
    if (quark == QUARK_ADDSIGN) {
      addsign (argv);
      return nilp;
    }
    if (quark == QUARK_ADDHEAD) {
      addhead (argv);
      return nilp;
    }
    if (quark == QUARK_ADDFOOT) {
      addfoot (argv);
      return nilp;
    }

    // get the number of arguments
    long argc = (argv == nilp) ? 0 : argv->length ();

    // dispatch 0 argument
    if (argc == 0) {
      if (quark == QUARK_LENGTH ) return new Integer (length  ());
      if (quark == QUARK_COLLEN ) return new Integer (getcols ());
      if (quark == QUARK_TAGLEN ) return new Integer (tagslen ());
      if (quark == QUARK_MARKLEN) return new Integer (marklen ());
      if (quark == QUARK_SIGNLEN) return new Integer (signlen ());
      if (quark == QUARK_GETINFO) return new String  (getinfo ());
      if (quark == QUARK_CONVERT) return convert (0, 0, true);
      if (quark == QUARK_RESET) {
	reset ();
	return nilp;
      }
      if (quark == QUARK_SORT) {
	sort (0, true);
	return nilp;
      }
    }

    // dispatch 1 argument
    if (argc == 1) {
      if (quark == QUARK_SETNAME) {
	String name = argv->getstring (0);
	setname (name);
	return nilp;
      }
      if (quark == QUARK_SETINFO) {
	String info = argv->getstring (0);
	setinfo (info);
	return nilp;
      }
      if (quark == QUARK_ISTAGP) {
	Object*   obj = argv->get (0);
	Literal* lobj = dynamic_cast <Literal*> (obj);
	if (lobj == nilp) {
	  throw Exception ("type-error", "non literal object with tag-p",
			   Object::repr (obj));
	}	  
	String tag = lobj->tostring ();
	return new Boolean (istag (tag));
      }
      if (quark == QUARK_GETTAG) {
	long index = argv->getlong (0);
	return new String (gettag (index));
      }
      if (quark == QUARK_ISMARKP) {
	Object*   obj = argv->get (0);
	Literal* lobj = dynamic_cast <Literal*> (obj);
	if (lobj == nilp) {
	  throw Exception ("type-error", "non literal object with marker-p",
			   Object::repr (obj));
	}	  
	String mark = lobj->tostring ();
	return new Boolean (ismark (mark));
      }
      if (quark == QUARK_ISSIGNP) {
	Object*   obj = argv->get (0);
	Literal* lobj = dynamic_cast <Literal*> (obj);
	if (lobj == nilp) {
	  throw Exception ("type-error", "non literal object with sign-p",
			   Object::repr (obj));
	}	  
	String sign = lobj->tostring ();
	return new Boolean (issign (sign));
      }
      if (quark == QUARK_GETMARK) {
	long index = argv->getlong (0);
	return new String (getmark (index));
      }
      if (quark == QUARK_GETSIGN) {
	long index = argv->getlong (0);
	return new String (getsign (index));
      }
      if (quark == QUARK_FINDTAG) {
	Object*   obj = argv->get (0);
	Literal* lobj = dynamic_cast <Literal*> (obj);
	if (lobj == nilp) {
	  throw Exception ("type-error", "non literal object with find-tag",
			   Object::repr (obj));
	}	  
	String tag = lobj->tostring ();
	return new Integer (findtag (tag));
      }
      if (quark == QUARK_FINDMRK) {
	Object*   obj = argv->get (0);
	Literal* lobj = dynamic_cast <Literal*> (obj);
	if (lobj == nilp) {
	  throw Exception ("type-error", "non literal object with find-marker",
			   Object::repr (obj));
	}	  
	String mark = lobj->tostring ();
	return new Integer (findmark (mark));
      }
      if (quark == QUARK_FINDSGN) {
	Object*   obj = argv->get (0);
	Literal* lobj = dynamic_cast <Literal*> (obj);
	if (lobj == nilp) {
	  throw Exception ("type-error", "non literal object with find-sign",
			   Object::repr (obj));
	}	  
	String sign = lobj->tostring ();
	return new Integer (findsign (sign));
      }
      if (quark == QUARK_GET) {
	long idx = argv->getlong (0);
	rdlock ();
	try {
	  Object* result = get (idx);
	  robj->post (result);
	  unlock ();
	  return result;
	} catch (...) {
	  unlock ();
	  throw;
	}
      }
      if (quark == QUARK_ADD) {
	Object* obj = argv->get (0);
	Record* rcd = dynamic_cast <Record*> (obj);
	if (rcd != nilp) {
	  add (rcd);
	  return nilp;
	}
	throw Exception ("type-error", "invalid object to add in sheet",
			 Object::repr (obj));
      }
      if (quark == QUARK_GETHEAD) {
	long index = argv->getlong (0);
	rdlock ();
	try {
	  Object* result = gethead (index);
	  robj->post (result);
	  unlock ();
	  return result;
	} catch (...) {
	  unlock ();
	  throw;
	}
      }
      if (quark == QUARK_MAPHEAD) {
	long index = argv->getlong (0);
	rdlock ();
	try {
	  Object* result = maphead (index);
	  robj->post (result);
	  unlock ();
	  return result;
	} catch (...) {
	  unlock ();
	  throw;
	}
      }
      if (quark == QUARK_GETFOOT) {
	long index = argv->getlong (0);
	rdlock ();
	try {
	  Object* result = getfoot (index);
	  robj->post (result);
	  unlock ();
	  return result;
	} catch (...) {
	  unlock ();
	  throw;
	}
      }
      if (quark == QUARK_MAPFOOT) {
	long index = argv->getlong (0);
	rdlock ();
	try {
	  Object* result = mapfoot (index);
	  robj->post (result);
	  unlock ();
	  return result;
	} catch (...) {
	  unlock ();
	  throw;
	}
      }
      if (quark == QUARK_REMOVE) {
	long index= argv->getlong (0);
	remove (index);
	return nilp;
      }
      if (quark == QUARK_IMPORT) {
	Object* obj = argv->get (0);
	InputStream* is = dynamic_cast <InputStream*> (obj);
	if (is != nilp) {
	  import (is);
	  return nilp;
	}
	throw Exception ("type-error", "invalid object for sheet importation",
			 Object::repr (obj));
      }
      if (quark == QUARK_CONVERT) {
	long max = argv->getlong (0);
	return convert (max, 0, true);
      }
      if (quark == QUARK_SORT) {
	Object* obj = argv->get (0);
	// check for integer
	Integer* col = dynamic_cast <Integer*> (obj);
	if (col != nilp) {
	  sort (col->tolong (), true);
	  return nilp;
	}
	// check for boolean
	Boolean* mode = dynamic_cast <Boolean*> (obj);
	if (mode != nilp) {
	  sort (0, mode->tobool ());
	  return nilp;
	}
	// invalid arguments
	throw Exception ("type-error", "invalid object for sorting ",
			 Object::repr (obj));
      }
    }

    // dispatch 2 argument
    if (argc == 2) {
      if (quark == QUARK_SETTAG) {
	long     idx  = argv->getlong (0);
	Object*  obj  = argv->get (1);
	Literal* lobj = dynamic_cast <Literal*> (obj);
	if ((obj != nilp) && (lobj == nilp)) 
	  throw Exception ("type-error", "invalid object to set in tags",
			   Object::repr (obj));
	settag (idx, lobj);
	return nilp;
      }
      if (quark == QUARK_SETMARK) {
	long     idx  = argv->getlong (0);
	Object*  obj  = argv->get (1);
	Literal* lobj = dynamic_cast <Literal*> (obj);
	if ((obj != nilp) && (lobj == nilp)) 
	  throw Exception ("type-error", "invalid object to set in marker",
			   Object::repr (obj));
	setmark (idx, lobj);
	return nilp;
      }
      if (quark == QUARK_SETSIGN) {
	long     idx  = argv->getlong (0);
	Object*  obj  = argv->get (1);
	Literal* lobj = dynamic_cast <Literal*> (obj);
	if ((obj != nilp) && (lobj == nilp)) 
	  throw Exception ("type-error", "invalid object to set in signature",
			   Object::repr (obj));
	setsign (idx, lobj);
	return nilp;
      }
      if (quark == QUARK_SETHEAD) {
	long    idx = argv->getlong (0);
	Object*  obj  = argv->get (1);
	Literal* lobj = dynamic_cast <Literal*> (obj);
	if ((obj != nilp) && (lobj == nilp)) 
	  throw Exception ("type-error", "invalid object to set in header",
			   Object::repr (obj));
	sethead (idx, lobj);
	return nilp;
      }
      if (quark == QUARK_SETFOOT) {
	long    idx = argv->getlong (0);
	Object*  obj  = argv->get (1);
	Literal* lobj = dynamic_cast <Literal*> (obj);
	if ((obj != nilp) && (lobj == nilp)) 
	  throw Exception ("type-error", "invalid object to set in footer",
			   Object::repr (obj));
	setfoot (idx, lobj);
	return nilp;
      }
      if (quark == QUARK_GET) {
	long row = argv->getlong (0);
	long col = argv->getlong (1);
	rdlock ();
	try {
	  Object* result = get (row, col);
	  robj->post (result);
	  unlock ();
	  return result;
	} catch (...) {
	  unlock ();
	  throw;
	}
      }
      if (quark == QUARK_MAP) {
	long row = argv->getlong (0);
	long col = argv->getlong (1);
	rdlock ();
	try {
	  Object* result = map (row, col);
	  robj->post (result);
	  unlock ();
	  return result;
	} catch (...) {
	  unlock ();
	  throw;
	}
      }
      if (quark == QUARK_SET) {
	long    idx = argv->getlong (0);
	Object* obj = argv->get (1);
	Record* rcd = dynamic_cast <Record*> (obj);
	if ((obj != nilp) && (rcd == nilp)) 
	  throw Exception ("type-error", "invalid object to set in sheet",
			   Object::repr (obj));
	set (idx, rcd);
	return nilp;
      }
      if (quark == QUARK_CONVERT) {
	long max   = argv->getlong (0);
	long start = argv->getlong (1);
	return convert (max, start, true);
      }
      if (quark == QUARK_SORT) {
	long col  = argv->getlong (0);
	bool mode = argv->getbool (1);
	sort (col, mode);
	return nilp;
      }
      if (quark == QUARK_LINKCOL) {
	Object* obj = argv->get (0);
	Sheet*  sht = dynamic_cast <Sheet*> (obj);
	if (sht == nilp) {
	  throw Exception ("type-error", "invalid object with link-column",
			   Object::repr (obj));
	}
	long col = argv->getlong (1);
	lkcol (sht, col);
	return nilp;
      }
      if (quark == QUARK_ISROWP) {
	long      col = argv->getlong (0);
	Object*   obj = argv->get (1);
	Literal* lobj = dynamic_cast <Literal*> (obj);
	if ((obj != nilp) && (lobj == nilp)) 
	  throw Exception ("type-error", "invalid object for row check",
			   Object::repr (obj));
	bool result = isrow (col, *lobj);
	return new Boolean (result);
      }
      if (quark == QUARK_RFIND) {
	long      col = argv->getlong (0);
	Object*   obj = argv->get (1);
	Literal* lobj = dynamic_cast <Literal*> (obj);
	if ((obj != nilp) && (lobj == nilp)) 
	  throw Exception ("type-error", "invalid object for row find",
			   Object::repr (obj));
        long result = rfind (col, *lobj);
	return new Integer (result);
      }
      if (quark == QUARK_RLOOKUP) {
	long      col = argv->getlong (0);
	Object*   obj = argv->get (1);
	Literal* lobj = dynamic_cast <Literal*> (obj);
	if ((obj != nilp) && (lobj == nilp)) 
	  throw Exception ("type-error", "invalid object for row lookup",
			   Object::repr (obj));
        long result = rlookup (col, *lobj);
	return new Integer (result);
      }
    }
    // dispatch 3 arguments
    if (argc == 3) {
      if (quark == QUARK_CONVERT) {
	long max   = argv->getlong (0);
	long start = argv->getlong (1);
	bool flag  = argv->getbool (2);
	return convert (max, start, flag);
      }
      if (quark == QUARK_SET) {
	long    row = argv->getlong (0);
	long    col = argv->getlong (1);
	Object* obj = argv->get    (2);
	set (row, col, obj);
	return nilp;
      }
    }
    // call the persist method
    return Persist::apply (robj, nset, quark, argv);
  }
}
