Files correlati : ve1 Ricompilazione Demo : [ ] Commento : Corretta gestione MESSAGE _TABLEREAD sulle tabelle di modulo git-svn-id: svn://10.65.10.50/trunk@18729 c028cbd2-c16b-5b4b-a496-9718f37d4682
		
			
				
	
	
		
			1617 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1617 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
#include "../xvaga/incstr.h"
 | 
						|
 | 
						|
#include <applicat.h>
 | 
						|
#include <codeb.h>
 | 
						|
#include <modaut.h>
 | 
						|
#include <progind.h>
 | 
						|
#include <recset.h>
 | 
						|
#include <relation.h>
 | 
						|
#include <utility.h>
 | 
						|
#include <xml.h>
 | 
						|
 | 
						|
#include <statbar.h>
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
// Utility
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
 | 
						|
static bool is_numeric(const char* str)
 | 
						|
{
 | 
						|
  if (str == NULL || *str == '\0' || (*str == '0' && isdigit(str[1])))
 | 
						|
    return false;    // Se comincia per zero va preservato!
 | 
						|
  if (*str == '-')
 | 
						|
  {
 | 
						|
    str++;
 | 
						|
    if (*str <= ' ')
 | 
						|
      return false;
 | 
						|
  }
 | 
						|
  while (*str)
 | 
						|
  {
 | 
						|
    if (strchr("0123456789.,", *str) == NULL)
 | 
						|
      return false;
 | 
						|
    str++;
 | 
						|
  }
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static void num_reformat(TString& val)
 | 
						|
{
 | 
						|
	xvt_str_number_format(val.get_buffer(), val.size());
 | 
						|
  const int comma = val.find(',');
 | 
						|
  const int point = val.find('.');
 | 
						|
  if (comma >= 0 && comma < point) 
 | 
						|
    val.strip(","); else
 | 
						|
  if (point >= 0 && point < comma) 
 | 
						|
    val.strip(".");
 | 
						|
}
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
// TRecordset
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
 | 
						|
const TString& TRecordset::query_text() const
 | 
						|
{
 | 
						|
  return EMPTY_STRING;
 | 
						|
}
 | 
						|
 | 
						|
const TToken_string& TRecordset::sheet_head() const
 | 
						|
{
 | 
						|
  TToken_string head;
 | 
						|
  TToken_string tablefield(32, '.');
 | 
						|
	const unsigned int cols = columns();
 | 
						|
 | 
						|
  for (unsigned int c = 0; c < cols; c++)
 | 
						|
  {
 | 
						|
    const TRecordset_column_info& ci = column_info(c);
 | 
						|
    tablefield = ci._name;
 | 
						|
    int maxlen = 0;
 | 
						|
    FOR_EACH_TOKEN(tablefield, tok)
 | 
						|
    {
 | 
						|
      if (maxlen == 0)
 | 
						|
        head.add(tok);
 | 
						|
      else
 | 
						|
        head << '\n' << tok;
 | 
						|
      const int len = strlen(tok);
 | 
						|
      if (len > maxlen)
 | 
						|
        maxlen = len;
 | 
						|
    }
 | 
						|
    head << '@' << max(ci._width, maxlen);
 | 
						|
    if (ci._type == _wordfld || ci._type == _intfld || ci._type == _longfld || ci._type == _realfld)
 | 
						|
      head << 'R';
 | 
						|
  }
 | 
						|
 | 
						|
  // Creo la stringa temporanea solo ora, altrimenti puo' essere sovrascritta!
 | 
						|
  TToken_string& h = get_tmp_string();
 | 
						|
  h = head;
 | 
						|
  return h;
 | 
						|
}
 | 
						|
 | 
						|
bool TRecordset::save_as_html(const char* path)
 | 
						|
{
 | 
						|
  TProgind pi(items(), TR("Esportazione in corso..."), true, true);
 | 
						|
  ofstream out(path);
 | 
						|
  out << "<html>" << endl;
 | 
						|
  out << "<body>" << endl;
 | 
						|
 | 
						|
  TString qry; parsed_text(qry);
 | 
						|
  for (int i = qry.find('\n'); i > 0; i = qry.find('\n', i+1))
 | 
						|
    qry.insert("<br/>", i+1);
 | 
						|
 | 
						|
  out << "<p><b>" << qry << "</b></p>" << endl;
 | 
						|
 | 
						|
  out << "<table border=\"1\">" << endl;
 | 
						|
 | 
						|
  const unsigned int cols = columns();
 | 
						|
  if (cols > 0)
 | 
						|
  {
 | 
						|
    out << " <thead>";
 | 
						|
    for (unsigned int c = 0; c < cols; c++)
 | 
						|
    {
 | 
						|
      const TRecordset_column_info& ci = column_info(c);
 | 
						|
      TToken_string header(ci._name, '.');
 | 
						|
      TString str;
 | 
						|
      FOR_EACH_TOKEN(header, tok)
 | 
						|
      {
 | 
						|
        if (str.not_empty())
 | 
						|
          str << "<br/>";
 | 
						|
        str << tok;
 | 
						|
      }
 | 
						|
      out << "  <th>" << str << "</th>" << endl;
 | 
						|
    }
 | 
						|
    out << " </thead>" << endl;
 | 
						|
  }
 | 
						|
 | 
						|
  TString val;
 | 
						|
  for (bool ok = move_first(); ok; ok = move_next())
 | 
						|
  {
 | 
						|
    if (!pi.addstatus(1))
 | 
						|
      break;
 | 
						|
    out << " <tr>" << endl;
 | 
						|
    for (unsigned int c = 0; c < cols; c++)
 | 
						|
    {
 | 
						|
      const TRecordset_column_info& ci = column_info(c);
 | 
						|
      out << "  <td";
 | 
						|
      if (ci._type == _longfld || ci._type == _realfld)
 | 
						|
        out << " align=\"right\"";
 | 
						|
      out << ">";
 | 
						|
      get(c).as_string(val);
 | 
						|
      if (!val.blank())
 | 
						|
      {
 | 
						|
        val.rtrim();
 | 
						|
        out << val;
 | 
						|
      }
 | 
						|
      out << "  </td>" << endl;
 | 
						|
    }
 | 
						|
    out << " </tr>" << endl;
 | 
						|
  }
 | 
						|
  
 | 
						|
  out << "</table>" << endl;
 | 
						|
  out << "</body>" << endl;
 | 
						|
  out << "</html>" << endl;
 | 
						|
 | 
						|
  return !pi.iscancelled();
 | 
						|
}
 | 
						|
 | 
						|
bool TRecordset::save_as_silk(const char* path)
 | 
						|
{
 | 
						|
  TProgind pi(items(), TR("Esportazione in corso..."), true, true);
 | 
						|
 | 
						|
  ofstream out(path);
 | 
						|
  out << "ID;PWXL;N;E" << endl;
 | 
						|
 | 
						|
	const unsigned int cols = columns();
 | 
						|
 | 
						|
	int header = 1;
 | 
						|
	if (cols > 0)
 | 
						|
	{
 | 
						|
		// Larghezza colonne
 | 
						|
		for (unsigned int h = 0; h < cols; h++)
 | 
						|
		{
 | 
						|
			const TRecordset_column_info& ci = column_info(h);
 | 
						|
			const int w = max(ci._width, ci._name.len());
 | 
						|
			out << "F;W" << (h+1) << ' ' << (h+1) << ' ' << w << endl;
 | 
						|
		}
 | 
						|
 | 
						|
		// Intestazioni colonne
 | 
						|
		for (unsigned int c = 0; c < cols; c++)
 | 
						|
		{
 | 
						|
			const TRecordset_column_info& ci = column_info(c);
 | 
						|
			out << 'C';
 | 
						|
			if (c == 0) out << ";Y1";
 | 
						|
			out << ";X" << (c+1) << ";K\"" << ci._name << '"' << endl;
 | 
						|
		}
 | 
						|
		header++;
 | 
						|
	}
 | 
						|
 | 
						|
  TString val;
 | 
						|
  for (bool ok = move_first(); ok; ok = move_next())
 | 
						|
  {
 | 
						|
    for (unsigned int c = 0; ; c++)
 | 
						|
    {
 | 
						|
      const TVariant& var = get(c);
 | 
						|
      if (var.is_null() && c >= cols)
 | 
						|
				break;
 | 
						|
 | 
						|
      out << 'C';
 | 
						|
      if (c == 0) out << ";Y" << (current_row()+header);
 | 
						|
      if (!var.is_empty())
 | 
						|
      {
 | 
						|
        out << ";X" << (c+1) << ";K";
 | 
						|
        var.as_string(val);
 | 
						|
        switch (var.type())
 | 
						|
        {
 | 
						|
        case _alfafld:
 | 
						|
        case _datefld:
 | 
						|
	if (cols == 0 && is_numeric(val))
 | 
						|
	{
 | 
						|
	  out << val;
 | 
						|
	}
 | 
						|
	else
 | 
						|
          {
 | 
						|
            val.rtrim();
 | 
						|
            val.replace('"', '\'');
 | 
						|
            out << '"' << val << '"';
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        default:
 | 
						|
          out << val;
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      out << endl;
 | 
						|
    }
 | 
						|
    if (!pi.addstatus(1))
 | 
						|
      break;
 | 
						|
  }
 | 
						|
  out << "E" << endl;
 | 
						|
 
 | 
						|
  return !pi.iscancelled();
 | 
						|
}
 | 
						|
 | 
						|
bool TRecordset::save_as_text(const char* path)
 | 
						|
{
 | 
						|
  TProgind pi(items(), TR("Esportazione in corso..."), true, true);
 | 
						|
 | 
						|
  ofstream out(path);
 | 
						|
  TString val;
 | 
						|
 | 
						|
	const unsigned int cols = columns();
 | 
						|
  for (bool ok = move_first(); ok; ok = move_next())
 | 
						|
  {
 | 
						|
    for (unsigned int c = 0; ; c++)
 | 
						|
    {
 | 
						|
      const TVariant& var = get(c);
 | 
						|
      if (var.is_null() && c >= cols)
 | 
						|
				break;
 | 
						|
 | 
						|
      if (c > 0)
 | 
						|
        out << '\t';
 | 
						|
      if (!var.is_empty())
 | 
						|
      {
 | 
						|
        var.as_string(val);
 | 
						|
        val.rtrim();
 | 
						|
        if (val.find('\n') >= 0 || val.find('\t') >= 0)
 | 
						|
          out << unesc(val); // Evitiamo doppi separatori di campo e record
 | 
						|
        else
 | 
						|
        {
 | 
						|
          if (var.type() == _realfld || (cols == 0 && is_numeric(val)))
 | 
						|
            num_reformat(val);
 | 
						|
          out << val;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    out << endl;
 | 
						|
    if (!pi.addstatus(1))
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 
 | 
						|
  return !pi.iscancelled();
 | 
						|
}
 | 
						|
 | 
						|
bool TRecordset::save_as_campo(const char* path)
 | 
						|
{
 | 
						|
  TProgind pi(items(), TR("Esportazione in corso..."), true, true);
 | 
						|
  ofstream out(path);
 | 
						|
 | 
						|
  out << "[Head]" << endl;
 | 
						|
  out << "Version=0";
 | 
						|
 | 
						|
	const unsigned int cols = columns();
 | 
						|
  for (unsigned int c = 0; c < cols; c++)
 | 
						|
  {
 | 
						|
    const TRecordset_column_info& ci = column_info(c);
 | 
						|
    if ((c % 8) == 0)
 | 
						|
      out << endl << "Fields=";
 | 
						|
    else
 | 
						|
      out << '|';
 | 
						|
    out << ci._name;
 | 
						|
  }
 | 
						|
  out << endl << endl << "[Data]" << endl;
 | 
						|
 | 
						|
  TString val;
 | 
						|
  for (bool ok = move_first(); ok; ok = move_next())
 | 
						|
  {
 | 
						|
    for (unsigned int c = 0; ; c++)
 | 
						|
    {
 | 
						|
      const TVariant& var = get(c);
 | 
						|
      if (var.is_null() && c >= cols)
 | 
						|
				break;
 | 
						|
 | 
						|
      if (c > 0)
 | 
						|
        out << '|';
 | 
						|
      if (!var.is_empty())
 | 
						|
      {
 | 
						|
        var.as_string(val);
 | 
						|
        if (var.type() == _alfafld)
 | 
						|
        {
 | 
						|
          val.rtrim();
 | 
						|
          val.replace('|', SAFE_PIPE_CHR);  // Evitiamo doppi separatori di campo
 | 
						|
          if (val.find('\n') >= 0)          // Evitiamo doppi separatori di record  
 | 
						|
            out << unesc(val);
 | 
						|
          else
 | 
						|
            out << val;
 | 
						|
        }
 | 
						|
        else
 | 
						|
          out << val;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    out << endl;
 | 
						|
    if (!pi.addstatus(1))
 | 
						|
      break;
 | 
						|
  }
 | 
						|
  return !pi.iscancelled();
 | 
						|
}
 | 
						|
 | 
						|
bool TRecordset::save_as_dbf(const char* table, int mode)
 | 
						|
{
 | 
						|
  char volume[_MAX_DRIVE];
 | 
						|
  char dirname[_MAX_PATH];
 | 
						|
  char name[_MAX_FNAME];
 | 
						|
  char ext[_MAX_EXT];
 | 
						|
  xvt_fsys_parse_pathname (table, volume, dirname, name, ext, NULL);
 | 
						|
  
 | 
						|
  const int logicnum = table2logic(name);
 | 
						|
 
 | 
						|
  if (mode == 0x0)
 | 
						|
    mode = 0x1;
 | 
						|
  if (mode & 0x4)
 | 
						|
  {
 | 
						|
    if (logicnum >= LF_USER && *dirname == '\0')
 | 
						|
    {
 | 
						|
      TSystemisamfile isam(logicnum);
 | 
						|
      if (isam.zap() != NOERR)
 | 
						|
        return error_box(TR("Impossibile cancellare il file '%s'"), table);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      TFilename n;
 | 
						|
      n = volume; n.add(dirname); n.add(name); n.ext("*");
 | 
						|
      TString_array files; list_files(n, files);
 | 
						|
      FOR_EACH_ARRAY_ROW(files, f, row)
 | 
						|
        xvt_fsys_remove_file(*row);
 | 
						|
    }
 | 
						|
    mode = 0x1;
 | 
						|
  }
 | 
						|
 | 
						|
  TLocalisamfile* pisam = NULL;
 | 
						|
  if (logicnum >= LF_USER)
 | 
						|
  {
 | 
						|
    if (*dirname)
 | 
						|
      pisam = new TIsamtempfile(logicnum, table);
 | 
						|
    else
 | 
						|
      pisam = new TLocalisamfile(logicnum);
 | 
						|
  }
 | 
						|
  if (pisam == NULL)
 | 
						|
    return error_box("Impossibile creare il file %s", table);
 | 
						|
 | 
						|
  TLocalisamfile& isam = *pisam;
 | 
						|
 | 
						|
  TProgind pi(items(), TR("Esportazione in corso..."));
 | 
						|
  TRectype& rec = isam.curr();
 | 
						|
 | 
						|
  TString_array names;
 | 
						|
  int valid = 0;
 | 
						|
  for (unsigned int j = 0; j < columns(); j++)
 | 
						|
  {
 | 
						|
    const TVariant& var = get(j);
 | 
						|
    const TString& name = column_info(j)._name;
 | 
						|
    if (rec.exist(name))
 | 
						|
    {
 | 
						|
      names.add(name);
 | 
						|
      valid++;
 | 
						|
    }
 | 
						|
    else
 | 
						|
      names.add(EMPTY_STRING);
 | 
						|
  }
 | 
						|
 | 
						|
  bool ok = true;
 | 
						|
  for (bool go = move_first(); go; go = move_next())
 | 
						|
  {
 | 
						|
    pi.addstatus(1);
 | 
						|
    rec.zero();
 | 
						|
    FOR_EACH_ARRAY_ROW(names, j, name) if (name->not_empty())
 | 
						|
      rec.put(*name, get(j).as_string());
 | 
						|
 | 
						|
    int err = NOERR;
 | 
						|
    bool to_be_written = true;
 | 
						|
 | 
						|
    // Devo solo aggiornare parte dei campi?
 | 
						|
    if ((mode & 0x2) && valid < rec.items())
 | 
						|
    {
 | 
						|
      err = isam.read(_isequal, _lock);
 | 
						|
      if (err != NOERR)
 | 
						|
        rec.zero();
 | 
						|
      FOR_EACH_ARRAY_ROW(names, j, name) if (name->not_empty())
 | 
						|
        rec.put(*name, get(j).as_string());
 | 
						|
      if (err == NOERR)
 | 
						|
        to_be_written = isam.rewrite() != NOERR;
 | 
						|
    }
 | 
						|
    if (to_be_written)
 | 
						|
    {
 | 
						|
      err = (mode & 0x1) ? isam.write() : isam.rewrite();
 | 
						|
      if (err != NOERR && (mode & 0x3) != 0)
 | 
						|
        err = (mode & 0x1) ? isam.rewrite() : isam.write();
 | 
						|
    }
 | 
						|
    if (err != NOERR)
 | 
						|
    {
 | 
						|
      ok = error_box(FR("Errore %d durante la scrittura del record %s"), err, rec.build_key());
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return ok;
 | 
						|
}
 | 
						|
 | 
						|
bool TRecordset::save_as(const char* path, TRecordsetExportFormat fmt, int mode)
 | 
						|
{
 | 
						|
  if (fmt == fmt_unknown)
 | 
						|
  {
 | 
						|
    TString ext;
 | 
						|
    xvt_fsys_parse_pathname(path, NULL, NULL, NULL, ext.get_buffer(), NULL);
 | 
						|
    ext.lower();
 | 
						|
    if (ext == "htm" || ext == "html" || ext == "xml")
 | 
						|
      fmt = fmt_html; else
 | 
						|
    if (ext == "xls" || ext == "slk")
 | 
						|
      fmt = fmt_silk;
 | 
						|
  }
 | 
						|
  bool ok = false;
 | 
						|
  switch (fmt)
 | 
						|
  {
 | 
						|
  case fmt_html : ok = save_as_html(path);  break;
 | 
						|
  case fmt_silk : ok = save_as_silk(path);  break;
 | 
						|
  case fmt_campo: ok = save_as_campo(path); break;
 | 
						|
  case fmt_dbf  : ok = save_as_dbf(path, mode);   break;
 | 
						|
  default       : ok = save_as_text(path);  break;
 | 
						|
  }
 | 
						|
 | 
						|
  return ok;
 | 
						|
}
 | 
						|
 | 
						|
int TRecordset::find_column(const char* column_name) const
 | 
						|
{
 | 
						|
	int i;
 | 
						|
  for (i = columns()-1; i >= 0; i--)
 | 
						|
  {
 | 
						|
    const TRecordset_column_info& info = column_info(i);
 | 
						|
    if (info._name == column_name)
 | 
						|
      break;
 | 
						|
  }
 | 
						|
  return i;
 | 
						|
}
 | 
						|
 | 
						|
TVariant& TRecordset::get_tmp_var() const
 | 
						|
{
 | 
						|
  static TArray _page;      // Variants to be returned by get
 | 
						|
  static int _next_var = 0; // Index of next variant to be returned
 | 
						|
 | 
						|
  if (_next_var >= 32)
 | 
						|
    _next_var = 0;
 | 
						|
  TVariant* var = (TVariant*)_page.objptr(_next_var);
 | 
						|
  if (var == NULL)
 | 
						|
  {
 | 
						|
    var = new TVariant;
 | 
						|
    _page.add(var, _next_var);
 | 
						|
  }
 | 
						|
  _next_var++;
 | 
						|
  return *var;
 | 
						|
}
 | 
						|
 | 
						|
const TVariant& TRecordset::get(const char* column_name) const
 | 
						|
{
 | 
						|
  if (*column_name == '#')
 | 
						|
    return get_var(column_name);
 | 
						|
 | 
						|
  char* colon = (char*)strchr(column_name, ':');  //antica porcata
 | 
						|
  if (colon != NULL)
 | 
						|
  {
 | 
						|
    *colon = '\0';
 | 
						|
    const int i = find_column(column_name);
 | 
						|
    *colon = ':';
 | 
						|
    if (i >= 0)
 | 
						|
    {
 | 
						|
      const TString& str = get(i).as_string();
 | 
						|
      TString subfield; subfield << (colon+1) << '=';
 | 
						|
      int s = str.find(subfield);
 | 
						|
      if (s == 0 || (s > 0 && str[s-1] < ' '))
 | 
						|
      {
 | 
						|
        s += subfield.len();
 | 
						|
        const int e = str.find('\n', s);
 | 
						|
        TVariant& var = get_tmp_var();
 | 
						|
        var.set(str.sub(s, e));
 | 
						|
        return var;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    const int i = find_column(column_name);
 | 
						|
    if (i >= 0)
 | 
						|
      return get(i);
 | 
						|
  }
 | 
						|
 | 
						|
  return NULL_VARIANT;
 | 
						|
}
 | 
						|
 | 
						|
const TVariant& TRecordset::get_var(const char* name) const
 | 
						|
{
 | 
						|
	const TVariant* var = (const TVariant*)_var.objptr(name);
 | 
						|
 | 
						|
  // Non so che variabile sia: provo con quelle di sistema
 | 
						|
  if (var == NULL && *name == '#')
 | 
						|
  {
 | 
						|
    const TFixed_string n(name);
 | 
						|
  // Se mi accorgo che posso e voglio accedere ad un campo del recordset padre
 | 
						|
    if (_parentset != NULL && n.starts_with("#PARENT.")) 
 | 
						|
      var = &_parentset->get(name+8); // Attenzione! E' giusto usare get() e non get_var()
 | 
						|
    else
 | 
						|
  {
 | 
						|
      if (n.starts_with("#RECORD."))
 | 
						|
      {
 | 
						|
        const TFixed_string fn(name + 8);
 | 
						|
 | 
						|
        TVariant& v = get_tmp_var();
 | 
						|
        if (fn == "NUMBER")
 | 
						|
          v.set(current_row()+1); else
 | 
						|
        if (fn == "LAST")
 | 
						|
          v.set(items());
 | 
						|
        var = &v;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return var != NULL ? *var : NULL_VARIANT;
 | 
						|
}
 | 
						|
 | 
						|
bool TRecordset::set_var(const char* name, const TVariant& var, bool create)
 | 
						|
{
 | 
						|
  bool ok = false;
 | 
						|
  TVariant* old = (TVariant*)_var.objptr(name);
 | 
						|
  if (old != NULL)
 | 
						|
  {
 | 
						|
    *old = var;
 | 
						|
    ok = true;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    if (create)
 | 
						|
    {
 | 
						|
      const TFixed_string n(name);
 | 
						|
      if (n.starts_with("#PARENT.") || n.starts_with("#RECORD."))
 | 
						|
      {
 | 
						|
        // Aggiungo solo il nome alla lista: niente valore!
 | 
						|
        _varnames.add(name);
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
      _var.add(name, var);
 | 
						|
      _varnames.add(name);
 | 
						|
      }
 | 
						|
      ok = true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (ok)
 | 
						|
    requery();
 | 
						|
  return ok;
 | 
						|
}
 | 
						|
 | 
						|
bool is_var_separator(char c)
 | 
						|
{
 | 
						|
  if (isspace(c))
 | 
						|
    return true;
 | 
						|
  return strchr("<=>+-*/(,.", c) != NULL;
 | 
						|
}
 | 
						|
 | 
						|
// Cerca le variabili nel testo SQL:
 | 
						|
// Una variabile comincia per # ed e' composta da soli caratteri alfanumerici.
 | 
						|
// Prima del simbolo # e dopo il nome della variabile deve esserci un separatore o blank
 | 
						|
void TRecordset::find_and_reset_vars()
 | 
						|
{
 | 
						|
  _var.destroy();
 | 
						|
  _varnames.destroy();
 | 
						|
  
 | 
						|
  const TString& sql = query_text();
 | 
						|
  int diesis = sql.find('#'); // cerco il primo #
 | 
						|
  for ( ; diesis > 0; diesis = sql.find('#', diesis+1)) // Cerco tutti i #
 | 
						|
  {
 | 
						|
    if (is_var_separator(sql[diesis-1])) // Controllo che ci sia un separatore prima del #
 | 
						|
    {
 | 
						|
      int i = diesis+1;
 | 
						|
      for ( ; sql[i] && (isalnum(sql[i]) || strchr("@_.#", sql[i]) != NULL); i++);
 | 
						|
      if (i > diesis+1)
 | 
						|
      {
 | 
						|
        const TString& name = sql.sub(diesis, i);
 | 
						|
        set_var(name, NULL_VARIANT, true);
 | 
						|
				diesis = i;		//ricomincia a cercare variabili dopo la fine della variabile corrente;senza questa istruzione non possono funzionare cose tip #PARENT.#PARENT.33.CAMPO
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void TRecordset::parsed_text(TString& sql) const
 | 
						|
{
 | 
						|
  sql = query_text();
 | 
						|
  const bool is_isam = sql.starts_with("US");
 | 
						|
  const bool is_sql = !is_isam;
 | 
						|
  const char* apici = is_isam ? "\"" : "'";
 | 
						|
 | 
						|
  const bool vars = ((TRecordset*)this)->ask_variables(false);
 | 
						|
  if (vars) // Se ci sono variabili faccio le sostituzioni
 | 
						|
  {
 | 
						|
    const TString_array& names = variables();
 | 
						|
    TString s;
 | 
						|
    FOR_EACH_ARRAY_ROW(names, i, name) // Scandisco tutte le variabili
 | 
						|
    {
 | 
						|
      TVariant var = get_var(*name);
 | 
						|
      int pos = sql.find(*name);
 | 
						|
      for ( ; pos > 0; pos = sql.find(*name, pos+1))
 | 
						|
      {
 | 
						|
        const TString& after = sql.mid(pos+name->len());
 | 
						|
        sql.cut(pos);
 | 
						|
        
 | 
						|
        if (var.type() == _datefld)
 | 
						|
          s.format("%ld", var.as_date().date2ansi());
 | 
						|
        else
 | 
						|
        {
 | 
						|
          s = var.as_string();
 | 
						|
          if (is_sql) // Raddoppia gli apici in SQL
 | 
						|
          {
 | 
						|
            for (int i = 0; s[i]; i++)
 | 
						|
            {
 | 
						|
              if (s[i] == '\'')
 | 
						|
                s.insert("'", i++);
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
        if ((var.is_string() && s[0] != *apici && sql.right(1) != apici) || var.is_null())
 | 
						|
        {
 | 
						|
          s.insert(apici);
 | 
						|
          s << apici;
 | 
						|
        }
 | 
						|
        sql << s << after;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool ask_variable(const char* name, TVariant& var)
 | 
						|
{
 | 
						|
  TMask m(TR("Richiesta variabile"), 1, 52, 4);
 | 
						|
  m.add_static(-1, 0, name, 1, 0);
 | 
						|
  m.add_string(101, 0, "", 1, 1, 80, "", 50);
 | 
						|
  m.add_button(DLG_OK, 0, "", -12, -1, 10, 2);
 | 
						|
  m.add_button(DLG_CANCEL, 0, "", -22, -1, 10, 2);
 | 
						|
  m.set(101, var.as_string());
 | 
						|
  const bool ok = m.run() == K_ENTER;
 | 
						|
  if (ok)
 | 
						|
  {
 | 
						|
    const TString& str = m.get(101);
 | 
						|
    if (is_numeric(str))
 | 
						|
      var = real(str);
 | 
						|
    else
 | 
						|
      var = str;
 | 
						|
  }
 | 
						|
  return ok;
 | 
						|
}
 | 
						|
 | 
						|
bool TRecordset::ask_variables(bool all)
 | 
						|
{
 | 
						|
  const bool ok = variables().items() > 0;
 | 
						|
  if (ok) // Se ci sono variabili faccio le sostituzioni
 | 
						|
  {
 | 
						|
    FOR_EACH_ARRAY_ROW(_varnames, i, name)
 | 
						|
    {
 | 
						|
      TVariant var = get_var(*name);
 | 
						|
      if (var.is_null() || all) 
 | 
						|
      {
 | 
						|
        ask_variable(*name, var);
 | 
						|
        if (var.is_null())
 | 
						|
          var.set(""); // Mi serve assolutamente un valore!
 | 
						|
        set_var(*name, var);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return ok;
 | 
						|
}
 | 
						|
 | 
						|
const TString& TRecordset::driver_version() const
 | 
						|
{ return EMPTY_STRING; }
 | 
						|
 | 
						|
TRecordset::TRecordset() : _parentset(NULL)
 | 
						|
{ }
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
// Utility
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
 | 
						|
static void sort_files(TString_array& files)
 | 
						|
{
 | 
						|
  TFilename path;
 | 
						|
 | 
						|
  // Trasforma i path completi in nomi senza estensione
 | 
						|
  FOR_EACH_ARRAY_ROW(files, i, row)
 | 
						|
  {
 | 
						|
    path = *row; 
 | 
						|
    path = path.name(); 
 | 
						|
    path.ext("");
 | 
						|
    path.lower();
 | 
						|
    *row = path;
 | 
						|
  }
 | 
						|
  files.sort();  // Ordina alfabeticamente
 | 
						|
 | 
						|
  // Rimuove i files doppi proveninenti da Campo e Custom
 | 
						|
  for (int j = files.last(); j > 0; j--)
 | 
						|
  {
 | 
						|
    if (files.row(j) == files.row(j-1))
 | 
						|
      files.destroy(j);
 | 
						|
  }
 | 
						|
  files.pack();
 | 
						|
}
 | 
						|
 | 
						|
static bool get_xml_attr(const TString& line, const char* attr, TString& value)
 | 
						|
{
 | 
						|
  TString80 str; str << ' ' << attr << "=\"";
 | 
						|
  const int pos = line.find(str);
 | 
						|
  if (pos >= 0)
 | 
						|
  {
 | 
						|
    const int apicia = line.find('"', pos)+1;
 | 
						|
    const int apicic = line.find('"', apicia);
 | 
						|
    if (apicic > apicia)
 | 
						|
    {
 | 
						|
      value = line.sub(apicia, apicic);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
	value.cut(0);
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
static bool get_xml_child(const TString& line, const char* tag, TString& value)
 | 
						|
{
 | 
						|
  TString80 str; str << '<' << tag << '>';
 | 
						|
  const int pos = line.find(str);
 | 
						|
  if (pos >= 0)
 | 
						|
  {
 | 
						|
    const int apicia = line.find('>', pos)+1;
 | 
						|
    const int apicic = line.find('<', apicia);
 | 
						|
    if (apicic > apicia)
 | 
						|
    {
 | 
						|
      value = line.sub(apicia, apicic);
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
	value.cut(0);
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
bool list_custom_files(const char* ext, const char* classe, TString_array& files)
 | 
						|
{
 | 
						|
  TString_array lista;
 | 
						|
  TFilename path;
 | 
						|
  TWait_cursor hourglass;
 | 
						|
	TFilename name = path.name();
 | 
						|
	const bool wild = (name.find('*') >= 0) || (name.find('?') >= 0);
 | 
						|
 | 
						|
	if (!wild)
 | 
						|
		name = "*";
 | 
						|
 | 
						|
  // Leggo i files in custom
 | 
						|
  if (main_app().has_module(RSAUT))
 | 
						|
  {
 | 
						|
    TFilename custom = firm2dir(-1);
 | 
						|
    custom.add("custom");
 | 
						|
    if (!custom.exist())
 | 
						|
      xvt_fsys_mkdir(custom);
 | 
						|
 | 
						|
    path = custom;
 | 
						|
    path.add(name);
 | 
						|
    path.ext(ext);
 | 
						|
	}
 | 
						|
  list_files(path, lista);
 | 
						|
  path = name; path.ext(ext);  // Leggo i files in campo
 | 
						|
	list_files(path, lista);
 | 
						|
	sort_files(lista);           // Ordino i files e rimuovo i doppioni
 | 
						|
 | 
						|
  TString8 acqua;
 | 
						|
  TString stringona, desc;
 | 
						|
 | 
						|
  FOR_EACH_ARRAY_ROW(lista, i, row)
 | 
						|
  {
 | 
						|
    path = *row; path.ext(ext);
 | 
						|
    bool ok = path.custom_path();
 | 
						|
    if (ok)
 | 
						|
    { 
 | 
						|
      TScanner scan(path);
 | 
						|
      stringona.cut(0);
 | 
						|
      for (int i = 0; i < 4 && scan.good(); i++) // Leggo solo le prime righe
 | 
						|
        stringona << scan.line();
 | 
						|
  
 | 
						|
      get_xml_attr(stringona, "class", acqua);
 | 
						|
      if (classe && *classe)
 | 
						|
        ok = acqua == classe;
 | 
						|
   
 | 
						|
      if (ok)
 | 
						|
      {
 | 
						|
        get_xml_child(stringona, "description", desc);
 | 
						|
        TToken_string* riga = new TToken_string;
 | 
						|
        riga->add(*row);
 | 
						|
        riga->add(acqua);
 | 
						|
        riga->add(desc); 
 | 
						|
        riga->add(path.find("custom") > 0 ? "X" : "");
 | 
						|
        files.add(riga);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return !files.empty();
 | 
						|
}
 | 
						|
 | 
						|
bool select_custom_file(TFilename& path, const char* ext, const char* classe)
 | 
						|
{
 | 
						|
  TArray_sheet sheet(-1, -1, 78, 20, TR("Selezione"), HR("Nome@16|Classe@8|Descrizione@50|Custom"));
 | 
						|
  TString_array& files = sheet.rows_array();
 | 
						|
  TFilename first = path.name(); first.ext(""); // Riga su cui posizionarsi
 | 
						|
  bool ok = list_custom_files(ext, classe, files);
 | 
						|
 | 
						|
  if (ok)
 | 
						|
  {
 | 
						|
    if (first.full()) // Cerco la prima riga da selezionare se possibile
 | 
						|
    {
 | 
						|
      FOR_EACH_ARRAY_ROW(files, i, row)
 | 
						|
      {
 | 
						|
        if (first.compare(row->get(0), -1, true) == 0) // Ho trovato la selezione corrente
 | 
						|
        {
 | 
						|
          sheet.select(i);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    ok = sheet.run() == K_ENTER;
 | 
						|
    if (ok)
 | 
						|
    {
 | 
						|
      path = sheet.row(-1).get(0);
 | 
						|
      path.ext(ext);
 | 
						|
      ok = path.custom_path();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return ok;
 | 
						|
}
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
// TCursor_parser
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
 | 
						|
class TCursor_parser
 | 
						|
{
 | 
						|
  istream& _instr;
 | 
						|
  TArray& _column;
 | 
						|
 | 
						|
  TString _pushed;
 | 
						|
  TString _token;
 | 
						|
  
 | 
						|
  TRelation* _relation;
 | 
						|
  TCursor* _cursor;
 | 
						|
 | 
						|
protected:
 | 
						|
  const TString& pop();
 | 
						|
  const TString& line();
 | 
						|
  void push();
 | 
						|
  void add_column_info(const char* table, const TRectype& rec);
 | 
						|
 | 
						|
  void parse_sortexpr(TToken_string& se);
 | 
						|
  void parse_filter(TToken_string& filter);
 | 
						|
  void parse_select(TToken_string& filter);
 | 
						|
  void parse_region(TRectype& rec);
 | 
						|
  void parse_join_param(TRelation* rel, const TString& j, int to);
 | 
						|
  void parse_join();
 | 
						|
  void parse_sortedjoin();
 | 
						|
 | 
						|
public:
 | 
						|
  TRelation* get_relation() { return _relation; }
 | 
						|
  TCursor* get_cursor() { return _cursor; }
 | 
						|
 | 
						|
  TCursor_parser(istream& instr, TArray& column);
 | 
						|
};
 | 
						|
 | 
						|
const TString& TCursor_parser::pop()
 | 
						|
{
 | 
						|
  if (_pushed.not_empty())
 | 
						|
  {
 | 
						|
    _token = _pushed;
 | 
						|
    _pushed.cut(0);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    _token.cut(0);  
 | 
						|
    eatwhite(_instr);
 | 
						|
    char instring = '\0';
 | 
						|
 | 
						|
    while (_instr)
 | 
						|
    {
 | 
						|
      char c; 
 | 
						|
      _instr.get(c);
 | 
						|
      if (c == EOF)
 | 
						|
        break;
 | 
						|
      if (instring > ' ') // Sono dentro ad una stringa
 | 
						|
      {
 | 
						|
        if (c == '\n')
 | 
						|
          break;
 | 
						|
        if (c == instring)
 | 
						|
          instring = '\0';
 | 
						|
        _token << c;
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        if (c == '"' || c == '\'')
 | 
						|
        {
 | 
						|
          instring = c;
 | 
						|
          _token << c;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
          if (isspace(c))
 | 
						|
            break;
 | 
						|
          _token << c;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return _token;
 | 
						|
}
 | 
						|
 | 
						|
const TString& TCursor_parser::line()
 | 
						|
{
 | 
						|
  if (_pushed.not_empty())
 | 
						|
    return pop();
 | 
						|
  char* buff = _token.get_buffer(256);
 | 
						|
  _instr.getline(buff, _token.size());
 | 
						|
  return _token;
 | 
						|
}
 | 
						|
 | 
						|
void TCursor_parser::push()
 | 
						|
{
 | 
						|
  CHECK(_pushed.empty(), "Repushing?");
 | 
						|
  _pushed = _token;
 | 
						|
}
 | 
						|
 | 
						|
void TCursor_parser::add_column_info(const char* table, const TRectype& rec)
 | 
						|
{
 | 
						|
  for (int i = 0; i < rec.items(); i++)
 | 
						|
  {
 | 
						|
    TRecordset_column_info* info = new TRecordset_column_info;
 | 
						|
    const char* name = rec.fieldname(i);
 | 
						|
    info->_name << table << '.' << name;
 | 
						|
    info->_type = rec.type(name);
 | 
						|
    switch (info->_type)
 | 
						|
    {
 | 
						|
    case _datefld: info->_width = 10; break;
 | 
						|
    case _memofld: info->_width = 50; break;
 | 
						|
    default      : info->_width = rec.length(name); break;
 | 
						|
    }
 | 
						|
    _column.add(info);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void TCursor_parser::parse_sortexpr(TToken_string& se)
 | 
						|
{
 | 
						|
  const char sep = se.separator();
 | 
						|
  se.separator(' ');
 | 
						|
  _instr.getline(se.get_buffer(), se.size());
 | 
						|
  se.strip_double_spaces();
 | 
						|
  se.replace(' ', sep);
 | 
						|
  se.separator(sep);
 | 
						|
 | 
						|
  // Trasforma i nomi dei files in numeri se necessario
 | 
						|
  if (se.find('.') > 0)
 | 
						|
  {
 | 
						|
    TToken_string fld(16, '.');
 | 
						|
    TString16 name;
 | 
						|
    for (int i = 0; se.get(i, fld); i++)
 | 
						|
    {
 | 
						|
      if (!isdigit(fld[0]) && fld.find('.') > 0)
 | 
						|
      {
 | 
						|
        fld.get(0, name);
 | 
						|
        const int num = ::table2logic(name);
 | 
						|
        if (num != 0)
 | 
						|
        {
 | 
						|
          fld.add(num, 0);
 | 
						|
          se.add(fld, i);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void TCursor_parser::parse_filter(TToken_string& filter)
 | 
						|
{
 | 
						|
  const TString& str = pop();
 | 
						|
  while (str.find('=') > 0)
 | 
						|
  {
 | 
						|
    filter.add(str);
 | 
						|
    pop();
 | 
						|
  }
 | 
						|
  push();
 | 
						|
}
 | 
						|
 | 
						|
void TCursor_parser::parse_select(TToken_string& filter)
 | 
						|
{
 | 
						|
  filter = line();
 | 
						|
  filter.trim();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void TCursor_parser::parse_region(TRectype& rec)
 | 
						|
{
 | 
						|
  TString16 field;
 | 
						|
  TString80 value;
 | 
						|
  while (true)
 | 
						|
  {
 | 
						|
    const TString& ass = pop();
 | 
						|
    const int equal = ass.find('=');
 | 
						|
    if (equal > 0)
 | 
						|
    {
 | 
						|
      field = ass.left(equal);
 | 
						|
      value = ass.mid(equal+1);
 | 
						|
      value.trim();
 | 
						|
      if (value[0] == '"' || value[0] == '\'')
 | 
						|
      {
 | 
						|
        value.rtrim(1);
 | 
						|
        value.ltrim(1);
 | 
						|
      }
 | 
						|
      rec.put(field, value);       
 | 
						|
    }
 | 
						|
    else
 | 
						|
      break;
 | 
						|
  }
 | 
						|
  push();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void TCursor_parser::parse_join_param(TRelation* rel, const TString& j, int to)
 | 
						|
{
 | 
						|
  int key = 1;
 | 
						|
  const TString& tok = pop();
 | 
						|
  if (tok.starts_with("KE"))
 | 
						|
  {
 | 
						|
    pop();
 | 
						|
    key = atoi(tok);
 | 
						|
  }
 | 
						|
  else 
 | 
						|
    push();
 | 
						|
 | 
						|
  int alias = 0;
 | 
						|
  pop();
 | 
						|
  if (tok.starts_with("AL"))
 | 
						|
  {
 | 
						|
    pop();
 | 
						|
    alias = atoi(tok);
 | 
						|
  }
 | 
						|
  else 
 | 
						|
    push();
 | 
						|
 | 
						|
  TToken_string exp(80);
 | 
						|
  pop();
 | 
						|
  if (tok == "INTO")
 | 
						|
  {
 | 
						|
    parse_filter(exp);
 | 
						|
  }
 | 
						|
  if (exp.empty())
 | 
						|
    yesnofatal_box("JOIN senza espressioni INTO");
 | 
						|
 | 
						|
  const int logicnum = table2logic(j);
 | 
						|
  switch (logicnum)
 | 
						|
  {
 | 
						|
  case LF_TABGEN:
 | 
						|
  case LF_TABCOM:
 | 
						|
  case LF_TAB:
 | 
						|
  case LF_TABMOD:
 | 
						|
    rel->add(j, exp, key, to, alias);          // join table
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    rel->add(logicnum, exp, key, to, alias);   // join file
 | 
						|
    break;
 | 
						|
  } 
 | 
						|
 | 
						|
  TString16 tabname;
 | 
						|
  if (alias > 0)
 | 
						|
    tabname << alias << '@'; 
 | 
						|
  else
 | 
						|
    tabname = j;
 | 
						|
  const TRectype& rec = rel->curr(logicnum);
 | 
						|
  add_column_info(tabname, rec);    
 | 
						|
}
 | 
						|
 | 
						|
void TCursor_parser::parse_join()
 | 
						|
{
 | 
						|
  const TString j = pop();           // File or table
 | 
						|
 | 
						|
  int to = 0;
 | 
						|
  const TString& tok = pop();
 | 
						|
  if (tok == "TO")         // TO keyword
 | 
						|
  {
 | 
						|
    pop();
 | 
						|
    to = table2logic(tok);
 | 
						|
  }
 | 
						|
  else 
 | 
						|
    push();
 | 
						|
  
 | 
						|
  parse_join_param(_relation, j, to);
 | 
						|
}
 | 
						|
 | 
						|
void TCursor_parser::parse_sortedjoin()
 | 
						|
{
 | 
						|
  TToken_string filter,sortexp;
 | 
						|
  const TString j = pop();           // File or table
 | 
						|
  const TString& tok = pop(); 
 | 
						|
  if (tok == "BY" )
 | 
						|
    parse_sortexpr(sortexp);
 | 
						|
  else 
 | 
						|
    push();
 | 
						|
 
 | 
						|
  pop();
 | 
						|
  if (tok.starts_with("FI") || tok.starts_with("SE")) 
 | 
						|
  {
 | 
						|
    parse_select(filter);
 | 
						|
  } 
 | 
						|
  else 
 | 
						|
    push();
 | 
						|
  
 | 
						|
  TRelation* sortrel = new TRelation(table2logic(j)); 
 | 
						|
  while (true)
 | 
						|
  {
 | 
						|
    pop();
 | 
						|
    if (tok.empty() || tok.starts_with("JO"))
 | 
						|
    {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    if (tok.starts_with("US"))         // USING keyword
 | 
						|
    {
 | 
						|
      const TString subj = pop();      // File or table
 | 
						|
      parse_join_param(sortrel, subj, table2logic(j));
 | 
						|
    } 
 | 
						|
  } 
 | 
						|
 | 
						|
  int to = 0;
 | 
						|
  pop();
 | 
						|
  if (tok == "TO")         // TO keyword
 | 
						|
  {
 | 
						|
    pop();
 | 
						|
    to = table2logic(tok);
 | 
						|
  }
 | 
						|
  else 
 | 
						|
    push();
 | 
						|
 | 
						|
  int key = 1;
 | 
						|
  pop();
 | 
						|
  if (tok.starts_with("KE"))
 | 
						|
  {
 | 
						|
    pop();
 | 
						|
    key = atoi(tok);
 | 
						|
  }
 | 
						|
  else 
 | 
						|
    push();
 | 
						|
 | 
						|
  int alias = 0;
 | 
						|
  pop();
 | 
						|
  if (tok.starts_with("AL"))
 | 
						|
  {
 | 
						|
    pop();
 | 
						|
    alias = atoi(tok);
 | 
						|
  }
 | 
						|
  else 
 | 
						|
    push();
 | 
						|
 | 
						|
  TToken_string exp(80);
 | 
						|
  if (pop() == "INTO")
 | 
						|
  {
 | 
						|
    pop();
 | 
						|
    while (tok.find('=') > 0)
 | 
						|
    {
 | 
						|
      exp.add(tok);
 | 
						|
      pop();
 | 
						|
    }
 | 
						|
  }
 | 
						|
  push();
 | 
						|
 | 
						|
  TSortedfile *sf= new TSortedfile(atoi(j),sortrel,sortexp,filter,key);
 | 
						|
  _relation->add((TLocalisamfile *)sf, exp, key, to, alias, false);         // join table
 | 
						|
 | 
						|
  TString16 tabname = j;
 | 
						|
  if (alias > 0)
 | 
						|
    tabname.cut(0) << alias << '@'; 
 | 
						|
  add_column_info(tabname, sf->curr());    
 | 
						|
}
 | 
						|
 | 
						|
TCursor_parser::TCursor_parser(istream& instr, TArray& col) 
 | 
						|
                : _instr(instr), _column(col), _relation(NULL), _cursor(NULL)
 | 
						|
{
 | 
						|
  _column.destroy();
 | 
						|
  const TString& tok = pop();
 | 
						|
  if (!tok.starts_with("US"))
 | 
						|
    push();
 | 
						|
 | 
						|
  pop();
 | 
						|
  if (tok.blank())
 | 
						|
    return;
 | 
						|
 | 
						|
  TString table(tok); table.upper();
 | 
						|
  if (table.ends_with(".DBF"))
 | 
						|
  {
 | 
						|
    TFilename dbf = table;
 | 
						|
    if (table[0] == '$' || table[0] == '%')
 | 
						|
    {
 | 
						|
      if (table[0] == '%')
 | 
						|
        dbf = firm2dir(0);
 | 
						|
      else
 | 
						|
        dbf = firm2dir(prefix().get_codditta());
 | 
						|
      dbf.add(table.mid(1));
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      if (dbf.is_relative_path())
 | 
						|
      {
 | 
						|
        if (!dbf.custom_path())
 | 
						|
        {
 | 
						|
          dbf.tempdir();
 | 
						|
          dbf.add(table);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    dbf.lower();
 | 
						|
    TExternisamfile* eif = new TExternisamfile(dbf, true);
 | 
						|
    _relation = new TRelation(eif);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    int logicnum = table2logic(tok);
 | 
						|
    if (logicnum == LF_MAG && tok == "MAG")  // Faccio prevalere la tabella MAG sul file MAG
 | 
						|
      logicnum = LF_TAB;
 | 
						|
 | 
						|
    if (logicnum == LF_TAB || logicnum == LF_TABCOM || logicnum == LF_TABMOD)
 | 
						|
      _relation = new TRelation(tok);
 | 
						|
    else
 | 
						|
      _relation = new TRelation(logicnum);
 | 
						|
  }
 | 
						|
  pop();
 | 
						|
	
 | 
						|
  add_column_info(table, _relation->curr());
 | 
						|
 | 
						|
  int key = 1;  // key number
 | 
						|
  if (tok.starts_with("KE"))
 | 
						|
  {
 | 
						|
    pop();
 | 
						|
    key = atoi(tok);
 | 
						|
  }
 | 
						|
  else
 | 
						|
    push();
 | 
						|
 | 
						|
  pop();
 | 
						|
  
 | 
						|
	TToken_string filter;
 | 
						|
 | 
						|
	if (tok.starts_with("FI") || tok.starts_with("SE"))
 | 
						|
    parse_select(filter);
 | 
						|
  else
 | 
						|
    push();
 | 
						|
 | 
						|
  pop();
 | 
						|
  if (tok.starts_with("BY"))  // "sort BY": user-defined sort
 | 
						|
  { 
 | 
						|
    TToken_string ordexpr(256);
 | 
						|
    parse_sortexpr(ordexpr);
 | 
						|
    _cursor = new TSorted_cursor(_relation, ordexpr, "", key);
 | 
						|
  }
 | 
						|
  else
 | 
						|
    push();
 | 
						|
 | 
						|
 | 
						|
  if (_cursor == NULL)
 | 
						|
    _cursor = new TCursor(_relation, "", key);
 | 
						|
 | 
						|
  _relation->lfile().zero();  // Azzera correttamente tabelle normali e di modulo!
 | 
						|
  TRectype rec_start(_relation->curr());
 | 
						|
  TRectype rec_stop(rec_start);
 | 
						|
 | 
						|
  TString_array fields;
 | 
						|
	pop();
 | 
						|
	while (tok.starts_with("DI"))
 | 
						|
  {
 | 
						|
		pop();
 | 
						|
		pop();
 | 
						|
		TString field;
 | 
						|
 | 
						|
		if (tok.find(".") < 0)
 | 
						|
			field <<  table << ".";
 | 
						|
		field << tok;
 | 
						|
		fields.add(field);
 | 
						|
		pop();
 | 
						|
  }
 | 
						|
	push();
 | 
						|
  while (true)
 | 
						|
  {
 | 
						|
    pop();
 | 
						|
    if (tok.starts_with("FR"))
 | 
						|
      parse_region(rec_start); else
 | 
						|
    if (tok.starts_with("TO"))
 | 
						|
      parse_region(rec_stop); else
 | 
						|
    if (tok.starts_with("JO"))
 | 
						|
      parse_join(); else
 | 
						|
    if (tok.starts_with("SO"))
 | 
						|
      parse_sortedjoin(); 
 | 
						|
    else
 | 
						|
      break;
 | 
						|
  }
 | 
						|
  push(); 
 | 
						|
 | 
						|
  if (!rec_start.empty() || !rec_stop.empty())
 | 
						|
    _cursor->setregion(rec_start, rec_stop);
 | 
						|
	if (!filter.empty())
 | 
						|
		_cursor->setfilter(filter);
 | 
						|
  if (fields.items() > 0)
 | 
						|
  {
 | 
						|
    FOR_EACH_ARRAY_ITEM_BACK(_column, i, obj)
 | 
						|
    {
 | 
						|
      TRecordset_column_info* info = (TRecordset_column_info*)obj;
 | 
						|
			
 | 
						|
			if (fields.find(info->_name) == -1)
 | 
						|
				_column.destroy(i, true);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (_relation->items() == 0) // Non ci sono anche tabelle collegate
 | 
						|
  {
 | 
						|
    FOR_EACH_ARRAY_ITEM(_column, i, obj)
 | 
						|
    {
 | 
						|
      TRecordset_column_info* info = (TRecordset_column_info*)obj;
 | 
						|
      const int arrow = info->_name.find('.');
 | 
						|
      if (arrow > 0)
 | 
						|
        info->_name = info->_name.mid(arrow+1);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
// TISAM_recordset
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
 | 
						|
const TVariant& TISAM_recordset::get_field(int logic, const char* fldname) const
 | 
						|
{
 | 
						|
  const int idx = relation()->log2ind(logic);
 | 
						|
  if (idx < 0)
 | 
						|
    return NULL_VARIANT;
 | 
						|
 | 
						|
  TString80 name = fldname;
 | 
						|
  TString16 subfield;
 | 
						|
  int from = 1, to = 0;
 | 
						|
 | 
						|
  const int open_bracket = name.find('[');
 | 
						|
  if (open_bracket > 0)
 | 
						|
  {
 | 
						|
    TToken_string range(name.mid(open_bracket+1), ',');
 | 
						|
    from = range.get_int();
 | 
						|
    to   = range.get_int();
 | 
						|
    name.cut(open_bracket);
 | 
						|
  }
 | 
						|
 | 
						|
  const int colon = name.find(':');
 | 
						|
 | 
						|
  if (colon > 0)
 | 
						|
  {
 | 
						|
    subfield = name.mid(colon+1);
 | 
						|
    name.cut(colon);
 | 
						|
  }
 | 
						|
 | 
						|
  const TRectype& rec = relation()->file(idx).curr();
 | 
						|
  const TFieldtypes ft = rec.type(name);
 | 
						|
 | 
						|
  if (ft == _nullfld)
 | 
						|
  {
 | 
						|
    if (logic == LF_DOC) // Proviamo la magia
 | 
						|
    {
 | 
						|
      subfield = name;
 | 
						|
      name = "G1";
 | 
						|
    }
 | 
						|
    else
 | 
						|
      return NULL_VARIANT;
 | 
						|
  }
 | 
						|
 | 
						|
  TVariant& var = get_tmp_var();
 | 
						|
  switch (ft)
 | 
						|
  {
 | 
						|
  case _datefld: var.set(rec.get_date(name)); break;
 | 
						|
  case _realfld: var.set(rec.get_real(name)); break;
 | 
						|
  case _intfld : 
 | 
						|
  case _longfld:
 | 
						|
  case _wordfld: var.set(rec.get_long(name)); break;
 | 
						|
  default      : var.set(rec.get(name)); break;
 | 
						|
  }
 | 
						|
 | 
						|
  if (subfield.not_empty())
 | 
						|
  {
 | 
						|
    subfield << '=';
 | 
						|
    const TString& str = var.as_string();
 | 
						|
    int s = str.find(subfield);
 | 
						|
    if (s == 0 || (s > 0 && str[s-1] < ' '))
 | 
						|
    {
 | 
						|
      s += subfield.len();
 | 
						|
      const int e = str.find('\n', s);
 | 
						|
      var.set(str.sub(s, e));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (to >= from)
 | 
						|
    var.set(var.as_string().sub(from-1, to));
 | 
						|
 | 
						|
  return var;
 | 
						|
}
 | 
						|
 | 
						|
const TVariant& TISAM_recordset::get(size_t c) const
 | 
						|
{
 | 
						|
  const TRecordset_column_info* info = (const TRecordset_column_info*)_column.objptr(c);
 | 
						|
  if (info != NULL)
 | 
						|
  {
 | 
						|
    int logic = 0;
 | 
						|
    const char* field = info->_name;
 | 
						|
    const int dot = info->_name.find('.');
 | 
						|
    if (dot > 0)
 | 
						|
    {
 | 
						|
      logic = table2logic(info->_name.left(dot));
 | 
						|
      field += dot+1;
 | 
						|
    }
 | 
						|
    return get_field(logic, field);
 | 
						|
  }
 | 
						|
  return NULL_VARIANT;
 | 
						|
}
 | 
						|
 | 
						|
const TVariant& TISAM_recordset::get(const char* name) const
 | 
						|
{
 | 
						|
  if (*name == '#')
 | 
						|
    return get_var(name);
 | 
						|
 | 
						|
  const TFixed_string fldname(name);
 | 
						|
  
 | 
						|
  int table_end = fldname.find('.');
 | 
						|
  int field_start = table_end+1;
 | 
						|
  if (table_end < 0)
 | 
						|
  {
 | 
						|
    table_end = fldname.find('-');
 | 
						|
    if (table_end > 0)
 | 
						|
      field_start = table_end+2;
 | 
						|
  }
 | 
						|
  int logic = 0;
 | 
						|
  const char* field = name;
 | 
						|
  if (table_end > 0)
 | 
						|
  {
 | 
						|
    const TString& table = fldname.left(table_end);
 | 
						|
    logic = table2logic(table);
 | 
						|
    field += field_start;
 | 
						|
  }
 | 
						|
  return get_field(logic, field);
 | 
						|
}
 | 
						|
 | 
						|
const TRecordset_column_info& TISAM_recordset::column_info(size_t i) const
 | 
						|
{
 | 
						|
  return (const TRecordset_column_info&)_column[i];
 | 
						|
}
 | 
						|
 | 
						|
TCursor* TISAM_recordset::cursor() const
 | 
						|
{
 | 
						|
  if (_cursor == NULL && _use.full())
 | 
						|
  {
 | 
						|
    TString use; parsed_text(use);
 | 
						|
    TPerformance_profiler prof("ISAM query");
 | 
						|
    TISAM_recordset* my = (TISAM_recordset*)this;
 | 
						|
#ifdef LINUX
 | 
						|
		string s(use.get_buffer());
 | 
						|
    istringstream instr(s);
 | 
						|
#else
 | 
						|
    istrstream instr(use.get_buffer(), use.len()+1);  //"barata" x aggiungere il carattere finale
 | 
						|
#endif
 | 
						|
    TCursor_parser parser(instr, my->_column);
 | 
						|
 | 
						|
    my->_relation = parser.get_relation();
 | 
						|
    my->_cursor = parser.get_cursor();
 | 
						|
 | 
						|
    if (_cursor != NULL)
 | 
						|
    {
 | 
						|
      set_custom_filter(*_cursor);
 | 
						|
      const TRecnotype items = _cursor->items();
 | 
						|
 | 
						|
      _cursor->freeze();
 | 
						|
			if (items > 0)
 | 
						|
				*_cursor = 0L;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return _cursor;
 | 
						|
}
 | 
						|
 | 
						|
TRelation* TISAM_recordset::relation() const
 | 
						|
{
 | 
						|
  cursor();
 | 
						|
  return _relation;
 | 
						|
}
 | 
						|
 | 
						|
TRecnotype TISAM_recordset::current_row() const
 | 
						|
{
 | 
						|
  TCursor* c = cursor();
 | 
						|
  return c != NULL ? c->pos() : -1;
 | 
						|
}
 | 
						|
 | 
						|
TRecnotype TISAM_recordset::items() const 
 | 
						|
{
 | 
						|
  TCursor* c = cursor();
 | 
						|
  return c != NULL ? c->items() : 0;
 | 
						|
}
 | 
						|
 | 
						|
unsigned int TISAM_recordset::columns() const 
 | 
						|
{ 
 | 
						|
  cursor();
 | 
						|
  return _column.items(); 
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool TISAM_recordset::move_to(TRecnotype pos)
 | 
						|
{
 | 
						|
  TCursor* c = cursor();
 | 
						|
  bool ok = c != NULL && pos >= 0;
 | 
						|
  if (ok)
 | 
						|
  {
 | 
						|
    *c = pos;
 | 
						|
    ok = pos >= 0 && pos < c->items();
 | 
						|
  }
 | 
						|
  return ok;
 | 
						|
}
 | 
						|
 | 
						|
void TISAM_recordset::reset()
 | 
						|
{
 | 
						|
  _column.destroy();
 | 
						|
  requery();
 | 
						|
}
 | 
						|
 | 
						|
void TISAM_recordset::requery()
 | 
						|
{
 | 
						|
  if (_cursor != NULL)
 | 
						|
  {
 | 
						|
    delete _cursor; 
 | 
						|
    _cursor = NULL;
 | 
						|
  }
 | 
						|
  if (_relation != NULL)
 | 
						|
  {
 | 
						|
    delete _relation; 
 | 
						|
    _relation = NULL;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void TISAM_recordset::set(const char* use)
 | 
						|
{
 | 
						|
  reset();
 | 
						|
  _use = use;
 | 
						|
  find_and_reset_vars(); 
 | 
						|
}
 | 
						|
 | 
						|
const TString& TISAM_recordset::driver_version() const
 | 
						|
{
 | 
						|
  TString& tmp = get_tmp_string(20);
 | 
						|
  DB_version(tmp.get_buffer(), tmp.size());
 | 
						|
  return tmp;
 | 
						|
}
 | 
						|
 | 
						|
TISAM_recordset::TISAM_recordset(const char* use) 
 | 
						|
               : _relation(NULL), _cursor(NULL)
 | 
						|
{ 
 | 
						|
  set(use); 
 | 
						|
}
 | 
						|
 | 
						|
TISAM_recordset::~TISAM_recordset()
 | 
						|
{
 | 
						|
  requery();
 | 
						|
}
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
// TRecordset_sheet
 | 
						|
///////////////////////////////////////////////////////////
 | 
						|
 | 
						|
void TRecordset_sheet::get_row(long r, TToken_string& row)
 | 
						|
{
 | 
						|
  row.separator('\t');
 | 
						|
  row.cut(0);
 | 
						|
  if (_query.move_to(r))
 | 
						|
  {
 | 
						|
    TString str;
 | 
						|
    for (unsigned int c = 0; c < _query.columns(); c++)
 | 
						|
    {
 | 
						|
      _query.get(c).as_string(str);
 | 
						|
      row.add(str);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
long TRecordset_sheet::get_items() const
 | 
						|
{
 | 
						|
  return _query.items();
 | 
						|
}
 | 
						|
 | 
						|
TRecordset_sheet::TRecordset_sheet(TRecordset& query, const char* title, byte buttons) 
 | 
						|
                : TSheet(-1, -1, -2, -4, title, query.sheet_head(), buttons), _query(query)
 | 
						|
{
 | 
						|
}
 |