campo-sirio/src/include/relation.cpp
Alessandro Bonazzi 6b5e71e756 Patch level : 12.0 998
Files correlati     : cg4.exe
Commento            :

Nella stampa registri la variabile agenzie di viagio non ere inizializzata per cui poteva considerare agenzie di viaggio per l'IVA per cassa anche ditte che non lo erano.

Nella liquidazione IVA la restore status non funzionava più. Questo aveva efftto per i trimestrali che cacolano 3 mesi e stampano sul terzo che non veniva mai esaminato.
2020-09-30 23:59:41 +02:00

3359 lines
82 KiB
C++
Executable File
Raw Blame History

#include <config.h>
#include <diction.h>
#include <expr.h>
#include <extcdecl.h>
#include <prefix.h>
#include <progind.h>
#include <relation.h>
#include <sheet.h>
#include <sort.h>
#include <tabmod.h>
#include <tabutil.h>
#include <utility.h>
#include <codeb.h>
// *** check if not already defined
#define NOTFOUND (-1)
HIDDEN void print_name(ostream& out, const TLocalisamfile& f)
{
switch (f.num())
{
case LF_TABCOM: out << '%'; break;
case LF_TABMOD: out << '&'; break;
default: break;
}
out << f.name();
}
HIDDEN void print_name(TToken_string& out, const TLocalisamfile& f)
{
switch (f.num())
{
case LF_TABCOM: out << '%'; break;
case LF_TABMOD: out << '&'; break;
default: break;
}
out << f.name();
}
extern int get_error(int);
HIDDEN const char* field_type_str(TFieldtypes f)
{
const char* c = "";
switch(f)
{
case _charfld:
c = "Carattere"; break;
case _realfld:
c = "Reale"; break;
case _datefld:
c = "Data"; break;
case _boolfld:
c = "Logico"; break;
// case _wordfld:
// case _intzerofld:
// case _longzerofld:
// case _intfld:
// case _longfld:
default:
c = "Intero"; break;
}
return c;
}
///////////////////////////////////////////////////////////
// TRelationdef
///////////////////////////////////////////////////////////
class TRelationdef : public TObject
{
friend class TRelation;
const TRelation* _rel; // Relazione padre
int _num; // Posizione file
int _numto; // Posizione padre
int _alias; // Alias
byte _key; // Chiave
TArray _fields; // Campi di join
TArray _exprs; // Condizioni di uguaglianza
TArray _altexprs; // Condizioni di uguaglianza alternative
TBit_array _forced;
bool _first_match : 1; // primo match (ed esiste)
bool _allow_lock : 1; // ??
bool _write_enable : 1;
public: // TObject
virtual void print_on(ostream& out) const;
public:
int num() const { return _num; }
int link() const { return _numto; }
int alias() const { return _alias; }
bool allow_lock() const { return _allow_lock; }
bool write_enable() const { return _write_enable; }
void write_enable(bool we) { _write_enable = we; }
TRectype& load_rec(TRectype& r, const TRectype& from) const;
const char* evaluate_expr(int j, const TLocalisamfile& to) const;
void print_on(TToken_string& out) const;
// Funzioni per costruttore di copia
virtual TRelationdef & copy(const TRelationdef & reldef);
virtual TRelationdef* dup() const { return new TRelationdef(*this); }
virtual TRelationdef& operator =(const TRelationdef & reldef) { return copy(reldef); }
TRelationdef(const TRelation* rel, int file, byte key,
int linkto, const char* relexprs, int alias,
bool allow_lock);
// Costruttore di copia
TRelationdef(const TRelationdef& reldef);
virtual ~TRelationdef() {}
};
TRelationdef & TRelationdef::copy(const TRelationdef & reldef)
{
_rel = reldef._rel;
_num = reldef._num;
_numto = reldef._numto;
_alias = reldef._alias;
_key = reldef._key;
_fields = reldef._fields;
_exprs = reldef._exprs;
_first_match = reldef._first_match;
_allow_lock = reldef._allow_lock;
_write_enable = reldef._write_enable;
return *this;
}
TRelationdef::TRelationdef(const TRelation* rel, int idx_file, byte key,
int idx_to, const char* relexprs, int alias,
bool allow_lock)
: _rel(rel), _num(idx_file), _numto(idx_to),
_alias(alias), _key(key), _fields(4), _exprs(4),
_first_match(FALSE), _allow_lock(allow_lock),
_write_enable(FALSE)
{
TToken_string rels(relexprs);
int i = 0;
TString80 r,s;
for (const char* g = rels.get(); g; g = rels.get(), i++)
{
r = g;
int eq = r.find('=');
CHECKS(eq > 0, "Ahoo! E l'uguale 'ndo sta? ", (const char*)g);
s = r.left(eq); // Parte a sinistra dell' =
#ifdef DBG
const char* n = s;
const int p = s.find('[');
if (p > 0) n = s.left(p);
if (rel->file(_num).curr().exist(n) == FALSE)
{
yesnofatal_box("Errore di JOIN: '%s' non e' un campo del file %d",
n, rel->file(_num).num());
continue;
}
#endif
if (r[eq+1] == '=')
{
_forced.set(i);
eq++;
}
_fields.add(new TFieldref(s, 0));
s = r.mid(eq+1);
const int par = s.find('(');
if (par > 0)
{
_exprs.add(new TExpression(s.left(par), _strexpr), i);
_altexprs.add(new TExpression(s.sub(par+1, s.len()-1), _strexpr), i);
}
else
_exprs.add(new TExpression(s, _strexpr), i);
}
}
TRelationdef::TRelationdef(const TRelationdef & reldef)
{
copy(reldef);
}
void TRelationdef::print_on(ostream& out) const
{
const TLocalisamfile& f = _rel->file(_num);
out << "JOIN ";
print_name(out, f);
if (_numto > 0)
{
out << " TO ";
const int alias = _rel->reldef(_numto-1).alias();
if (alias > 0) out << alias << '@';
else
{
const TLocalisamfile& t = _rel->file(_numto);
print_name(out, t);
}
}
if (_key > 1) out << " KEY " << (int)_key;
if (_alias > 0) out << " ALIAS " << _alias;
for (int i = 0; i < _fields.items(); i++)
{
if (i == 0) out << " INTO";
out << ' ' << _fields[i] << '=';
if (_forced[i]) out << '=';
out << _exprs[i];
if (_altexprs.objptr(i))
out << '(' << _altexprs[i] << ')';
}
}
void TRelationdef::print_on(TToken_string& out) const
{
const TLocalisamfile& f = _rel->file(_num);
out.cut(0);
print_name(out, f);
// add description
const char* name = f.name();
if (f.tab())
name = TDir::tab_des(name);
else
name = prefix().description(name);
out.add(name);
out << '|';
if (_numto > 0)
{
const int alias = _rel->reldef(_numto-1).alias();
if (alias > 0)
out << alias << '@';
else
{
const TLocalisamfile& t = _rel->file(_numto);
print_name(out, t);
}
} else out << ' ';
out.add(_key);
out.add(_alias);
out << '|';
for (int i = 0; i < _fields.items(); i++)
{
if (i) out << ' ';
out << _fields[i] << '=';
if (_forced[i]) out << '=';
out << _exprs[i];
if (_altexprs.objptr(i))
out << '(' << _altexprs[i] << ')';
}
}
const char* TRelationdef::evaluate_expr(int j, const TLocalisamfile& to) const
{
const TRectype& rec = to.curr();
TExpression& expr = (TExpression&)_exprs[j];
for (int k = 0; k < expr.numvar(); k++)
{
if (expr.varname(k)[0] != '#')
{
const TFieldref fr(expr.varname(k), rec.num());
expr.setvar(k, fr.read(rec));
}
}
const char* val = (const char*)expr.as_string();
if (*val == '\0' && _altexprs.objptr(j))
{
TExpression& altexpr = (TExpression&)_altexprs[j];
for (int k = 0; k < altexpr.numvar(); k++)
{
if (altexpr.varname(k)[0] != '#')
{
const TFieldref fr(altexpr.varname(k), rec.num());
altexpr.setvar(k, fr.read(rec));
}
}
val = (const char*)altexpr.as_string();
}
return val;
}
///////////////////////////////////////////////////////////
// TRelation
///////////////////////////////////////////////////////////
TRelation & TRelation::copy(const TRelation & rel)
{
_errors = rel._errors;
_status = rel._status;
FOR_EACH_ARRAY_ITEM(rel._files, i, file)
{
TLocalisamfile * f = (TLocalisamfile *)file;
int logic = f->num();
_files.add(new TLocalisamfile(logic));
lfile(logic).curr() = f->curr();
lfile(logic).setkey(f->getkey());
lfile(logic).read();
}
_reldefs = rel._reldefs;
return *this;
}
TRelation::TRelation(int logicnum)
: _files(4), _reldefs(4), _errors(NOERR)
{
TLocalisamfile* f = new TLocalisamfile(logicnum);
_files.add(f);
}
TRelation::TRelation(const char* tabname)
: _files(4), _reldefs(4), _errors(NOERR)
{
TLocalisamfile* t = NULL;
if (tabname[0] == '&')
t = new TModule_table(tabname);
else
t = new TTable(tabname);
_files.add(t);
}
TRelation::TRelation(TLocalisamfile* l)
: _files(4), _reldefs(4), _errors(NOERR)
{ _files.add(l); }
TRelation::TRelation(const TRelation & rel)
{
copy(rel);
}
TRelation::~TRelation()
{}
void TRelation::print_on(ostream& out) const
{
const TLocalisamfile& f = file();
out << "USE "; print_name(out, f);
const int k = f.getkey();
if (k > 1) out << " KEY " << k;
out << endl;
for (int r = 0; r < _reldefs.items(); r++)
out << _reldefs[r] << endl;
}
void TRelation::restore_status()
{
int i;
for (i = _files.last(); i >= 0 ; i--)
{
const int err = _status.get_int(i*3);
const TRecnotype recno = _status.get_long();
const int key = _status.get_int();
file(i).setkey(key);
if (recno >= 0l) file(i).readat(recno);
else file(i).zero();
file(i).setstatus(err);
}
_status.get(_files.items()*3 - 1); // mi riposiziono prima di tutti i first_match
for (i = 0; i < _reldefs.items(); i++)
{
const bool first_match = _status.get()[0] == 'X';
reldef(i)._first_match = first_match;
}
}
void TRelation::save_status()
{
_status.cut(0);
int i;
for (i = 0; i < _files.items(); i++)
{
const int err = file(i).status();
const TRecnotype recno = file(i).eof() ? -1l : file(i).recno();
const int key = file(i).getkey();
_status.add (err);
_status.add (recno);
_status.add (key);
}
for (i = 0; i < _reldefs.items(); i++)
{
const bool first_match = reldef(i)._first_match;
_status.add(first_match);
}
}
// @doc EXTERNAL
// @mfunc Ritorna l'indice di <p _files> del numero logico passato
//
// @rdesc Ritorna l'indice oppure NOTFOUND nel caso non sia presente
int TRelation::log2ind(
int log) const // @parm Numero logico del file di cui conoscere l'indice
// @comm Nel caso <p log> sia minore di 0 chiama la <mf TRelation::alias2ind>
//
// @xref <mf TRelation::alias2ind> <mf TRelation::name2ind>
{
// returns _files index of logical number or NOTFOUND if not present
if (log <= 0)
return alias2ind(-log);
const int nf = _files.items();
for (int i = 0; i < nf; i++)
if (file(i).num() == log)
return i;
return NOTFOUND;
}
// @doc EXTERNAL
// @mfunc Ritorna l'indice di <p _files> del alias del file passato
//
// @rdesc Ritorna l'indice oppure NOTFOUND nel caso non sia presente
int TRelation::alias2ind(
int alias) const // @parm Alias del file di cui conoscere l'indice
// @xref <mf TRelation::log2ind> <mf TRelation::name2ind>
{
if (alias <= 0) return 0;
for (int i = 0; i < _reldefs.items(); i++)
{
const TRelationdef& r = (const TRelationdef&)_reldefs[i];
if (r.alias() == alias) return r.num();
}
return NOTFOUND;
}
// @doc EXTERNAL
// @mfunc Ritorna l'indice di <p _files> del nome del file passato
//
// @rdesc Ritorna l'indice oppure NOTFOUND nel caso non sia presente
int TRelation::name2ind(
const char* name) const // @parm Nome del file di cui conoscere l'indice
// @xref <mf TRelation::alias2ind> <mf TRelation::alias2ind>
{
const int num = name2log(name);
const int ind = log2ind(num);
return ind;
}
// @doc EXTERNAL
// @mfunc Ritorna il descrittore del file
//
// @rdesc Ritorna il reference ad un <c TLocalisamfile> che indica il descrittore del
// file della relazione
TLocalisamfile& TRelation::lfile(
int logicnum) const // @parm Numero logico del file da ritornare (default 0)
// @parm const char* | name | Nome del file da ritornare
// @syntax TLocalisamfile& lfile(int logicnum)
// @syntax TLocalisamfile& lfile(const char* name)
//
// @comm E' comodo utilizzare anche l'operatore [] che richiama questa funzione con
// la prima sintassi.
{
const int idx = log2ind(logicnum);
CHECKD(idx != NOTFOUND, "Relation file not found n. ", logicnum);
return (TLocalisamfile&)_files[idx];
}
TLocalisamfile& TRelation::lfile(const char* name) const
{
const int idx = name2ind(name);
CHECKS(idx != NOTFOUND, "File or Table not found:", name);
return (TLocalisamfile&)_files[idx];
}
bool TRelation::has_children(int first) const
{
for (int i = first; i < _reldefs.items(); i++)
{
const TRelationdef& ref = reldef(i);
if (ref._numto == first)
return true;
}
return false;
}
// @doc EXTERNAL
// @mfunc Abilita/disabilita la scrittura sul file
void TRelation::write_enable(
int logicnum, // @parm Numero logico del file da abilitare/disabilitare (default 0)
// @parm cont char* | name | Nome del file da abilitare/disabilitare
const bool on) // @parm Indica l'operazione da effettuare sul file:
//
// @flag TRUE | Abilita la scrittura sul file (default)
// @flag FALSE | Disabilita la scrittura sul file
// @syntax void write_enable(int logicnum, const bool on)
// @syntax void write_enable(const char* name, const bool on)
//
// @comm Nel caso venga passato un numero logico uguale a 0 vengono abilitati/disabilitati tutti
// i file della relazione
{
if (logicnum == 0)
{
for (int i = 0; i < _reldefs.items(); i++)
reldef(i).write_enable(on);
}
else
{
const int idx = log2ind(logicnum);
CHECKD(idx > 0, "File not found n. ", logicnum);
reldef(idx-1).write_enable(on);
}
}
void TRelation::write_enable(const char* name, const bool on)
{
const int idx = name2ind(name);
CHECKS(idx > 0, "File or Table not found:", name);
reldef(idx-1).write_enable(on);
}
// @doc EXTERNAL
// @mfunc Aggiunge una nuovo file nella relazione
//
// @rdesc Ritorna se e' riuscito ad aggiungere il file alla relazione
bool TRelation::add(
TLocalisamfile* f, // @parm Descrittore del file da aggiungere
const char* relexprs, // @parm Espressione della relazione
int key, // @parm Chiave del file
int linkto, // @parm Posizione alla quale aggiungere il file
int alias, // @parm Alias da dare al file da aggiungere
bool allow_lock) // @parm Indica se fare il lock sul file oppure ignorarli
// @parm int | logicnum | Numero logico del file da aggiungere
// @parm const char* | tabname | Nome della tabella da aggiungere
// @syntax bool add(TLocalisamfile* f, const char* relexprs, int key, int linkto, int alias, bool allow_lock);
// @syntax bool add(int logicnum, const char* relexprs, int key, int linkto, int alias, bool allow_lock);
// @syntax bool add(const char* tabname, const char* relexprs, int key, int linkto, int alias, bool allow_lock);
// @comm Il parametro <p linkto> puo' assumere i valori:
//
// @flag 0 | Per il file principale
// @flag <gt>0 | Indica il numero logico del file
// @flag <lt>0 | Indica l'alias del file
{
const int idxto = log2ind(linkto);
if (idxto == NOTFOUND)
fatal_box("Can't join file %d to %d", f->num(), linkto);
const int idx = _files.add(f);
CHECK(relexprs && *relexprs, "Mancano le espressioni di collegamento");
TRelationdef* r = new TRelationdef(this, idx, key, idxto,
relexprs, alias, allow_lock);
_reldefs.add(r);
return TRUE;
}
bool TRelation::add(int logicnum, const char* relexprs, int key,
int linkto, int alias, bool allow_lock)
{
TLocalisamfile* f = new TLocalisamfile(logicnum);
return add(f, relexprs, key, linkto, alias, allow_lock);
}
bool TRelation::add(const char* tabname, const char* relexprs, int key,
int linkto, int alias, bool allow_lock)
{
TLocalisamfile* t = NULL;
if (*tabname == '&')
t = new TModule_table(tabname);
else
t = new TTable(tabname);
return add(t, relexprs, key, linkto, alias, allow_lock);
}
// @doc EXTERNAL
// @mfunc Sostituisce nella relazione un file
void TRelation::replace(
TLocalisamfile* f, // @parm Descrittore del file sostituto
int index, // @parm Posizione nel quale sostituire il file (default 0)
const char* relexprs, // @parm Nuova Espressione della relazione
int key) // @parm Nuova Chiave del file
{
const TLocalisamfile* p = (const TLocalisamfile*)_files.objptr(index);
CHECK(p && p->num() == f->num(), "Can't replace a file with different logic number");
_files.add(f, index);
if (relexprs && *relexprs)
{
TRelationdef* oldr=(TRelationdef*)_reldefs.objptr(index-1);
TRelationdef* r = new TRelationdef(this, index, key, oldr->link(),
relexprs, oldr->alias(), oldr->allow_lock());
_reldefs.add(r,index-1);
}
}
// @mfunc Sostituisce nella relazione un file
void TRelation::replacef(
TLocalisamfile* f, // @parm Descrittore del file sostituto
int lognum, // @parm Numero logico o alias
const char* relexprs, // @parm Nuova Espressione della relazione
int key) // @parm Nuova Chiave del file
{
int index=0;
if (lognum < 0) index=alias2ind(lognum);
if (lognum > 0) index=log2ind(lognum);
replace(f,index,relexprs,key);
}
TRectype& TRelationdef::load_rec(TRectype& r, const TRectype& from) const
{
r.zero();
TString80 val; // Fisso la lunghezza massima ad 80, devono essere campi chiave!
for (int j = 0 ; j < _fields.items(); j++) // for each field
{
const TFieldref& s = (const TFieldref&) _fields[j];
val = s.read(from);
s.write(val, r);
}
return r;
}
void TRelation::zero()
{
for (int i = 0; i < _files.items(); i++)
file(i).zero();
}
// @doc EXTERNAL
// @mfunc Permette di posizionare l'albero di relazioni tra i file
//
// @rdesc Ritorna il numero di errore avvenuto nell'opeerazione, NOERR nel caso
// venga eseguito tutto correttamente
int TRelation::position_rels(
TIsamop op, // @parm Operatore che esegue la relazione (default _isequal)
TReclock lockop, // @parm Tipo di lock sul file (default _nolock)
int first) // @parm Numero del file da cui costruire la relazione (default
// 0, cioe' dal file principale)
// @comm Viene eseguito tutto il lavoro di creazione delle relazione: se non
// trova un record adatto su un file, svuota il record corrente e non
// ritorna errore.
// <nl>Le altre funzioni sul record procedono normalmente
{
TString expr(80); // Stringa di lavoro per la valutazione delle espressioni
_errors = NOERR;
const int primo = first < 0 ? 0 : first;
const int ultimo = first < 0 && (-first) < _reldefs.items() ? -first : _reldefs.items();
// workhorse: position files for each active relation
for (int i = primo; i < ultimo; i++)
{
TRelationdef& rd = reldef(i);
if (primo > 0 && rd.link() < primo)
continue; // Inutile spostare file collegati a record precedenti
TLocalisamfile& from = file(rd.num());
TLocalisamfile& to = file(rd.link());
from.zero(); // Azzera il record corrente (tutti se TSortedfile)
if (to.curr().empty())
continue;
from.setkey(rd._key);
// build record
TRectype& furr = from.curr();
for (int j = 0; j < rd._fields.items(); j++) // for each field
{
expr = rd.evaluate_expr(j, to);
const TFieldref& s = (const TFieldref&) rd._fields[j];
s.write(expr, furr);
} // for each field
const TReclock lck = rd._allow_lock ? lockop : _nolock;
from.read(_isgteq, lck);
// record read : if not found, zero current record
bool eq = !from.bad();
for (int kk = 0; eq && kk < rd._fields.items(); kk++)
{
TFieldref& fl = (TFieldref&)rd._fields[kk];
TString80 f_fr(fl.read(furr));
TString80 f_ex(rd.evaluate_expr(kk, to));
if (rd._forced[kk])
eq = f_fr == f_ex;
else
{
f_fr.rtrim(); f_ex.rtrim();
eq = f_fr.compare(f_ex, f_ex.len()) == 0;
// Becca anche 000001=1
if (!eq && real::is_natural(f_fr) && real::is_natural(f_ex))
eq = atol(f_fr) == atol(f_ex);
}
}
rd._first_match = eq;
if (eq)
from.setstatus(NOERR);
else
furr.zero();
} // for each relation
return _errors;
}
// @doc EXTERNAL
// @mfunc Posiziona il numero logico sul record successivo
//
// @rdesc Ritorna se ha trovato la corrispondenza:
//
// @flag TRUE | Corrispondenza trovata
// @flag FALSE | Corrispondenza non trovata
bool TRelation::next_match(
int logicnum, // @parm Numero logico del file da fare avanzare
const char* fieldlist, // @parm Lista dei campi che devono rimanere costanti (default NULL)
int nkey) // @parm Numero della chiave (default 0)
// @comm Il posizionamento del numero logico non deve essere riferita al file
// principale; in ogni caso viene considerata consistente ad eccezione dei
// casi di inconsistenza nella prima posizione.
{
if (logicnum == file().num())
{
next();
return file().good();
}
const int i = log2ind(logicnum);
CHECKD(i != NOTFOUND,"Nonexistent file referenced in relation ",logicnum);
int j;
for (j = 0; j < _reldefs.items(); j++)
if (reldef(j).num() == i) break;
TLocalisamfile& from = file(i);
reldef(j)._first_match = FALSE;
if (from.bad())
return FALSE; // && vaffanculo();
const TRecnotype last = from.recno();
bool ok = TRUE; // Corrispondenza trovata ?
if (fieldlist == NULL)
{
TRectype rec(from.num());
reldef(j).load_rec(rec, from.curr());
from.setkey(reldef(j)._key);
from.next();
ok = (from.good() && from.curr() == rec);
for (int kk = 0; ok && kk < reldef(j)._fields.items(); kk++)
{
if (reldef(j)._forced[kk])
{
TFieldref& fl = (TFieldref&)reldef(j)._fields[kk];
const TString f_fr(fl.read(from.curr()));
const TString f_to(fl.read(rec));
ok = (f_fr == f_to);
}
}
if (ok) from.setstatus(NOERR);
}
else
{
if (nkey > 0)
from.setkey(nkey);
TToken_string fields(fieldlist);
TToken_string old(80);
const char * f;
for (f = fields.get(0); f; f = fields.get())
old.add(from.curr().get(f));
ok = from.next() == NOERR;
old.restart();
for (f = fields.get(0); f && ok; f = fields.get())
{
const TFixed_string v(from.curr().get(f));
const char* o = old.get();
if (v != o)
ok = false;
}
}
if (!ok)
{
from.readat(last);
return FALSE;
}
// Riposiziona gli eventuali files successivi
position_rels(_isequal, _nolock, j+1);
reldef(j)._first_match = FALSE;
return ok;
}
// @doc EXTERNAL
// @mfunc Controlla se c'e' un record ed e' il primo match (non si e' mai fatta
// <mf TRelation::position_rels>)
//
// @rdesc Ritorna TRUE se c'e' un record ed e' il primo match
bool TRelation::is_first_match(
int logicnum) // @parm Numero logico del file da controllare
{
const int i = log2ind(logicnum);
CHECKD(i != NOTFOUND,"Nonexistent file referenced in relation ",logicnum);
for (int j = 0; j < _reldefs.items(); j++)
{
TRelationdef& rdj = reldef(j);
if (rdj.num() == i)
return rdj._first_match;
}
const int err = file().status();
return err == NOERR;
}
#ifdef DBG
// @doc EXTERNAL
// @mfunc Controlla se la relazione e' corretta
//
// @rdesc Ritorna i seguent valori
//
// @flag TRUE | La relazione soddisfa i criteri di correttezza
// @flag FALSE | La relazione manca di almeno uno di criteri di correttezza
bool TRelation::isconsistent(
bool reset) // @parm Indica se cercare di riportare la relazione ad uno stato
// consistente (default FALSE)
// @comm Per essere corretta una relazione necessita che tutti i file siano
// corretti, che il record corrente non sia vuoto e che la relazione sia
// consistente
// <nl>Se <p reset> e' TRUE si cerca di riportare la relazione in uno stato
// consistente (basato sul record principale). Non viene fatto nessun altro
// controllo
// <nl>Questa funzione e' chiamata internamente da <mf TRelation::update>
// e <mf TRelation::remove>.
{
const int MAXREL = 24;
int bad = 0, i;
TRecnotype recnos[MAXREL];
for (i = 0; i < _files.items() && i < MAXREL; i++)
{
// must be file OK, non-empty record
bad |= file(i).good() || file(i).curr().empty();
recnos[i] = file(i).recno();
}
// if the above hold, check consistency
if (bad)
{
_errors = bad;
return FALSE;
}
position_rels(_isequal, _nolock);
for (i = 0; i < _files.items(); i++)
if (file(i).recno() != recnos[i])
{
bad = -1;
break;
}
if (reset == FALSE)
// leave as before
for(i = 0; i < _files.items(); i++)
file(i).readat(recnos[i]);
return TRUE;
}
#endif
int TRelation::write(bool force)
{
_errors = file(0).write();
if (_errors != NOERR)
return _errors;
for (int i = 0; i < _reldefs.items(); i++)
{
TRelationdef& rd = reldef(i);
TLocalisamfile& lf = file(rd.num());
if (!rd.write_enable() || lf.curr().empty())
continue;
int res = lf.write();
if (force && res == _isreinsert)
res = lf.rewrite();
if (_errors == NOERR)
_errors = res;
}
return _errors;
}
int TRelation::rewrite(bool force)
{
_errors = file(0).rewrite(); // Riscrive testata
if (force && _errors == _iskeynotfound) // Se non la trova ...
_errors = file(0).write(); // ... forza la scrittura
for (int i = 0; i < _reldefs.items(); i++)
{
TRelationdef& rd = reldef(i);
TLocalisamfile& lf = file(rd.num());
if (!rd.write_enable() || lf.curr().empty())
continue;
int res = lf.rewrite();
if (force && (res == _iskeynotfound || res == _iseof || res == _isemptyfile))
res = lf.write();
if (_errors == NOERR)
_errors = res;
}
return _errors;
}
int TRelation::remove()
{
const int res = file(0).remove();
if (_errors == NOERR && res != _iskeynotfound) _errors = res;
for (int i = 0; i < _reldefs.items(); i++)
{
TRelationdef& rd = reldef(i);
const int log = rd.num();
if (!rd.write_enable() ||
(file(log).curr()).empty()) continue;
const int res = file(log).remove();
if (_errors == NOERR && res != _iskeynotfound) _errors = res;
}
return _errors;
}
bool TRelation::exist(int logicnum) const
{
const bool lucky = log2ind(logicnum) >= 0;
return lucky;
}
void TRelation::mask2rel(const TMask & m)
{
for (int i = 0; i < _reldefs.items(); i++)
{
TRelationdef& rdi = reldef(i);
for (int j = 0; j < rdi._exprs.items(); j++)
{
TExpression& expr = (TExpression&)rdi._exprs[j];
for (int k = 0; k < expr.numvar(); k++)
{
const char * name = expr.varname(k);
if (name[0] == '#')
expr.setvar(k, m.focus_field().evaluate_field(name));
}
if (rdi._altexprs.objptr(j))
{
TExpression& altexpr = (TExpression&)rdi._altexprs[j];
for (int k = 0; k < altexpr.numvar(); k++)
{
const char * name = altexpr.varname(k);
if (name[0] == '#')
altexpr.setvar(k, m.focus_field().evaluate_field(name));
}
}
}
}
}
///////////////////////////////////////////////////////////
// TCursor //
///////////////////////////////////////////////////////////
HIDDEN bool __evalcondition(const TRelation& /* r */,TExpression* cond, const TArray& frefs)
{
for (int i = cond->numvar() - 1; i >= 0; i--)
{
const TRecfield* fr = (const TRecfield*)frefs.objptr(i);
if (fr)
cond->setvar(i, (const char*)*fr);
}
return cond->as_bool();
}
// @doc EXTERNAL
static TFilename _last_name;
static FILE* _last_ndx = NULL;
static bool _last_created = FALSE;
// @mfunc Apre il file di indice
//
// @rdesc Ritorna l'handle del file aperto
FILE* TCursor::open_index(
bool create) // @parm Indica se creare l'indice nel caso manchi (default FALSE)
{
_last_created = create;
if (_indexname.empty())
{
TString8 radix;
radix.format("c%d_", file().num());
_indexname.temp(radix);
}
if (_indexname != _last_name || create)
{
if (_last_ndx != NULL)
{
fclose(_last_ndx);
_last_ndx = NULL;
}
fopen_s(&_last_ndx, _indexname, create ? "wb" : "rb"); // Secured _last_ndx = fopen(_indexname, create ? "wb" : "rb");
if (_last_ndx == NULL)
fatal_box("Can't use cursor index for file %d: '%s'\n",
file().num(), (const char*)_indexname);
_last_name = _indexname;
}
return _last_ndx;
}
void TCursor::close_index(FILE* f)
{
CHECK(f == _last_ndx, "Bad open/close index sequence");
if (_last_created)
{
fclose(_last_ndx);
_last_ndx = NULL;
_last_name.cut(0);
_last_created = FALSE;
}
}
int TCursor::read_page(long page)
{
_pagefirstpos = page * _cmaxelpage;
CHECKD(_pagefirstpos < _totrec, "Bad cursor page ", page);
unsigned long elements = _totrec - _pagefirstpos;
if (elements > _cmaxelpage)
elements = _cmaxelpage;
const unsigned long startpos = sizeof(TRecnotype) * _pagefirstpos;
const unsigned long size = sizeof(TRecnotype) * elements;
FILE * indf = open_index();
fseek(indf, startpos, SEEK_SET);
if (fread(_page, size, 1, indf) != 1)
fatal_box("Can't read page number %d in cursor n. %d\n", page, file().num());
return (int) elements;
}
bool TCursor::has_simple_filter() const
{
bool yes = _filterfunction == NULL;
if (yes && _fexpr != NULL && _fexpr->numvar() > 0)
{
const RecDes& recd = curr().rec_des(); // Descrizione del record della testata
const KeyDes& kd = recd.Ky[_nkey-1]; // Elenco dei campi della chiave del cursore
for (int i = _fexpr->numvar()-1; yes && i >= 0; i--)
{
const char * vn = _fexpr->varname(i);
TFieldref f(vn, 0);
yes = f.file() == 0;
if (yes)
{
for (int k = kd.NkFields-1; k >= 0; k--)
{
const int nf = kd.FieldSeq[k] % MaxFields;
const RecFieldDes& rf = recd.Fd[nf];
yes = kd.FromCh[k] == 255 && f.name() == rf.Name;
if (yes) break;
}
}
}
}
return yes;
}
TRecnotype TCursor::buildcursor(TRecnotype rp)
{
_pagefirstpos = 0L;
memset(_page, 0, sizeof(TRecnotype)*_cmaxelpage);
_fpkey.destroy();
const int fhnd = file().handle();
if (DB_reccount(fhnd) == 0)
return 0;
TRecnotype oldrecno= RECORD_NON_FISICO,ap = 0;
int pagecnt = 0;
FILE* indf = NULL;
const int l = strlen(to());
double perc = 1.0;
if (l > 0 && DB_index_seek(fhnd, to()) >= 0)
perc = DB_pos_perc(fhnd);
int junk = DB_index_seek(fhnd, from());
if (junk < 0) junk=get_error(junk);
if (junk == _iseof) return 0;
perc -= DB_pos_perc(fhnd);
// DB_index_recno(fhnd); // A cosa cavolo serve?
_pos=-1;
const bool filtered = has_filter();
const bool simple_filter = filtered && has_simple_filter();
// Barra di progresso
const long items = (long)((double)_if->lfile().items() * perc);
TString msg("Elaborazione ");
TDir d(_if->lfile().num());
msg << d.des();
TProgress_monitor pi(items, msg);
while (!DB_index_eof(fhnd))
{
pi.add_status();
const char *s0 = DB_index_getkey(fhnd);
if (l && (strncmp(to(), s0, l) < 0))
break;
const TRecnotype recno = DB_index_recno(fhnd);
if (recno == oldrecno)
break; // means that no more keys are available
oldrecno=recno;
bool to_be_added = true;
if (filtered)
{
if (simple_filter)
to_be_added = simple_filtercursor(s0);
else
to_be_added = filtercursor(recno);
}
if (to_be_added)
{
_page[pagecnt++] = recno;
if (_pos < 0 && recno == rp)
_pos = ap;
ap++;
}
if (pagecnt == _cmaxelpage)
{
_fpkey.add(new TToken_string(s0));
pagecnt = 0;
if (indf == NULL)
{
indf = open_index(TRUE);
fseek(indf, 0L, SEEK_SET);
}
fwrite(_page,sizeof(TRecnotype)*_cmaxelpage, 1, indf);
}
DB_index_next(fhnd);
} // while
if (indf != NULL)
{
if (pagecnt > 0)
fwrite(_page,sizeof(TRecnotype)*pagecnt, 1, indf);
close_index(indf);
}
_pagefirstpos = ap > _cmaxelpage ? -(_cmaxelpage + 1) : 0L ;
if (_pos < 0L)
_pos = 0L;
return ap;
}
bool TCursor::call_filterfunction(TRecnotype recno) const
{
bool ok = true;
if (_filterfunction)
{
const int handle = file().handle();
// memorizzo la chiave prima di eventuali spostamenti
const TString256 s0 = DB_index_getkey(handle);
ok = _filterfunction(_if);
// ripristino la chiave dopo eventuali spostamenti
if (recno != DB_index_recno(handle))
DB_index_go(handle, s0, recno);
}
return ok;
}
bool TCursor::filtercursor(TRecnotype recno)
{
file().readat(recno);
if (update_relation())
{
// memorizzo la chiave prima di eventuali spostamenti
const TString256 s0 = DB_index_getkey(file().handle());
_if->update(-filter_limit());
// ripristino la chiave dopo eventuali spostamenti
const int handle = file().handle();
if (recno != DB_index_recno(handle))
DB_index_go(handle, s0, recno);
}
bool ok = call_filterfunction(recno);
if (ok && _fexpr)
ok = __evalcondition(*_if, _fexpr, _frefs);
return ok;
}
bool TCursor::simple_filtercursor(const char* key) const
{
if (_fexpr == NULL)
return true;
const RecDes& recd = curr().rec_des(); // Descrizione del record della testata
const KeyDes& kd = recd.Ky[_nkey-1]; // Elenco dei campi della chiave del cursore
TFieldref f;
for (int i = _fexpr->numvar()-1; i >= 0; i--)
{
f = _fexpr->varname(i);
int offset = 0;
for (int k = 0; k < kd.NkFields; k++)
{
const int nf = kd.FieldSeq[k] % MaxFields;
const RecFieldDes& rf = recd.Fd[nf];
int len = rf.Len;
if (f.name() == rf.Name)
{
offset += f.from();
if (f.to() > f.from())
len = f.to() - f.from();
else
len -= f.from();
char* val = (char*)(key+offset);
char oldchar = '\0';
int l;
for (l = len; l >= 0; l--) // rtrim
{
if (l == 0 || val[l-1] != ' ')
{
oldchar = val[l];
val[l] = '\0';
break;
}
}
switch (rf.TypeF)
{
case _boolfld:
_fexpr->setvar(i, strchr("1STXY", *val)!=NULL ? "X" : " ");
break;
case _intfld:
case _longfld:
{
const char* v;
for (v = val; *v == ' ' || *v == '0'; v++);
_fexpr->setvar(i, v);
}
break;
default:
_fexpr->setvar(i, val);
break;
}
if (oldchar)
val[l] = oldchar;
break;
}
offset += len;
}
}
return _fexpr->as_bool();
}
bool TCursor::ok() const
{
if (file().bad())
return FALSE;
const TRectype& rec = file().curr();
const TFixed_string key(rec.key(_nkey));
const TString& kf = from();
const TString& kt = to();
if (file().tab())
{
if (key.compare(kf, 3) < 0 || (kt.not_empty() && kt.compare(key, 3) < 0))
return FALSE;
}
else
{
if (key < kf || (kt.not_empty() && kt.compare(key, kt.len()) < 0))
return FALSE;
}
const TRecnotype old = file().recno();
if (update_relation())
{
_if->update();
if (DB_recno(file().handle()) != old)
file().readat(old);
}
if (call_filterfunction(old) &&
(_fexpr ? __evalcondition(*_if, _fexpr, _frefs) : true))
return true;
return false;
}
bool TCursor::changed()
{
if (_frozen && _lastrec > 0L)
return FALSE;
const TLocalisamfile& f = file();
if (prefix().get_dirtype(f.num()) == _nordir &&
_index_firm != prefix().get_codditta())
return TRUE;
const int handle = f.handle();
const TRecnotype eod = DB_reccount(handle);
if (_lastrec != eod ||
_lastkrec != DB_changed(handle))
return TRUE;
if (!f.curr().valid())
return TRUE;
return FALSE;
}
TRecnotype TCursor::update()
{
TWait_cursor hourglass;
TLocalisamfile& f = file();
f.setkey(_nkey);
if (f.curr().empty())
f.zero(); // Usare sempre l'azzeratore esperto del file non direttamente f.curr().zero()
f.read(_isgteq);
_totrec = buildcursor(f.recno());
const int handle = f.handle();
const TRecnotype eod = DB_reccount(handle);
_lastrec = eod;
_index_firm = prefix().get_codditta();
_lastkrec = DB_changed(handle);
return _totrec;
}
HIDDEN void matildator(const TRectype& oldrec, int key, bool tilde, TString& str)
{
/*
if (tilde)
{
TRectype rec(oldrec); // Utile duplicazione di record
const RecDes* recd = rec.rec_des(); // Descrizione del record della testata
const KeyDes& kd = recd->Ky[key-1]; // Elenco dei campi della chiave
TString80 val;
for (int i = kd.NkFields-1; i >= 0; i--)
{
const int nf = kd.FieldSeq[i] % MaxFields;
const RecFieldDes& rf = recd->Fd[nf];
val = rec.get(rf.Name);
if (val.not_empty())
{
if (rf.TypeF == _alfafld && val.len() < rf.Len)
{
val.left_just(rf.Len, '~');
rec.put(rf.Name, val);
}
break;
}
}
str = rec.key(key);
}
else
str = oldrec.key(key);
str.replace('~', ' ');
*/
str = oldrec.key(key);
if (tilde)
{
const RecDes& recd = oldrec.rec_des(); // Descrizione del record della testata
const KeyDes& kd = recd.Ky[key-1]; // Elenco dei campi della chiave
const int len = str.len();
const int kfields = kd.NkFields;
int foc = 0;
for (int k = 0; k < kfields; k++)
{
const int nf = kd.FieldSeq[k] % MaxFields;
const RecFieldDes& rf = recd.Fd[nf];
int toc = foc;
if (kd.FromCh[k] == 255) // Non e' stato specificato un range
toc += rf.Len; // Considero l'intera lunghezza del campo
else
toc += kd.ToCh[k] - kd.FromCh[k] + 1;
CHECK(toc > foc, "Invalid addition");
if (toc >= len)
{
if (str[foc] > ' ' && rf.TypeF == _alfafld)
str.left_just(toc, ' ');
break;
}
foc = toc;
}
}
str.replace('~', ' ');
}
// @doc EXTERNAL
// @mfunc Setta il filtro sul cursore
void TCursor::filter(
const char* fil, // @parm Espressione del filtro
const TRectype* from, // @parm Primo record del pacchetto da esaminare
const TRectype* to, // @parm Ultimo record del pacchetto da esaminare
int tilde // @parm Flag per riempire le chiavi di ~ from(0x1) o to(0x2)
)
// @comm ATTENZIONE: non e' possibile filtrare un cursore congelato
{
TString kf(_keyfrom), kto(_keyto);
const bool filterchanged = (fil != NULL) && (_filter != fil);
if (from != NULL)
matildator(*from, _nkey, (tilde & 0x1) != 0, kf);
if (to != NULL)
matildator(*to, _nkey, (tilde & 0x2) != 0, kto);
if (kf[0] <= ' ' || kto[0] <= ' ')
{
switch (file().num())
{
case LF_TABGEN:
case LF_TABCOM:
case LF_TAB:
if (!real::is_natural(file().name())) // E' veramente una tabella o <20> un file normale?
{
kf.overwrite(file().name(), 0);
kto.overwrite(file().name(), 0);
};
break;
case LF_TABMOD:
if (!real::is_natural(file().name())) // E' veramente una tabella di modulo o <20> un file normale?
{
const TModule_table& t = (TModule_table&)file();
TString16 k;
k.format("%2s%6ld%3s", t.module(), t.customer(), t.name());
kf.overwrite(k, 0);
kto.overwrite(k, 0);
}
break;
default:
break;
}
}
if (filterchanged || (_keyfrom != kf) || (_keyto != kto))
{
CHECK(!frozen(), "Impossibile filtrare un cursore congelato");
_pos = 0;
_totrec = 0;
_lastrec = 0;
if (filterchanged)
{
_filter = fil;
safe_delete(_fexpr);
_frefs.destroy();
TTypeexp type = (_filter.find('"') >= 0) ? _strexpr : _numexpr;
if (_filter.not_empty())
{
_fexpr = new TExpression(_filter, type);
if (_fexpr->type() == _numexpr)
for (int i = 0 ; i < _fexpr->numvar(); i++)
if (file().curr().type(_fexpr->varname(i)) == _alfafld)
{
_fexpr->set_type(_strexpr);
break;
}
}
else _fexpr = NULL;
if (_fexpr)
{
const int items = _fexpr->numvar();
for (int i = 0 ; i < items; i++)
{
const char * vn = _fexpr->varname(i); //occhio
if (vn[0] != '#')
{
const TFieldref f(vn, 0);
const TString & id = f.id();
if (id.full())
{
_filter_update = true;
const int file_id = _if->name2ind(id);
if (_filter_limit < file_id)
_filter_limit = file_id;
}
_frefs.add(new TRecfield(_if->curr(f.file()), f.name(), f.from(), f.to()), i);
}
else
NFCHECK("Variabile strana %s", (const char*)vn);
}
}
}
_keyfrom = kf;
_keyto = kto;
}
}
void TCursor::setkey(int nkey)
{
if (nkey != _nkey)
{
_lastrec = 0L;
_nkey = nkey;
file().setkey(_nkey);
filter(NULL);
}
}
int TCursor::test(TIsamop op, TReclock lockop)
{
TLocalisamfile& curfile = file();
const TRectype& currec = curfile.curr();
curfile.setkey(_nkey);
int err = NOERR;
if (op == _isequal)
{
const TString match(currec.key(_nkey));
bool trovato = FALSE;
for (err = curfile.read(op, lockop);
err == NOERR && match == currec.key(_nkey);
err = curfile.next(lockop))
{
if (ok())
{
trovato = true;
break;
}
else
{
if (lockop != _nolock)
curfile.reread(_unlock);
}
}
if (!trovato && err == NOERR)
{
err = _iskeynotfound;
curfile.setstatus(err);
}
}
else
{
for (err = curfile.read(op, lockop);
err == NOERR;
err = curfile.next(lockop))
{
if (ok())
break;
else
{
//if (((TCursor *)this)->items() == 0)
if (_totrec == 0)
{
err = _isemptyfile;
break;
}
if (lockop != _nolock)
curfile.reread(_unlock);
const TString& kto = to();
if (kto.not_empty())
{
const TFixed_string curkey(currec.key(_nkey));
if (kto.compare(curkey, kto.len()) < 0)
{
err = _iseof;
break;
}
}
}
}
}
return err;
}
TRecnotype TCursor::read(TIsamop op, TReclock lockop)
{
TLocalisamfile& curfile = file();
if (changed())
{
const TRectype savecurr = curfile.curr();
update();
curfile.curr() = savecurr;
}
int err = test(op, lockop);
const TRecnotype curpos = curfile.recno();
TString match;
if (err == NOERR)
match = curfile.curr().key(_nkey);
if (changed())
{
NFCHECK("Com'e' possibile che ci sia nuovamente bisogno di update?");
update();
}
if (err != NOERR)
{
_pos = _totrec - 1;
if (_pos < 0)
{
curfile.zero();
err = _isemptyfile;
}
else
readrec();
curfile.setstatus(err);
}
else
{
const int pages = (_totrec-1) / _cmaxelpage + 1;
_pos = -1;
if (pages > 1)
{
// const TString match(curfile.curr().key(_nkey));
bool found = FALSE;
FOR_EACH_ARRAY_ROW(_fpkey, p , s)
{
if (match <= *s)
{
const int pagecnt = read_page(p);
found = true;
for (int i = 0; i < pagecnt && _pos < 0L; i++)
if (_page[i] == curpos)
_pos = _pagefirstpos + i;
}
}
if (!found)
{
const int pagecnt = read_page(pages - 1);
for (int i = 0; i < pagecnt && _pos < 0L; i++)
if (_page[i] == curpos)
_pos = _pagefirstpos + i;
}
}
else
{
for (int i = 0; i < _cmaxelpage && _pos < 0L; i++)
if (_page[i] == curpos)
_pos = i;
}
if (_pos < 0L)
_pos = 0L;
readrec();
}
return _pos;
}
TCursor & TCursor::copy(const TCursor & cursor)
{
safe_delete(_if);
_if = new TRelation(*cursor._if);
_nkey = cursor._nkey;
file().setkey(_nkey);
_fexpr = cursor._fexpr == nullptr ? nullptr : new TExpression(*cursor._fexpr);
_filename = cursor._filename;
_filter = cursor._filter;
_keyfrom = cursor._keyfrom;
_keyto = cursor._keyto;
FOR_EACH_ARRAY_ITEM(cursor._frefs, i, obj)
{
TRecfield * fref = (TRecfield *)obj;
_frefs.add(new TRecfield(_if->curr(fref->record().num()), fref->name(), fref->from(), fref->to()));
}
_frozen = cursor._frozen;
_filter_update = cursor._filter_update;
_filterfunction_update = cursor._filterfunction_update;
_filter_limit = cursor._filter_limit;
_filterfunction = cursor._filterfunction;
_pos = 0;
_totrec = 0;
_lastrec = 0;
_lastkrec = 0;
_pagefirstpos = 0L;
return *this;
}
TCursor::TCursor(TRelation* r, const char* fil, int nkey,
const TRectype *from, const TRectype* to, int tilde)
: _if(r), _nkey(nkey), _fexpr(NULL), _frozen(false), _filter_update(false),
_filterfunction_update(false), _filter_limit(0), _filterfunction(NULL)
{
CHECK(r, "Null cursor relation");
file().setkey(_nkey);
_pos = 0;
_totrec = 0;
_lastrec = 0;
_lastkrec = 0;
_pagefirstpos = 0L;
_page = new TRecnotype[_cmaxelpage];
memset(_page, 0, _cmaxelpage * sizeof(TRecnotype));
filter(fil, from, to, tilde);
}
TCursor::TCursor(const TCursor & cursor) : _if(nullptr)
{
copy(cursor);
_page = new TRecnotype[_cmaxelpage];
memset(_page, 0, _cmaxelpage * sizeof(TRecnotype));
}
TCursor::~TCursor()
{
if (_indexname.not_empty())
{
if (_indexname == _last_name && _last_ndx != NULL)
{
_last_created = TRUE; // Force close
close_index(_last_ndx);
}
::remove(_indexname);
}
SAFE_DELETE(_page);
SAFE_DELETE(_fexpr);
}
TRecnotype TCursor::readrec()
{
TRecnotype nrec = 0L;
TLocalisamfile& f = file();
if (_pos >= items())
{
f.setstatus(_iseof);
f.zero();
return nrec;
}
f.setstatus(NOERR);
if (_pos < 0)
_pos = 0;
if (_pos < _pagefirstpos || _pos >= _pagefirstpos + _cmaxelpage)
read_page(_pos / _cmaxelpage);
nrec = _page[_pos - _pagefirstpos];
CHECKD(nrec >= 0, "Bad record position ", nrec);
curr().setdirty();
curr().readat(f, nrec);
repos();
return nrec;
}
// @doc EXTERNAL
// @mfunc Mette un lock sul record
//
// @rdesc Ritorna il numero di errore che si verifica nel porre il lock (NOERR) se
// non si verificano errori
int TCursor::lock(
TReclock l) // @parm Tipo di lock da porre sul record (vedi <t TReclock>)
{
int rt=NOERR;
switch(l)
{
case _lock:
rt=DB_lock_rec(file().handle(),_pos);
break;
case _unlock:
rt=DB_unlock(file().handle());
break;
default:
break;
}
if (rt != NOERR) rt=get_error(rt);
return(rt);
}
TRecnotype TCursor::operator =(const TRecnotype pos)
{
if (changed())
update();
CHECKD(pos >= 0 && pos <= _totrec, "Bad cursor position : ", pos);
_pos = pos;
readrec();
return _pos;
}
TRecnotype TCursor::operator +=(const TRecnotype npos)
{
if (changed())
update();
_pos += npos;
if (_pos > _totrec)
_pos = _totrec;
else
if (_pos < 0)
_pos = 0;
readrec();
return _pos;
}
TRecnotype TCursor::items()
{
if (changed())
update();
return _totrec;
}
bool TCursor::next_match(int lognum, const char* fl, int nk)
{
if (lognum == 0 || lognum == file().num())
{
++(*this);
return file().good();
}
else
return _if->next_match(lognum, fl, nk);
}
bool TCursor::is_first_match(int ln)
{
return (ln == 0 || ln == file().num()) ?
(_pos == 0 && file().good()) : (_if->is_first_match(ln));
}
bool TCursor::scan(CURSOR_SCAN_FUNC func, void* pJolly, const char* msg)
{
TRecnotype tot = 0; // Temporarily
{
TWait_cursor hourglass;
tot = items();
}
bool ok = true;
if (tot > 0)
{
TProgress_monitor* pi = NULL;
if (tot > 1)
{
if (msg == NULL || *msg == '\0')
msg = TR("Elaborazione in corso...");
pi = new TProgress_monitor(tot, msg, true);
}
freeze(true);
for (*this = 0; pos() < tot; ++*this)
{
if (!func(*relation(), pJolly))
{
ok = false;
break;
}
if (pi != NULL && !pi->add_status())
{
ok = false;
break;
}
}
freeze(false);
SAFE_DELETE(pi);
}
return ok;
}
///////////////////////////////////////////////////////////
// TSorted_cursor
///////////////////////////////////////////////////////////
typedef struct
{
char f[256];
TRecnotype p;
} El_To_Sort;
// @doc EXTERNAL
// @mfunc Controlla la validita' dell'espressione
//
// @rdesc Ritorna se si tratta di una espressione valida
bool TSorted_cursor::check_expr(
TString& s) // @parm Espressione da controllare
// @comm Modifica l'espressione in modo da avere una valido parametro per TFieldref!
// <nl>Es. UPPER(20->RAGSOC[1,40]+) ==> 20->RAGSOC[1,40]+
// <nl>N.B.: la direzione dell'ordinamento va messa indicata sempre dopo la definizione del campo, mai
// dopo l'operatore UPPER(). E' a cura delle altre funzioni esterne che utilizzano la check_expr()
// discriminare il segno + o - che sia.
{
int p=s.find('(');
int p1=s.find(')');
bool rt=TRUE;
if (p>-1 && p1>-1)
if (s.find("UPPER") < 0)
{
error_box("Only UPPER() operator is allowed");
rt=FALSE;
}
else
{
s=s.mid(6,s.len()-7);
p=s.find('(');
p1=s.find(')');
if (p>-1 || p1 >-1)
{
error_box("Too many parentheses!");
rt=FALSE;
}
}
else
if ((p>-1 && p1==-1) || (p==-1 && p1>-1))
{
error_box("Unbalanced parentheses in expression");
rt=FALSE;
}
return rt;
}
bool TSorted_cursor::is_upper(TString& s)
// Controlla se nell'espressione esiste l'operatore UPPER()
{
bool rt=FALSE;
if (s.find("UPPER") >= 0)
rt = check_expr(s);
return rt;
}
TRecnotype TSorted_cursor::buildcursor(TRecnotype rp)
{
TRecnotype oldrecno=0,pos,ap = 0;
int abspos=0,junk, l, pagecnt = 0;
TString s;
FILE* _f = NULL;
setpagefirstpos(0L);
memset(page(), 0, sizeof(TRecnotype) * pagesize());
fpkey().destroy();
if (file().empty())
return 0;
TSort sort(sizeof(El_To_Sort));
_order_expr.restart();
while ((s=_order_expr.get()).not_empty())
{
check_expr(s); // Toglie l'eventuale operatore UPPER(), in modo che l'ultimo carattere
// indichi eventualmente la direzione dell'ordinamento
const char last = s.right(1)[0];
const char versus = (last=='-') ? 'd' : 'a';
if (last == '-' || last == '+')
s.rtrim(1);
TFieldref f(s,0);
// Il controllo del file e' automatico in f.len()
const TString& id = f.id();
const TRectype& r = relation()->lfile(id).curr();
int flen = f.len(r);
if (flen == 0) // Campo virtuale
flen = 50;
if (id.full())
{
_sort_update = true;
const int file_id = relation()->name2ind(id);
if (filter_limit() < file_id)
set_filter_limit(file_id);
}
sort.addsortkey(abspos,flen,versus);
CHECKS(flen!=0,"Field can not have null length: ",(const char *) s);
abspos += flen;
CHECKD(abspos<=256, "Sort key too long: ", abspos);
}
const int handle = file().handle();
sort.init();
l = strlen(to());
double perc = 1.0;
if (l > 0 && DB_index_seek(handle, to()) >= 0)
perc = DB_pos_perc(handle);
junk=DB_index_seek(handle, (char*)(const char*) from());
if (junk < 0) junk=get_error(junk);
if (junk == _iseof) return 0;
pos = DB_index_recno(handle);
TCursor::pos()=-1;
perc -= DB_pos_perc(handle);
// Barra di progressione
const long items = (long)((double)relation()->lfile().items() * perc);
TString msg("Elaborazione ");
TDir d(relation()->lfile().num());
msg << d.des();
TProgress_monitor pi(items, msg);
while (!DB_index_eof(handle))
{
pi.add_status();
const char* s0 = DB_index_getkey(handle);
if (l && (strncmp(to(), s0, l) < 0))
break;
const TRecnotype recno = DB_index_recno(handle);
if (recno == oldrecno)
break; // means that no more keys are available
oldrecno=recno;
// Attenzione: la filtercursor non si si limita a filtrare ma avanza anche il cursore!!!!
const bool to_be_added = filtercursor(recno);
if (to_be_added)
{
El_To_Sort Element;
TFixed_string f(Element.f, sizeof(Element.f));
fill_sort_key(f);
Element.p=recno;
sort.sort((char *) &Element);
if (TCursor::pos() < 0 && recno == rp)
TCursor::pos() = ap;
ap++;
}
long rec = DB_index_next(handle);
if (rec < 0)
fatal_box("Can't read index n. %d - file n. %d",file().getkey(),file().num());
} // while
sort.endsort();
pagecnt = 0;
El_To_Sort* Element = NULL;
while ((Element=(El_To_Sort *)sort.retrieve()) != NULL)
{
page()[pagecnt++]=Element->p;
if (pagecnt == pagesize())
{
if (_f == NULL)
{
_f = open_index(TRUE);
fseek(_f, 0L, SEEK_SET);
}
fwrite(page(), sizeof(TRecnotype) * pagesize(), 1, _f);
pagecnt=0;
fpkey().add(new TToken_string(Element->f));
}
}
if (_f != NULL)
{
if (pagecnt > 0)
fwrite(page(),sizeof(TRecnotype) * pagecnt, 1, _f);
close_index(_f);
}
setpagefirstpos(ap > pagesize() ? -(pagesize() + 1) : 0L) ;
return ap;
}
const char* TSorted_cursor::fill_sort_key(TString& k)
{
TString256 sf;
k.cut(0);
for (TString80 s = _order_expr.get(0); s.not_empty(); s = _order_expr.get())
{
const bool is_up = is_upper(s);
const char last = s.right(1)[0];
const bool align = last == '*';
if (last == '-' || last == '+' || align)
s.rtrim(1);
const TFieldref f(s,0);
sf = f.read(*relation());
const TRectype& frec = curr(f.file());
TFieldtypes fld_type = frec.type(f.name());
int fld_len = f.len(frec);
// Converte in ANSI i campi data ed i sottocampi con una data!
if (fld_type == _datefld || (f.is_subfield() && TDate::isdate(sf)))
{
if (sf.not_empty()) // Aggiunta del 17-10-2011 per efficienza stampe mg (ma potrebbe non andar bene!)
{
const TDate d(sf);
sf.format("%08ld", d.date2ansi());
}
else
sf.spaces(8);
fld_type = _datefld;
fld_len = 8;
}
else
{
if (is_up) // Test inutile: tutte le chiavi sono maiuscole 08-02-2016
sf.upper();
}
switch (fld_type)
{
case _boolfld:
case _charfld:
case _memofld:
case _alfafld: sf.left_just(fld_len > 0 ? fld_len : 50); break;
case _intfld:
case _longfld:
case _intzerofld:
case _longzerofld: sf.right_just(fld_len > 0 ? fld_len : 50); break;
case _datefld: break; // Gia' lungo 8!
case _realfld:
if (align)
{
real r(sf);
sf = r.string(fld_len > 0 ? fld_len : 50, fld_len > 0 ? (fld_len-4)/2 : 10);
}
else
sf.right_just(fld_len > 0 ? fld_len : 50);
break;
default :
sf.left_just(fld_len > 0 ? fld_len : 50);
break;
}
k << sf;
}
return k;
}
bool TSorted_cursor::changed()
{
bool rt = false;
if (_is_valid_expr)
{
rt = TCursor::changed();
if (!rt) rt=_is_changed_expr;
_is_changed_expr = false;
}
else
NFCHECK("Can't perform changed() while sorted cursor expression is not valid!");
return rt;
}
int TSorted_cursor::test(TIsamop op, TReclock lockop)
{
int err = NOERR;
if (items() == 0L)
{
err = _isemptyfile;
file().setstatus(err);
return err;
}
TString256 searching; fill_sort_key(searching);
searching.rtrim();
const int cmplen = searching.len();
TRecnotype first = 0L;
TRecnotype last = items()-1;
TRecnotype found = -1L;
FOR_EACH_ARRAY_ROW(fpkey(), i, s)
{
const int cmp = searching.compare(*s, cmplen);
if (cmp <= 0)
last = (i + 1) * pagesize() - 1;
else
first = i * pagesize();
}
const bool ghiacciato = !frozen();
if (ghiacciato) freeze(true);
TString256 testing;
while (first <= last)
{
const TRecnotype test = (first+last)/2;
TCursor::operator=(test); // verif
fill_sort_key(testing);
const int cmp = searching.compare(testing, cmplen);
if (cmp == 0)
{
if (op != _isgreat)
{
if (found < 0l || test < found)
found = test;
last = test-1;
}
else
first = test+1;
}
else
{
if (cmp < 0)
{
last = test-1;
if (op != _isequal)
{
if (found < 0l || test < found)
found = test;
}
}
else
first = test+1;
}
}
if (found >= 0L)
{
TCursor::operator=(found); // verif
file().setstatus(NOERR);
if (lockop != _nolock)
lock(lockop);
}
else
{
found = items()-1;
if (found >= 0)
TCursor::operator=(found);
err = op == _isequal ? _iskeynotfound : _iseof;
file().setstatus(err);
}
if (ghiacciato)
freeze(false);
return err;
}
TRecnotype TSorted_cursor::read(TIsamop op, TReclock lockop)
{
TString256 searching; fill_sort_key(searching);
searching.rtrim();
const int cmplen = searching.len();
TRecnotype first = 0L;
TRecnotype last = items()-1;
TRecnotype found = -1L;
FOR_EACH_ARRAY_ROW(fpkey(), i, s)
{
const int cmp = searching.compare(*s, cmplen);
if (cmp <= 0)
last = (i + 1) * pagesize() - 1;
else
first = i * pagesize();
}
const bool ghiacciato = !frozen();
if (ghiacciato) freeze(true);
TString256 testing;
while (first <= last)
{
const TRecnotype test = (first+last)/2;
TCursor::operator=(test);
fill_sort_key(testing);
const int cmp = searching.compare(testing, cmplen);
if (cmp == 0)
{
if (op != _isgreat)
{
if (found < 0l || test < found)
found = test;
last = test-1;
}
else
first = test+1;
}
else
{
if (cmp < 0)
{
last = test-1;
if (op != _isequal)
{
if (found < 0l || test < found)
found = test;
}
}
else
first = test+1;
}
}
if (found >= 0L)
{
TCursor::operator=(found);
file().setstatus(NOERR);
if (lockop != _nolock)
lock(lockop);
}
else
{
found = items()-1;
if (found >= 0)
TCursor::operator=(found);
file().setstatus(op == _isequal ? _iskeynotfound : _iseof);
}
if (ghiacciato)
freeze(false);
return found;
}
void TSorted_cursor::change_order(const char* order_expr)
{
_sort_update = false;
if (order_expr && *order_expr && _order_expr != order_expr)
{
TString s;
_order_expr = order_expr;
_order_expr.restart();
while ((s=_order_expr.get()).not_empty() && (_is_valid_expr=check_expr(s))) ;
if (_is_valid_expr)
_is_changed_expr=true;
}
}
TSorted_cursor::TSorted_cursor(TRelation *f, const char * order_expr, const char * filter, int key, const TRectype* from, const TRectype* to)
: TCursor(f,filter,key,from,to)
{
change_order(order_expr);
}
TSorted_cursor::~TSorted_cursor()
{
}
///////////////////////////////////////////////////////////
// TFieldRef
///////////////////////////////////////////////////////////
// @doc EXTERNAL
// @func Converte una stringa in numero logico o numero tabella
//
// @rdesc Ritorna il numero logico del file o il numero della tabella
int name2log(
const char* name) // @parm Stringa da convertire
{
int log = 0;
if (name)
{
while (isspace(*name))
name++;
if (*name)
{
log = table2logic(TFixed_string(name));
CHECKS(log != 0, "Non e' un file ne' una tabella: ", name);
}
}
return log;
}
TFieldref::TFieldref() : _fileid(0), _name(0) {}
TFieldref::TFieldref(const TString& s, short defid)
{
operator=(s);
if (_fileid == 0) _fileid = defid;
}
void TFieldref::copy(const TFieldref& f)
{
_fileid = f._fileid;
_id = f._id;
_name = f._name;
_from = f._from;
_to = f._to;
}
TObject* TFieldref::dup() const
{
TFieldref* f = new TFieldref(*this);
return f;
}
// @doc EXTERNAL
// @mfunc Operatore di assegnamento
//
// @rdesc Ritorna l'oggetto assegnato
TFieldref& TFieldref::operator =(
const TString& s) // @parm Stringa da assegnare all'oggetto
// @comm Un <c TFieldref> deve avere il seguente formato (solamente NAME e il mandante):
// <nl>FILE->NAME[FROM,TO]
{
int pos = s.find("->");
if (pos > 0)
{
_id = s.left(pos); _id.strip(" ");
_fileid = name2log(_id);
pos += 2;
}
else
{
pos = s.find('.');
if (pos > 0)
{
_id = s.left(pos); _id.strip(" ");
_fileid = name2log(_id);
pos++;
}
else
{
_id.cut(0);
_fileid = pos = 0;
}
}
int par = s.find('[', pos); // Cerca la fine del nome del campo
_name = s.sub(pos, par); // Estrae il nome del campo
_name.strip(" "); // Elimina eventuali spazi superflui
if (par > 0)
{
pos = par+1;
set_from(atoi(s.mid(pos)));
par = s.find(',', pos);
if (par > 0) set_to(atoi(s.mid(par+1)));
else set_to(from()+1);
}
else
{
set_from(0);
set_to(-1);
}
return *this;
}
void TFieldref::set_file(int f)
{
_id.cut(0);
if (f != 0)
{
_fileid = f;
_id << abs(f);
if (f < 0) _id << '@';
}
else _fileid = 0;
}
void TFieldref::print_on(ostream& out) const
{
if (_id.not_empty()) out << _id << "->";
out << _name;
if (_from > 0 || _to > 0)
{
out << '[' << (_from+1);
out << ',' << _to << ']';
}
}
static TString buffer;
const char* TFieldref::read(TConfig& ini, const char* defpar) const
{
ini.set_paragraph(_id.empty() ? defpar : (const char *) _id);
if (!ini.exist(_name))
return "";
buffer = ini.get(_name);
// Gestisce valori tra virgolette
const int l = buffer.len();
if (l > 1)
{
if ((buffer[0] == '"' || buffer[0] == '\'') && buffer[0] == buffer[l-1])
{
buffer.rtrim(1);
buffer.ltrim(1);
if (strchr(buffer, '\\'))
buffer = esc(buffer);
}
}
if (_from > 0 || _to > 0)
{
if (_to < l && _to > 0) buffer.cut(_to);
if (_from > 0) buffer.ltrim(_from);
}
return buffer;
}
static int quotes_needed(const char* val)
{
int yes = 0;
for ( ; *val; val++) if (strchr("\t\n\r\"' ", *val))
{
yes = 1;
if (*val < ' ')
{
yes = 2;
break;
}
}
return yes;
}
static void escapes(TString& val)
{
for (int i = 0; val[i]; i++)
{
if (val[i] == '\n')
{
val.insert(" ", i);
val[i++] = '\\';
val[i] = 'n';
} else
if (val[i] == '\r')
{
val.insert(" ", i);
val[i++] = '\\';
val[i] = 'r';
} else
if (val[i] == '\t')
{
val.insert(" ", i);
val[i++] = '\\';
val[i] = 't';
}
}
}
void TFieldref::write(TConfig& ini, const char* defpar, const char* val) const
{
if ((val == NULL || *val == '\0') && !ini.exist(_name))
return;
const char* para = _id.empty() ? defpar : (const char*)_id;
if (_from > 0 || _to > 0)
{
buffer = ini.get(_name, para);
if ((buffer[0] == '"' || buffer[0] == '\'') && buffer[0] == buffer[buffer.len()-1])
{
buffer.rtrim(1);
buffer.ltrim(1);
if (strchr(buffer, '\\'))
buffer = esc(buffer);
}
buffer.overwrite(val, _from);
val = buffer;
}
const int qn = quotes_needed(val); // Controlla se c'e' bisogno di virgolette
if (qn)
{
if (qn == 2)
{
buffer = val;
escapes(buffer);
buffer.insert("\"", 0);
buffer << '"';
}
else
{
const char* virg = strchr(val, '"') ? "'" : "\"";
buffer = val;
buffer.insert(virg, 0);
buffer << virg;
}
ini.set(_name, buffer, para);
}
else
ini.set(_name, val, para);
}
const char* TFieldref::read(const TRectype& rec) const
{
if (_fileid >= CNF_GENERAL)
{
TToken_string s(_name, '.');
TConfig c(_fileid - CNF_STUDIO, s.get(0));
buffer = c.get(s.get());
}
else
{
if (_from > 0 || _to > 0 || _name.find(':') > 0)
{
const TRecfield rf((TRectype&)rec, _name, _from, _to-1);
if (rf.type() == _memofld && _to > 0)
buffer = rec.get(_name).sub(_from, _to);
else
buffer = rf;
}
else
buffer = rec.get(_name);
}
return buffer;
}
const char* TFieldref::read(const TRelation& r) const
{
const TRectype& rec = _id.empty() ? r.curr() : r.lfile(_id).curr();
const char *s = read(rec);
return s;
}
void TFieldref::write(const char* val, TRelation& r) const
{
TRectype& rec = _id.empty() ? r.curr() : r.lfile(_id).curr();
write(val, rec);
}
void TFieldref::write(const char* val, TRectype& rec) const
{
if (_fileid >= CNF_GENERAL)
return;
if (_name.find(':') > 0)
{
TRecfield rf(rec, _name, _from, _to-1);
rf = val;
} else
if (_from > 0 || _to > 0)
{
buffer = rec.get(_name);
if (_to <= _from)
((TFieldref*)this)->_to = rec.length(_name);
buffer.overwrite(val, _from, _to - _from);
rec.put(_name, buffer);
}
else
{
if (val && *val)
rec.put(_name, val);
else
rec.zero(_name);
}
}
int TFieldref::len(const TRectype &rec) const
{
int len = 0;
if (_to < _from)
{
if (_fileid > 0)
{
const RecDes& recd = prefix().get_recdes(_fileid);
const int p = findfld(&recd, _name);
len = p != FIELDERR ? recd.Fd[p].Len : 0;
}
else
len = rec.length(_name);
}
else
len = _to;
len -= _from;
return len;
}
///////////////////////////////////////////////////////////
// TRelation_description
///////////////////////////////////////////////////////////
void TRelation_description::init_files_array()
{
const TLocalisamfile& f = _rel->file();
TToken_string s(128);
print_name(s, f); // Logic number
const char* name = f.name();
if (f.tab())
name = TDir::tab_des(name);
else
name = prefix().description(name);
s.add(name); // Description
s.add(" "); // No join
s.add(f.getkey()); // Key
s.add(" | "); // No Alias nor expression
_files.destroy();
_files.add(s); // Main file
for (int i = 0; i < _rel->items(); i++)
{
_rel->reldef(i).print_on(s);
_files.add(s);
}
}
void TRelation_description::read_rel()
{
// scan files and build description arrays
init_files_array();
_fields.destroy();
for (int i = 0; i < _files.items(); i++)
{
TToken_string& tt = (TToken_string&)_files[i];
TFilename descfname; descfname << DESCDIR << "/d";
const char* tn = tt.get(0); // Codice del file o tabella
const int which_file = name2log(tn); // Numero logico del file
if (tn[0] == '%' || tn[0] == '$')
descfname << (tn+1);
else
descfname << tn;
descfname << ".des";
TConfig conf(descfname, DESCPAR);
// new record descriptor for _fields array
TString_array* rdesc = new TString_array;
TTrec trec; trec.get(which_file);
TToken_string ttmp(64);
TString dfld(256);
for (int f = 0; f < trec.fields(); f++)
{
ttmp = trec.fielddef(f);
const TString16 name(ttmp.get(0));
if (!name.blank())
{
dfld = conf.get(name, NULL, -1, "Missing description");
if (!dfld.blank() && dfld[0] != '#')
{
ttmp.add(dfld,4);
// contiene: nome campo, tipo, lunghezza, decimali, descrizione
rdesc->add(ttmp);
}
}
}
_fields.add(rdesc, i);
}
}
// @doc INTERNAL
// @mfunc Cambia la relazione descritta
void TRelation_description::change_relation(
TRelation& r, // @parm Nuova relazione da assegnare
TString_array& a) // @parm Array di descrittore dei file
{
_rel = &r;
read_rel();
if (a.items() > 0)
_files = a;
}
void TRelation_description::print_on(ostream& out) const
{
for (int i = 0; i < _files.items(); i++)
{
TToken_string& r = ((TRelation_description*)this)->_files.row(i);
TString16 cod(r.get(4));
if (atoi(cod) == 0)
cod = r.get(0);
else
cod << '@';
out << " " << cod << "->* ";
out << "\"" << r.get(1) << "\"\n";
}
}
// @doc INTERNAL
// @mfunc Seleziona un file
//
// @rdesc Ritorna il risultato dell'operazione:
//
// @flag TRUE | E' stato selzionato il file
// @flag FALSE | Non e' riuscito a selezionare il file
bool TRelation_description::choose_file(
int file) // @parm Numero logico del file da selezionare (default 0)
// @comm Dopo aver scelto un file (occorre che ritorni TRUE) i metodi della classe
// permettono di conoscere tutti i dati riguardanti i parametri del file selezionato
{
TArray_sheet sht(-1,-1,-4,-4,"Selezione archivio", "Codice|Descrizione archivio@70");
TToken_string tt(80);
int sel = 0;
for (int i = 0; i < _files.items(); i++)
{
TToken_string& tf = _files.row(i);
tt = tf.get(4);
int num = atoi(tt);
if (num == 0)
{
tt = tf.get(0);
num = atoi(tt);
if (sel == 0 && file > 0 && num == file)
sel = i;
}
else
{
if (sel == 0 && file < 0 && num == -file)
sel = i;
tt << '@';
}
tt.add(tf.get(1));
sht.add(tt);
}
sht.select(sel);
if (sht.run() == K_ENTER)
{
_cur_file = (int)sht.selected();
_cfile = _files.row(_cur_file);
return TRUE;
}
return FALSE;
}
// @doc INTERNAL
// @mfunc Seleziona un campo <p fld> del file selezionato
//
// @rdesc Ritorna il risultato dell'operazione:
//
// @flag TRUE | E' stato selzionato il campo
// @flag FALSE | Non e' riuscito a selezionare il campo
bool TRelation_description::choose_field(
const char* fld) // @parm Nome del campo da selezionare (default "")
// @comm Occorre che sia selezionato un file, diversamente viene considerato
// selezionato il primo file
{
TArray_sheet sht(-1,-1,76,20,"Selezione campo",
"Campo@10|Descrizione@50|Tipo@10|Dim.");
TString_array& fd = (TString_array&)_fields[_cur_file];
TToken_string tt(80);
int sel = 0;
for (int i = 0; i < fd.items(); i++)
{
TToken_string& tf = fd.row(i);
tt = tf.get(0);
if (sel == 0 && tt == fld)
sel = i;
tt.add(tf.get(4));
tt.add(field_type_str((TFieldtypes)tf.get_int(1)));
tt.add(tf.get(2));
sht.add(tt);
}
sht.select(sel);
if (sht.run() == K_ENTER)
{
_cur_field = (int)sht.selected();
_cfield = fd.row(_cur_field);
return true;
}
return false;
}
// @doc INTERNAL
// @mfunc Costruisce un menu
//
// @rdesc Ritorna solamente FALSE
bool TRelation_description::build_menu(
const char* title) // @parm Titolo del menu'
// @comm Costruisce un albero di menu' e setta il campo corrente quando viene
// selezionato (deve essere popup)
//
// @devnote Funzione non implementata.
{
return false;
}
bool TRelation_description::remove_menu()
{
return false;
}
bool TRelation_description::set_cur_file(int id)
{
const int n = _rel->log2ind(id);
if (n >= 0 && n != _cur_file)
{
_cur_file = n;
_cfile = _files.row(n);
}
return n >= 0;
}
int TRelation_description::file_num()
{
int n = -_cfile.get_int(4);
if (n == 0)
n = _cfile.get_int(0);
return n;
}
const char* TRelation_description::file_desc()
{
return _cfile.get(1);
}
void TRelation_description::file_desc(const char* desc)
{
_cfile.add(desc, 1);
_files.row(_cur_file) = _cfile;
}
const char* TRelation_description::field_desc()
{
return _cfield.get(4);
}
const char* TRelation_description::field_name()
{
return _cfield.get(0);
}
int TRelation_description::field_len()
{
return _cfield.get_int(2);
}
TFieldtypes TRelation_description::field_type()
{
return (TFieldtypes)_cfield.get_int(1);
}
// @doc INTERNAL
// @mfunc Setta la descrizione del campo
//
// @rdesc Ritorna se ha trovato il campo da settare
bool TRelation_description::set_field_description(
const char* field, // @parm Campo a cui assegnare la descrizione
const char* des) // @parm Descrizione da assegnare al campo
{
TString_array& fld = (TString_array&)_fields[_cur_file];
for (int i = 0; i < fld.items(); i++)
{
TToken_string& tt = fld.row(i);
if (strcmp(tt.get(0),field) == 0)
{
if (des && *des)
tt.add(des, 4);
else
{
fld.destroy(i);
fld.pack();
}
return TRUE;
}
}
if (des && *des)
{
TToken_string tt(80);
tt.add(field);
tt.add(des, 4);
fld.add(tt);
return TRUE;
}
return FALSE;
}
const char* TRelation_description::get_field_description(const char* field)
{
TString_array& fld = (TString_array&)_fields[_cur_file];
for (int i = fld.items()-1; i >= 0; i--)
{
TToken_string& tt = fld.row(i);
if (strcmp(tt.get(0),field) == 0)
return tt.get(4);
}
return "";
}
TRelation_description::TRelation_description(TRelation& r)
: _rel(&r), _cur_file(0), _cur_field(0), _menu(FALSE),
_cfile(80), _cfield(80)
{
read_rel();
if (_files.items() > 0)
{
_cfile = _files.row(0);
_cfield = "";
}
}
TRelation_description::~TRelation_description()
{
if (_menu) remove_menu();
}
////////////////////////////////////
// TSortedfile
////////////////////////////////////
// @mfunc Avanza di <p npos> record
int TSortedfile::operator +=(const TRecnotype npos)
{
int err = NOERR;
TRecnotype p = _curs->pos() + npos;
if (p < 0)
{
p = 0;
err = _isbof;
}
if (p >= _curs->items())
{
err = _iseof;
p = _curs->items();
}
*_curs = p;
setstatus(err);
return err;
}
// @mfunc Sposta indietro di <p npos> record
int TSortedfile::operator -=(const TRecnotype npos)
{
return operator+=(-npos);
}
// @mfunc Avanza al record successivo
int TSortedfile::operator ++()
{
return *this+=1;
}
// @mfunc Indietreggia al record precedente
int TSortedfile::operator --()
{
return *this-=1;
}
// @mfunc Si posiziona sul primo record del file (vedi <t TReclock>)
int TSortedfile::first(word lockop)
{
int err = _curs->items() > 0 ? NOERR : _iseof;
_curs->first_item();
setstatus(err);
return err;
}
// @mfunc Si posiziona sull'ultimo record del file (vedi <t TReclock>)
int TSortedfile::last(word lockop)
{
int err = _curs->items() > 0 ? NOERR : _iseof;
_curs->last_item();
setstatus(err);
return err;
}
// @mfunc Si posiziona sul successivo record del file (vedi <t TReclock>)
int TSortedfile::next(word lockop )
{
int err = _curs->pos() < _curs->items() ? NOERR : _iseof;
if (err == NOERR)
_curs->succ_item();
setstatus(err);
return err;
}
// @mfunc Si posiziona sul precedente record del file (vedi <t TReclock>)
int TSortedfile::prev(word lockop )
{
int err = _curs->pos() > 0 ? NOERR : _isbof;
if (err == NOERR)
_curs->pred_item();
setstatus(err);
return err;
}
// @mfunc Salta <p nrec> record dalla posizione corrente (vedi <t TReclock>)
int TSortedfile::skip(TRecnotype nrec, word lockop )
{
*_curs += nrec; // manca il lock...
setstatus(_curs->file().status());
NFCHECK("Operazione 'skip' non consentita sul cursore");
return 0;
}
// @mfunc Rilegge l'ultimo record letto (vedi <t TReclock>)
int TSortedfile::reread(word lockop )
{
return reread(curr(),lockop);
}
// @mfunc Rilegge l'ultimo record letto e lo copia in <p rec> (vedi <t TReclock>)
int TSortedfile::reread(TRectype& rec, word lockop)
{
return 0;
}
// @mfunc Legge il record (vedi <t TReclock> e <t TIsamop>)
int TSortedfile::read(word op , word lockop )
{
return read(curr(),op,lockop);
}
// @mfunc Legge il record e lo copia in <p rec> (vedi <t TReclock> e <t TIsamop>)
int TSortedfile::read(TRectype& rec, word op , word lockop )
{
if (&curr() != &rec)
curr() = rec;
_curs->read((TIsamop)op,(TReclock)lockop);
setstatus(_curs->file().status());
if (&curr() != &rec)
rec = curr();
return status();
}
// @mfunc Legge il record alla posizione <p nrec> e lo copia in <p rec> (vedi <t TReclock>)
int TSortedfile::readat(TRectype& rec, TRecnotype nrec, word lockop )
{
// read(rec,_isequal,lockop);
// !!!!!!!!!!!!!! attenzione !!!!!!!!!!!!!!
// non usare un sorted file come file principale di un sorted cursor;
// portare invece le espressioni del sorted file a livello di cursore
// altrimenti questa readat non sempre funziona , perche' viene chiamata con
// parametri presi dalle funzioni a basso livello sugli isam, quindi relativi alle posizioni fisiche
// e non a quanto restituito da recno() (che da' la posizione all'interno del cursore)
*_curs=nrec;
if (&curr() != &rec)
rec=curr();
setstatus(_curs->file().status());
return status();
}
// @mfunc Legge il record alla posizione <p nrec> e lo copia in <p rec> (vedi <t TReclock>)
int TSortedfile::readat(TRecnotype nrec, word lockop )
{
return readat(curr(),nrec, lockop );
}
// @mfunc Aggiunge un record
int TSortedfile::write()
{
return write(curr());
}
// @mfunc Aggiunge un record copiando da <p rec>
int TSortedfile::write(const TRectype& rec)
{
NFCHECK("Operazione 'write' non consentita sul TSortedfile");
return 0;
}
// @mfunc Riscrive un record
int TSortedfile::rewrite()
{
rewrite(curr());
return 0;
}
// @mfunc Riscrive un record (il record <p rec>)
int TSortedfile::rewrite(const TRectype& rec)
{
NFCHECK("Operazione 'rewrite' non consentita sul TSortedfile");
return 0;
}
// @mfunc Riscrive un record alla posizione <p nrec> copiando da <p rec>
int TSortedfile::rewriteat(TRecnotype nrec)
{
return rewriteat(curr(),nrec);
}
// @mfunc Riscrive un record alla posizione <p nrec> copiando da <p rec>
int TSortedfile::rewriteat(const TRectype& rec, TRecnotype nrec)
{
NFCHECK("Operazione 'rewriteat' non consentita sul TSortedfile");
return 0;
}
// @mfunc Elimina il record
int TSortedfile::remove()
{
return remove(curr());
}
// @mfunc Elimina il record copiando da <p rec>
int TSortedfile::remove(const TRectype& rec)
{
NFCHECK("Operazione 'remove' non consentita sul TSortedfile");
return 0;
}
// @mfunc Attiva la chiave <p nkey> sul file aperto
void TSortedfile::setkey(int nkey)
{
_curs->setkey(nkey);
}
// @mfunc Resetta la regione del file (chiama <p TCursor::setregion>
void TSortedfile::setregion(const TRectype &f, const TRectype &t, int tilde)
{
_curs->setregion(f, t, tilde);
}
// @mfunc
TRecnotype TSortedfile::eod() const
{
return _curs->items();
}
// @mfunc
bool TSortedfile::empty()
{
return _curs->items()==0;
}
TRectype& TSortedfile::curr() const
{
return _curs->curr();
}
void TSortedfile::set_curr(TRectype* curr)
{
_curs->file().set_curr(curr);
}
// @mfunc Costruttore.
TSortedfile::TSortedfile(int logicnum, TRelation* rel, const char* ordexpr, const char* filter, int nkey)
: TLocalisamfile(logicnum), _rel(NULL)
{
// costruisce il cursore
if (!rel)
_rel = new TRelation(logicnum);
else
_rel = rel;
_curs = new TSorted_cursor(_rel, ordexpr, "", nkey);
if (&curr()!=&(_curs->file().curr()))
_curs->file().set_curr(&curr());
_curs->setfilter(filter,TRUE); //must pass TRUE because cursors doesn't update rel (BUG).
// DON't move this line BEFORE set_curr : filter's fieldrefs are allocated at cursor setfilter
}
// @mfunc Distruttore
TSortedfile::~TSortedfile()
{
SAFE_DELETE(_curs);
SAFE_DELETE(_rel);
}
//
// *** EOF relation.cpp