campo-sirio/ab/ablib01.cpp
augusto 553b5799ea Rinominata madekey in put_headkey e tolta la ridefinizione virtuale di
commi body per l'albero saldi


git-svn-id: svn://10.65.10.50/trunk@5974 c028cbd2-c16b-5b4b-a496-9718f37d4682
1998-01-19 16:19:12 +00:00

1428 lines
43 KiB
C++
Executable File
Raw Blame History

#include "ablib01.h"
#define MAX_ID_REL 999999999
#define NODO_AGGIUNTO "A"
#define NODO_MODIFICATO "M"
#define NODO_RIMOSSO "R"
/**************************************************************************************************/
// TAlbero_AB
/**************************************************************************************************/
TAlbero_AB::TAlbero_AB(int filenum)
:TRectype(filenum)
{
}
int TAlbero_AB::write(TBaseisamfile &analisi) const
{
int err = TRectype::write(analisi);
if (err==NOERR)
{
commit_body();
}
else
{
err = rewrite(analisi);
}
if (err)
CHECK (FALSE,"Errore di scrittura del record sull'isamfile");
return err;
}
int TAlbero_AB::rewrite(TBaseisamfile &analisi) const
{
int err = TRectype::rewrite(analisi);
if (err==NOERR)
{
commit_body();
}
if (err)
CHECK (FALSE,"Errore di scrittura del record sull'isamfile");
return err;
}
//Leggo la "testata" del record
int TAlbero_AB::read(TBaseisamfile& saldi, word isop, word lockop)
{
int err=TRectype::read(saldi, isop, lockop);
if (err==NOERR)
read_body(FALSE);
return err;
}
//Leggo la testata del record basandomi sulla posizione
int TAlbero_AB::readat(TBaseisamfile& analisi, TRecnotype nrec, word lockop)
{
int err=TRectype::readat(analisi, nrec, lockop);
if (err==NOERR)
read_body(FALSE);
return err;
}
int TAlbero_AB::remove(TBaseisamfile& f) const
{
//*Abilitare questa parte solo per effettuare un debug della delete_node
// anche se messa in questa posizione, la chiamata non ha senso
// TLocalisamfile lf(LF_RELANA);
TNumeric_id id;
id = 17;
_inter_tree->delete_node(id);
int errc = commit_body();
if (errc != NOERR)
{
message_box("Errore nell'aggiornameno del file dati");
//return errc;
}
return 0;
/*Abilitare solo questa parte per il test della remove_tree
_inter_tree->delete_tree();
int errc = commit_body();
if (errc != NOERR)
{
message_box("Errore nell'aggiornameno del file dati");
//return errc;
}
int err = TRectype::remove(f);
return err; */
}
void TAlbero_AB::zero()
{
_inter_tree->zero();
TRectype::zero() ;
}
/**************************************************************************************************/
// TABsaldo
/**************************************************************************************************/
TABsaldo::TABsaldo()
:TAlbero_AB(LF_ABSALDI)
{
_saldi = new TLocalisamfile(LF_ABSALDI);
_movdett = new TLocalisamfile(LF_MOVDETT);
// _relaz = NULL;
// _ana = NULL;
_inter_tree = new TLocal_balance3();
_newrec = new TRectype(LF_MOVDETT);
}
TABsaldo::~TABsaldo()
{
delete _saldi;
delete _movdett;
delete _inter_tree;
delete _newrec;
}
void TABsaldo::put_headkey(TRectype &rec) const
{
rec.put(ABMD_CODDITTA, get(ABS_CODDITTA));
rec.put(ABMD_ANNO, get(ABS_ANNO));
rec.put(ABMD_CODPDB, get(ABS_CODPDB));
rec.put(ABMD_TIPOBIL, get(ABS_TIPOBIL));
rec.put(ABMD_CODCBL, get(ABS_CODCBL));
}
/*
int TABsaldo::commit_body() const
{
_inter_tree->write_cache();
TRectype rec(_inter_tree->current().num());
TLocalisamfile filebody(rec.num());
TString status;
//Estraggo un nodo per volta basandomi sull'assoc_array degli status
while (_inter_tree->dirty_nodes() > 0)
{
TNumeric_id id;
//int prova = _inter_tree->dirty_nodes(); //solo debug
id=ID_NULLO;
rec= _inter_tree->extract_dirtynode(id, status);
put_headkey(rec);
//Ho un rectype e uno status node
if (status == NODO_AGGIUNTO)
{
if (filebody.curr().empty())
{ //Se il cursor di relana <20> posizionato in modo errato:
//lo sistemo sull'ultimo record del file
//DOMANDA: - Perch<63> ???
//TENATATIVO DI RISPOSTA: - Quando l'ultima azione effettuata (prima di questa "aggiunta") <20> la rimozione
// dell'ultimo record probabilmente (ma non sono sicuro) il cursore non <20> posizionato (o forse
// <20> posizionato in modo errato come ad es. dopo l'EOF, visto che l'EOF era sull'ultimo record rimosso)
// e quando eseguo un tentativo di scrittura da un errore di chiave duplicata (anche se questa non esiste);
// In tutti gli altri casi (quando l'ultima azione <20> stata una write o una rewrite o una remove di un record
// che non sia l'ultimo) questo problema non esiste.
//RISPOSTA ESATTA: - Bho !!!
filebody.last();
}
int err = filebody.write(rec);
if (err != NOERR)
return err;
}
if (status == NODO_MODIFICATO)
{
int err = filebody.rewrite(rec);
if (err != NOERR)
return err;
}
if (status == NODO_RIMOSSO)
{
filebody.read(rec); //Devo leggere sul file per posizionare il cursore: stesso motivo di prima
int err = filebody.remove(rec);
if (err != NOERR)
return err;
}
// In tutti gli altri casi il nodo non viene modificato
}
return NOERR;
}
*/
//Leggo il "corpo" del record
int TABsaldo::read_body(bool lockstruct)
{
//_ana = new TAlbero_relana(get(ABAN_CODAN));
TString codditta = get(ABS_CODDITTA);
TToken_string key = codditta.right_just(5,'0');
key.add(get(ABS_ANNO));
key.add(get(ABS_CODPDB));
key.add(get(ABS_TIPOBIL));
key.add(get(ABS_CODCBL));
_mov = new TAlbero_movdett(key);
if (_mov->goto_root())
{
//TNumeric_id id_movdett;
//id_movdett = _mov->curr().get_real(ABMD_ID);
naviga_movdett();
}
//_inter_tree->write_cache(); //solo per debug
return 0;
}
//Naviga l'albero dei movimenti-dettagli
void TABsaldo::naviga_movdett()
{
TNodeId key_rel;
_mov->curr_id(key_rel);
TNumeric_id lbrother_id, pather_id, my_id;
TRectype new_node(LF_MOVDETT);
my_id = _mov->curr().get_real(ABMD_ID);
pather_id = _mov->curr().get_real(ABMD_IDPADRE);
lbrother_id = _mov->curr().get_real(ABMD_IDPREC);
new_node = _mov->curr();
_inter_tree->insert_node(new_node,lbrother_id,pather_id,my_id);
if (_mov->has_rbrother())
{
if (_mov->goto_rbrother())
{ //Fratello di movdett trovato
naviga_movdett();
_mov->goto_node(key_rel);
}
}
if (_mov->has_son())
{
if (_mov->goto_firstson())
{ //Fratello di movdett trovato
naviga_movdett();
_mov->goto_node(key_rel);
}
}
}
/**************************************************************************************************/
// TAnalisi_bil
/**************************************************************************************************/
TAnalisi_bil::TAnalisi_bil()
:TAlbero_AB(LF_ANALISI)
{
_voci = new TLocalisamfile(LF_VOCI);
_analisi= new TLocalisamfile(LF_ANALISI);
_relaz = NULL;
_ana = NULL;
_inter_tree = new TLocal_relana3();
_newrec = new TRectype(LF_RELANA);
}
TAnalisi_bil::~TAnalisi_bil()
{
delete _voci;
delete _ana;
delete _relaz;
delete _analisi;
delete _newrec;
}
void TAnalisi_bil::put_headkey(TRectype &rec) const
{
rec.put(ABAN_CODAN, get(ABAN_CODAN));
}
//Scrive tutto il contenuto della cache sull'isamfile, aggiungendo, modificando
//o rimuovendo i nodi necessari
int TAlbero_AB::commit_body() const
{
TRectype rec(_inter_tree->current().num());
TLocalisamfile filebody(rec.num());
TString status;
//Estraggo un nodo per volta basandomi sull'assoc_array degli status
while (_inter_tree->dirty_nodes() > 0)
{
TNumeric_id id;
// int prova = _inter_tree->dirty_nodes(); solo debug
id=ID_NULLO;
rec= _inter_tree->extract_dirtynode(id, status);
put_headkey(rec);
//Ho un rectype e uno status node
if (status == NODO_AGGIUNTO)
{
if (filebody.curr().empty())
{ //Se il cursor di relana <20> posizionato in modo errato:
//lo sistemo sull'ultimo record del file
//DOMANDA: - Perch<63> ???
//TENATATIVO DI RISPOSTA: - Quando l'ultima azione effettuata (prima di questa "aggiunta") <20> la rimozione
// dell'ultimo record probabilmente (ma non sono sicuro) il cursore non <20> posizionato (o forse
// <20> posizionato in modo errato come ad es. dopo l'EOF, visto che l'EOF era sull'ultimo record rimosso)
// e quando eseguo un tentativo di scrittura da un errore di chiave duplicata (anche se questa non esiste);
// In tutti gli altri casi (quando l'ultima azione <20> stata una write o una rewrite o una remove di un record
// che non sia l'ultimo) questo problema non esiste.
//RISPOSTA ESATTA: - Bho !!!
filebody.last();
}
int err = filebody.write(rec);
if (err != NOERR)
return err;
}
if (status == NODO_MODIFICATO)
{
int err = filebody.rewrite(rec);
if (err != NOERR)
return err;
}
if (status == NODO_RIMOSSO)
{
filebody.read(rec); //Devo leggere sul file per posizionare il cursore: stesso motivo di prima
int err = filebody.remove(rec);
if (err != NOERR)
return err;
}
// In tutti gli altri casi il nodo non viene modificato
}
return NOERR;
}
//Leggo il "corpo" del record
int TAnalisi_bil::read_body(bool lock_struct )
{
if (_relaz)
{
if (_relaz->codtab() != get(ABAN_CODAN))
{
delete _relaz;
delete _ana;
_relaz=NULL;
_ana=NULL;
}
}
if (!_relaz)
{
_relaz = new TAlbero_relvoci(get(ABAN_CODAN));
_ana = new TAlbero_relana(get(ABAN_CODAN));
}
// carica la copia locale
TNumeric_id id_relana;
if (lock_struct)
_relaz->lock();
if (_relaz->goto_root())
{
if (_ana->goto_root())
id_relana=_ana->curr().get_real(ABRA_ID);
naviga_relazioni(id_relana,ID_NULLO,ID_NULLO);
}
return 0;
}
//Naviga l'albero delle relazioni
void TAnalisi_bil::naviga_relazioni(const TNumeric_id & begin_relana, const TNumeric_id & id_prec,const TNumeric_id & id_padre)
{
TNumeric_id id_relana, currid;
TNodeId key_rel;
_relaz->curr_id(key_rel);
_newrec = sincronizza_relana(begin_relana, id_relana);
currid=_newrec->get_real(ABRA_ID);
_inter_tree->insert_node(*_newrec,id_prec,id_padre,currid);
//Controllo se c'<27> un fratello di rel_voci
if (_relaz->has_rbrother())
{
if (_relaz->goto_rbrother())
{ //Fratello di rel_voci trovato
//Cerco un fratello anche su rel_ana: se non si trova c'<27> una inconsistenza nei dati
//fra relvoci e relana
TNumeric_id frat_relana;
naviga_relazioni(frat_relana,currid , id_padre);
//_ana->goto_node(key_ana); //Per risalire al nodo che mi ha chiamato in relana
_relaz->goto_node(key_rel);
}
else
{//Fratello di rel_voci non trovato
error_box ("Errore di consistenza nei dati di relvoci: <20> previsto un fratello ma non <20> stato trovato");
}
} // Il record di _relaz non ha un fratello destro
if (_relaz->goto_firstson())
{ //Figlio di rel_voci trovato
//Cerco il figlio di rel_ana: se non si trova c'<27> una inconsistenza nei dati
//fra relvoci e relana
if (id_relana!=ID_NULLO)
{
_ana->goto_id(id_relana);
if (_ana->goto_firstson())
id_relana=_ana->curr().get_real(ABRA_ID);
}
naviga_relazioni(id_relana, ID_NULLO , currid);
//_ana->goto_node(key_ana); //Per risalire al nodo che mi ha chiamato in relana
}//Se non trovo il figlio di una relazione, potrebbe essere una foglia di un ramo dell'albero
_relaz->goto_node(key_rel); //Per risalire al nodo che mi ha chiamato in relvoci
}
//Sincronizza relana con quello che trovo in relaz: relaz <20> navigato da naviga_relazioni
TRectype *TAnalisi_bil::sincronizza_relana(const TNumeric_id &begin_relana,TNumeric_id &id_relana)
{
id_relana=begin_relana;
TToken_string key;
// ****** caricamento array dei nodi di questo livello dall'isam file LF_RELANA ....
if (!_relaz->has_lbrother())
{
key = _relaz->curr().get(ABRL_CODVC);
if (id_relana != ID_NULLO)
{ //carica l'array con i nodi componenti di questa voce (analisi)
_ana->goto_id(id_relana);
_nodifratelli.destroy();
do
{
TString codvc =_ana->curr().get(ABRA_CODVC);
TString idcomp =_ana->curr().get(ABRA_NCOMP);
_nodifratelli.setkey(codvc,idcomp,key);
_nodifratelli.add(key,_ana->curr());
} while (_ana->has_rbrother() && _ana->goto_rbrother());
}
}
// ****** ... ricerca il nodo letto da LF_RELVOCI sull'array del livello ...
// rintraccia il record di relana sull'array
TString codvc = _relaz->curr().get(ABRL_CODVC);
TString idcomp = _relaz->curr().get(ABRL_IDCOMP);
if (_nodifratelli.find1stkey(codvc,idcomp, key))
{
*_newrec = ((TRectype &)_nodifratelli[key]);
_nodifratelli.mark_used(key); //segna il record nell'array come utilizzato
id_relana=_newrec->get_real(ABRA_ID);
}
else
{//Il record viene rigenerato con i dati minimi per ripristinare l'integrit<69> degli archivi
_newrec->zero(); //Azzero l'archivio
//Lo riempio con i dati basilari
_newrec->put(ABRA_CODAN,get(ABAN_CODAN));
_newrec->put(ABRA_ID,ID_NULLO);
_newrec->put(ABRA_TIPOCOD,_relaz->curr().get(ABRL_TIPOCOD));
_newrec->put(ABRA_CODVC,_relaz->curr().get(ABRL_CODCOMP));
_newrec->put(ABRA_NCOMP,_relaz->curr().get(ABRL_IDCOMP));
id_relana = ID_NULLO; // Serve per eseguire correttamente la new_id
_newrec->put(ABRA_ID,_ana->new_id(id_relana));
_inter_tree->set_status_node(_newrec->get_real(ABRA_ID),NODO_AGGIUNTO);
}
// ******** ...scarica l'array
if (!_relaz->has_rbrother()) // ultimo figlio
{
TObject *o;
_nodifratelli.restart();
while ((o = _nodifratelli.get()) != NULL)
{
if (!_nodifratelli.used(o)) //Marca il record in pi<70> su relana come da cancellare
_inter_tree->set_status_node(((TRectype *)o)->get_real(ABRA_ID),NODO_RIMOSSO);
}
}
return _newrec;
}
// ************
// array interno
void TAnalisi_bil::TArray_fratelli::setkey(const char *tipocod, const char *codvc, TToken_string &key) const
{
key.cut(0);
key.add(tipocod);
key.add(codvc);
}
bool TAnalisi_bil::TArray_fratelli::find1stkey(const char *tipocod, const char *codvc, TToken_string &key)
{
TObject *o;
setkey(tipocod,codvc,key);
o = objptr((const char *)key);
return ((o != NULL) && (!((TRectype *)o)->empty()));
}
void TAnalisi_bil::TArray_fratelli::mark_used(const char *key)
{
((TRectype *)objptr(key))->zero(); // segna il record nell'array come utilizzato
}
bool TAnalisi_bil::TArray_fratelli::used(TObject *o)
{
return ((TRectype *)o)->empty();
}
/**************************************************************************************************/
// TLocal_relana3 & TLocal_balance3
/**************************************************************************************************/
TLocal_relana3::TLocal_relana3() :
TAlbero_locale(LF_RELANA)
{
}
TLocal_relana3::~TLocal_relana3()
{}
TLocal_balance3::TLocal_balance3() :
TAlbero_locale(LF_MOVDETT)
{
}
TLocal_balance3::~TLocal_balance3()
{}
void TLocal_balance3::put_headkey(TRectype & node)
{
TToken_string s(get_headkey());
node.put(ABMD_CODDITTA,s.get());
node.put(ABMD_ANNO,s.get());
node.put(ABMD_CODPDB,s.get());
node.put(ABMD_TIPOBIL,s.get());
node.put(ABMD_CODCBL,s.get());
}
/**************************************************************************************************/
// TAlbero_locale
/**************************************************************************************************/
TAlbero_locale::TAlbero_locale(int filenum)
{
// versione definitiva:
// _currnode = new TRectype(filenum);
// _local_cache = new TRWrecord_cache(new TIsamtempfile(filenum,"",TRUE,TRUE));
// debug mods:
_currnode = new TRectype(filenum);
if (filenum==LF_RELANA)
_f= new TIsamtempfile(filenum,"/com/relana",FALSE,FALSE);
else
_f= new TIsamtempfile(filenum,"/com/movdett",FALSE,FALSE);
_local_cache = new TRWrecord_cache(_f);
//zero(); // resetta l'albero locale
}
TAlbero_locale::~TAlbero_locale()
{
delete _local_cache;
delete _f;
}
//Estraggo un nodo: se specifico l'id in ingresso estrae il nodo con quell'id;
//altrimenti ne sceglie uno a caso tra quelli "dirty"
//in output ho il record e le informazioni per sapere cosa fare di quel record
TRectype TAlbero_locale::extract_dirtynode(TNumeric_id &id, TString &status)
{
TToken_string key = get_headkey();
TRectype rec(current());
THash_object *row_status;
if (id == ID_NULLO)
{ //Gestione interna
_status_node.restart();
row_status = _status_node.get_hashobj();
status = (TString&)row_status->obj();
id = (TNumeric_id)row_status->key();
}
else
{
status = *(TString*)_status_node.objptr(id.string());
}
//Posso accedere alla cache (con la get) solo se il nodo non
//<2F> stato rimosso: se il nodo <20> stato rimosso, non sar<61> presente
//nella cache, e quindi la get di un nodo che non esiste <20> un record
//vuoto; questo record vuoto causer<65> errori nel momento in cui chiamer<65>
//la flush per il distruttore; devo quindi evitare questo
//caso l'utilizzo della get
if (status != NODO_RIMOSSO)
{
key.add(id.string()); //La chiave <20> completamente ricostruita per la ricerca sulla cache
rec = _local_cache->get(key); //passo la chiave (formata da codtab e id) e ritorna il rectype
}
else
{ //Riempio manualmente il record che deve essere rimosso: inserisco solo la chiave
//rec.put(ABRA_CODAN, ??? );
rec.put(id_fieldname(),id.string());
}
//A questo punto il record <20> completo e so gi<67> quale operazione devo compierci sopra
//Questa parte serve per gestire l'assoc_array degli status
if ((rec.empty()) && (status != NODO_RIMOSSO))
{ //Chiave non trovata: la chiave ricercata non <20> presente nella cache
fatal_box("Errore nella lettura della cache o nella costruzione del record.");
}
_status_node.remove(id.string());
return rec; //Ritorno un Trectype e passo la stauts node
}
//Setta lo stato del nodo: cio<69> decide che cosa dovr<76> fare con questo nodo
void TAlbero_locale::set_status_node(const TNumeric_id &id, const char *status)
{
// STATUS A priorit<69> assoluta e rimane sempre A
// STATUS M priorit<69> su R ma non su A
// STATUS R priorit<69> sul vuoto ma non su M o A
// STATUS vuoto: nessuna priorit<69>
// In TInser_albero::insert_node posso capire solo A o M ma non R
// In TAlbero_relana::sincronizza posso capire solo R ma non A e M
TString key = id.string();
const TString *prec_status = (const TString *)_status_node.objptr(key);
if (!prec_status)
{ //Elemento ancora non presente nell'array
_status_node.add(key,TString (status));
}
else
{ //Elemento gi<67> inserito
if (stricmp(status,NODO_AGGIUNTO)==0 && (*prec_status != NODO_AGGIUNTO))
_status_node.add(key,TString (status),TRUE); //Forzo la sovrascrittura in ogni caso
if (stricmp(status,NODO_MODIFICATO)== 0 && (*prec_status != NODO_AGGIUNTO))
{
//Se il nodo era gi<67> inserito e il suo flag <20> diverso da NODO_AGGIUNTO
//allora devo settare che questo nodo <20> modificato (se non lo <20> ancora)
if (*prec_status != NODO_MODIFICATO)
{
_status_node.add(key,TString (status),TRUE); //Forzo la sovrascrittura in ogni caso
}
// Arriva qui se il nodo <20> inserito e il flag era gi<67> stato settato da una
//operazione precedente a NODO_MODIFICATO
}
if (stricmp(status,NODO_RIMOSSO)==0 && *prec_status != NODO_RIMOSSO)
{ //Sto rimuovento un nodo che prima era stato modificato
CHECK(FALSE,"Sto rimuovendo un nodo che probabilmente non <20> da rimuovere");
}
}
}
//Restituisce un nuovo id
TNumeric_id &TAlbero_locale::new_id(TNumeric_id id)
{
if (id == ID_NULLO)
_last_insert_id+1;
else
_last_insert_id = max(_last_insert_id,id);
return _last_insert_id;
}
//Inserisce il nodo (che ho in ingresso) nella struttura ad albero doppiamente linkata
//di relana aggiornando tutti i link
bool TAlbero_locale::insert_node(TRectype & node,const TNumeric_id id_prec, const TNumeric_id id_padre, TNumeric_id id)
{
const int dirty_value=NODO_MODIFICATO;
TNumeric_id id_succ;
TToken_string key;
key=get_headkey();
TRectype parente(current());
if (id == ID_NULLO)
id = new_id(id);
else
new_id(id);
//Cerco il mio fratello precedente
if (id_prec != ID_NULLO)
{ //Aggiorno il successivo del mio precedente dicendogli che il suo successivo sono io
key.add(id_prec.string());
parente = _local_cache->get(key);
parente.put(idsucc_fieldname(),id.string());
_local_cache->put(parente);
set_status_node(id_prec,dirty_value); //Setto l'eventuale flag di array modificato per il fratello
}
else
{ //Se non ho il mio precedente, allora o sono una radice principale o divento il primo figlio:
if (id_padre != ID_NULLO)
{ //Sono un primo figlio: rintraccio mio padre e gli dico che il suo primo figlio ora, sono io
key.add(id_padre.string());
parente = _local_cache->get(key);
parente.put(idson_fieldname(),id.string());
_local_cache->put(parente);
set_status_node(id_padre,dirty_value); //Setto l'eventuale flag di array modificato per il padre
} // Sono una radice principale e quindi sono orfana di padre
}
//Ora aggiorno me stesso
put_headkey(node);
// node.put(ABRA_CODAN,get_headkey()); //Eseguo un combio della chiave: non serve avere il codice della tabella nella cache
node.put(idprec_fieldname(),id_prec.string());
node.put(idfather_fieldname(),id_padre.string());
node.put(idsucc_fieldname(),id_succ.string());
node.put(idson_fieldname(),ID_NULLO);
_local_cache->put(node);
//set_current(node);
goto_node(node);
set_status_node(id,dirty_value);
return TRUE;
}
//Questa funzione aggiorna i link dei "parenti" di un nodo che sta per essere rimosso
bool TAlbero_locale::delete_node(const TNumeric_id &id)
{
TRectype parente(current());
TNumeric_id id_padre, id_figlio, id_prec, id_succ;
goto_node(id);
TToken_string key;
// id = _currnode->get_real(ABRA_ID);
id_padre = _currnode->get_real(idfather_fieldname());
id_figlio = _currnode->get_real(idson_fieldname());
id_prec = _currnode->get_real(idprec_fieldname());
id_succ = _currnode->get_real(idsucc_fieldname());
//Ricerco il padre per modificare il suo primo figlio, solo se io sono il suo primo figlio
if (id_padre != ID_NULLO)
{
key.cut(0);
key = get_headkey();
key.add(id_padre.string());
parente=_local_cache->get(key); //padre
if (parente.get(idson_fieldname()) == id.string())
{ //Il nodo che sta per essere rimosso <20> il primo figlio di un padre
parente.put(idson_fieldname(),id_succ.string()); //Aggiorno il padre
_local_cache->put(parente);
}
}
//Ricerco il mio fratello precedente per dirgli che io non sono pi<70>
//suo fratello successivo, e gli dico anche quale sar<61> il suo successivo
if (id_prec != ID_NULLO)
{
key.cut(0);
key = get_headkey();
key.add(id_prec.string());
parente=_local_cache->get(key); //fratello sinistro
parente.put(idsucc_fieldname(),id_succ.string()); //Assegno al mio fratello sinistro il mio fratello destro
_local_cache->put(parente);
}
//Dico a mio fratello successivo chi il suo precedente non sono pi<70> io,
//e gli dico che il suo precedente <20> il MIO precedente
if (id_succ != ID_NULLO)
{
key.cut(0);
key = get_headkey();
key.add(id_succ.string());
parente=_local_cache->get(key); //fratello destro
parente.put(idprec_fieldname(),id_prec.string()); //Assegno al mio fratello destro il mio fratello sinistro
_local_cache->put(parente);
}
//I miei vicini mi hanno gi<67> escluso: non sono pi<70> raggiungibile
//Ricostruisco la chiave
//Elimino i miei link dai fratelli
key.cut(0);
key = get_headkey();
key.add(id.string());
//node.put(ABRA_IDPADRE,ID_NULLO);
_currnode->put(idprec_fieldname(),ID_NULLO);
_currnode->put(idsucc_fieldname(),ID_NULLO);
_local_cache->put(*_currnode);
//Elimino i miei figli
TNumeric_id nextnode_id;
if (id_prec != ID_NULLO)
{
nextnode_id = id_prec;
}
else
{
if (id_succ != ID_NULLO)
{
nextnode_id = id_succ;
}
else
{
if (id_padre != ID_NULLO)
{
nextnode_id = id_padre;
}
else
{
CHECK (FALSE,"NON SO DOVE POSIZIONARMI !!!!");
}
}
}
remove_subtree(nextnode_id);
//A questo punto l'assoc_array degli status <20> gi<67> completamente aggiornato
//write_cache(); //Solo per debug
return TRUE;
}
void TAlbero_locale::zero()
{
delete_tree(); // cancella l'albero
_status_node.destroy();
_currnode->put(id_fieldname(),ID_NULLO);
}
bool TAlbero_locale::delete_tree()
{
//write_cache();
goto_root();
remove_subtree(ID_NULLO); //Non eliminando il link coi fratelli della radice, remove_subtree naviga tutto l'albero
return TRUE;
}
//Questa funzione elimina il nodo corrente e tutti i nodi sottostanti
bool TAlbero_locale::remove_subtree(const TNumeric_id return_id)
{
TNumeric_id myself_id;
//cerco il figlio
if (has_son())
{
myself_id = _currnode->get_real(id_fieldname());
goto_firstson();
remove_subtree(myself_id);
}
//cerco il fratello
if (has_rbrother())
{
myself_id = _currnode->get_real(id_fieldname());
goto_rbrother();
remove_subtree(myself_id);
}
TNumeric_id id = _currnode->get(id_fieldname());
//Questa parte esegue la gestione dell'assoc_array degli stati
//in modo tale da mantenerlo allineato con il contenuto della cache
TString *status;
status = (TString*)_status_node.objptr(id.string());
if (status != ID_NULLO)
{//Se esiste uno stato per questo nodo
if ((*status == NODO_MODIFICATO) || (*status == NODO_RIMOSSO))
{//In questi due casi il nodo esisteva gi<67> nel file originario
_status_node.add(id.string(),(TString)NODO_RIMOSSO,TRUE); //Forzo la sovrascrittura in ogni caso
}
else
{//Nel caso di NODO_AGGIUNTO
_status_node.remove(id.string());
}
}
else
{
//Questo caso non dovrebbe verificarsi poich<63> un nodo per cui
//non <20> previsto uno status, non dovrebbe esistere nella cache
CHECK (FALSE,"Errore nella consistenza della cache");
}
if (return_id != ID_NULLO)
goto_node(return_id);
else
_currnode->put(id_fieldname(),ID_NULLO);
return TRUE;
}
bool TAlbero_locale::has_son()
{
return (_currnode->get_real(idson_fieldname()) != ID_NULLO);
}
bool TAlbero_locale::goto_firstson()
{
goto_node(_currnode->get_real(idson_fieldname()));
return TRUE;
}
bool TAlbero_locale::has_rbrother()
{
return (_currnode->get_real(idsucc_fieldname()) != ID_NULLO);
}
bool TAlbero_locale::goto_rbrother()
{
goto_node(_currnode->get_real(idsucc_fieldname()));
return TRUE;
}
bool TAlbero_locale::goto_root()
{ //Il _current sar<61> posizionato sull'ultimo nodo che <20> stato visitato
if (current().get_real(id_fieldname())==ID_NULLO)
return FALSE;
TToken_string key;
TNumeric_id id_prec, id_padre;
key=get_headkey();
key.add(_currnode->get(id_fieldname()));
do
{
*_currnode = _local_cache->get(key);
id_prec = _currnode->get_real(idprec_fieldname());
id_padre = _currnode->get_real(idfather_fieldname());
if (id_prec != ID_NULLO)
{
key.cut(0);
key=get_headkey();
key.add(id_prec.string());
}
else
{
if (id_padre != ID_NULLO)
{
key.cut(0);
key=get_headkey();
key.add(id_padre.string());
}
}
} while (id_prec != ID_NULLO || id_padre != ID_NULLO);
return TRUE;
}
void TAlbero_locale::goto_node(const TNumeric_id id)
{
TToken_string key;
key=get_headkey();
key.add(id.string());
*_currnode = _local_cache->get(key);
}
/**************************************************************************************************/
// TAlbero_relana
/**************************************************************************************************/
//In questo costruttore cerco di capire anche quale <20> l'ultimo id utilizzato
TAlbero_relana::TAlbero_relana( const char * tabcode)
{
_codtab=tabcode;
_relana=new TLocalisamfile(LF_RELANA);
_relana->put(ABRA_CODAN,_codtab);
_relana->put(ABRA_ID,MAX_ID_REL);
//Trova l'ultimo id usato in relana: DA COMPLETARE !!!
int err = _relana->read(_isgteq); //Codifica errori in TIsamerr
if (err == NOERR)
{ // Ho trovato il primo della tabella successiva: leggendo il precedente
// ho l'ultimo record dalla tabella cercata
_relana->prev();
if (_relana->get(ABRA_CODAN) != _codtab)
{ // Non esiste la tabella
; //_last_id viene lasciato a 0
}
_last_id = _relana->get_real(ABRA_ID);
}
if (err == _isemptyfile)
{ //Il file <20> vuoto
;//_last_id viene lasciato a 0
}
if (err == _iseof)
{ // La tabella successiva non esiste: provo a leggere l'ultimo record
_relana->last();
if (_relana->get(ABRA_CODAN) != _codtab)
{ //Tabella non trovata
; //_last_id viene lasciato a 0
}
//Tabella trovata: prendo l'id dell'ultimo record
_last_id = _relana->get_real(ABRA_ID);
}
}
TAlbero_relana::~TAlbero_relana()
{
delete _relana;
}
//Incremento l'id per dare il primo id disponibile
TNumeric_id & TAlbero_relana::new_id(TNumeric_id id)
{
if (id == ID_NULLO)
_last_id = _last_id+1;
else
_last_id = max(_last_id,id);
return _last_id;
}
//Posiziona relana sul record voluto
bool TAlbero_relana::goto_id(const TNumeric_id &id)
{
_relana->put(ABRA_CODAN,_codtab);
_relana->put(ABRA_ID,id);
if (_relana->read() == NOERR)
return TRUE;
return FALSE; //Non sono riuscito a posizionarmi sul nodo
}
bool TAlbero_relana::lock()
{
//Potrebbe usare come parametro _codtab
return TRUE;
}
void TAlbero_relana::unlock()
{
//Potrebbe usare come parametro _codtab
return ;
}
//Ricerca la radice di relana
bool TAlbero_relana::goto_root()
{
_relana->put(ABRA_CODAN,_codtab); //Ricerca il codice della tabella: <20> l'unica cosa che conosco
//No so nulla sull'id
_relana->read(_isgteq); //Leggo la prima chiave che si avvicina a quella specificata
if (_relana->get(ABRA_CODAN) == _codtab)
{
while (has_father())
{
goto_father();
} // Ho raggiunto il primo livello
while (has_lbrother())
{
goto_lbrother();
}// Ho raggiunto la radice
return TRUE;
}
//Non ho trovato una voce con codice tabella richiesta
return FALSE;
}
//Dice se un nodo di relana ha il primo figlio
bool TAlbero_relana::has_son() const
{
return _relana->get_real(ABRA_IDFIGLIO)!=ID_NULLO;
}
//Ricerca il primo filglio di un nodo di relana
bool TAlbero_relana::goto_firstson()
{
TNumeric_id id_figlio(_relana->get_real(ABRA_IDFIGLIO));
if (id_figlio > 0)
{ //Ci deve essere un figlio: lo cerco
//Il CODAN deve rimanere uguale a quello del padre
_relana->put(ABRA_ID,_relana->get(ABRA_IDFIGLIO));
if (_relana->read() == NOERR)
return TRUE; //Figlio trovato con successo
}
//Non c'<27> un figlio: non si dovrebbe mai verificare visto che
//si entra in questa funzione solo quando <20> previsto che ci sia un figlio.
return FALSE;
}
//Dice se un nodo di relana ha un fratello destro
bool TAlbero_relana::has_rbrother() const
{
return _relana->get_real(ABRA_IDSUCC) != ID_NULLO;
}
//Ricerca il fratello destro di un nodo di relana
bool TAlbero_relana::goto_rbrother()
{
TNumeric_id id_fratello(_relana->get_real(ABRA_IDSUCC));
if (id_fratello > 0)
{ //Ci deve essere un fratello: lo cerco
//Il CODAN deve rimanere uguale a quello del fratello precedente
_relana->put(ABRA_ID,_relana->get(ABRA_IDSUCC));
if (_relana->read() == NOERR)
return TRUE; //Fratello trovato con successo
}
//Non c'<27> un fratello: non si dovrebbe mai verificare visto che
//si entra in questa funzione solo quando <20> previsto che ci sia un fratello.
return FALSE;
}
//Dice se un nodo di relana ha un padre
bool TAlbero_relana::has_father() const
{
return _relana->get_real(ABRA_IDPADRE) != ID_NULLO;
}
//Ricerca il padre del nodo di relana
bool TAlbero_relana::goto_father()
{
_relana->put(ABRA_ID,_relana->get(ABRA_IDPADRE));
if (_relana->read() != NOERR)
CHECK (FALSE,"ERRORE DI PADRE"); //*!*!*!*! DA gestire
else
return TRUE;
return FALSE;
}
//Ritrona il nodo corrente di relana
TObject *TAlbero_relana::curr_node() const
{
return &_relana->curr();
}
//Dice se un nodo di relana ha un fratello sinistro
bool TAlbero_relana::has_lbrother() const
{
return _relana->get_real(ABRA_IDPREC) != ID_NULLO;
}
//Ricerca il fratello sinistro di un nodo di relana
bool TAlbero_relana::goto_lbrother()
{
TNumeric_id id_fratello(_relana->get_real(ABRA_IDPREC));
if (id_fratello > 0)
{//Ci deve essere un fratello: lo cerco
//Il CODAN deve rimanere uguale a quello del fratello successivo
_relana->put(ABRA_ID,_relana->get(ABRA_IDPREC));
if (_relana->read() == NOERR)
return TRUE; //Fratello trovato con successo
}
//ERRORE !!!
return FALSE;
}
void TAlbero_relana::node2id(const TObject * node,TNodeId & id) const
{
id.format("%ld",(long)_relana->recno());
}
/**************************************************************************************************/
// TAlbero_relvoci
/**************************************************************************************************/
TAlbero_relvoci::TAlbero_relvoci(const char * tabcode)
{
_codtab=tabcode;
_relvoci=new TLocalisamfile(LF_RELVOCI);
}
TAlbero_relvoci::~TAlbero_relvoci()
{
delete _relvoci;
}
//Ricerca la radice di relvoci
bool TAlbero_relvoci::goto_root()
{
_relvoci->put(ABRL_TIPOCOD,TIPO_ANALISI); // La radice ha tipocod = "A"
TString codtab = _codtab; //Mi serve per eseguire una formattazione senza modificare _codtab
_relvoci->put(ABRL_CODVC,codtab.right_just(10,' ')); //Inserisco il codice della tabella su cui sto lavorando
//No so nulla sull'id
_relvoci->read(_isgteq); //Leggo la prima chiave che si avvicina a quella specificata
if ((_relvoci->get(ABRL_TIPOCOD) == TIPO_ANALISI) || (_relvoci->get(ABRL_CODVC) == _codtab))
{
while (has_lbrother())
{
goto_lbrother();
}
return TRUE;
}
//Non ho trovato una voce di analisi richiesta o
//non ho trovato una voce con codice tabella richiesta
return FALSE;
}
bool TAlbero_relvoci::lock()
{
//Potrebbe usare come parametro _codtab
return TRUE;
}
void TAlbero_relvoci::unlock()
{
//Potrebbe usare come parametro _codtab
return ;
}
//Dice se il nodo ha un primo figlio; DA DEFINIRE
bool TAlbero_relvoci::has_son() const
{
CHECK(FALSE,"!!!");
return FALSE;
}
//Ricerca il primo figlio di un nodo di relvoci
bool TAlbero_relvoci::goto_firstson()
{
TRecnotype temp_pos = _relvoci->recno();
_relvoci->put(ABRL_TIPOCOD,TIPO_VOCE); //Il tipo codice del filgio di una radice o di un livello inferiore <20> sempre "V"
TString controllo_comp = _relvoci->get(ABRL_CODCOMP);
_relvoci->put(ABRL_CODVC,controllo_comp); //Setto il figlio
//Non posso sapere nulla sull'id corretto: lo azzero perch<63> il record contiene gi<67> l'id del record letto in precedenza
//e leggo la prima chiave che assomiglia a quella settata
_relvoci->put(ABRL_IDCOMP,0);
_relvoci->read(_isgteq); //Leggo la prima chiave che si avvicina a quella specificata
if ((_relvoci->get(ABRL_TIPOCOD) == TIPO_VOCE) && (_relvoci->get(ABRL_CODVC) == controllo_comp))
{//Ho trovato un figlio, ma non so se <20> proprio il primo figlio:
//se non sono sul primo figlio navigo all'indietro
while (has_lbrother())
{
goto_lbrother();
}
return TRUE;
}
else
{ //Non ho trovato il filgio: mi riposiziono alla posizione precedente
//Questo non rappresenta un errore perch<63> potrebbe essere
//la foglia di un ramo dell'albero
_relvoci->readat(temp_pos);
return FALSE;
}
}
//Dice su un nodo di relvoci ha un fratello destro
bool TAlbero_relvoci::has_rbrother() const
{
return _relvoci->get_long(ABRL_NEXTCOMP) != ID_NULLO;
}
//Ricerca il fratello destro di un nodo di relvoci
bool TAlbero_relvoci::goto_rbrother()
{
//Il tipocod di un fratello deve essere uguale a quello attuale
//Il codice voce di un fratello deve essere uguale a quella attuale
_relvoci->put(ABRL_IDCOMP,_relvoci->get(ABRL_NEXTCOMP)); //Assegno all'id il successivo
if (_relvoci->read() == NOERR)
return TRUE; //Fratello trovato
else
return FALSE; //Fratello non trovato
}
//Dice se un nodo di relvoci ha un fratello sinistro
bool TAlbero_relvoci::has_lbrother() const
{
return _relvoci->get_long(ABRL_PREVCOMP) != ID_NULLO;
}
//Ricerca il fratello sinistro di un nodo di relvoci
bool TAlbero_relvoci::goto_lbrother()
{
//Il tipocod di un fratello deve essere uguale a quello attuale
//Il codice voce di un fratello deve essere uguale a quella attuale
_relvoci->put(ABRL_IDCOMP,_relvoci->get(ABRL_PREVCOMP)); //Assegno all'id il successivo
if (_relvoci->read() == NOERR)
return TRUE; //Fratello trovato
else
return FALSE; //Fratello non trovato
}
//Ritorna il nodo corrente di relvoci
TObject *TAlbero_relvoci::curr_node() const
{
return &_relvoci->curr();
}
void TAlbero_relvoci::node2id(const TObject * node,TNodeId & id) const
{
id.format("%ld",_relvoci->recno());
}
/**************************************************************************************************/
// TAlbero_movdett
/**************************************************************************************************/
TAlbero_movdett::TAlbero_movdett( const char * tabcode)
{
_codtab=tabcode;
_movdett=new TLocalisamfile(LF_MOVDETT);
/* _relana->put(ABRA_CODAN,_codtab);
_relana->put(ABRA_ID,MAX_ID_REL);
//Trova l'ultimo id usato in relana: DA COMPLETARE !!!
int err = _relana->read(_isgteq); //Codifica errori in TIsamerr
if (err == NOERR)
{ // Ho trovato il primo della tabella successiva: leggendo il precedente
// ho l'ultimo record dalla tabella cercata
_relana->prev();
if (_relana->get(ABRA_CODAN) != _codtab)
{ // Non esiste la tabella
; //_last_id viene lasciato a 0
}
_last_id = _relana->get_real(ABRA_ID);
}
if (err == _isemptyfile)
{ //Il file <20> vuoto
;//_last_id viene lasciato a 0
}
if (err == _iseof)
{ // La tabella successiva non esiste: provo a leggere l'ultimo record
_relana->last();
if (_relana->get(ABRA_CODAN) != _codtab)
{ //Tabella non trovata
; //_last_id viene lasciato a 0
}
//Tabella trovata: prendo l'id dell'ultimo record
_last_id = _relana->get_real(ABRA_ID);
} */
}
//Posiziona relana sul record voluto
bool TAlbero_movdett::goto_id(const TNumeric_id &id)
{
_movdett->put(ABMD_CODDITTA,_codtab);
_movdett->put(ABMD_ID,id);
if (_movdett->read() == NOERR)
return TRUE;
return FALSE; //Non sono riuscito a posizionarmi sul nodo
}
//Ricerca la radice di relana
bool TAlbero_movdett::goto_root()
{
_movdett->put(ABMD_CODDITTA,_codtab.get(0));
_movdett->put(ABMD_ANNO,_codtab.get(1));
_movdett->put(ABMD_CODPDB,_codtab.get(2));
_movdett->put(ABMD_TIPOBIL,_codtab.get(3));
_movdett->put(ABMD_CODCBL,_codtab.get(4));
//No so nulla sull'id
_movdett->read(_isgteq); //Leggo la prima chiave che si avvicina a quella specificata
TString fctrl = _movdett->get(ABMD_CODDITTA);
TToken_string controllo = fctrl.right_just(5,'0');
controllo.add(_movdett->get(ABMD_ANNO));
controllo.add(_movdett->get(ABMD_CODPDB));
controllo.add(_movdett->get(ABMD_TIPOBIL));
controllo.add(_movdett->get(ABMD_CODCBL));
if (controllo == _codtab)
{
while (has_father())
{
goto_father();
} // Ho raggiunto il primo livello
while (has_lbrother())
{
goto_lbrother();
}// Ho raggiunto la radice
return TRUE;
}
//Non ho trovato una voce con codice tabella richiesta
return FALSE;
}
//Dice se un nodo di relana ha il primo figlio
bool TAlbero_movdett::has_son() const
{
return _movdett->get_real(ABMD_IDFIGLIO)!=ID_NULLO;
}
//Ricerca il primo filglio di un nodo di relana
bool TAlbero_movdett::goto_firstson()
{
TNumeric_id id_figlio(_movdett->get_real(ABMD_IDFIGLIO));
if (id_figlio > 0)
{ //Ci deve essere un figlio: lo cerco
//Il CODAN deve rimanere uguale a quello del padre
_movdett->put(ABMD_ID,_movdett->get(ABMD_IDFIGLIO));
if (_movdett->read() == NOERR)
return TRUE; //Figlio trovato con successo
}
//Non c'<27> un figlio: non si dovrebbe mai verificare visto che
//si entra in questa funzione solo quando <20> previsto che ci sia un figlio.
return FALSE;
}
//Dice se un nodo di relana ha un fratello destro
bool TAlbero_movdett::has_rbrother() const
{
return _movdett->get_real(ABMD_IDSUCC) != ID_NULLO;
}
//Ricerca il fratello destro di un nodo di relana
bool TAlbero_movdett::goto_rbrother()
{
TNumeric_id id_fratello(_movdett->get_real(ABMD_IDSUCC));
if (id_fratello > 0)
{ //Ci deve essere un fratello: lo cerco
//Il CODAN deve rimanere uguale a quello del fratello precedente
_movdett->put(ABMD_ID,_movdett->get(ABMD_IDSUCC));
if (_movdett->read() == NOERR)
return TRUE; //Fratello trovato con successo
}
//Non c'<27> un fratello: non si dovrebbe mai verificare visto che
//si entra in questa funzione solo quando <20> previsto che ci sia un fratello.
return FALSE;
}
//Dice se un nodo di relana ha un padre
bool TAlbero_movdett::has_father() const
{
return _movdett->get_real(ABMD_IDPADRE) != ID_NULLO;
}
//Ricerca il padre del nodo di relana
bool TAlbero_movdett::goto_father()
{
_movdett->put(ABMD_ID,_movdett->get(ABMD_IDPADRE));
if (_movdett->read() != NOERR)
CHECK (FALSE,"ERRORE DI PADRE"); //*!*!*!*! DA gestire
else
return TRUE;
return FALSE;
}
//Ritrona il nodo corrente di relana
TObject *TAlbero_movdett::curr_node() const
{
return &_movdett->curr();
}
//Dice se un nodo di relana ha un fratello sinistro
bool TAlbero_movdett::has_lbrother() const
{
return _movdett->get_real(ABMD_IDPREC) != ID_NULLO;
}
//Ricerca il fratello sinistro di un nodo di relana
bool TAlbero_movdett::goto_lbrother()
{
TNumeric_id id_fratello(_movdett->get_real(ABMD_IDPREC));
if (id_fratello > 0)
{//Ci deve essere un fratello: lo cerco
//Il CODAN deve rimanere uguale a quello del fratello successivo
_movdett->put(ABMD_ID,_movdett->get(ABMD_IDPREC));
if (_movdett->read() == NOERR)
return TRUE; //Fratello trovato con successo
}
//ERRORE !!!
return FALSE;
}
void TAlbero_movdett::node2id(const TObject * node,TNodeId & id) const
{
id.format("%ld",_movdett->recno());
}