campo-sirio/include/recarray.cpp

565 lines
15 KiB
C++
Raw Normal View History

#include <prefix.h>
#include <recarray.h>
#include <tabutil.h>
///////////////////////////////////////////////////////////
// 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)
: _data(a._data), _file(a._file), _offset(a._offset), _num(a._num)
{}
TRecord_array::~TRecord_array()
{
}
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");
_data.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*)_data.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;
TObject* r = _data.objptr(i);
return r != NULL;
}
// @doc EXTERNAL
// @mfunc Ritorna la riga <p r>-esima; se tale riga non esiste e se <p create> assume il valore TRUE la riga viene creata
//
// @rdesc Ritorna un TRectype alla riga cercata o eventualemete il reference della nuova creata,
// NULL nel caso non esista e non venga creata.
TRectype& TRecord_array::row(
int n, // @parm Numero della riga da creare
bool create) // @parm Indica se creare una nuova riga se non esiste:
//
// @flag TRUE | Se la riga <p n> non esiste viene creata
// @flag FALSE | Se la riga <p n> non esiste non viene creata
// @comm Nel caso <p create> sia TRUE e venga richiesta una riga non esistente si crea un nuovo
// record copiando la chiave.
{
const int i = n >= 0 ? n - _offset : -1;
TRectype* r = (TRectype*)_data.objptr(i);
if (r == NULL && create)
{
r = (TRectype*)key().dup(); // Crea nuovo record copiando la chiave
n = _data.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;
}
// @doc EXTERNAL
// @mfunc Rinumera il campo chiave in seguito a reinsert
//
// @rdesc Ritorna se e' stato rinumerato il campo chiave
bool TRecord_array::renum_key(
const char* field, // @parm Campo della chiave a cui assegnare un nuovo valore
const TString& num) // @parm Nuovo valore da assegnare al campo
// @parm long | num | Nuovo valore da assegnare al campo
// @syntax bool renum_key(const char* field, const TString& num);
// @syntax bool renum_key(const char* field, long num);
{
CHECKS(_num != field, "You can't renumber field ", field);
const TString& curr = key().get(field);
if (curr == num)
return FALSE;
for (int i = _data.last(); i >= 0; i--)
{
TRectype* r = (TRectype*)_data.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::insert_row(TRectype* r)
{
const int nr = rec2row(*r);
CHECK(nr > 0, "You cannot insert a new key");
const bool shift = exist(nr);
_data.insert(r, nr);
if (shift)
{
for (int f = _data.last(); f > nr; f = _data.pred(f))
{
char n[16];
TRectype & rec = row(f, FALSE);
sprintf(n, "%ld", (long)_offset + f);
rec.renum_key(_num, n);
}
}
return nr;
}
int TRecord_array::add_row(TRectype* r)
{
const int nr = rec2row(*r);
_data.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;
}
// @doc EXTERNAL
// @mfunc Elimina le righe vuote
void TRecord_array::pack()
{
_data.pack();
for (int i = _data.size()-1; i > 0; i--)
{
TRectype* r = (TRectype*)_data.objptr(i);
if (r != NULL)
{
TString16 n; n << i+_offset;
r->renum_key(_num, n);
}
}
}
// @doc EXTERNAL
// @mfunc Cancella una riga
//
// @rdesc Ritorna se e' riuscito ad eliminare la riga
bool TRecord_array::destroy_row(
int r, // @parm Numero della riga da eliminare
bool pack) // @parm Indica se compattare gli elementi dell'array (default FALSE)
// @parm const TRectype& | r | Record da eliminare
// @syntax bool destroy_row(int n, bool pack);
// @syntax bool destroy_row(const TRectype& r, bool pack);
{
CHECKD(r > _offset, "Can't destroy row ", r);
const int index = r - _offset;
const bool ok = _data.destroy(index, pack);
if (ok && pack)
{
for (int i = _data.size()-1; i >= index; i--)
{
TRectype* r = (TRectype*)_data.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 = _data.last(); i > 0; i = _data.pred(i))
_data.destroy(i);
}
// @doc EXTERNAL
// @mfunc Confronta i campi della chiave 1 scartando l'ultimo
//
// @rdesc Ritorna il risulato del confronto:
//
// @flag TRUE | Se i campi sono uguali
// @flag FALSE | Se i campi sono diversi
bool TRecord_array::good(
const TRectype& rec) const // @parm Record di cui confrontare i campi
{
const bool yes = key().same_key(rec, 1, 1);
return yes;
}
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);
_data.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 && good(*rec); e = rec->next(f))
{
add_row(rec);
rec = (TRectype*)(key().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 && good(*rec); e = rec->next(f))
{
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 = _data.last();
for (int i = 1; i <= u; i++)
{
const TRectype* r = (TRectype*)_data.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;
// Se pos < last_on_file sicuramente la read fallira' per cui la riga
// non puo' esistere su file: non esistendo nemmeno in memoria non devo
// fare assolutamente nulla!
if (pos >= last_on_file) // Puo' esistere su 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 && good(*rec)) // Cerca una riga >= pos sul file
{
last_on_file = atoi(rec->get(_num));
if (last_on_file == pos) // La riga c'era ma ora non piu'
{
err = (rec->remove(f)); // Cancello il record indesiderato
if (err != NOERR)
{
delete rec; // Orrore! Un errore di cancellazione!
break;
}
}
}
else
last_on_file = EOR; // Sul file non ci sono piu' righe da cancellare
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);
}
///////////////////////////////////////////////////////////
// TFile_cache
///////////////////////////////////////////////////////////
TFile_cache::TFile_cache(int num, int key)
: _file(NULL), _key(key)
{
_code.format("%d", num);
}
TFile_cache::TFile_cache(const char* tab, int key)
: _file(NULL), _key(key)
{
_code = tab;
}
TFile_cache::~TFile_cache()
{
if (_file != NULL)
delete _file;
}
void TFile_cache::init_file()
{
CHECK(_file == NULL, "File already initialized");
int logicnum = atoi(_code);
if (logicnum == 0)
{
_file = new TTable(_code);
logicnum = _file->num();
}
else
_file = new TLocalisamfile(logicnum);
TDir dir;
dir.get(logicnum);
// Se e' un file comune metti a -1, altrimenti alla ditta corrente
_last_firm = dir.is_com() ? -1 : prefix().get_codditta();
}
void TFile_cache::test_firm()
{
if (_file == NULL)
init_file();
if (_last_firm >= 0) // Se e' un file di ditta ...
{ // ... controlla che non sia cambiata
const long cur_firm = prefix().get_codditta();
if (cur_firm != _last_firm)
{
_last_firm = cur_firm;
destroy();
}
}
}
const TObject& TFile_cache::query(const char* code)
{
test_firm();
_code = code;
TObject* obj = objptr(_code);
if (obj == NULL)
{
int err = ~NOERR;
TRectype& curr = _file->curr();
if (_code.not_empty())
{
const RecDes* recd = curr.rec_des(); // Descrizione del record della testata
const KeyDes& kd = recd->Ky[_key-1]; // Elenco dei campi della chiave
for (int i = _file->tab() ? 1 :0; i < kd.NkFields; i++) // Riempie la chiave selezionata
{
const int nf = kd.FieldSeq[i] % MaxFields;
const RecFieldDes& rf = recd->Fd[nf];
const char* val = _code.get();
if (val)
curr.put(rf.Name, val);
else
NFCHECK("Valore del campo chiave %s non specificato per il decoder", rf.Name);
}
_file->setkey(_key);
err = _file->read();
}
if (err != NOERR)
curr.zero();
obj = rec2obj(curr);
add(_code, obj);
}
return *obj;
}
int TFile_cache::fill()
{
test_firm();
_file->setkey(_key);
TRectype& curr = _file->curr();
const RecDes* recd = curr.rec_des(); // Descrizione del record della testata
const KeyDes& kd = recd->Ky[_key-1]; // Elenco dei campi della chiave
for (int err = _file->first(); err == NOERR; err = _file->next())
{
_code.cut(0);
for (int i = 0; i < kd.NkFields; i++) // Riempie la chiave selezionata
{
const int nf = kd.FieldSeq[i] % MaxFields;
const RecFieldDes& rf = recd->Fd[nf];
const char* val = curr.get(rf.Name);
_code.add(val);
}
TObject* obj = rec2obj(curr);
add(_code, obj);
}
return items();
}
///////////////////////////////////////////////////////////
// TDecoder
///////////////////////////////////////////////////////////
TDecoder::TDecoder(int num, const char* outf, int key)
: TFile_cache(num, key), _outf(outf)
{ }
TDecoder::TDecoder(const char* tab, const char* outf, int key)
: TFile_cache(tab, key), _outf(outf)
{ }
TObject* TDecoder::rec2obj(const TRectype& curr) const
{
return new TString(curr.get(_outf));
}
const TString& TDecoder::decode(const char* code)
{
const TString& obj = (const TString&)query(code);
return obj;
}
const TString& TDecoder::decode(long code)
{
TString16 c;
if (code > 0)
c << code;
return decode(c);
}
///////////////////////////////////////////////////////////
// TRecord_cache
///////////////////////////////////////////////////////////
TRecord_cache::TRecord_cache(int num, int key)
: TFile_cache(num, key)
{ }
TRecord_cache::TRecord_cache(const char* tab, int key)
: TFile_cache(tab, key)
{ }
TObject* TRecord_cache::rec2obj(const TRectype& curr) const
{
return new TRectype(curr);
}
const TString& TRecord_cache::get(const char* key, const char* field)
{
const TRectype& rec = (const TRectype&)query(key);
return rec.get(field);
}
const TRectype& TRecord_cache::get(const char* key)
{
const TRectype& rec = (const TRectype&)query(key);
return rec;
}