campo-sirio/include/relation.cpp
guy 76719be75d isam.cpp Aggiunta funzione per costruire la stringa chiave da un record
isam.h       Aggiunti prototipo funzione precedente
maskfld.h    Aggiunta funzione per settare il modo di trim dei campi
relation.cpp Aggiunto codice di debug nella TRecord_array::remove_from


git-svn-id: svn://10.65.10.50/trunk@2125 c028cbd2-c16b-5b4b-a496-9718f37d4682
1995-11-10 13:37:28 +00:00

2177 lines
51 KiB
C++
Executable File

// $Id: relation.cpp,v 1.68 1995-11-10 13:37:28 guy Exp $
// relation.cpp
// fv 12/8/93
// relation class for isam files
#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 <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
{
// @DPRIV
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 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];
for (int k = 0; k < expr.numvar(); k++)
expr.setvar(k, to.get(expr.varname(k)));
const char* val = expr;
if (*val == '\0' && _altexprs.objptr(j))
{
TExpression& altexpr = (TExpression&)_altexprs[j];
for (int k = 0; k < expr.numvar(); k++)
altexpr.setvar(k, to.get(altexpr.varname(k)));
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));
}
}
int TRelation::log2ind(int log) const
{
// 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;
}
int TRelation::alias2ind(int alias) const
{
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;
}
int TRelation::name2ind(const char* name) const
{
const int num = name2log(name);
const int ind = log2ind(num);
return ind;
}
TLocalisamfile& TRelation::lfile(int logicnum) const
{
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];
}
void TRelation::write_enable(int logicnum, const bool on)
{
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);
}
bool TRelation::add(TLocalisamfile* f, const char* relexprs, int key,
int linkto, int alias, bool allow_lock)
{
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);
}
void TRelation::replace(TLocalisamfile* f, int index)
{
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);
}
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();
}
int TRelation::position_rels(TIsamop op, TReclock lockop,
TDate& atdate, int first)
{
_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;
if (to.curr().empty())
{ from.zero(); continue; }
from.setkey(rd._key);
from.curr().zero();
// build record
if (rd._fields.items() && rd._exprs.items())
{
for (int j = 0 ; j < rd._fields.items(); j++) // for each field
{
/*
TExpression& expr = (TExpression&)rd._exprs[j];
for (int k = 0; k < expr.numvar(); k++)
expr.setvar(k, to.get(expr.varname(k)));
*/
const char* expr = rd.evaluate_expr(j, to);
TFieldref& s = (TFieldref&) rd._fields[j];
s.write(expr, from.curr());
} // for each field
}
// read record: if not found, zero current record
const TRectype rec(from.curr());
from.read(op, lck, atdate);
if (from.bad())
{
rd._first_match = (from.curr() == rec);
if (rd._first_match)
{
bool eq = TRUE;
for (int kk = 0; eq && kk < rd._fields.items(); kk++)
{
if (rd._forced[kk])
{
TFieldref& fl = (TFieldref&)rd._fields[kk];
const TString f_fr(fl.read(from.curr()));
eq = (f_fr == rd.evaluate_expr(kk, to));
}
}
if (eq) from.setstatus(NOERR);
else { rd._first_match = FALSE; from.curr().zero(); }
}
else from.curr().zero();
}
else rd._first_match = TRUE;
} // for each relation
return _errors;
}
bool TRelation::next_match(int logicnum, const char* fieldlist, int nkey)
{
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(to.curr()));
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, (TDate&)botime, j+1);
reldef(j)._first_match = FALSE;
return ok;
}
bool TRelation::is_first_match(int logicnum)
// TRUE se c'e' un record ed e' il primo match (non si e' mai fatta
// position_rels)
{
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
bool TRelation::isconsistent(bool reset)
{
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, (TDate&)botime);
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, TDate& atdate)
{
_errors = file(0).write(atdate);
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(atdate);
if (force && res == _isreinsert)
res = lf.rewrite(atdate);
if (_errors == NOERR)
_errors = res;
}
return _errors;
}
int TRelation::rewrite(bool force, TDate& atdate)
{
_errors = file(0).rewrite(atdate); // Riscrive testata
if (force && _errors == _iskeynotfound) // Se non la trova ...
_errors = file(0).write(atdate); // ... 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(atdate);
if (force && res == _iskeynotfound)
res = lf.write(atdate);
if (_errors == NOERR) _errors = res;
}
return _errors;
}
int TRelation::remove(TDate& atdate)
{
const int res = file(0).remove(atdate);
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(atdate);
if (_errors == NOERR && res != _iskeynotfound) _errors = res;
}
return _errors;
}
///////////////////////////////////////////////////////////
// TCursor
///////////////////////////////////////////////////////////
HIDDEN bool __evalcondition(const TRelation* r,TExpression* cond)
{
for (int i = 0; i < cond->numvar(); i++)
{
const char* s = cond->varname(i);
TFieldref f(s,0);
cond->setvar(i, f.read(r));
}
return (bool) *cond;
}
FILE* TCursor::open_index(bool create)
{
#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();
TString key(rec.key(_nkey)), kf(from()), kt(to());
if (file().tab())
{
key.ltrim(3);
kf.ltrim(3);
kt.ltrim(3);
}
if (key < kf || (kt.not_empty() && kt < key.left(kt.len())))
return FALSE;
if (_filter_update || _filterfunction_update) _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;
}
else return FALSE;
}
TRecnotype TCursor::update()
{
main_app().begin_wait();
file().setkey(_nkey);
if (file().curr().empty()) file().curr().zero();
file().read(_isgteq);
const TRecnotype totrec = buildcursor(file().recno());
main_app().end_wait();
isdef* fh = file().filehnd();
const TRecnotype eod = DB_reccount(fh->fhnd);
_lastrec = eod;
_filename = fh->d->SysName;
_lastkrec = DB_changed(fh->fhnd);
return totrec;
}
void TCursor::filter(const char* fil, const TRectype *from,
const TRectype* to)
{
CHECK(!_frozen, "Impossibile filtrare un cursore congelato");
TString kf(_keyfrom), kto(_keyto);
const bool filterchanged = (fil != NULL) && (_filter != fil);
if (file().tab())
{
TTable& f = (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))
{
_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);
}
}
TRecnotype TCursor::read(TIsamop op, TReclock lockop, TDate& atdate)
{
TRecnotype *page;
int pagecnt;
file().setkey(_nkey);
const bool approx = (op == _isgteq);
_if->file().read(op, lockop, atdate);
if (approx)
{
while (_if->file().good() && !ok())
_if->file().next();
}
const TRecnotype curpos = file().recno();
if (changed())
_totrec = update();
if (approx && _if->file().status() == _iseof)
{
_pos = _totrec - 1;
return _pos;
}
FILE* _f = open_index();
if (fseek(_f, 0L, SEEK_SET) != 0)
fatal_box("Can't repos cursor : File %d\n", file().filehnd()->ln);
page = new TRecnotype [CMAXELPAGE];
_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;
}
}
if (_pos == -1) _pos = 0;
delete page;
fclose(_f);
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;
}
int TCursor::lock(TReclock l)
{
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[512];
TRecnotype p;
} El_To_Sort;
bool TSorted_cursor::check_expr(TString& s)
// Controlla la validita' dell'espressione ritornando un bool
// Modifica l'espressione in modo da avere una valido parametro per TFieldref!
// Es. UPPER(20->RAGSOC[1,40]+) ==> 20->RAGSOC[1,40]+
// 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") == -1)
{
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")>-1)
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;
_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()
_sort->addsortkey(abspos+f.from(),f.len(relation()->curr()),versus);
CHECKS(f.len(relation()->curr())!=0,"Field can not have null length: ",(const char *) s);
int lf = (f.file()!=0 ? f.file() : file().num());
TRectype r(lf);
abspos+=r.length(f.name());
CHECKD(abspos<=512,"Cannot exceed 512 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();
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;
}
}
if (pagecnt)
fwrite(page,sizeof(TRecnotype),pagecnt,_f);
if (TCursor::pos() == -1) pos=0;
delete page;
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());
TFieldtypes fld_type = file(f.file()).curr().type(s);
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(rec));
else
fmt.format("%%%ds",f.len(rec));
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
{
error_box("Can't perform changed() while expression ain't 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);
_sort = new TSort(sizeof(El_To_Sort));
}
TSorted_cursor::~TSorted_cursor()
{
if (_sort) delete _sort;
}
///////////////////////////////////////////////////////////
// TFieldRef
///////////////////////////////////////////////////////////
int name2log(const char* name)
{
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;
}
// A Fieldref should have the following format (only NAME is mandatory):
// FILE->NAME[FROM,TO]
TFieldref& TFieldref::operator =(const TString& s)
{
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);
_name = s.sub(pos, par); _name.strip(" ");
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 << ']';
}
}
const char* TFieldref::read(const TRectype& rec) const
{
static TString256 buffer;
if (_fileid >= CNF_GENERAL)
{
TToken_string s(_name, '.');
TConfig c(_fileid - CNF_STUDIO, s.get());
buffer = c.get(s.get());
}
else
{
buffer = rec.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;
}
const char* TFieldref::read(const TRelation* c) const
{
CHECK(c != NULL, "Can't read field from NULL relation");
const char * s = read(c->lfile(_id).curr());
return s;
}
void TFieldref::write(const char* val, TRelation* c) const
{
CHECK(c != NULL, "Can't write field on NULL relation");
TRectype &curr = c->lfile(_id).curr();
write(val, curr);
}
void TFieldref::write(const char* val, TRectype& rec) const
{
if (_fileid >= CNF_GENERAL) return;
if (_from > 0)
{
TString256 campo(rec.get(_name));
campo.overwrite(val, _from);
rec.put(_name, campo);
}
else
rec.put(_name, val);
}
int TFieldref::len(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;
}
///////////////////////////////////////////////////////////
// TRecord_Array
///////////////////////////////////////////////////////////
TRecord_array::TRecord_array(const TRectype& r, const char* numfield, int first)
: _file(r.num()), _num(numfield), _offset(first - 1)
{
read(r);
}
TRecord_array::TRecord_array(int logicnum, const char* numfield, int first)
: _file(logicnum), _num(numfield), _offset(first - 1)
{
set_key(new TRectype(logicnum));
}
TRecord_array::TRecord_array(const TRecord_array& a)
: TArray(a), _file(a._file), _offset(a._offset), _num(a._num)
{}
void TRecord_array::set_key(TRectype* r)
{
CHECK(r != NULL, "TRecord_array can't have a null key");
CHECK(r->num() == _file, "Bad key record");
add(r, 0); // Setta il nuovo campo chiave
if (rows() > 0)
{
const RecDes* recd = r->rec_des(); // Descrizione del record della testata
const KeyDes& kd = recd->Ky[0]; // Elenco dei campi della chiave 1
// Copia tutti i campi chiave, tranne l'ultimo, in tutti i records
for (int i = kd.NkFields-2; i >= 0; i--)
{
const int nf = kd.FieldSeq[i] % MaxFields;
const RecFieldDes& rf = recd->Fd[nf];
const TString& val = r->get(rf.Name);
renum_key(rf.Name, val);
}
}
}
const TRectype& TRecord_array::key() const
{
TRectype* r = (TRectype*)objptr(0);
CHECK(r, "TRecord_array lost its key");
return *r;
}
bool TRecord_array::exist(int n) const
{
const int i = n > 0 ? n - _offset : -1;
TRectype* r = (TRectype*)objptr(i);
return r != NULL;
}
TRectype& TRecord_array::row(int n, bool create)
{
const int i = n >= 0 ? n - _offset : -1;
TRectype* r = (TRectype*)objptr(i);
if (r == NULL && create)
{
r = (TRectype*)key().dup(); // Crea nuovo record copiando la chiave
n = add(r, i) + _offset; // Riassegna n se era negativo!
char str[16]; sprintf(str, "%d", n);
r->renum_key(_num, str); // Aggiorna campo numero riga
}
CHECKD(r && n > 0, "Bad record number ", n);
return *r;
}
bool TRecord_array::renum_key(const char* field, const TString& num)
{
const TString& curr = key().get(field);
if (curr == num)
return FALSE;
for (int i = last(); i >= 0; i--)
{
TRectype* r = (TRectype*)objptr(i);
if (r != NULL)
r->renum_key(field, num);
}
return TRUE;
}
bool TRecord_array::renum_key(const char* field, long num)
{
CHECKS(num > 0, "Null key value for field: ", field);
TString16 n; n << num;
return renum_key(field, n);
}
int TRecord_array::rec2row(const TRectype& r) const
{
CHECK(r.num() == _file, "Incompatible record");
const int n = atoi(r.get(_num)) - _offset; // Non e' detto che sia un int!
CHECKD(n >= 0 && n < 30000, "Bad line number in record ", n + _offset);
return n;
}
int TRecord_array::add_row(TRectype* r)
{
const int nr = rec2row(*r);
add(r, nr);
if (nr == 0 && rows() > 0) // Se ho cambiato il record campione
{ // e ci sono altre righe ...
for (int f = r->items()-1; f >= 0; f--)
{
const char* fn = r->fieldname(f);
const TString& v = r->get(fn);
if (v.not_empty()) renum_key(fn, v); // ... aggiorna tutte le righe in base
} // ai campi non vuoti del campione
}
return nr;
}
bool TRecord_array::destroy_row(int r, bool pack)
{
CHECKD(r > _offset, "Can't destroy row ", r);
const int index = r - _offset;
const bool ok = destroy(index, pack);
if (ok && pack)
{
for (int i = size()-1; i > index; i--)
{
TRectype* r = (TRectype*)objptr(i);
if (r != NULL)
{
TString16 n; n << i+_offset;
r->renum_key(_num, n);
}
}
}
return ok;
}
void TRecord_array::destroy_rows()
{
for (int i = last(); i > 0; i = pred(i))
destroy(i);
}
int TRecord_array::read(TRectype* filter)
{
CHECKD(filter->num() == _file, "Bad key record ", filter->num());
CHECKS(filter->get(_num).empty(), "You can't specify in the filter the field ", (const char*)_num);
destroy();
int err = NOERR;
if (filter != NULL && !filter->empty())
{
TLocalisamfile f(_file);
set_key(filter);
TRectype* rec = (TRectype*)filter->dup();
err = rec->read(f, _isgteq);
for (int e = err; e == NOERR && *rec == *filter; e = rec->next(f))
{
add_row(rec);
rec = (TRectype*)filter->dup();
}
delete rec;
}
return err;
}
int TRecord_array::read(const TRectype& filter)
{
TRectype* f = (TRectype*)filter.dup();
return read(f);
}
int TRecord_array::remove_from(int pos) const
{
int err = NOERR;
TRectype* rec = (TRectype*)key().dup();
CHECK(!rec->empty(), "Can't use empty key");
TLocalisamfile f(_file);
rec->put(_num, pos);
for (int e = rec->read(f, _isgteq); e == NOERR && *rec == key(); e = rec->next(f))
{
#ifdef DBG
TString k_rec = rec->build_key();
TString k_key = key().build_key();
#endif
const int found = rec->get_int(_num);
if (found >= pos)
{
err = rec->remove(f);
if (err != NOERR)
break;
pos = found+1;
}
else
break;
}
delete rec;
return err;
}
int TRecord_array::write(bool re) const
{
const int EOR = 32000; // End of records on file
int last_on_file = 0; // Last record found on file
int err = NOERR;
TLocalisamfile f(_file);
const int u = last();
for (int i = 1; i <= u; i++)
{
const TRectype* r = (TRectype*)objptr(i);
if (r != NULL)
{
if (re)
{
err = r->rewrite(f);
if (err == _iskeynotfound || err == _iseof || err == _isemptyfile)
err = r->write(f);
if (err != NOERR)
break;
}
else
{
err = r->write(f);
if (err == _isreinsert)
{
err = r->rewrite(f);
re = TRUE;
}
if (err != NOERR)
break;
}
}
else
{
const int pos = i+_offset;
if (pos > last_on_file)
{
TRectype* rec = (TRectype*)key().dup();
CHECK(!rec->empty(), "TRecord_array has an empty key");
rec->put(_num, pos);
err = rec->read(f, _isgteq);
if (err == NOERR && *rec == key()) // La riga c'era ma ora non piu'
{
last_on_file = atoi(rec->get(_num));
if (last_on_file == pos)
{
err = (rec->remove(f));
if (err != NOERR)
{
delete rec;
break;
}
}
}
else
last_on_file = EOR;
delete rec;
}
}
}
// Cancella eventuali residui successivi
if (err == NOERR && last_on_file != EOR)
remove_from(i + _offset);
return err;
}
int TRecord_array::remove() const
{
return remove_from(1 + _offset);
}
///////////////////////////////////////////////////////////
// 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);
}
}
void TRelation_description::change_relation(TRelation& r, TString_array& a)
{
_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";
}
}
bool TRelation_description::choose_file(int file)
{
TArray_sheet sht(-1,-1,0,0,"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;
}
bool TRelation_description::choose_field(const char* fld)
{
TArray_sheet sht(-1,-1,0,0,"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;
}
bool TRelation_description::build_menu(const char* title)
{
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);
}
bool TRelation_description::set_field_description(const char* field, const char* des)
{
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();
}
// *** EOF relation.cpp