// ---------------------------------------------------------------------------
// - PrintTable.cpp                                                          -
// - standard object library - printable table class definition              -
// ---------------------------------------------------------------------------
// - 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 "Stdsid.hxx"
#include "Vector.hpp"
#include "Utility.hpp"
#include "Integer.hpp"
#include "Boolean.hpp"
#include "Runnable.hpp"
#include "Character.hpp"
#include "QuarkZone.hpp"
#include "Exception.hpp"
#include "PrintTable.hpp"

namespace afnix {

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

  // this procedure compute the maximum of two integers
  static inline long max (const long x, const long y) {
    return (x < y) ? y : x;
  }

  // this procedure serialize an integer
  static inline void ptbl_write_intg (OutputStream& os, const long val) {
    Integer ival = val;
    ival.wrstream (os);
  }

  // this procedure serialize a character
  static inline void ptbl_write_char (OutputStream& os, const t_quad val) {
    Character cval = val;
    cval.wrstream (os);
  }

  // this procedure serialize a character
  static inline void ptbl_write_bool (OutputStream& os, const bool val) {
    Boolean bval = val;
    bval.wrstream (os);
  }

  // this procedure deserialize an integer
  static inline long ptbl_read_intg (InputStream& is) {
    Integer ival;
    ival.rdstream (is);
    return ival.tolong ();
  }

  // this procedure deserialize a character
  static inline t_quad ptbl_read_char (InputStream& is) {
    Character cval;
    cval.rdstream (is);
    return cval.toquad ();
  }

  // this procedure deserialize a boolean
  static inline t_quad ptbl_read_bool (InputStream& is) {
    Boolean bval;
    bval.rdstream (is);
    return bval.tobool ();
  }

  // this procedure format a string according to the desired width, the
  // filling character and direction
  static String fmtstr (const String& value, const long csiz, 
			const t_quad  fill,  const bool cdir, 
			const long    cwth) {
    // format the result string using non combining length
    String result;
    long len = value.ncclen ();
    // check if we process the size or not
    if (csiz == 0) {
      if (len < cwth) {
	if (cdir == false) {
	  result = value.rfill (fill, cwth);
	} else {
	  result = value.lfill (fill, cwth);
	}
      } else {
	result = value;
      }	     
    } else {
      if (len == csiz) result = value;
      if (len < csiz) {
	if (cdir == false) {
	  result = value.rfill (fill, csiz);
	} else {
	  result = value.lfill (fill, csiz);
	}
      }
      if (len > csiz) {
	if (cdir == false) {
	  result = value.lsubstr (csiz);
	} else {
	  result = value.rsubstr (len - csiz);
	}
      }
    }
    return result;
  }

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

  // create a default print table

  PrintTable::PrintTable (void) {
    d_size = 16;
    d_cols = 1;
    d_rows = 0;
    d_psiz = 0;
    d_sflg = false;
    // initialize the data table
    p_head = new String[d_cols];
    p_data = new String*[d_size];
    for (long i = 0; i < d_size; i++) p_data[i] = nilp;
    // initialize the format size and column width
    p_csiz = new long[d_cols];
    p_fill = new t_quad[d_cols];
    p_cdir = new bool[d_cols];
    p_cwth = new long[d_cols];
    for (long i = 0; i < d_cols; i++) {
      p_csiz[i] = 0;
      p_fill[i] = blkq;
      p_cdir[i] = false;
      p_cwth[i] = 0;
    }
  }

  // create a print table with a number of columns

  PrintTable::PrintTable (const long cols) {
    d_size = 16;
    d_cols = cols;
    d_rows = 0;
    d_psiz = 0;
    d_sflg = false;
    // initialize the data table
    p_head = new String[d_cols];
    p_data = new String*[d_size];
    for (long i = 0; i < d_size; i++) p_data[i] = nilp;
    // initialize the format size and column width
    p_csiz = new long[d_cols];
    p_fill = new t_quad[d_cols];
    p_cdir = new bool[d_cols];
    p_cwth = new long[d_cols];
    for (long i = 0; i < d_cols; i++) {
      p_csiz[i] = 0;
      p_fill[i] = blkq;
      p_cdir[i] = false;
      p_cwth[i] = 0;
    }
  }

