git-svn-id: svn://10.65.10.50/branches/R_10_00@23139 c028cbd2-c16b-5b4b-a496-9718f37d4682
1694 lines
37 KiB
C++
Executable File
1694 lines
37 KiB
C++
Executable File
#include "../xvaga/incstr.h"
|
|
|
|
#include <applicat.h>
|
|
#include <codeb.h>
|
|
#include <colors.h>
|
|
#include <dongle.h>
|
|
#include <expr.h>
|
|
#include <progind.h>
|
|
#include <recarray.h>
|
|
#include <recset.h>
|
|
#include <sheet.h>
|
|
#include <utility.h>
|
|
#include <xml.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);
|
|
switch (ci._type)
|
|
{
|
|
case _boolfld: head << 'C'; break;
|
|
case _wordfld:
|
|
case _intfld:
|
|
case _longfld: head << 'R'; break;
|
|
case _datefld: head << 'D'; break;
|
|
case _realfld: head << (ci._width > 7 ? 'V' : 'R'); break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
ofstream out(path);
|
|
if (out.fail())
|
|
return false;
|
|
|
|
TProgress_monitor pi(items(), TR("Esportazione in corso..."), true);
|
|
|
|
out << "<html>" << endl;
|
|
save_html_head(out, main_app().title());
|
|
out << "<body>" << endl;
|
|
|
|
/* More annoyng than useful
|
|
TString qry; parsed_text(qry);
|
|
if (qry.full())
|
|
{
|
|
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\">";
|
|
out << " <caption>" << main_app().title() << "</caption>" << endl;
|
|
|
|
const unsigned int cols = columns();
|
|
TAttributes attr;
|
|
|
|
if (cols > 0)
|
|
{
|
|
out << " <thead>" << endl;
|
|
for (unsigned int c = 0; c < cols; c++)
|
|
{
|
|
const TRecordset_column_info& ci = column_info(c);
|
|
out << " <col ";
|
|
switch (ci._type)
|
|
{
|
|
case _intfld :
|
|
case _longfld:
|
|
case _realfld: out << "align=\"right\""; break;
|
|
case _boolfld: out << "align=\"center\""; break;
|
|
case _datefld: out << "style=\"mso-number-format:\\Short Date\""; break;
|
|
default : out << "style=\"mso-number-format:\\@\""; break; // Stringa!
|
|
}
|
|
out << " />" << endl;
|
|
}
|
|
|
|
TXmlItem tr; tr.SetTag("tr");
|
|
tr.SetColorAttr("bgcolor", BTN_BACK_COLOR);
|
|
tr.Write(out, 2);
|
|
out << endl;
|
|
|
|
for (unsigned int c = 0; c < cols; c++)
|
|
{
|
|
const TRecordset_column_info& ci = column_info(c);
|
|
TToken_string header(ci._name, '\n');
|
|
TString str;
|
|
FOR_EACH_TOKEN(header, tok)
|
|
{
|
|
if (str.not_empty())
|
|
str << "<br/>";
|
|
str << tok;
|
|
}
|
|
if (get_attr(c, attr, true))
|
|
out << " <th style=\"color : #" << format("%06x", attr.get_foreground() & 0xFFFFFF)
|
|
<< "; background-color : #" << format("%06x", attr.get_background() & 0xFFFFFF) << "\">" ;
|
|
else
|
|
out << " <th>";
|
|
|
|
out << str << "</th>" << endl;
|
|
}
|
|
out << " </tr>" << endl;
|
|
out << " </thead>" << endl;
|
|
}
|
|
|
|
out << " <tbody>" << endl;
|
|
TString val;
|
|
for (bool ok = move_first(); ok; ok = move_next())
|
|
{
|
|
if (!pi.add_status())
|
|
break;
|
|
|
|
out << " <tr>" << endl;
|
|
for (unsigned int c = 0; c < cols; c++)
|
|
{
|
|
const TRecordset_column_info& ci = column_info(c);
|
|
|
|
if (get_attr(c, attr))
|
|
out << " <td style=\"color : #" << format("%06x", attr.get_foreground() & 0xFFFFFF)
|
|
<< "; background-color : #" << format("%06x", attr.get_background() & 0xFFFFFF) << "\">" ;
|
|
else
|
|
out << " <td>";
|
|
switch (ci._type)
|
|
{
|
|
case _intfld:
|
|
case _longfld:
|
|
{
|
|
const TVariant& var = get(c);
|
|
const long r = var.as_int();
|
|
val.cut(0);
|
|
if (r != 0)
|
|
val << r;
|
|
else
|
|
var.as_string(val);
|
|
}
|
|
break;
|
|
case _realfld:
|
|
{
|
|
get(c).as_string(val);
|
|
if (val.full() && real::is_real(val))
|
|
{
|
|
const real r(val);
|
|
if (r.is_zero()) //elimina gli '0' dalle celle vuote in excel, rendendo leggibile il file excel
|
|
val.cut(0);
|
|
else
|
|
val = r.stringe();
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
get(c).as_string(val);
|
|
break;
|
|
}
|
|
if (val.full())
|
|
{
|
|
val.rtrim();
|
|
out << val;
|
|
}
|
|
out << "</td>" << endl;
|
|
}
|
|
out << " </tr>" << endl;
|
|
}
|
|
out << " </tbody>" << endl;
|
|
out << "</table>" << endl;
|
|
out << "</body>" << endl;
|
|
out << "</html>" << endl;
|
|
|
|
return !pi.is_cancelled();
|
|
}
|
|
|
|
bool TRecordset::save_as_text(const char* path)
|
|
{
|
|
ofstream out(path);
|
|
if (out.fail())
|
|
return false;
|
|
|
|
TProgress_monitor pi(items(), TR("Esportazione in corso..."), true);
|
|
|
|
TString val;
|
|
const char sep = text_separator();
|
|
const bool as400 = (sep == ' ');
|
|
|
|
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 && !as400)
|
|
out << sep;
|
|
if (as400)
|
|
{
|
|
var.as_string(val);
|
|
if (var.type() == _realfld || (cols == 0 && is_numeric(val)))
|
|
{
|
|
num_reformat(val);
|
|
val.lpad(column_info(c)._width);
|
|
}
|
|
else
|
|
val.rpad(column_info(c)._width);
|
|
out << val;
|
|
}
|
|
else
|
|
{
|
|
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.add_status())
|
|
break;
|
|
}
|
|
|
|
return !pi.is_cancelled();
|
|
}
|
|
|
|
bool TRecordset::save_as_campo(const char* path)
|
|
{
|
|
ofstream out(path);
|
|
if (out.fail())
|
|
return false;
|
|
|
|
TProgress_monitor pi(items(), TR("Esportazione in corso..."), true);
|
|
|
|
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.add_status())
|
|
break;
|
|
}
|
|
return !pi.is_cancelled();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
TBaseisamfile* pisam = NULL;
|
|
if (logicnum >= LF_USER)
|
|
{
|
|
if (*dirname)
|
|
pisam = new TIsamtempfile(logicnum, table);
|
|
else
|
|
pisam = new TFast_isamfile(logicnum);
|
|
}
|
|
if (pisam == NULL)
|
|
return error_box("Impossibile creare il file %s", table);
|
|
|
|
TBaseisamfile& isam = *pisam;
|
|
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);
|
|
}
|
|
|
|
TProgress_monitor pi(items(), TR("Esportazione in corso..."), true);
|
|
bool ok = true;
|
|
for (bool go = move_first(); go; go = move_next())
|
|
{
|
|
if (!pi.add_status())
|
|
break;
|
|
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" || ext == "xls")
|
|
fmt = fmt_html;
|
|
else
|
|
if (ext == "dbf")
|
|
fmt = fmt_dbf;
|
|
else
|
|
if (ext == "csv")
|
|
fmt = fmt_csv;
|
|
}
|
|
bool ok = false;
|
|
switch (fmt)
|
|
{
|
|
case fmt_html : ok = save_as_html(path); break;
|
|
case fmt_campo : ok = save_as_campo(path); break;
|
|
case fmt_dbf : ok = save_as_dbf(path, mode); break;
|
|
case fmt_as400 : ok = save_as_as400(path); break;
|
|
case fmt_csv : ok = save_as_csv(path); 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)
|
|
{
|
|
if (var.is_empty())
|
|
s.cut(0) << *apici << *apici;
|
|
else
|
|
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, 5);
|
|
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 (str.match("??-??-2???"))
|
|
var = TDate(str); else
|
|
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), _text_separator('\t')
|
|
{ }
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// 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, bool final);
|
|
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(512);
|
|
_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, bool final)
|
|
{
|
|
TString16 field;
|
|
TString256 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();
|
|
|
|
const bool tilde = value[0] == '=';
|
|
if (tilde)
|
|
value.ltrim(1);
|
|
|
|
if (value[0] == '"' || value[0] == '\'')
|
|
{
|
|
int fr = -1, to = -1;
|
|
if (value.ends_with("]"))
|
|
{
|
|
const int pa = value.rfind('[');
|
|
if (pa > 0 && value[pa-1] == value[0])
|
|
{
|
|
TToken_string range(value.mid(pa+1), ',');
|
|
fr = range.get_int()-1;
|
|
to = range.get_int();
|
|
value.cut(pa);
|
|
}
|
|
}
|
|
value.rtrim(1);
|
|
value.ltrim(1);
|
|
if (fr >= 0)
|
|
value = value.sub(fr, to);
|
|
} else
|
|
if (value.find('(') > 0)
|
|
{
|
|
TExpression expr(value, _strexpr, true);
|
|
const TString& str = expr.as_string();
|
|
if (expr.error() == 0)
|
|
{
|
|
value = str;
|
|
value.trim();
|
|
}
|
|
}
|
|
if (tilde && final)
|
|
value << '~';
|
|
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, false); else
|
|
if (tok.starts_with("TO"))
|
|
parse_region(rec_stop, true); 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)
|
|
{
|
|
switch (logic) // Proviamo la magia dei campi virtuali
|
|
{
|
|
case LF_DOC : subfield = name; name = "G1"; break;
|
|
case LF_RIGHEDOC: subfield = name; name = "RG1"; break;
|
|
default: return NULL_VARIANT;
|
|
}
|
|
}
|
|
|
|
TVariant& var = get_tmp_var();
|
|
switch (ft)
|
|
{
|
|
case _boolfld: var.set(rec.get_bool(name)); break;
|
|
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;
|
|
case _intzerofld : // Non usare il convertitore degll interi
|
|
case _longzerofld: // in modo da preservare gli zeri iniziali
|
|
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));
|
|
}
|
|
else
|
|
var.set_null();
|
|
}
|
|
|
|
if (to >= from && !var.is_empty())
|
|
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);
|
|
TParagraph_string msg(use, 64);
|
|
TPerformance_profiler prof(msg.get(0));
|
|
TISAM_recordset* my = (TISAM_recordset*)this;
|
|
|
|
istrstream instr(use.get_buffer(), use.len()+1); //"barata" x aggiungere il carattere finale
|
|
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();
|
|
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;
|
|
unsigned int cols = _query.sheet_head().items();
|
|
if (cols == 0)
|
|
cols = _query.columns();
|
|
for (unsigned int c = 0; c < cols; 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, -5, title, query.sheet_head(), buttons, 1), _query(query)
|
|
{
|
|
}
|