From eb541488d185f17a4eb93b014b91c9ae6d96a95a Mon Sep 17 00:00:00 2001 From: sauro Date: Wed, 14 Jan 1998 15:03:20 +0000 Subject: [PATCH] Questi file contengono gli strumenti e gli oggetti per l'utilizzo e la gestione delle strutture ad albero git-svn-id: svn://10.65.10.50/trunk@5933 c028cbd2-c16b-5b4b-a496-9718f37d4682 --- ab/ab3.cpp | 10 + ab/ab3.h | 6 + ab/ab3100.cpp | 38 ++ ab/ablib01.cpp | 1003 ++++++++++++++++++++++++++++++++++++++++++++++++ ab/ablib01.h | 342 +++++++++++++++++ ab/ablib09.cpp | 148 +++++++ ab/ablib09.h | 24 ++ 7 files changed, 1571 insertions(+) create mode 100755 ab/ab3.cpp create mode 100755 ab/ab3.h create mode 100755 ab/ab3100.cpp create mode 100755 ab/ablib01.cpp create mode 100755 ab/ablib01.h create mode 100755 ab/ablib09.cpp create mode 100755 ab/ablib09.h diff --git a/ab/ab3.cpp b/ab/ab3.cpp new file mode 100755 index 000000000..fd9ea1ea3 --- /dev/null +++ b/ab/ab3.cpp @@ -0,0 +1,10 @@ +#include +#include + +#include "ab3.h" + +int main(int argc,char **argv) +{ + ab3100(argc,argv); + return 0; +} diff --git a/ab/ab3.h b/ab/ab3.h new file mode 100755 index 000000000..f58dd9ace --- /dev/null +++ b/ab/ab3.h @@ -0,0 +1,6 @@ +#ifndef __AB3_H +#define __AB3_H + +void ab3100(int argc, char **argv); + +#endif //__AB3_H \ No newline at end of file diff --git a/ab/ab3100.cpp b/ab/ab3100.cpp new file mode 100755 index 000000000..2aed717dd --- /dev/null +++ b/ab/ab3100.cpp @@ -0,0 +1,38 @@ +#include "ab3.h" +#include "ablib01.h" + + +void TTest::main_loop() +{ + + //Questa parte di funzione serve per testare la read, write, delete_node, delete_tree, delete_subtree ecc + + TLocalisamfile anas(LF_ANALISI); + anas.set_curr(new TAnalisi_bil()); + anas.put(ABAN_CODAN,"A1"); + anas.read(); + anas.remove(); //Solo per il test della delete_node o la rimozione di una intera tabella + //anas.write(); + + + /*Questa parte della funzione serve solo per testare la readat + + + TRecnotype nrec = 10; //Per capire quale numero ci va scommenta la riga successiva e provare col debug + //nrec = anas.recno(); //nrec è un TRecnotype: dice quale numero ci va dopo + TLocalisamfile anas1(LF_ANALISI); + anas1.set_curr(new TAnalisi_bil()); + anas1.readat(nrec); + //Abilitare solo una di queste funzioni + anas1.remove(); + //anas1.write(); + + E deve esserci solo questo pezzo: tutto il resto deve essere commentato */ +} + + +void ab3100(int argc, char **argv) +{ + TTest a; + a.run(argc,argv, "Prova"); +} diff --git a/ab/ablib01.cpp b/ab/ablib01.cpp new file mode 100755 index 000000000..d03aa66d1 --- /dev/null +++ b/ab/ablib01.cpp @@ -0,0 +1,1003 @@ +#include "ablib01.h" + +#define MAX_ID_REL 999999999 +#define NODO_AGGIUNTO "A" +#define NODO_MODIFICATO "M" +#define NODO_RIMOSSO "R" + + +/**************************************************************************************************/ +// TAnalisi_bil +/**************************************************************************************************/ +TAnalisi_bil::TAnalisi_bil() + :TRectype(LF_ANALISI) +{ + _voci = new TLocalisamfile(LF_VOCI); + _analisi= new TLocalisamfile(LF_ANALISI); + _relaz = NULL; + _ana = NULL; + _inter_tree = new TInsert_albero(); + _newrec = new TRectype(LF_RELANA); +} + + +TAnalisi_bil::~TAnalisi_bil() +{ + delete _inter_tree ; + delete _voci; + delete _ana; + delete _relaz; + delete _analisi; + delete _newrec; +} + +//Estraggo un nodo: in ingresso ho un id e il codice della tabella; +//in output ho il record e le informazioni per sapere cosa fare di quel record +TRectype TInsert_albero::extract_node(const TString &codtab,TRelana_id &id, TString &status) +{ + TToken_string key = codtab; + TRectype rec(LF_RELANA); + 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 = (TRelana_id)row_status->key(); + } + else + { + status = *(TString*)_status_node.objptr(id.string()); + } + + //Posso accedere alla cache (con la get) solo se il nodo non + //è stato rimosso: se il nodo è stato rimosso, non sarà presente + //nella cache, e quindi la get di un nodo che non esiste è un record + //vuoto; questo record vuoto causerà errori nel momento in cui chiamerò + //la flush per il distruttore; devo quindi evitare questo + //caso l'utilizzo della get + if (status != NODO_RIMOSSO) + { + key.add(id.string()); //La chiave è completamente ricostruita per la ricerca sulla cache + rec = _ana_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,codtab); + rec.put(ABRA_ID,id.string()); + } + //A questo punto il record è completo e so già 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 è 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 +} + +int TAnalisi_bil::write(TBaseisamfile &analisi) const +{ + TLocalisamfile relana(LF_RELANA); + int err = commit_body(relana); + if (err) + CHECK (FALSE,"Errore di scrittura del record sull'isamfile"); + return err; +} + +//Scrive tutto il contenuto della cache sull'isamfile, aggiungendo, modificando +//o rimuovendo i nodi necessari +int TAnalisi_bil::commit_body(TLocalisamfile &relana) const +{ + TRectype rec(LF_RELANA); + TString status; + //Estraggo un nodo per volta basandomi sull'assoc_array degli status + while (_inter_tree->status_node_items() > 0) + { + TRelana_id id; + int prova = _inter_tree->status_node_items(); + rec= _inter_tree->extract_node(get(ABAN_CODAN), id, status); + //Ho un rectype e uno status node + if (status == NODO_AGGIUNTO) + { + if (relana.curr().empty()) + { //Se il cursor di relana è posizionato in modo errato: + //lo sistemo sull'ultimo record del file + //DOMANDA: - Perchè ??? + //TENATATIVO DI RISPOSTA: - Quando l'ultima azione effettuata (prima di questa "aggiunta") è la rimozione + // dell'ultimo record probabilmente (ma non sono sicuro) il cursore non è posizionato (o forse + // è 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 è stata una write o una rewrite o una remove di un record + // che non sia l'ultimo) questo problema non esiste. + //RISPOSTA ESATTA: - Bho !!! + relana.last(); + } + int err = relana.write(rec); + if (err != NOERR) + return err; + } + if (status == NODO_MODIFICATO) + { + int err = relana.rewrite(rec); + if (err != NOERR) + return err; + } + if (status == NODO_RIMOSSO) + { + relana.read(rec); //Devo leggere sul file per posizionare il cursore: stesso motivo di prima + int err = relana.remove(rec); + if (err != NOERR) + return err; + } + // In tutti gli altri casi il nodo non viene modificato + } + return NOERR; +} + +//Leggo la "testata" del record +int TAnalisi_bil::read(TBaseisamfile& analisi, word isop, word lockop) +{ + int err=TRectype::read(analisi, isop, lockop); + if (err==NOERR) + read_body(FALSE); + return err; +} + +//Leggo la testata del record basandomi sulla posizione +int TAnalisi_bil::readat(TBaseisamfile& analisi, TRecnotype nrec, word lockop) +{ + int err=TRectype::readat(analisi, nrec, lockop); + if (err==NOERR) + read_body(FALSE); + return err; +} + +//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 + TRelana_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; +} + +int TAnalisi_bil::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); + TRelana_id id; + id = 17; + _inter_tree->delete_node(id); + int errc = commit_body(lf); + 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 + TLocalisamfile lf(LF_RELANA); + _inter_tree->delete_tree(); + int errc = commit_body(lf); + if (errc != NOERR) + { + message_box("Errore nell'aggiornameno del file dati"); + //return errc; + } + int err = TRectype::remove(f); + return err; +} + +void TAnalisi_bil::zero() +{ + _inter_tree->zero(); + TRectype::zero() ; +} + +//Naviga l'albero delle relazioni +void TAnalisi_bil::naviga_relazioni(const TRelana_id & begin_relana, const TRelana_id & id_prec,const TRelana_id & id_padre) +{ + TRelana_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'è 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'è una inconsistenza nei dati + //fra relvoci e relana + TRelana_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: è previsto un fratello ma non è 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'è 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 è navigato da naviga_relazioni +TRectype *TAnalisi_bil::sincronizza_relana(const TRelana_id &begin_relana,TRelana_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à 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ù 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(); +} + +/**************************************************************************************************/ +// TInsert_albero +/**************************************************************************************************/ +TInsert_albero::TInsert_albero() +{ +// versione definitiva: +// _currnode = new TRectype(LF_RELANA); +// _ana_cache = new TRWrecord_cache(new TIsamtempfile(LF_RELANA,"",TRUE,TRUE)); + +// debug mods: + _currnode = new TRectype(LF_RELANA); + _f= new TIsamtempfile(LF_RELANA,"/com/relana",FALSE,FALSE); + _ana_cache = new TRWrecord_cache(_f); + + //zero(); // resetta l'albero locale +} + +TInsert_albero::~TInsert_albero() +{ + delete _ana_cache; + delete _f; +} + +//Setta lo stato del nodo: cioè decide che cosa dovrò fare con questo nodo +void TInsert_albero::set_status_node(const TRelana_id &id, const char *status) +{ + // STATUS A priorità assoluta e rimane sempre A + // STATUS M priorità su R ma non su A + // STATUS R priorità sul vuoto ma non su M o A + // STATUS vuoto: nessuna priorità + + // 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à 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à inserito e il suo flag è diverso da NODO_AGGIUNTO + //allora devo settare che questo nodo è modificato (se non lo è ancora) + if (*prec_status != NODO_MODIFICATO) + { + _status_node.add(key,TString (status),TRUE); //Forzo la sovrascrittura in ogni caso + } + // Arriva qui se il nodo è inserito e il flag era già 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 è da rimuovere"); + } + } +} + +//Restituisce un nuovo id +TRelana_id &TInsert_albero::new_id(TRelana_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 TInsert_albero::insert_node(TRectype & node,const TRelana_id id_prec, const TRelana_id id_padre, TRelana_id id) +{ + TRelana_id id_succ; + TToken_string key; + key.add(node.get(ABRA_CODAN)); + TRectype parente(LF_RELANA); + 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 = _ana_cache->get(key); + parente.put(ABRA_IDSUCC,id.string()); + _ana_cache->put(parente); + set_status_node(id_prec,NODO_MODIFICATO); //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 = _ana_cache->get(key); + parente.put(ABRA_IDFIGLIO,id.string()); + _ana_cache->put(parente); + set_status_node(id_padre,NODO_MODIFICATO); //Setto l'eventuale flag di array modificato per il padre + } // Sono una radice principale e quindi sono orfana di padre + } + + //Ora aggiorno me stesso + node.put(ABRA_IDPREC,id_prec.string()); + node.put(ABRA_IDPADRE,id_padre.string()); + node.put(ABRA_IDSUCC,id_succ.string()); + node.put(ABRA_IDFIGLIO,ID_NULLO); + _ana_cache->put(node); + //set_current(node); + goto_node(node); + set_status_node(id,NODO_MODIFICATO); + return TRUE; +} + + +//Questa funzione aggiorna i link dei "parenti" di un nodo che sta per essere rimosso +bool TInsert_albero::delete_node(const TRelana_id &id) +{ + + TRectype parente(LF_RELANA); + TRelana_id id_padre, id_figlio, id_prec, id_succ; + + goto_node(id); + TToken_string key = _currnode->get(ABRA_CODAN); + +// id = _currnode->get_real(ABRA_ID); + id_padre = _currnode->get_real(ABRA_IDPADRE); + id_figlio = _currnode->get_real(ABRA_IDFIGLIO); + id_prec = _currnode->get_real(ABRA_IDPREC); + id_succ = _currnode->get_real(ABRA_IDSUCC); + + //Ricerco il padre per modificare il suo primo figlio, solo se io sono il suo primo figlio + if (id_padre != ID_NULLO) + { + key.add(id_padre.string(),1); + parente=_ana_cache->get(key); //padre + if (parente.get(ABRA_IDFIGLIO) == id.string()) + { //Il nodo che sta per essere rimosso è il primo figlio di un padre + parente.put(ABRA_IDFIGLIO,id_succ.string()); //Aggiorno il padre + _ana_cache->put(parente); + } + } + + //Ricerco il mio fratello precedente per dirgli che io non sono più + //suo fratello successivo, e gli dico anche quale sarà il suo successivo + if (id_prec != ID_NULLO) + { + key.add(id_prec.string(),1); + parente=_ana_cache->get(key); //fratello sinistro + parente.put(ABRA_IDSUCC,id_succ.string()); //Assegno al mio fratello sinistro il mio fratello destro + _ana_cache->put(parente); + } + + //Dico a mio fratello successivo chi il suo precedente non sono più io, + //e gli dico che il suo precedente è il MIO precedente + if (id_succ != ID_NULLO) + { + key.add(id_succ.string(),1); + parente=_ana_cache->get(key); //fratello destro + parente.put(ABRA_IDPREC,id_prec.string()); //Assegno al mio fratello destro il mio fratello sinistro + _ana_cache->put(parente); + } + + //I miei vicini mi hanno già escluso: non sono più raggiungibile + //Ricostruisco la chiave + //Elimino i miei link dai fratelli + key.add(id.string(),1); + //node.put(ABRA_IDPADRE,ID_NULLO); + _currnode->put(ABRA_IDPREC,ID_NULLO); + _currnode->put(ABRA_IDSUCC,ID_NULLO); + _ana_cache->put(*_currnode); + //Elimino i miei figli + + TRelana_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 è già completamente aggiornato + //write_cache(); //Solo per debug + return TRUE; +} + +void TInsert_albero::zero() +{ + delete_tree(); // cancella l'albero + _status_node.destroy(); + _currnode->put(ABRA_ID,ID_NULLO); +} + +bool TInsert_albero::delete_tree() +{ + 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 tutti i nodi sottostanti al nodo +//"principale" che sta per essere eliminato +bool TInsert_albero::remove_subtree(const TRelana_id thisnode_id) +{ + TRelana_id myself_id; + //cerco il figlio + if (has_son()) + { + myself_id = _currnode->get_real(ABRA_ID); + goto_firstson(); + remove_subtree(myself_id); + } + + //cerco il fratello + if (has_rbrother()) + { + myself_id = _currnode->get_real(ABRA_ID); + goto_rbrother(); + remove_subtree(myself_id); + } + TRelana_id id = _currnode->get(ABRA_ID); + + //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à 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è un nodo per cui + //non è previsto uno status, non dovrebbe esistere nella cache + CHECK (FALSE,"Errore nella consistenza della cache"); + } + if (thisnode_id != ID_NULLO) + goto_node(thisnode_id); + else + _currnode->put(ABRA_ID,ID_NULLO); + + return TRUE; +} + +bool TInsert_albero::has_son() +{ + return (_currnode->get_real(ABRA_IDFIGLIO) != ID_NULLO); +} + +bool TInsert_albero::goto_firstson() +{ + goto_node(_currnode->get_real(ABRA_IDFIGLIO)); + return TRUE; +} + +bool TInsert_albero::has_rbrother() +{ + return (_currnode->get_real(ABRA_IDSUCC) != ID_NULLO); +} + +bool TInsert_albero::goto_rbrother() +{ + goto_node(_currnode->get_real(ABRA_IDSUCC)); + return TRUE; +} + +bool TInsert_albero::goto_root() +{ //Il _current sarà posizionato sull'ultimo nodo che è stato visitato + if (current()->get_real(ABRA_ID)==ID_NULLO) + return FALSE; + TToken_string key; + TRelana_id id_prec, id_padre; + key.add(_currnode->get(ABRA_CODAN)); + key.add(_currnode->get(ABRA_ID)); + do + { + *_currnode = _ana_cache->get(key); + id_prec = _currnode->get_real(ABRA_IDPREC); + id_padre = _currnode->get_real(ABRA_IDPADRE); + if (id_prec != ID_NULLO) + { + key.add(id_prec.string(),1); + } + else + { + if (id_padre != ID_NULLO) + key.add(id_padre.string(),1); + } + } while (id_prec != ID_NULLO || id_padre != ID_NULLO); + return TRUE; +} + +/**************************************************************************************************/ +// TAlbero_relana +/**************************************************************************************************/ +//In questo costruttore cerco di capire anche quale è 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 è 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 +TRelana_id & TAlbero_relana::new_id(TRelana_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 TRelana_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: è 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() +{ + TRelana_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'è un figlio: non si dovrebbe mai verificare visto che + //si entra in questa funzione solo quando è 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() +{ + TRelana_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'è un fratello: non si dovrebbe mai verificare visto che + //si entra in questa funzione solo quando è 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() +{ + TRelana_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 è 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è il record contiene già 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 è 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è 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()); +} diff --git a/ab/ablib01.h b/ab/ablib01.h new file mode 100755 index 000000000..972fcc3de --- /dev/null +++ b/ab/ablib01.h @@ -0,0 +1,342 @@ +#ifndef __ANALISIB_H +#define __ANALISIB_H + +#ifndef __RELATION_H +#define __RELATION_H +#include +#endif //__RELATION_H + +#ifndef __APPLICAT_H +#define __APPLICAT_H +#include //Definizione della classe TApplication +#endif //__APPLICAT_H + +#ifndef _ABLI09_H +#define _ABLI09_H +#include "ablib09.h" +#endif //_ABLIB09_H + +#ifndef _ANALIS_H +#define _ANALIS_H +#include "analisi.h" +#endif //_ANALIS_H + +#ifndef _RELVOCI_H +//#define _RELVOCI_H +#include "relvoci.h" +#endif //_RELVOCI_H + +#ifndef _RELANA_H +//#define _RELANA_H +#include "relana.h" +#endif //_RELANA_H + + + +#define TIPO_ANALISI "A" +#define TIPO_VOCE "V" +#define ID_NULLO 0 + +//Serve per eseguire il test +class TTest: public TSkeleton_application +{ + protected: + virtual void main_loop(); + public: + // @cmember Costruttore + TTest() + { + } + // @cmember Distruttore + virtual ~TTest() + { + } +}; + +class TNodeId : public TString +{ +}; + +// classe per un albero navigabile in una direzione +// (depth first o breadth first) +class TAlbero : public TObject +{ + protected: + virtual void node2id(const TObject * node,TNodeId & id) const + {return;} + public: + virtual bool empty() {return !goto_root();} + virtual bool goto_root() pure ; + virtual bool goto_firstson() pure ; + virtual bool goto_rbrother() pure ; + virtual TObject * curr_node() const pure ; + void curr_id(TNodeId & var) const + {node2id(curr_node(),var);} + virtual bool goto_node(TNodeId &id) + {return FALSE;} + + virtual bool has_son() const + {return FALSE;} + virtual bool has_rbrother() const + {return FALSE;} + TAlbero() {} + virtual ~TAlbero() {} +}; + +// classe per un albero navigabile in due direzioni +class TAlbero_bidir : public TAlbero +{ +public: + virtual bool has_father() const + {return FALSE;} + virtual bool has_lbrother() const + {return FALSE;} + virtual bool goto_father() + {return FALSE;} + virtual bool goto_lbrother() + {return FALSE;} + virtual bool goto_root() {return FALSE;} + virtual bool goto_firstson() {return FALSE;} + virtual bool goto_rbrother() {return FALSE;} + virtual TObject * curr_node() const {return NULL;} + TAlbero_bidir() {} + virtual ~TAlbero_bidir() {} +}; + +typedef real TRelana_id; +/*class TRelana_id : public real +{ +public: + TRelana_id &operator =(const int &v) + {*((real *)this)=v; return *this;} + TRelana_id &operator =(const real &v) + {*((real *)this)=v; return *this;} + TRelana_id(const real &v) + {*this=v;} + TRelana_id() + {} +};*/ + +//Classe per gestire l'albero di relana +class TAlbero_relana : public TAlbero_bidir +{ + TString16 _codtab; + TLocalisamfile *_relana; + TRelana_id _last_id; +protected: + virtual void node2id(const TObject * node,TNodeId & id) const; +public: + // @cmember Ritorna TRUE se riesce a trovare il primo figlio + virtual bool has_son() const; + // @cmember Ritorna TRUE se riesce a trovare il fratello destro + virtual bool has_rbrother() const; + // @cmember Ritorna TRUE se riesce a trovare il padre + virtual bool has_father() const; + // @cmember Ritorna TRUE se riesce a trovare il fratello sinistro + virtual bool has_lbrother() const; + // @cmember Si posiziona sulla radice principale + virtual bool goto_root(); + // @cmember Si posiziona sul primo figlio + virtual bool goto_firstson(); + // @cmember Si posiziona sul padre + virtual bool goto_father(); + // @cmember Si posiziona sul fratello destro + virtual bool goto_rbrother(); + // @cmember Si posiziona sul fratello sinistro + virtual bool goto_lbrother(); + // @cmember Ritorna TRUE se può posizionarsi sul nodo indicato dall'id specificato + virtual bool goto_node(TNodeId & id) + {return(_relana->readat(atol(id))==NOERR); } + // @cmember Si posiziona sul nodo specificato dall'id + bool goto_id(const TRelana_id &id); + // @cmember Ritorna il nodo corrente + virtual TObject * curr_node() const; + // @cmember Ritorna il nodo corrente + TRectype & curr() {return (TRectype &)*curr_node();} + // @cmember Esegue un lock + bool lock(); + // @cmember Esegue un unlock + void unlock(); + // @cmember Inserisce un nodo nella struttura ad albero + //bool insert_node(real id); + // @cmember Restituisce un nuovo id (il primo disponibile) + TRelana_id &new_id(real id); + // @cmember Costruttore + TAlbero_relana( const char * tabcode); + // @cmember Distruttore + virtual ~TAlbero_relana(); +}; + +//Classe per gestire la struttura di una tabella di analisi +// basandosi sull'albero di relvoci +class TAlbero_relvoci : public TAlbero +{ + TString16 _codtab; + TLocalisamfile *_relvoci; +protected: + virtual void node2id(const TObject * node, TNodeId & id) const; +public: + // @cmember Si posiziona sulla radice principale + virtual bool goto_root(); + // @cmember Ritorna TRUE se trova un figlio + virtual bool has_son() const; + // @cmember Si posiziona sul primo figlio + virtual bool goto_firstson(); + // @cmember Ritorna TRUE se trova un fratello destro + virtual bool has_rbrother() const; + // @cmember Si posiziona sul fratello destro + virtual bool goto_rbrother(); + // @cmember Ritorna TRUE se trova un fratello sinistro + bool has_lbrother() const; + // @cmember Si posiziona sul fratello sinistro + bool goto_lbrother(); + // @cmember Restituisce il nodo corrente + virtual TObject * curr_node() const; + // @cmember Restituisce il nodo specificato dall'id + virtual bool goto_node(TNodeId & id) + {return (_relvoci->readat(atol(id))==NOERR);} + // @cmember Restituisce il nodo corrente + TRectype & curr() {return (TRectype &)*curr_node();} + // @cmember Esegue un lock + bool lock(); + // @cmember Esegue un unlock + void unlock(); + // @cmember Restituisce il codice della tabella che si sta usando + const TString & codtab() + {return _codtab;} + // @cmember Costruttore + TAlbero_relvoci(const char * tabcode); + // @cmember Distruttore + virtual ~TAlbero_relvoci() ; +}; + +// Struttura da albero basata su un file temporaneo cached +class TInsert_albero : public TAlbero_bidir +{ + TRWrecord_cache * _ana_cache; + + TLocalisamfile *_f; // da rimuovere !! + + TRectype *_currnode; + TRelana_id _last_insert_id; + TAssoc_array _status_node; + +// debug + void write_cache() { _ana_cache->flush(); } +protected: + // @cmember Elimina il sottoalbero dei figli di un nodo + // @cmember che sta per essere eliminato da un struttura di tipo lista doppiamente lincata + bool remove_subtree(const TRelana_id lastnode_id); + // @cmember genera/setta il nuovo id + TRelana_id &new_id(TRelana_id); + + void goto_node(const TRelana_id id) + { + TToken_string key; + key.add(_currnode->get(ABRA_CODAN)); + key.add(id.string()); + *_currnode = _ana_cache->get(key); + } + void goto_node(TRectype &node) {*_currnode = node;} + + public: + // @cmember Inserisce un nodo in una struttura di tipo lista doppiamente lincata + bool insert_node(TRectype &node, const TRelana_id id_prec, const TRelana_id id_padre, TRelana_id id); + // @cmember Elimina un nodo da una struttura di tipo lista doppiamente lincata + bool delete_node(const TRelana_id &id); + // @cmember Lancia la rimozione di tutto l'albero + bool delete_tree(); + // @cmember Lancia la rimozione di tutto l'albero + void zero(); + // @cmember Restituisce il nodo corrente + TRectype * current() const {return (TRectype * )_currnode;} + + // @cmember Restituisce TRUE se esiste un figlio di un nodo + virtual bool has_son(); + // @cmember Si posizione sul primo figlio di un nodo + virtual bool goto_firstson(); + // @cmember Restituisce TRUE se esiste il fratello destro di un nodo + virtual bool has_rbrother(); + // @cmember Si posiziona sul fratello destro di un nodo + virtual bool goto_rbrother(); + // @cmember Si posiziona sul primo nodo della struttura ad albero + virtual bool goto_root(); + // @cmember Restituisce il nodo corrente + virtual TObject * curr_node() const {return _currnode;} + + // ***** gestione interfaccia Database/copia locale + // @cmember Estrae un nodo dall'albero + TRectype extract_node(const TString &codtab,TRelana_id &id, TString& status); + // @cmember Ritorna il numero di elementi dell'assoc_array status_node + int status_node_items() { return _status_node.items();} + // @cmember Setta lo stato del nodo: valori ammessi = nodo ggiunto, nodo odificato, nodo imosso + void set_status_node(const TRelana_id &id, const char *status); + // @cmember Ritorna lo stato di un nodo + const char* get_status_node(const TRelana_id &id); + + // @cmember Costruttore + TInsert_albero(); + // @cmember Distruttore + virtual ~TInsert_albero(); +}; + +class TAnalisi_bil: public TRectype +{ + class TArray_fratelli: public TAssoc_array + { + public: + void setkey(const char *tipocod, const char *codvc, TToken_string &key) const; + bool find1stkey(const char *tipocod, const char *codvc, TToken_string &key); + void mark_used(const char *key); + bool used(TObject *o); + }; + TArray_fratelli _nodifratelli; + TLocalisamfile *_analisi; + TLocalisamfile *_voci; + TAlbero_relvoci *_relaz; + TAlbero_relana *_ana; + TInsert_albero *_inter_tree; + TRectype * _newrec; //buffer per l'inserimento dodo per nodo + protected: + // @cmember Esegue una navigazione dell'albero di rel_voci e di rel_ana + // @cmember parallelamente verificanto eventualmente delle inconsistenze + void naviga_relazioni(const TRelana_id & id_relana,const TRelana_id & prec,const TRelana_id & padre ); + // @cmember Sincronizza la copia locale basandosi su relvoci (per la struttura) e su relana (per le info aggiuntive) + TRectype * sincronizza_relana(const TRelana_id &begin_relana,TRelana_id &id_relana); + // @cmember Esegue un lock di una struttura ad albero + bool lock_struttura(); + // @cmember Esegue un unlock di una struttura ad albero + bool unlock_struttura(); + // @cmember Esegue un lock su una tabella di analisi + bool lock_tabella(); + // @cmember Sblocca una tabella di analisi + bool unlock_tabella(); + public: + // @cmember Costruttore + TAnalisi_bil(); + // #cmember Distruttore + virtual ~TAnalisi_bil() ; + // @cmember Ritrona il tipo di analisi + const TString &type() {return get(ABAN_TIPOAN);} + // @cmember Setta il tipo di analisi + void set_type(const char * tipo) {put(ABAN_TIPOAN,tipo);} + // @cmember Esegue la lettura della testata di un record da LF_ANALISI + virtual int read(TBaseisamfile& analisi, word isop=_isequal, word lockop=_nolock); + // @cmember Esegue la lettura della testata di un record alla posizione nrec da LF_ANALISI + virtual int readat(TBaseisamfile& f, TRecnotype nrec, word lockop = _nolock); + // @cmember Serve per lanciare la scrittura del body del record + virtual int write(TBaseisamfile& analisi) const; + // @cmember Elimina una intera tabella di analisi con tutto il suo contenuto + virtual int remove(TBaseisamfile& f) const; + // @cmember Azzera l record + virtual void zero(); + // @cmember Esegue la scrittura del body del record scrive il contenuto di una tabella di analisi + int commit_body(TLocalisamfile &relana) const; + // @cmember Esegue la lettura del body del rocord: legge il contenuto di una tabella di analisi + int read_body(bool lockstruct); + // @cmember Restituisce l'oggetto ad albero "copia locale" + TInsert_albero & user_tree() + {return *_inter_tree;} +}; + +#endif //__ANALISI_H \ No newline at end of file diff --git a/ab/ablib09.cpp b/ab/ablib09.cpp new file mode 100755 index 000000000..56458d355 --- /dev/null +++ b/ab/ablib09.cpp @@ -0,0 +1,148 @@ +/////////////////////////////////////////////////////////// +// Cache +/////////////////////////////////////////////////////////// +#include "ablib09.h" +#define RWCACHE_SIZE 100 + +// funzione di default: prende una chiave a caso chiave +const TString & TRWrecord_cache::getkey2discard() +{ + THash_object * o=get_some_obj(); + CHECK(o,"E' stata chiamata la funzione getkey2discard con la cache vuota"); + return o->key(); +} + +//Scarica una parte della cache +void TRWrecord_cache::discard(const TString & vittima) +{ + if (items()) //Usa l'items di TFile_cahe che richiame l'items dell'assoc_array _cache + { + if (_flags.is_key(vittima)) //_flags è un assoc_array di TRWcache, usa is_key di assoc_array + { + const char fl=((TString &)_flags[vittima])[1]; + // record modificato o nuovo + int err; + TRectype & rec=(TRectype & )TRecord_cache::get(vittima); + file().curr()=rec; + if (fl == 'D') + { + err=file().rewrite(); + if (err!=NOERR) + error_box("Errore nella riscrittura della cache"); + } else { + err=file().write(); + if (err!=NOERR) + if (err == _isreinsert) + file().rewrite(); + else + error_box("Errore nella scrittura della cache"); + } + _flags.remove(vittima); + + }//Se non esiste nel assoc_array di TRWcache + _cache.remove(vittima); + } +} + +const TRectype& TRWrecord_cache::get(const char* chiave) +{ + if (items()>=RWCACHE_SIZE) + discard(getkey2discard()); + const TRectype & rec=TRecord_cache::get(chiave); + if (io_result() != NOERR) + { + // record non trovato: è nuovo + _flags.add(chiave,new TString("N")); + } + return rec; +} + +void TRWrecord_cache::put(const TRectype &r) +{ + test_firm(); + + TToken_string cachekey; + if (!r.empty()) + { + const RecDes* recd = r.rec_des(); // Descrizione del record della testata + const KeyDes& kd = recd->Ky[key_number()-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]; + cachekey.add(r.get(rf.Name)); + } + } + + TObject* obj = _cache.objptr(cachekey); + + if (obj != NULL) + { + // esiste in cache ; tenta di settare il flag a "D"irty; se il flag esiste già è a + TRectype & rec=(TRectype &)*obj; + rec=r; + _flags.add(cachekey , new TString("D")); + } else { + // non esiste in cache + obj = rec2obj(r); + _cache.add(cachekey, obj); + // qui assume che non esista nemmeno su file, perciò sia "N"uovo; al flush correggerà l'errore + _flags.add(cachekey , new TString("N")); + } + if (items()>=RWCACHE_SIZE) + discard(getkey2discard()); +} + +//Vuota una cache +void TRWrecord_cache::clear() +{ + while (items()>0) + { + const TString & vittima=getkey2discard(); + if (_flags.is_key(vittima)) + _flags.remove(vittima); + _cache.remove(vittima); + } +} + +//Forza la scrittura sulla cache +void TRWrecord_cache::flush() +{ + while (items()>0) + { + discard(TRWrecord_cache::getkey2discard()); + } +} + +THash_object * TRWrecord_cache::get_some_obj() +{ + if (items()==0) //Usa l'items() di TFilecache fa items() di un assoc_array _cahce + return NULL; + THash_object * o; + while ((o=_cache.get_hashobj()) == NULL) ; //Ritorna l'oggetto e la relativa chiave dell'assoc_array _cache + return o; +} + +//Costruttori vari +TRWrecord_cache::TRWrecord_cache(TLocalisamfile *f, int key, bool lock) + :TRecord_cache(f,key) +{ + if (lock) + file().lock(); +} + + +TRWrecord_cache::TRWrecord_cache(int num, int key, bool lock) + :TRecord_cache(num,key) +{ + if (lock) + file().lock(); +} + + +TRWrecord_cache::~TRWrecord_cache() +{ + flush(); +} + + diff --git a/ab/ablib09.h b/ab/ablib09.h new file mode 100755 index 000000000..cb8fbbe41 --- /dev/null +++ b/ab/ablib09.h @@ -0,0 +1,24 @@ +#ifndef _RECARRAY_H +#include +#endif + +class TRWrecord_cache : public TRecord_cache +{ + TAssoc_array _flags; + +protected: + THash_object * get_some_obj(); + virtual const TString & getkey2discard(); +public: + virtual void discard(const TString & k); + virtual void put(const TRectype &r); + virtual const TRectype& get(const char* chiave); + + void clear(); + void flush(); + + TRWrecord_cache(TLocalisamfile *f, int key = 1, bool lock=FALSE); + TRWrecord_cache(int num, int key = 1, bool lock=FALSE); + TRWrecord_cache(const char* table, int key = 1, bool lock=FALSE); + virtual ~TRWrecord_cache() ; +};