  // create a print table with a number of columns and rows

  PrintTable::PrintTable (const long cols, const long rows) {
    d_size = rows;
    d_cols = cols;
    d_rows = 0;
    d_psiz = 0;
    d_sflg = false;
    // initialize the data table
    p_head = new String[d_cols];
    p_data = new String*[d_size];
    for (long i = 0; i < d_size; i++) p_data[i] = nilp;
    // initialize the format size and column width
    p_csiz = new long[d_cols];
    p_fill = new t_quad[d_cols];
    p_cdir = new bool[d_cols];
    p_cwth = new long[d_cols];
    for (long i = 0; i < d_cols; i++) {
      p_csiz[i] = 0;
      p_fill[i] = blkq;
      p_cdir[i] = false;
      p_cwth[i] = 0;
    }
  }

  // destroy this print table

  PrintTable::~PrintTable (void) {
    for (long i = 0; i < d_rows; i++) delete [] p_data[i];
    delete [] p_head;
    delete [] p_data;
    delete [] p_csiz;
    delete [] p_fill;
    delete [] p_cdir;
    delete [] p_cwth;
  }

  // return the class name

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

  // return the print table serial code

  t_byte PrintTable::serialid (void) const {
    return SERIAL_PTBL_ID;
  }

  // serialize this print table

