campo-sirio/include/relation.cpp
guy 21ea6df4df applicat.cpp Gestione di EdApp oltre che EdMask nelle configurazioni
execp.cpp    Aggiunta forzatura user sulla riga di comando
form.cpp     Aggiunte sezioni ad altezza variabile
form.h       Aggiuto metodo virtual bool TForm_item::is_section() const
isam.cpp     Forzatura user in chiamata delle conversioni
lffiles.h    Sostituito LF_ABPROF con LF_SVRIEP
mask.*       Aggiunto metodo sfield() che ritorna direttamente uno spreadsheet
rdoc.h       Risolti conflitti
relapp.cpp   Corretta cancellazione veloce di documenti protetti
relation.cpp Tolto spazio inutile


git-svn-id: svn://10.65.10.50/trunk@5062 c028cbd2-c16b-5b4b-a496-9718f37d4682
1997-08-18 14:11:37 +00:00

2474 lines
60 KiB
C++
Executable File

#include <ctype.h>
#include <stdlib.h>
#include <applicat.h>
#include <config.h>
#include <expr.h>
#include <extcdecl.h>
#include <prefix.h>
#include <relation.h>
#include <sheet.h>
#include <sort.h>
#include <tabutil.h>
#include <utility.h>
#include <xvtility.h>
#include <codeb.h>
// *** check if not already defined
#define NOTFOUND (-1)
// *** maximum number of elements in a cursor working page
#define CMAXELPAGE 8000
#define print_name(out, f) out << (f.num() == LF_TABCOM ? "%" : "") << 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 TBaseisamfile& from) const;
const char* evaluate_expr(int j, const TLocalisamfile& to);
void print_on(TToken_string& out) const;
TRelationdef(const TRelation* rel, int file, byte key,
int linkto, const char* relexprs, int alias,
bool allow_lock);
virtual ~TRelationdef() {}
};
TRelationdef::TRelationdef(const TRelation* rel, int idx_file, byte key,
int idx_to, const char* relexprs, int alias,
bool allow_lock)
: _num(idx_file), _key(key), _numto(idx_to),
_rel(rel), _fields(4), _exprs(4), _alias(alias),
_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);
}
}
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 = "";
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)
{
TExpression& expr = (TExpression&)_exprs[j];
TString16 name;
for (int k = 0; k < expr.numvar(); k++)
{
name = expr.varname(k); name.upper();
expr.setvar(k, to.get(name));
}
const char* val = expr;
if (*val == '\0' && _altexprs.objptr(j))
{
TExpression& altexpr = (TExpression&)_altexprs[j];
for (int k = 0; k < expr.numvar(); k++)
{
name = altexpr.varname(k); name.upper();
altexpr.setvar(k, to.get(name));
}
val = altexpr;
}
return val;
}
///////////////////////////////////////////////////////////
// TRelation
///////////////////////////////////////////////////////////
TRelation::TRelation(int logicnum)
: _files(4), _reldefs(4), _errors(NOERR)
{
TLocalisamfile* f = new TLocalisamfile(logicnum, FALSE);
_files.add(f);
}
TRelation::TRelation(const char* tabname)
: _files(4), _reldefs(4), _errors(NOERR)
{
TTable* t = new TTable(tabname, FALSE);
_files.add(t);
}
TRelation::TRelation(TLocalisamfile* l)
: _files(4), _reldefs(4), _errors(NOERR)
{
_files.add(l);
}
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()
{
_status.restart();
for (int i = 0; i < _files.items(); i++)
{
const int err = _status.get_int();
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).curr().zero();
file(i).setstatus(err);
}
for (i = 0; i < _reldefs.items(); i++)
{
const bool first_match = _status.get_int() ? TRUE : FALSE;
reldef(i)._first_match = first_match;
}
}
void TRelation::save_status()
{
_status.cut(0);
for (int 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(format("%d",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, "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];
}
// @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 _errors;
}
bool TRelation::add(int logicnum, const char* relexprs, int key,
int linkto, int alias, bool allow_lock)
{
TLocalisamfile* f = new TLocalisamfile(logicnum, FALSE);
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)
{
TTable* t = new TTable(tabname, FALSE);
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);
}
}
TRectype& TRelationdef::load_rec(TRectype& r, const TBaseisamfile& from) const
{
r.zero();
for (int j = 0 ; j < _fields.items(); j++) // for each field
{
TFieldref& s = (TFieldref&) _fields[j];
s.write(s.read(from.curr()),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
{
_errors = NOERR;
// workhorse: position files for each active relation
for (int i = first; i < _reldefs.items(); i++)
{
TRelationdef& rd = reldef(i);
TLocalisamfile& from = file(rd.num());
TLocalisamfile& to = file(rd.link());
TReclock lck = rd._allow_lock ? lockop : _nolock;
TRectype& furr = from.curr();
furr.zero();
if (to.curr().empty())
continue;
from.setkey(rd._key);
// build record
for (int j = 0 ; j < rd._fields.items(); j++) // for each field
{
TString expr (rd.evaluate_expr(j, to));
TFieldref& s = (TFieldref&) rd._fields[j];
if (from.is_sorted() && from.curr().type(s.name()) ==_alfafld)
expr.rpad(s.len(from.curr()),'~');
s.write(expr, furr);
} // for each field
if (from.is_sorted())
{
((TSortedfile &)from).setregion(from.curr(),from.curr());
from.first();
// read record: if not found, zero current record
} else
from.read(op, lck);
if (from.bad())
{
bool eq = TRUE;
for (int kk = 0; eq && kk < rd._fields.items(); kk++)
{
TFieldref& fl = (TFieldref&)rd._fields[kk];
const TString80 f_fr(fl.read(furr));
const TString80 f_ex(rd.evaluate_expr(kk, to));
if (rd._forced[kk])
eq = f_fr == f_ex;
else
eq = f_fr.compare(f_ex, f_ex.len()) == 0;
}
rd._first_match = eq;
if (eq)
from.setstatus(NOERR);
else
furr.zero();
}
else
rd._first_match = TRUE;
} // 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);
for (int j = 0; j < _reldefs.items(); j++)
if (reldef(j).num() == i) break;
TLocalisamfile& from = file(i);
TLocalisamfile& to = file(reldef(j).link());
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.curr());
reldef(j).load_rec(rec, from);
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);
for (const char* f = fields.get(0); f; f = fields.get())
old.add(from.curr().get(f));
from.next();
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++)
if (reldef(j).num() == i) return reldef(j)._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;
TRecnotype recnos[MAXREL];
for (int 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)
return _errors = bad;
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 _errors = bad;
}
#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;
}
///////////////////////////////////////////////////////////
// TCursor
///////////////////////////////////////////////////////////
HIDDEN bool __evalcondition(const TRelation& r,TExpression* cond)
{
TFieldref f;
for (int i = cond->numvar() - 1; i >= 0; i--)
{
const TFixed_string s(cond->varname(i));
f = s;
cond->setvar(i, f.read(r));
}
return (bool) *cond;
}
// @doc EXTERNAL
// @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)
{
#if XVT_OS == XVT_OS_SCOUNIX
const char* const r = "r";
const char* const w = "w";
#else
const char* const r = "rb";
const char* const w = "wb";
#endif
if (create && _indexname.empty()) _indexname.temp("ci$$");
FILE* f = fopen(_indexname, create ? w : r);
if (f == NULL)
fatal_box("Can't use cursor index for file %d: '%s'\n",
file().filehnd()->ln, (const char*)_indexname);
return f;
}
TRecnotype TCursor::buildcursor(TRecnotype rp)
{
TRecnotype oldrecno=0,pos,ap = 0;
int junk, l, pagecnt = 0;
const bool filtered = has_filter();
FILE* _f = open_index(TRUE);
if (DB_reccount(file().filehnd()->fhnd) == 0)
{
fclose(_f);
return 0;
}
fseek(_f, 0L, SEEK_SET);
l = strlen(to());
junk=DB_index_seek(file().filehnd()->fhnd, (char*)(const char*) from());
if (junk < 0) junk=get_error(junk);
if (junk == _iseof) return 0;
TRecnotype* page = new TRecnotype [CMAXELPAGE];
pos = DB_index_recno(file().filehnd()->fhnd);
_pos=-1;
while (TRUE)
{
if (DB_index_eof(file().filehnd()->fhnd)) break;
const char* s0 = DB_index_getkey(file().filehnd()->fhnd);
const TRecnotype recno = DB_index_recno(file().filehnd()->fhnd);
if (l && (strncmp(to(), s0, l) < 0)) break;
if (recno == oldrecno) break; // means that no more keys are available
oldrecno=recno;
if (pagecnt == CMAXELPAGE)
{
if (filtered) pagecnt = filtercursor(pagecnt,page);
fwrite(page,sizeof(TRecnotype),pagecnt,_f);
for (int i= 0; i< pagecnt; i++)
if (page[i] == rp)
{
_pos = ap + i;
break;
}
ap += pagecnt;
pagecnt = 0;
}
page[pagecnt++] = recno;
DB_index_next(file().filehnd()->fhnd);
int rt=get_error(-1);
if (rt != NOERR)
fatal_box("Can't read index n. %d - file n. %d",DB_tagget(file().filehnd()->fhnd),file().filehnd()->ln);
} // while
if (pagecnt)
{
if (filtered) pagecnt = filtercursor(pagecnt, page);
fwrite(page, sizeof(TRecnotype), pagecnt, _f);
for (int i = 0; i < pagecnt; i++)
if (page[i] == rp)
{
_pos = ap + i;
break;
}
ap += pagecnt;
}
if (_pos == -1) pos=0;
delete page;
fclose (_f);
return ap;
}
int TCursor::filtercursor(int pagecnt, TRecnotype* page)
{
int np=0, handle=file().filehnd()->fhnd;
TRectype& rec=file().curr();
for (int i=0; i< pagecnt; i++)
{
file().readat(rec,page[i]);
if (update_relation()) _if->update();
if ((_filterfunction ? _filterfunction(_if) : TRUE ) &&
(_fexpr ? __evalcondition(*_if, _fexpr) : TRUE))
{
if (np < i) page[np] = page[i];
np++;
}
}
return np;
}
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;
}
if (update_relation())
_if->update();
if ((_filterfunction ? _filterfunction(_if) : TRUE ) &&
(_fexpr ? __evalcondition(*_if, _fexpr) : TRUE))
return TRUE;
return FALSE;
}
bool TCursor::changed()
{
isdef* fh = file().filehnd();
if (_frozen && _lastrec > 0L)
return _filename != fh->d->SysName;
const TRecnotype eod = DB_reccount(fh->fhnd);
if (_lastrec != eod ||
(_lastkrec != DB_changed(fh->fhnd)) ||
(_filename != fh->d->SysName))
{
// _lastrec = eod;
// _filename = fh->d->SysName;
// _lastkrec = DB_changed(fh->fhnd);
return TRUE;
}
if (!curr().valid())
return TRUE;
return FALSE;
}
TRecnotype TCursor::update()
{
begin_wait();
file().setkey(_nkey);
if (file().curr().empty()) file().curr().zero();
file().read(_isgteq);
const TRecnotype totrec = buildcursor(file().recno());
isdef* fh = file().filehnd();
const TRecnotype eod = DB_reccount(fh->fhnd);
_lastrec = eod;
_filename = fh->d->SysName;
_lastkrec = DB_changed(fh->fhnd);
end_wait();
return totrec;
}
// @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
// @comm ATTENZIONE: non e' possibile filtrare un cursore congelato
{
TString kf(_keyfrom), kto(_keyto);
const bool filterchanged = (fil != NULL) && (_filter != fil);
if (file().tab())
{
const TTable& f = (const TTable&)file();
kf = kto = f.name();
}
if (from != NULL)
{
kf = from->key(_nkey);
int p;
while ((p = kf.find('~')) != -1) kf[p] = ' ';
}
if (to != NULL)
{
kto = to->key(_nkey);
int p;
while ((p = kto.find('~')) != -1) kto[p] = ' ';
}
if (filterchanged || (_keyfrom != kf) || (_keyto != kto))
{
CHECK(!frozen(), "Impossibile filtrare un cursore congelato");
_pos = 0;
_totrec = 0;
_lastrec = 0;
if (filterchanged)
{
_filter = fil;
if (_fexpr) delete _fexpr;
TTypeexp type = (_filter.find('"') != -1) ? _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;
}
_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) const
{
TLocalisamfile& curfile = file();
curfile.setkey(_nkey);
int err = NOERR;
if (op == _isequal)
{
const TString80 match(curfile.curr().key(_nkey));
bool trovato = FALSE;
for (err = curfile.read(op, lockop);
err == NOERR && match == curfile.curr().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 (lockop != _nolock)
curfile.reread(_unlock);
}
}
}
return err;
}
TRecnotype TCursor::read(TIsamop op, TReclock lockop)
{
int err = test(op, lockop);
TLocalisamfile& curfile = file();
const TRecnotype curpos = curfile.recno();
if (changed())
_totrec = update();
if (err != NOERR)
{
_pos = _totrec - 1;
if (_pos < 0)
{
curfile.zero();
err = _isemptyfile;
}
else
readrec();
curfile.setstatus(err);
}
else
{
FILE* _f = open_index();
if (fseek(_f, 0L, SEEK_SET) != 0)
fatal_box("Can't repos cursor : File %d\n", file().filehnd()->ln);
TRecnotype *page = new TRecnotype [CMAXELPAGE];
int pagecnt;
_pos = -1;
for (TRecnotype max = _totrec; _pos == -1 && max > 0; max -= pagecnt)
{
pagecnt = (max < CMAXELPAGE) ? (int)max : CMAXELPAGE;
fread(page, sizeof(TRecnotype), pagecnt, _f);
for (int i = 0; i < pagecnt; i++)
if (page[i] == curpos)
{
_pos = _totrec - max + i;
break;
}
}
delete page;
fclose(_f);
if (_pos < 0L)
_pos = 0L;
readrec();
}
return _pos;
}
TCursor::TCursor(TRelation* r, const char* fil, int nkey,
const TRectype *from, const TRectype* to)
: _if(r), _nkey(nkey), _frozen(FALSE), _filterfunction(NULL), _fexpr(NULL),
_filter_update(FALSE), _filterfunction_update(FALSE)
{
file().setkey(_nkey);
_pos = 0;
_totrec = 0;
_lastrec = 0;
_lastkrec = 0;
filter(fil, from, to);
}
TCursor::~TCursor()
{
if (_indexname.not_empty())
::remove(_indexname);
if (_fexpr)
delete _fexpr;
}
TRecnotype TCursor::readrec()
{
TRecnotype& nrec = file().filehnd()->RecNo;
if (_pos >= items())
{
file().setstatus(_iseof);
curr().zero();
return nrec = 0L;
}
file().setstatus(NOERR);
FILE* _f = open_index();
if (fseek(_f, _pos * sizeof(TRecnotype), SEEK_SET) != 0)
fatal_box("Can't seek position %ld in cursor n. %d\n", _pos, file().filehnd()->ln);
if (fread(&nrec, sizeof(TRecnotype), 1, _f) != 1)
fatal_box("Can't read position %ld in cursor n. %d\n", _pos, file().filehnd()->ln);
fclose(_f);
curr().setdirty();
file().readat(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().filehnd()->fhnd,_pos);
break;
case _unlock:
rt=DB_unlock(file().filehnd()->fhnd);
break;
default:
break;
}
if (rt != NOERR) rt=get_error(rt);
return(rt);
}
TRecnotype TCursor::operator =(const TRecnotype pos)
{
if (changed())
_totrec = update();
CHECKD(pos >= 0 && pos <= _totrec, "Bad cursor position : ", pos);
_pos = pos;
readrec();
return _pos;
}
TRecnotype TCursor::operator +=(const TRecnotype npos)
{
if (changed())
_totrec = update();
_pos += npos;
if (_pos > _totrec) _pos = _totrec;
else
if (_pos < 0) _pos = 0;
readrec();
return _pos;
}
TRecnotype TCursor::items()
{
if (changed())
_totrec = 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));
}
///////////////////////////////////////////////////////////
// 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(')');
int 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=TRUE;
if (s.find("UPPER") >= 0)
rt=check_expr(s);
else
rt=FALSE;
return rt;
}
TRecnotype TSorted_cursor::buildcursor(TRecnotype rp)
{
TRecnotype oldrecno=0,pos,ap = 0;
int abspos=0,junk, l, pagecnt = 0;
const bool filtered = has_filter();
TString s;
El_To_Sort * Element;
_sort = new TSort(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
char versus = (s.right(1)=="-") ? 'd' : 'a';
if (s.right(1) == "-" || s.right(1) == "+")
s.cut(s.len()-1);
TFieldref f(s,0);
// Il controllo del file e' automatico in f.len()
int n = f.file();
int flen = f.len(n<0 ? relation()->lfile(n).curr() : relation()->curr());
_sort->addsortkey(abspos+f.from(),flen,versus);
CHECKS(flen!=0,"Field can not have null length: ",(const char *) s);
int lf = (relation()->lfile(n).num());
TRectype r(lf);
abspos+=r.length(f.name());
CHECKD(abspos<=256,"Cannot exceed 256 bytes-key %d",abspos);
}
_sort->init();
FILE* _f = open_index(TRUE);
if (DB_reccount(file().filehnd()->fhnd) == 0)
{
fclose(_f);
return 0;
}
fseek(_f, 0L, SEEK_SET);
l = strlen(to());
junk=DB_index_seek(file().filehnd()->fhnd, (char*)(const char*) from());
if (junk < 0) junk=get_error(junk);
if (junk == _iseof) return 0;
TRecnotype* page = new TRecnotype [CMAXELPAGE];
pos = DB_index_recno(file().filehnd()->fhnd);
TCursor::pos()=-1;
while (TRUE)
{
if (DB_index_eof(file().filehnd()->fhnd)) break;
const char* s0 = DB_index_getkey(file().filehnd()->fhnd);
const TRecnotype recno = DB_index_recno(file().filehnd()->fhnd);
if (l && (strncmp(to(), s0, l) < 0)) break;
if (recno == oldrecno) break; // means that no more keys are available
oldrecno=recno;
if (pagecnt == CMAXELPAGE)
{
pagecnt = filtercursor(pagecnt,page);
for (int i= 0; i< pagecnt; i++)
if (page[i] == rp)
{
TCursor::pos() = ap + i;
break;
}
ap += pagecnt;
pagecnt = 0;
}
page[pagecnt++] = recno; // lasciare cosi' altrimenti la readat legge due volte lo stesso record
DB_index_next(file().filehnd()->fhnd);
int rt=get_error(-1);
if (rt != NOERR)
fatal_box("Can't read index n. %d - file n. %d",DB_tagget(file().filehnd()->fhnd),file().filehnd()->ln);
} // while
if (pagecnt)
{
pagecnt = filtercursor(pagecnt, page);
for (int i = 0; i < pagecnt; i++)
if (page[i] == rp)
{
TCursor::pos() = ap + i;
break;
}
ap += pagecnt;
}
_sort->endsort();
ap = 0;
pagecnt = 0;
while ((Element=(El_To_Sort *)_sort->retrieve()) != NULL)
{
page[pagecnt++]=Element->p;
if (pagecnt==CMAXELPAGE)
{
fwrite(page,sizeof(TRecnotype),pagecnt,_f);
pagecnt=0;
}
ap++;
}
if (pagecnt)
fwrite(page,sizeof(TRecnotype),pagecnt,_f);
if (TCursor::pos() == -1) pos=0;
delete page;
if (_sort) delete _sort;
fclose (_f);
return ap;
}
int TSorted_cursor::filtercursor(int pagecnt, TRecnotype* page)
{
int np=0, handle=file().filehnd()->fhnd;
TRectype& rec=file().curr();
TString s;
El_To_Sort Element;
for (int i=0; i< pagecnt; i++)
{
file().readat(rec,page[i]);
if (update_relation()) relation()->update();
if ((filterfunction() ? filterfunction()(relation()) : TRUE ) &&
(expression() ? __evalcondition(*relation(), expression()) : TRUE))
{
if (np < i) page[np] = page[i];
np++;
_order_expr.restart();
strcpy(Element.f,"");
while ((s=_order_expr.get()).not_empty())
{
bool is_up=is_upper(s);
int p=s.find('[');// Qui estrae il nome del campo:
if (p>-1) // rimane solo l'indicazione del file (eventuale)
s.cut(p); // ed il nome del campo:
else // UPPER(20->RAGSOC[1,30]+) ===>> 20->RAGSOC
if (s.right(1) == "-" || s.right(1) == "+")
s.cut(s.len()-1);
TFieldref f(s,0);
TString sf=f.read(*relation());
TRectype& frec = file(f.file()).curr();
TFieldtypes fld_type = frec.type(f.name());
if (fld_type == _datefld) // Se il campo e' di tipo data, la converte in ANSI!
{
TDate d(sf);
sf=d.string(ANSI);
}
if (is_up) sf.upper();
TString fmt;
if (fld_type == _alfafld || fld_type == _datefld)
fmt.format("%%-%ds",f.len(frec));
else
fmt.format("%%%ds",f.len(frec));
strcat(Element.f,(const char*)sf.format((const char *)fmt,(const char *)sf));
}
Element.p=page[i];
_sort->sort((char *) &Element);
}
}
return np;
}
bool TSorted_cursor::changed()
{
bool rt;
if (_is_valid_expr)
{
rt = TCursor::changed();
if (!rt) rt=_is_changed_expr;
_is_changed_expr = FALSE;
}
else
{
NFCHECK("Can't perform changed() while expression is not valid!");
rt=FALSE;
}
return rt;
}
void TSorted_cursor::change_order(const char* order_expr)
{
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 (*name == ' ') name++;
if (name && *name)
{
if (isdigit(*name) || *name == '-')
{
log = atoi(name);
if (strchr(name, '@')) log = -log;
}
else
{
const int len = strlen(name);
if (len == 3 || len == 4)
log = TTable::name2log(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;
}
TObject* TFieldref::dup() const
{
TFieldref* f = new TFieldref();
f->_fileid = _fileid;
f->_id = _id;
f->_name = _name;
f->_from = _from;
f->_to = _to;
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 TFildref> 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
{
_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 : _id);
if (!ini.exist(_name))
return "";
buffer = ini.get(_name);
if (_from > 0 || _to > 0)
{
const int l = buffer.len();
if (_to < l && _to > 0) buffer.cut(_to);
if (_from > 0) buffer.ltrim(_from);
}
return buffer;
}
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 : _id;
if (_from > 0)
{
buffer = ini.get(_name, para);
buffer.overwrite(val, _from);
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
{
buffer = _name; buffer.upper();
buffer = rec.get(buffer);
if (_from > 0 || _to > 0)
{
const int l = buffer.len();
if (_to < l && _to > 0) buffer.cut(_to);
if (_from > 0) buffer.ltrim(_from);
}
}
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 (_from > 0)
{
TString campo(rec.get(_name));
campo.overwrite(val, _from);
rec.put(_name, campo);
}
else
{
rec.put(_name, val);
}
}
int TFieldref::len(const TRectype &rec) const
{
if (_to >= 0)
return _to - _from;
if (_fileid <= 0)
return rec.length(_name) - _from;
const TRectype r(_fileid);
const int len = r.length(_name);
return len - _from;
}
///////////////////////////////////////////////////////////
// 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
int which_file = atoi(tn); // Non spostare questa riga (tn puo' cambiare!)
if (which_file == 0)
which_file = TTable::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);
for (int f = 0; f < trec.fields(); f++)
{
ttmp = trec.fielddef(f);
const TString16 name(ttmp.get(0));
if (!name.blank())
{
TString80 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 assgnare 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 = 0; i < fld.items(); 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), _menu(FALSE), _cur_file(0), _cur_field(0),
_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)
{
*_curs+=npos;
setstatus(_curs->file().status());
return _curs->test();
}
// @mfunc Sposta indietro di <p npos> record
int TSortedfile::operator -=(const TRecnotype npos)
{
*_curs-=npos;
setstatus(_curs->file().status());
return _curs->test();
}
// @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 )
{
_curs->first_item();
setstatus(_curs->file().status());
return _curs->items()>0;
}
// @mfunc Si posiziona sull'ultimo record del file (vedi <t TReclock>)
int TSortedfile::last(word lockop )
{
_curs->last_item();
setstatus(_curs->file().status());
return _curs->items()>0;
}
// @mfunc Si posiziona sul successivo record del file (vedi <t TReclock>)
int TSortedfile::next(word lockop )
{
_curs->succ_item();
setstatus(_curs->file().status());
return _curs->pos()<=_curs->items();
}
// @mfunc Si posiziona sul precedente record del file (vedi <t TReclock>)
int TSortedfile::prev(word lockop )
{
_curs->pred_item();
setstatus(_curs->file().status());
return _curs->pos()>0;
}
// @mfunc Salta <p nrec> record dalla posizione corrente (vedi <t TReclock>)
int TSortedfile::skip(TRecnotype nrec, word lockop )
{
// return *this+=nrec; manca il lock...
setstatus(_curs->file().status());
error_box("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 )
{
_curs->read((TIsamop)op,(TReclock)lockop);
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(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;
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)
{
return 0;
}
// @mfunc Riscrive un record
int TSortedfile::rewrite()
{
return 0;
}
// @mfunc Riscrive un record (il record <p rec>)
int TSortedfile::rewrite(const TRectype& rec)
{
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)
{
error_box("Operazione 'rewriteat' non consentita sul cursore");
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)
{
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)
{
_curs->setregion(f,t);
}
// @mfunc
TRecnotype TSortedfile::eod() const
{
return _curs->items();
}
// @mfunc
bool TSortedfile::empty()
{
return _curs->items()==0;
}
void TSortedfile::set_curr(TRectype * curr)
{
TLocalisamfile::set_curr(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,filter,nkey);
_curs->setfilter(filter,TRUE); //BUG: cursors doesn't update rel.
if (&curr()!=&(_curs->file().curr()))
_curs->file().set_curr(&curr());
}
// @mfunc Distruttore
TSortedfile::~TSortedfile()
{
delete _curs;
if (_rel) delete _rel;
}
//
// *** EOF relation.cpp