  void PrintTable::wrstream (OutputStream& os) const {
    rdlock ();
    try {
      // write the table size
      ptbl_write_intg (os, d_size);
      // write the columns and rows
      ptbl_write_intg (os, d_cols);
      ptbl_write_intg (os, d_rows);
      // write the number formating
      ptbl_write_intg (os, d_psiz);
      ptbl_write_bool (os, d_sflg);
      // write the table body
      if (p_data == nilp) {
	throw Exception ("table-error", "invalid nil table body");
      }
      for (long i = 0; i < d_rows; i++) {
	String* row = p_data[i];
	if (row == nilp) {
	  throw Exception ("table-error", "invalid nil table row");
	}
	for (long j = 0; j < d_cols; j++) row[j].wrstream (os);
      }
      // write the table head
      if (p_head == nilp) {
	throw Exception ("table-error", "invalid nil table head");
      }
      for (long i = 0; i < d_cols; i++) p_head[i].wrstream (os);
      // write the column size
      if (p_csiz == nilp) {
	throw Exception ("table-error", "invalid nil table column format");
      }
      for (long i = 0; i < d_cols; i++) ptbl_write_intg (os, p_csiz[i]);
      // write the columns filling characters
      if (p_fill == nilp) {
	throw Exception ("table-error", "invalid nil table column filling");
      }
      for (long i = 0; i < d_cols; i++) ptbl_write_char (os, p_fill[i]);
      // write the columns direction
      if (p_cdir == nilp) {
	throw Exception ("table-error", "invalid nil table column direction");
      }
      for (long i = 0; i < d_cols; i++) ptbl_write_bool (os, p_cdir[i]);
      // write the column maximum width
      if (p_cwth == nilp) {
	throw Exception ("table-error", "invalid nil table column width");
      }
      for (long i = 0; i < d_cols; i++) ptbl_write_intg (os, p_cwth[i]);
      // unlock and return
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // deserialize this print table

  void PrintTable::rdstream (InputStream& is) {
    wrlock ();
    try {
      // start to clear the table
      for (long i = 0; i < d_rows; i++) delete [] p_data[i];
      delete [] p_head;
      delete [] p_data;
      delete [] p_csiz;
      delete [] p_fill;
      delete [] p_cdir;
      delete [] p_cwth;
      // get the table size
      d_size = ptbl_read_intg (is);
      p_data = new String*[d_size];
      for (long i = 0; i < d_size; i++) p_data[i] = nilp;
      // get the table row and column
      d_cols = ptbl_read_intg (is);
      d_rows = ptbl_read_intg (is);
      // get the number formating
      d_psiz =  ptbl_read_intg (is);
      d_sflg =  ptbl_read_bool (is);
      // get the table body
      for (long i = 0; i < d_rows; i++) {
	p_data[i] = new String[d_cols];
	for (long j = 0; j < d_cols; j++) p_data[i][j].rdstream (is);
      }
      // get the table head
      p_head = new String[d_cols];
      for (long i = 0; i < d_cols; i++) p_head[i].rdstream (is);
      // get the column size
      p_csiz = new long[d_cols];
      for (long i = 0; i < d_cols; i++) p_csiz[i] = ptbl_read_intg (is);
      // get the column filling characters
      p_fill = new t_quad[d_cols];
      for (long i = 0; i < d_cols; i++) p_fill[i] = ptbl_read_char (is);
      // get the column direction
      p_cdir = new bool[d_cols];
      for (long i = 0; i < d_cols; i++) p_cdir[i] = ptbl_read_bool (is);
      // get the column width
      p_cwth = new long[d_cols];
      for (long i = 0; i < d_cols; i++) p_cwth[i] = ptbl_read_intg (is);
      // unlock and return
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return true if the header is defined

  bool PrintTable::ishead (void) const {
    rdlock ();
    try {
      bool result = false;
      for (long i = 0; i < d_cols; i++) result |= !p_head[i].isnil ();
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the number of rows

  long PrintTable::getrows (void) const {
    rdlock ();
    try {
      long result = d_rows;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the number of columns

  long PrintTable::getcols (void) const {
    rdlock ();
    try {
      long result = d_cols;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set the column size

  void PrintTable::setsize (const long col, const long size) {
    wrlock ();
    try {
      // check for column and size
      if ((col < 0) || (col >= d_cols)) {
	throw Exception ("table-error", "invalid column index");
      }
      if (size < 0) {
	throw Exception ("table-error", "invalid column size");
      }
      p_csiz[col] = size;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the column size

  long PrintTable::getsize (const long col) const {
    rdlock ();
    try {
      if ((col < 0) || (col >= d_cols)) {
	throw Exception ("table-error", "invalid column index");
      }
      long result = p_csiz[col];
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set the table number precision

  void PrintTable::setpsiz (const long psiz) {
    wrlock ();
    try {
      if (psiz < 0) {
	throw Exception ("table-error", "invalid negative number precision");
      }
      d_psiz = psiz;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get the table number precision

  long PrintTable::getpsiz (void) const {
    rdlock ();
    try {
      long result = d_psiz;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set the table scientific flag

  void PrintTable::setsflg (const bool sflg) {
    wrlock ();
    try {
      d_sflg = sflg;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get the table scientific flag

  bool PrintTable::getsflg (void) const {
    rdlock ();
    try {
      bool result = d_sflg;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set the column filling character

  void PrintTable::setfill (const long col, const t_quad fill) {
    wrlock ();
    try {
      // check for column and size
      if ((col < 0) || (col >= d_cols)) {
	throw Exception ("table-error", "invalid column index");
      }
      p_fill[col] = fill;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the column fill character

  t_quad PrintTable::getfill (const long col) const {
    rdlock ();
    try {
      if ((col < 0) || (col >= d_cols)) {
	throw Exception ("table-error", "invalid column index");
      }
      t_quad result = p_fill[col];
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set the column direction

  void PrintTable::setcdir (const long col, const bool cdir) {
    wrlock ();
    try {
      // check for column and size
      if ((col < 0) || (col >= d_cols)) {
	throw Exception ("table-error", "invalid column index");
      }
      p_cdir[col] = cdir;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return the column direction flag

  bool PrintTable::getcdir (const long col) const {
    rdlock ();
    try {
      if ((col < 0) || (col >= d_cols)) {
	throw Exception ("table-error", "invalid column index");
      }
      bool result = p_cdir[col];
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // add a new row and return the row index

  long PrintTable::add (void) {
    wrlock ();
    try {
      // check if we need to resize
      if ((d_rows + 1) >= d_size) resize (d_size * 2);
      long result = d_rows;
      p_data[d_rows++] = new String[d_cols];
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set a head element by column

  void PrintTable::sethead (const long col, const String& val) {
    wrlock ();
    try {
      // check for valid column
      if ((col < 0) || (col >= d_cols)) {
	throw Exception ("table-error", "invalid column index");
      }
      // udpate the header
      p_head[col] = val;
      // update the maximum width
      p_cwth[col] = max (p_cwth[col], val.length ());
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a head element by column

  String PrintTable::gethead (const long col) const {
    rdlock ();
    try {
      // check for valid row and column
      if ((col < 0) || (col >= d_cols)) {
	throw Exception ("table-error", "invalid column index");
      }
      String result = p_head[col];
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set a data element at row and column

  void PrintTable::set (const long row, const long col, const String& val) {
    wrlock ();
    try {
      // check for valid row and column
      if ((row < 0) || (row >= d_rows) || (col < 0) || (col >= d_cols)) {
	throw Exception ("table-error", "invalid row or column index");
      }
      // udpate the table
      String* line = p_data[row];
      line[col] = val;
      // update the maximum width
      p_cwth[col] = max (p_cwth[col], val.length ());
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set a data element at row and column

  void PrintTable::set (const long row, const long col, const long val) {
    wrlock ();
    try {
      String sval = Utility::tostring (val);
      set (row, col, sval);
      unlock ();
    } catch(...) {
      unlock ();
      throw;
    }
  }

  // set a data element at row and column

  void PrintTable::set (const long row, const long col, const t_long val) {
    wrlock ();
    try {
      String sval = Utility::tostring (val);
      set (row, col, sval);
      unlock ();
    } catch(...) {
      unlock ();
      throw;
    }
  }

  // set a data element at row and column

  void PrintTable::set (const long row, const long col, const t_octa val) {
    wrlock ();
    try {
      String sval = "0x"; sval += Utility::tohexa (val, true);
      set (row, col, sval);
      unlock ();
    } catch(...) {
      unlock ();
      throw;
    }
  }

  // set a data element at row and column

  void PrintTable::set (const long row, const long col, const t_real val) {
    wrlock ();
    try {
      String sval = Utility::tostring (val, d_psiz, d_sflg);
      set (row, col, sval);
      unlock ();
    } catch(...) {
      unlock ();
      throw;
    }
  }

  // set a data element with a literal object

  void PrintTable::set (const long row, const long col, Literal* obj) {
    wrlock ();
    try {
      if (obj == nilp) return;
      set (row, col, obj->tostring ());
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a table element by row and column

  String PrintTable::get (const long row, const long col) const {
    rdlock ();
    try {
      // check for valid row and column
      if ((row < 0) || (row >= d_rows) || (col < 0) || (col >= d_cols)) {
	throw Exception ("table-error", "invalid row or column index");
      }
      String* line = p_data[row];
      String result = line[col];
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a formatted row suitable for dumping

  String PrintTable::dump (const long row) const {
    rdlock ();
    try {
      // check for valid row and column
      if ((row < 0) || (row >= d_rows)) {
	throw Exception ("table-error", "invalid row index");
      }
      // get the line and preapre for result
      String* line = p_data[row];
      String result;
      for (long i = 0; i < d_cols; i++) {
	String data = line[i].toliteral ();
	long   cwth = p_cwth[i] + 2;
	result = result + fmtstr (data, 0, ' ', false, cwth);
	if (i < (d_cols - 1)) result = result + ' ';
      }
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // dump the table into a buffer

  void PrintTable::dump (Buffer& buffer) const {
    rdlock ();
    try {
      for (long i = 0; i < d_rows; i++) {
	buffer.add (dump (i));
	buffer.add (eolc);
      }
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // dump the table for an output stream

  void PrintTable::dump (OutputStream& os) const {
    rdlock ();
    try {
      for (long i = 0; i < d_rows; i++)  os.writeln (dump (i));
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }
  
  // get a formatted head suitable for printing

  String PrintTable::fmhead (void) const {
    rdlock ();
    try {
      String result;
      for (long i = 0; i < d_cols; i++) {
	result = result + fmtstr (p_head[i], p_csiz[i], 
				  p_fill[i], p_cdir[i], p_cwth[i]);
	if (i < (d_cols - 1)) result = result + ' ';
      }
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get a formatted row suitable for printing

  String PrintTable::format (const long row) const {
    rdlock ();
    try {
      // check for valid row and column
      if ((row < 0) || (row >= d_rows)) {
	throw Exception ("table-error", "invalid row index");
      }
      // get the line and prepare for result
      String* line = p_data[row];
      String result;
      for (long i = 0; i < d_cols; i++) {
	result = result + fmtstr (line[i], p_csiz[i], p_fill[i], p_cdir[i],
				  p_cwth[i]);
	if (i < (d_cols - 1)) result = result + ' ';
      }
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // format the table into a buffer

  void PrintTable::format (Buffer& buffer) const {
    rdlock ();
    try {
      // add the head if defined
      if (ishead () == true) {
	buffer.add (fmhead ());
	buffer.add (eolc);
      }
      // add the data
      for (long i = 0; i < d_rows; i++) {
	buffer.add (format (i));
	buffer.add (eolc);
      }
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // format the table for an output stream

  void PrintTable::format (OutputStream& os) const {
    rdlock ();
    try {
      // add the if defined
      if (ishead () == true) os.writeln (fmhead ());
      // add the data
      for (long i = 0; i < d_rows; i++)  os.writeln (format (i));
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // resize this print table

  void PrintTable::resize (const long size) {
    wrlock ();
    try {
      // check for valid size
      if (size <= d_size) {
	unlock ();
	return;
      }
      // create a new table
      String** data = new String* [size];
      for (long i = 0;      i < d_rows; i++) data[i] = p_data[i];
      for (long i = d_rows; i < size;   i++) data[i] = nilp;
      // update table and remove old one
      delete [] p_data;
      p_data = data;
      d_size = size;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // merge a print table into this one

  void PrintTable::merge (const PrintTable& ptbl) {
    wrlock ();
    try {
      // make sure e have enough columns
      long cols = ptbl.getcols ();
      if (d_cols < cols) {
	throw Exception ("merge-error", "print table is too large");
      }
      // get the number of rows and iterate
      long rows = ptbl.getrows ();
      for (long row = 0; row < rows; row++) {
	long idx = add ();
	for (long col = 0; col < cols; col++) {
	  // get the table value
	  String value = ptbl.get (row, col);
	  // merge it locally
	  set (idx, col, value);
	}
      }
      // done
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // -------------------------------------------------------------------------
  // - class 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_ADD     = zone.intern ("add");
  static const long QUARK_GET     = zone.intern ("get");
  static const long QUARK_SET     = zone.intern ("set");
  static const long QUARK_ADDH    = zone.intern ("add-head");
  static const long QUARK_GETH    = zone.intern ("get-head");
  static const long QUARK_SETH    = zone.intern ("set-head");
  static const long QUARK_DUMP    = zone.intern ("dump");
  static const long QUARK_HEADP   = zone.intern ("head-p");
  static const long QUARK_MERGE   = zone.intern ("merge");
  static const long QUARK_FORMAT  = zone.intern ("format");
  static const long QUARK_GETCOLS = zone.intern ("get-columns");
  static const long QUARK_GETROWS = zone.intern ("get-rows");
  static const long QUARK_SETSIZE = zone.intern ("set-column-size");
  static const long QUARK_SETFILL = zone.intern ("set-column-fill");
  static const long QUARK_GETSIZE = zone.intern ("get-column-size");
  static const long QUARK_GETFILL = zone.intern ("get-column-fill");
  static const long QUARK_SETCDIR = zone.intern ("set-column-direction");
  static const long QUARK_GETCDIR = zone.intern ("get-column-direction");
  static const long QUARK_SETPSIZ = zone.intern ("set-number-precision");
  static const long QUARK_GETPSIZ = zone.intern ("get-number-precision");
  static const long QUARK_SETSFLG = zone.intern ("set-scientific-notation");
  static const long QUARK_GETSFLG = zone.intern ("get-scientific-notation");

  // create a new object in a generic way

  Object* PrintTable::mknew (Vector* argv) {
    // get the number of arguments
    long argc = (argv == nilp) ? 0 : argv->length ();
    // check for 0 argument
    if (argc == 0) return new PrintTable;
    // check for 1 argument
    if (argc == 1) {
      long cols = argv->getlong (0);
      return new PrintTable (cols);
    }
    // check for 2 arguments
    if (argc == 2) {
      long cols = argv->getlong (0);
      long rows = argv->getlong (1);
      return new PrintTable (cols, rows);
    }
    throw Exception ("argument-error", "invavlid argument for print table");
  }

  // return true if the given quark is defined

  bool PrintTable::isquark (const long quark, const bool hflg) const {
    rdlock ();
    if (zone.exists (quark) == true) {
      unlock ();
      return true;
    }
    bool result = hflg ? Object::isquark (quark, hflg) : false;
    unlock ();
    return result;
  }
  
  // apply an object method with a set of arguments and a quark
  
  Object* PrintTable::apply (Runnable* robj, Nameset* nset, const long quark,
			     Vector* argv) {
    // get the number of arguments
    long argc = (argv == nilp) ? 0 : argv->length ();

    // dispatch generic quark
    if (quark == QUARK_ADDH) {
      wrlock ();
      try {
	if (argc != d_cols) {
	  throw Exception ("argument-error", 
			   "invalid number of arguments with add-header");
	}
	for (long i = 0; i < argc; i++) {
	  Object*  obj = argv->get (i);
	  String* sobj = dynamic_cast <String*> (obj);
	  if (sobj == nilp) {
	    throw Exception ("type-error", "invalid object for add-header",
			     Object::repr (obj));
	  }
	  sethead (i, *sobj);
	}
	unlock ();
	return nilp;
      } catch (...) {
	unlock ();
	throw;
      }
    }
    if (quark == QUARK_ADD) {
      if (argc == 0) return new Integer (add ());
      wrlock ();
      // get the new row
      long row = -1;
      try {
	row = add ();
      } catch (...) {
	unlock ();
	throw;
      }
      // loop in the arguments
      try {
	if (argc != d_cols) {
	  throw Exception ("argument-error", 
			   "invalid number of arguments with add");
	}
	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", "invalid object for add",
			     Object::repr (obj));
	  }
	  set (row, i, lobj);
	}
	unlock ();
	return new Integer (row);
      } catch (...) {
	if (row >= 0) delete [] p_data[row];
	d_rows--;
	unlock ();
	throw;
      }
    }

    // dispatch 0 argument
    if (argc == 0) {
      if (quark == QUARK_HEADP)   return new Boolean (ishead  ());
      if (quark == QUARK_GETROWS) return new Integer (getrows ());
      if (quark == QUARK_GETCOLS) return new Integer (getcols ());
      if (quark == QUARK_GETPSIZ) return new Integer (getpsiz ());
      if (quark == QUARK_GETSFLG) return new Boolean (getsflg ());
      if (quark == QUARK_DUMP) {
	OutputStream* os = robj->getos ();
	dump (*os);
	return nilp;
      }
      if (quark == QUARK_FORMAT) {
	OutputStream* os = robj->getos ();
	format (*os);
	return nilp;
      }
    }
    // dispatch 1 argument
    if (argc == 1) {
      if (quark == QUARK_GETSIZE) {
	long col = argv->getlong (0);
	return new Integer (getsize (col));
      }
      if (quark == QUARK_SETPSIZ) {
	long psiz = argv->getlong (0);
	setpsiz (psiz);
	return nilp;
      }
      if (quark == QUARK_SETSFLG) {
	bool sflg = argv->getbool (0);
	setsflg (sflg);
	return nilp;
      }
      if (quark == QUARK_GETFILL) {
	long col = argv->getlong (0);
	return new Character (getfill (col));
      }
      if (quark == QUARK_GETCDIR) {
	long col = argv->getlong (0);
	return new Boolean (getcdir (col));
      }
      if (quark == QUARK_GETH) {
	long col = argv->getlong (0);
	return new String (gethead (col));
      }
      if (quark == QUARK_MERGE) {
	Object* obj = argv->get (0);
	PrintTable* pobj = dynamic_cast <PrintTable*> (obj);
	if (pobj == nilp) {
	  throw Exception ("type-error", "invalid object with merge",
			   Object::repr (obj));
	}
	merge (*pobj);
	return nilp;
      }
      if (quark == QUARK_DUMP) {
	Object* obj = argv->get (0);
	// check for integer
	Integer* ival = dynamic_cast <Integer*> (obj);
	if (ival != nilp) {
	  return new String (dump (ival->tolong ()));
	}
	// check for buffer
	Buffer* buffer = dynamic_cast <Buffer*> (obj);
	if (buffer != nilp) {
	  dump (*buffer);
	  return nilp;
	}
	// check for output stream
	OutputStream* os = dynamic_cast <OutputStream*> (obj);
	if (os != nilp) {
	  dump (*os);
	  return nilp;
	}
	throw Exception ("type-error", "invalid object with format",
			 Object::repr (obj));
      }
      if (quark == QUARK_FORMAT) {
	Object* obj = argv->get (0);
	// check for integer
	Integer* ival = dynamic_cast <Integer*> (obj);
	if (ival != nilp) {
	  return new String (format (ival->tolong ()));
	}
	// check for buffer
	Buffer* buffer = dynamic_cast <Buffer*> (obj);
	if (buffer != nilp) {
	  format (*buffer);
	  return nilp;
	}
	// check for output stream
	OutputStream* os = dynamic_cast <OutputStream*> (obj);
	if (os != nilp) {
	  format (*os);
	  return nilp;
	}
	throw Exception ("type-error", "invalid object with format",
			 Object::repr (obj));
      }
    }
    // dispatch 2 arguments
    if (argc == 2) {
      if (quark == QUARK_GET) {
	long row = argv->getlong (0);
	long col = argv->getlong (1);
	return new String (get (row, col));
      }
      if (quark == QUARK_SETH) {
	long   col = argv->getlong (0);
	String val = argv->getstring (1);
	sethead (col, val);
	return nilp;
      }
      if (quark == QUARK_SETSIZE) {
	long col  = argv->getlong (0);
	long size = argv->getlong (1);
	setsize (col, size);
	return nilp;
      }
      if (quark == QUARK_SETFILL) {
	long   col  = argv->getlong (0);
	t_quad fill = argv->getchar (1);
	setsize (col, fill);
	return nilp;
      }
      if (quark == QUARK_SETCDIR) {
	long col  = argv->getlong (0);
	bool cdir = argv->getbool (1);
	setcdir (col, cdir);
	return nilp;
      }
    }
    // dispatch 3 arguments
    if (argc == 3) {
      if (quark == QUARK_SET) {
	long   row = argv->getlong (0);
	long   col = argv->getlong (1);
	String val = argv->getstring (2);
	set (row, col, val);
	return nilp;
      }
    }
    // call the object method
    return Object::apply (robj, nset, quark, argv);
  }
}
