#include "ablib01.h" /**************************************************************************************************/ // TAlbero_AB /**************************************************************************************************/ TAlbero_AB::TAlbero_AB(int filenum) :TRectype(filenum), _inter_tree(NULL), _newrec(NULL) { } TAlbero_AB::~TAlbero_AB() { } 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 { // _inter_tree->write_cache(); //Solo debug 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) //Eliminare questa lettura con isop = 0 o isop = 1 { read_body(FALSE); _inter_tree->make_root(saldi); } _inter_tree->write_cache(); //solo debig 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); // E' stata tolta la read_body perchè la readat viene richiamata // prima di fare una write della relazione: si perdono le modifiche // effettuate in quanto la read_body ripristina la vecchia situazione // 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 = 4; _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(NULL), _movdett(NULL) { _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 _newrec; delete _inter_tree; _saldi = NULL; _movdett = NULL; _newrec = NULL; _inter_tree = NULL; } 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)); } //Leggo il "corpo" del record int TABsaldo::read_body(bool lockstruct) { 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()) { naviga_movdett(); } //_inter_tree->write_cache(); //solo per debug //prova_inserimento(); //Solo debug per inserimento diretto di un nodo return 0; } //Naviga l'albero dei movimenti-dettagli void TABsaldo::naviga_movdett() { TString key_rel; _mov->curr_id(key_rel); TNumeric_id lbrother_id, pather_id, my_id; my_id = _mov->curr().get_real(ABMD_ID); pather_id = _mov->curr().get_real(ABMD_IDPADRE); lbrother_id = _mov->curr().get_real(ABMD_IDPREC); *_newrec = _mov->curr(); _inter_tree->insert_node(*_newrec,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); } } } // Questa rudimentale funzione serve solo per il debug: richiama direttamente //la insert_node di un saldo IMMETTENDO un nuovo nodo nella struttura. // Cambiando i parametri id posiziona il nodo in posizione diverse void TABsaldo::prova_inserimento() { TRectype prova(LF_MOVDETT); TNumeric_id lbrother_id,pather_id,my_id; lbrother_id = 13; pather_id = 0; prova.put(ABMD_CODDITTA,"00001"); prova.put(ABMD_ANNO,"1996"); prova.put(ABMD_CODPDB,"001"); prova.put(ABMD_TIPOBIL,"E2"); prova.put(ABMD_CODCBL,"000000000200"); _inter_tree->insert_node(prova,lbrother_id,pather_id,my_id,IMMESSO); } /**************************************************************************************************/ // TAnalisi_bil /**************************************************************************************************/ TAnalisi_bil::TAnalisi_bil() :TAlbero_AB(LF_ANALISI), _voci(NULL), _analisi(NULL) { _voci = new TLocalisamfile(LF_VOCI); _analisi= new TLocalisamfile(LF_ANALISI); _relaz = NULL; _ana = NULL; _inter_tree = new TLocal_relana3(); _inter_tree_relvoci = new TAlbero_locale_relvoci(); _record_caradd = new TRecord_caradd(); _record_colldich = new TRecord_colldich(); _newrec = new TRectype(LF_RELANA); last_id_caradd(); last_id_colldich(); } TAnalisi_bil::~TAnalisi_bil() { delete _newrec; delete _inter_tree; _analisi = NULL; _voci = NULL; _newrec = NULL; _inter_tree = NULL; } void TAnalisi_bil::last_id_caradd() { TLocalisamfile caradd(LF_CARADD); if (caradd.last() == NOERR) { _last_id_caradd = caradd.get_real(ABCA_ID); } else { error_box ("Errore nella lettura dell'archivio %CARADD"); } } void TAnalisi_bil::last_id_colldich() { TLocalisamfile coll(LF_COLLDICH); if (coll.last() == NOERR) { _last_id_colldich = coll.get_real(ABCD_ID); } else { error_box ("Errore nella lettura dell'archivio %COLLDICH"); } } 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) { TString id = ID_NULLO_STR; int prova = _inter_tree->dirty_nodes(); //solo debug 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 è 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 !!! 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 ) { //Vuoto la cache: da eventuali residui _inter_tree->clear_cache(); if (_ana) //Azzero l'id del TAlbero_relana: deve essere ricostruito _ana->zero_id(); if (_relaz) { delete _relaz; delete _ana; _relaz=NULL; _ana=NULL; /* 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); } //prova_inserimento(); //Solo debug per inserimento diretto di un nodo _inter_tree->write_cache(); //solo debug _inter_tree_relvoci->write_cache(); //solo debug 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; TString key_rel; _relaz->curr_id(key_rel); _newrec = sincronizza_relana(begin_relana, id_relana); currid=_newrec->get_real(ABRA_ID); // Inserico relana _inter_tree->insert_node(*_newrec,id_prec,id_padre,currid); // Inserico relvoci _inter_tree_relvoci->insert_node(_relaz->curr(),ID_NULLO,ID_NULLO,ID_NULLO); //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 TNumeric_id frat_relana; naviga_relazioni(frat_relana,currid , id_padre); _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 _inter_tree->link_succ_zero(currid); //Azzera link fratello 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); }//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 _inter_tree->link_son_zero(currid); //azzera link filgio } //Sincronizza relana con quello che trovo in relaz: relaz è 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à degli archivi TLocalisamfile voc(LF_VOCI); _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_CODVC)); _newrec->put(ABRA_NCOMP,_relaz->curr().get(ABRL_IDCOMP)); // Prelevo la descrizione di default voc.put(ABVC_CODVC,_relaz->curr().get(ABRL_CODCOMP)); voc.read(); _newrec->put(ABRA_DESCRIZ,voc.get(ABVC_DESCR)); id_relana = ID_NULLO; // Serve per eseguire correttamente la new_id _newrec->put(ABRA_ID,_ana->new_id(id_relana)); //Questa è stata spostata alla insert_node _inter_tree->set_status_node(_newrec->get(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(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(); } // Questa rudimentale funzione serve solo per il debug: richiama direttamente //la insert_node di un saldo IMMETTENDO un nuovo nodo nella struttura. // Cambiando i parametri id posiziona il nodo in posizione diverse void TAnalisi_bil::prova_inserimento() { TRectype prova(LF_RELANA); TNumeric_id lbrother_id,pather_id,my_id; lbrother_id = 0; pather_id = 14; prova.put(ABRA_CODAN,"A1"); _inter_tree->insert_node(prova,lbrother_id,pather_id,my_id,IMMESSO); } /**************************************************************************************************/ // TLocal_relana3 & TLocal_balance3 /**************************************************************************************************/ 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); } if (filenum == LF_MOVDETT) { _f= new TIsamtempfile(filenum,"/com/movdett",FALSE,FALSE); } if (filenum==LF_RELVOCI) { _f= new TIsamtempfile(filenum,"/com/relvoci",FALSE,FALSE); } _local_cache = new TRWrecord_cache(_f); } TAlbero_locale::~TAlbero_locale() { delete _currnode; delete _local_cache; delete _f; _currnode = NULL; _local_cache = NULL; _f = NULL; } TAlbero_locale_link::TAlbero_locale_link(int filenum) : TAlbero_locale(filenum) { } TAlbero_locale_relvoci::TAlbero_locale_relvoci() : TAlbero_locale(LF_RELVOCI) { } TLocal_relana3::TLocal_relana3() : TAlbero_locale_link(LF_RELANA) { ; } TLocal_relana3::~TLocal_relana3() { } TLocal_balance3::TLocal_balance3() : TAlbero_locale_link(LF_MOVDETT) { ; } TLocal_balance3::~TLocal_balance3() { } bool TLocal_relana3::get_description(TString& desc) const { desc.cut(0); // curr_id(desc); desc << " " << current().get(ABRA_DESCRIZ); return desc.not_empty(); } bool TLocal_relana3::make_root(TBaseisamfile& saldi) { TString descriz; descriz << "-- " << saldi.get(ABAN_DESCRIZ); goto_son_first_level(); //Vado sul primo figlio TNumeric_id temp_id = current().get_real(id_fieldname()); // e ne estraggo l'id current().zero(); //Vuoto il record current().put(id_fieldname(),ID_NULLO); //ID current().put(idson_fieldname(),temp_id); //Assegno il link al primo figlio current().put(ABRA_DESCRIZ,descriz); //Aggiungo la descrizione alla radice current_cache().put(current()); //Aggiungo alla cache il record della radice principale return TRUE; } bool TLocal_balance3::get_description(TString& desc) const { curr_id(desc); // desc << "|" << curnode.get(); return desc.not_empty(); } 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()); } bool TLocal_balance3::make_root(TBaseisamfile& saldi) { goto_son_first_level(); //Vado sul primo figlio TNumeric_id temp_id = current().get_real(id_fieldname()); // e ne estraggo l'id current().zero(); //Vuoto il record current().put(id_fieldname(),ID_NULLO); current().put(idson_fieldname(),temp_id); //Assegno il link al primo figlio current_cache().put(current()); //Aggiungo alla cache il record della radice principale return TRUE; } /**************************************************************************************************/ // TAlbero_locale /**************************************************************************************************/ //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_link::extract_dirtynode(TString &id, TString &status) { TToken_string key = get_headkey(); TRectype rec(current()); THash_object *row_status; if (id == ID_NULLO_STR) { //Gestione interna _status_node.restart(); row_status = _status_node.get_hashobj(); status = (TString&)row_status->obj(); id = (TString)row_status->key(); } else { status = *(TString*)_status_node.objptr(id); } //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); //La chiave è 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); } //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); return rec; //Ritorno un Trectype e passo lo stauts node } //Setta lo stato del nodo: cioè decide che cosa dovrò fare con questo nodo void TAlbero_locale::set_status_node(const TString &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; 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 //message_box("Sto rimuovendo un nodo appena modificato e non ancora registrato"); } } } //Restituisce un nuovo id TNumeric_id &TAlbero_locale_link::new_id(TNumeric_id id) { if (id == ID_NULLO) _last_insert_id=_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_link::insert_node(TRectype & node,const TNumeric_id id_prec, const TNumeric_id id_padre, TNumeric_id id, const char flag_it) { const char* dirty_value=NODO_MODIFICATO; TNumeric_id id_succ; if (id == ID_NULLO) { id = new_id(id); set_status_node(id.string(),NODO_AGGIUNTO); } 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 goto_node(id_prec); if (_currnode->get_real(idsucc_fieldname()) != id) {// Il nodo è effettivamente modificato: apporto le modifiche e setto lo status // Se il nodo è IMMESSO devo mantenere il link con il successivo per mantenere integra la struttutra if (flag_it == IMMESSO) { id_succ = _currnode->get_real(idsucc_fieldname()); } _currnode->put(idsucc_fieldname(),id.string()); _local_cache->put(*_currnode); set_status_node(id_prec.string(),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 goto_node(id_padre); if(_currnode->get_real(idson_fieldname()) != id) {// Il nodo è effettivamente modificato: apporto le modifiche e setto lo status if (flag_it == IMMESSO) { id_succ = _currnode->get_real(idson_fieldname()); } _currnode->put(idson_fieldname(),id.string()); _local_cache->put(*_currnode); set_status_node(id_padre.string(),dirty_value); //Setto l'eventuale flag di array modificato per il padre } } // Sono una radice principale e quindi sono orfana di padre: posso essere solo un nuovo nodo IMMESSO direttamente else { if (flag_it == IMMESSO) { goto_son_first_level(); id_succ = _currnode->get_real(id_fieldname()); } } } // Se il nodo è immesso controllo che ci sia un link al successivo da ripristinare if (flag_it == IMMESSO) { if (id_succ != ID_NULLO) { //Esiste un nodo che prima era il successivo dell'attuale mio precedente: devo modificarlo per //dirgli che ora il suo precedente sono io goto_node(id_succ); // Il mio successivo deve essere effettivamente modificato if (_currnode->get_real(idprec_fieldname()) != id) { _currnode->put(idprec_fieldname(),id.string()); _local_cache->put(*_currnode); set_status_node(id_succ.string(),dirty_value); //Setto l'eventuale flag di array modificato per il fratello }// Altrimenti il nodo non è da modificare //Aggiorno me stesso node.put(idsucc_fieldname(),id_succ.string()); node.put(idson_fieldname(),ID_NULLO); //set_current(node); } } //Ora aggiorno me stesso: solo quella parte che rimane da aggiornare put_headkey(node); node.put(id_fieldname(),id.string()); if ((node.get_real(idprec_fieldname()) != id_prec) || (node.get_real(idfather_fieldname()) != id_padre)) {// Il nodo è effettivamente modificato: apporto le modifiche e setto lo status node.put(idprec_fieldname(),id_prec.string()); node.put(idfather_fieldname(),id_padre.string()); set_status_node(id.string(),dirty_value); } _local_cache->put(node); goto_node(node); return TRUE; } //Questa funzione aggiorna i link dei "parenti" di un nodo che sta per essere rimosso bool TAlbero_locale_link::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()); // if (id_padre != ID_NULLO) // { // Il padre esiste sempre: al limite è la radice stessa 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 è il primo figlio di un padre parente.put(idson_fieldname(),id_succ.string()); //Aggiorno il padre if (id_padre != ID_NULLO) { // Se l'id del padre = ID_NULLO vuol dire che è la radice e non devo settare //nessun status perchè questa non dovrà essere salvata nel DB set_status_node(id_padre.string(), NODO_MODIFICATO); //Aggiorno lo status } _local_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.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 set_status_node(id_prec.string(),NODO_MODIFICATO); //Aggiorno lo status _local_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.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 set_status_node(id_succ.string(),NODO_MODIFICATO); //Aggiorno lo status _local_cache->put(parente); } //I miei vicini mi hanno già escluso: non sono più 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 è già completamente aggiornato //write_cache(); //Solo per debug return TRUE; } void TAlbero_locale_link::zero() { delete_tree(); // cancella l'albero _status_node.destroy(); _currnode->put(id_fieldname(),ID_NULLO); } bool TAlbero_locale_link::delete_tree() { //write_cache(); goto_son_first_level(); 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_link::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 == NULL) || (*status != NODO_AGGIUNTO)) {//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()); } if (return_id != ID_NULLO) goto_node(return_id); else _currnode->put(id_fieldname(),ID_NULLO); return TRUE; } bool TAlbero_locale_link::has_son() const { return (_currnode->get_real(idson_fieldname()) != ID_NULLO); } bool TAlbero_locale_link::goto_firstson() { if (has_son()) { goto_node(_currnode->get_real(idson_fieldname())); return TRUE; } return FALSE; } bool TAlbero_locale_link::has_father() const { // return (_currnode->get_real(idfather_fieldname()) != ID_NULLO); //Se l'id = 0 allora sono la radice return (_currnode->get_real(id_fieldname()) != ID_NULLO); } bool TAlbero_locale_link::goto_father() { if (has_father()) { goto_node(_currnode->get_real(idfather_fieldname())); return TRUE; } return FALSE; } bool TAlbero_locale_link::has_rbrother() const { return (_currnode->get_real(idsucc_fieldname()) != ID_NULLO); } bool TAlbero_locale_link::goto_rbrother() { if (has_rbrother()) { goto_node(_currnode->get_real(idsucc_fieldname())); return TRUE; } return FALSE; } bool TAlbero_locale_link::has_lbrother() const { return (_currnode->get_real(idprec_fieldname()) != ID_NULLO); } bool TAlbero_locale_link::goto_lbrother() { if (has_lbrother()) { goto_node(_currnode->get_real(idprec_fieldname())); return TRUE; } return FALSE; } bool TAlbero_locale_link::goto_root() { if (_currnode->get_real(id_fieldname()) != ID_NULLO ) { //Significa che non sono sulla radice: sono su un livello sottostante goto_son_first_level(); goto_father(); } return TRUE; } bool TAlbero_locale_link::goto_son_first_level() { //Il _current sarà posizionato sull'ultimo nodo che è stato visitato if (current().get_real(id_fieldname())==ID_NULLO) { CHECK (FALSE,"Non so dove posizionarmi !*!*!"); return FALSE; } TToken_string key; TNumeric_id id_prec, id_padre; key=get_headkey(); key.add(_currnode->get(id_fieldname())); do { _currnode->zero(); *_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_link::goto_node(const TNumeric_id id) { /* if (id != ID_NULLO) { */ TToken_string key; key=get_headkey(); key.add(id.string()); *_currnode = _local_cache->get(key); /* } else _currnode = NULL;*/ } void TAlbero_locale_link::link_succ_zero(const TNumeric_id currid) { // Se il nodo è da rimuovere non eseguo nessuna operazione: potrebbe causre problemi alla cache TString *status = get_status_node(currid.string()); // if ((status != NULL) && (*status != NODO_RIMOSSO)) if ((status == NULL) || (*status != NODO_RIMOSSO)) { //Posiziono il nodo che sto aggiornando goto_node(currid); //Tento di posizionarmi sul fratello se questo può avere senso if (current().get_real(idsucc_fieldname()) != ID_NULLO) { //Secondo il link, ci dovrebbe essere un fratello status = get_status_node(current().get(idsucc_fieldname())); if ((status != NULL) && (*status != NODO_RIMOSSO)) { // Entro qui solo se il nodo esite, e lo status è stato fissato ad un valore diverso dal RIMOSSO goto_node(current().get_real(idsucc_fieldname())); if (current().empty()) { // Se però non esiste modifico il record goto_node(currid); current().put(idsucc_fieldname(),ID_NULLO); _local_cache->put(current()); set_status_node(currid.string(),NODO_MODIFICATO); } } } } } void TAlbero_locale_link::link_son_zero(const TNumeric_id currid) { // Se il nodo è da rimuovere non eseguo nessuna operazione: potrebbe causre problemi alla cache TString *status = get_status_node(currid.string()); // if ((status != NULL) && (*status != NODO_RIMOSSO)) if ((status == NULL) || (*status != NODO_RIMOSSO)) { //Posiziono il nodo che sto aggiornando goto_node(currid); //Tento di posizionarmi sul fratello se questo può avere senso if (current().get_real(idson_fieldname()) != ID_NULLO) { //Secondo il link, ci dovrebbe essere un fratello status = get_status_node(current().get(idson_fieldname())); if ((status != NULL) && (*status != NODO_RIMOSSO)) { // Entro qui solo se il nodo esite, e lo status è stato fissato ad un valore diverso dal RIMOSSO goto_node(current().get_real(idson_fieldname())); if (current().empty()) { // Se però non esiste modifico il record goto_node(currid); current().put(idson_fieldname(),ID_NULLO); _local_cache->put(current()); set_status_node(currid.string(),NODO_MODIFICATO); } } } } } /**************************************************************************************************/ // 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 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: è 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'è 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'è 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() { 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,TString & 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,TString & id) const { id.format("%ld",_relvoci->recno()); } /**************************************************************************************************/ // TAlbero_movdett /**************************************************************************************************/ TAlbero_movdett::TAlbero_movdett( const char * tabcode) { _codtab = tabcode; _movdett=new TLocalisamfile(LF_MOVDETT); /* _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)); _movdett->put(ABMD_ID,MAX_ID_MOVDETT); //Trova l'ultimo id usato in MOVDETT: DA COMPLETARE !!! int err = _movdett->read(_isgteq); //Codifica errori in TIsamerr if (err == NOERR) { // Ho trovato il primo della "categoria" successiva: leggendo il precedente // ho l'ultimo record cercato _movdett->prev(); TString16 format =_movdett->get(ABMD_CODDITTA); TToken_string controllo = format.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) { // Non esiste la tabella ; //_last_id viene lasciato a 0 } _last_id = _movdett->get_real(ABMD_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 _movdett->last(); TString16 format =_movdett->get(ABMD_CODDITTA); TToken_string controllo = format.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) { //Tabella non trovata ; //_last_id viene lasciato a 0 } //Tabella trovata: prendo l'id dell'ultimo record _last_id = _movdett->get_real(ABMD_ID); } */ } TAlbero_movdett::~TAlbero_movdett() { delete _movdett; } //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'è 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_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'è 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_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,TString & id) const { id.format("%ld",_movdett->recno()); } /****************************************/ /* TAlbero_locale_relvoci */ /*********************************/ TRectype TAlbero_locale_relvoci::extract_dirtynode(TString &id, TString &status) { TToken_string key_rel = (TToken_string&)id; TRectype rec(current()); THash_object *row_status; _status_node.restart(); row_status = _status_node.get_hashobj(); status = (TString&)row_status->obj(); key_rel = (TToken_string)row_status->key(); rec = current_cache().get(key_rel); _status_node.remove(key_rel); return rec; //Ritorno un Trectype e passo lo stauts node } bool TAlbero_locale_relvoci::has_lbrother() const { return current().get_real(ABRL_PREVCOMP) != ID_NULLO; } bool TAlbero_locale_relvoci::goto_lbrother() { TToken_string key_rel; key_rel.add(current().get(ABRL_TIPOCOD)); key_rel.add(current().get(ABRL_CODVC)); key_rel.add(current().get(ABRL_PREVCOMP)); return goto_node(key_rel); } bool TAlbero_locale_relvoci::has_rbrother() const { return current().get_real(ABRL_NEXTCOMP) != ID_NULLO; } bool TAlbero_locale_relvoci::goto_rbrother() { TToken_string key_rel; key_rel.add(current().get(ABRL_TIPOCOD)); key_rel.add(current().get(ABRL_CODVC)); key_rel.add(current().get(ABRL_NEXTCOMP)); return goto_node(key_rel); } TNumeric_id TAlbero_locale_relvoci::new_id() { TNumeric_id max_id; // Vado sul primo figlio while (has_lbrother()) { goto_lbrother(); } max_id = current().get_real(ABRL_IDCOMP); while (has_rbrother()) { goto_rbrother(); if ((current().get_real(ABRL_IDCOMP)) > max_id) max_id = current().get_real(ABRL_IDCOMP); } return max_id + 1; } bool TAlbero_locale_relvoci::insert_node(TRectype &node, const TNumeric_id id_prec, const TNumeric_id id_padre, TNumeric_id id, const char trasf_immes) { if (trasf_immes == IMMESSO) { TNumeric_id prev_comp = current().get_real(ABRL_IDCOMP); TNumeric_id next_comp = current().get_real(ABRL_NEXTCOMP); TNumeric_id new_comp; new_comp = new_id(); TToken_string key_rel; // Mi riposiziono sul record PRECEDENTE a quello che sto inserendo key_rel.cut(0); key_rel.add(current().get(ABRL_TIPOCOD)); key_rel.add(current().get(ABRL_CODVC)); key_rel.add(prev_comp.string()); goto_node(key_rel); // Creo il nuovo record di relvoci e lo inserisco TRectype new_rel(current()); new_rel.zero(); new_rel.put(ABRL_TIPOCOD,current().get(ABRL_TIPOCOD)); new_rel.put(ABRL_CODVC,current().get(ABRL_CODVC)); new_rel.put(ABRL_IDCOMP,new_comp.string()); new_rel.put(ABRL_PREVCOMP,prev_comp.string()); new_rel.put(ABRL_NEXTCOMP,next_comp.string()); _local_cache->put(new_rel); // Creo la chiave corretta per lo status node key_rel.cut(0); key_rel.add(new_rel.get(ABRL_TIPOCOD)); key_rel.add(new_rel.get(ABRL_CODVC)); key_rel.add(new_rel.get(ABRL_IDCOMP)); // Setto lo status set_status_node(key_rel,NODO_AGGIUNTO); //Aggiorno eventualmente il precedente del nodo che sto inserendo if (prev_comp > ID_NULLO) { key_rel.cut(0); key_rel.add(current().get(ABRL_TIPOCOD)); key_rel.add(current().get(ABRL_CODVC)); key_rel.add(prev_comp.string()); goto_node(key_rel); current().put(ABRL_NEXTCOMP,new_comp); // Il successivo del precedente sono io !! _local_cache->put(*_currnode); set_status_node(key_rel,NODO_MODIFICATO); } //Aggiorno eventualmente il successivo if (next_comp > ID_NULLO) { key_rel.cut(0); key_rel.add(current().get(ABRL_TIPOCOD)); key_rel.add(current().get(ABRL_CODVC)); key_rel.add(next_comp.string()); goto_node(key_rel); current().put(ABRL_PREVCOMP,new_comp); // Il precedente del successivo sono io !! _local_cache->put(*_currnode); set_status_node(key_rel,NODO_MODIFICATO); } //Mi posizione sul nodo di relvoci appena immesso key_rel.cut(0); key_rel.add(current().get(ABRL_TIPOCOD)); key_rel.add(current().get(ABRL_CODVC)); key_rel.add(new_comp.string()); goto_node(key_rel); } else { _local_cache->put(node); } return TRUE; } bool TAlbero_locale_relvoci::delete_node(const TNumeric_id &id_nullo) { // current_cache().put(current()); TToken_string key = current().get(ABRL_TIPOCOD); key.add(current().get(ABRL_CODVC)); key.add(current().get(ABRL_IDCOMP)); set_status_node(key,NODO_RIMOSSO); TNumeric_id id, next, prev; id = current().get_real(ABRL_IDCOMP); prev = current().get_real(ABRL_PREVCOMP); next = current().get_real(ABRL_NEXTCOMP); // Aggiorno eventualmente il precedente if (prev != ID_NULLO) { // Costruisco la chiave di ricerca, lo ricerco e gli dico che il suo successivo //è diventato il mio successivo, aggiorno key.cut(0); key.add(current().get(ABRL_TIPOCOD)); key.add(current().get(ABRL_CODVC)); key.add(prev.string()); goto_node(key); current().put(ABRL_NEXTCOMP,next); current_cache().put(current()); set_status_node(key,NODO_MODIFICATO); } if (next != ID_NULLO) { key.cut(0); key.add(current().get(ABRL_TIPOCOD)); key.add(current().get(ABRL_CODVC)); key.add(next.string()); goto_node(key); current().put(ABRL_PREVCOMP,prev); current_cache().put(current()); set_status_node(key,NODO_MODIFICATO); } return TRUE; } bool TAlbero_locale_relvoci::goto_node(const TString &key) { TToken_string key_rel = (TToken_string &) key; *_currnode = current_cache().get(key); return TRUE; } bool TAnalisi_bil::insert_new_node(TToken_string &codice) { TNumeric_id prec_id = codice.get(0); user_tree()->goto_node(prec_id); TToken_string key = user_tree()->current().get(ABRA_TIPOCOD); key.add(user_tree()->current().get(ABRA_CODVC)); key.add(user_tree()->current().get(ABRA_NCOMP)); if (user_tree_voc()->goto_node(key)) { TNumeric_id father_id = user_tree()->current().get(ABRA_IDPADRE); TRectype new_node(user_tree()->current()); new_node.zero(); //Inserisco il nodo corrispondente nella struttura di relvoci user_tree_voc()->insert_node(new_node,ID_NULLO,ID_NULLO,ID_NULLO,IMMESSO); //Completo le informazioni di new_node con quelle appena inserite in relvoci new_node.put(ABRA_TIPOCOD,user_tree_voc()->current().get(ABRL_TIPOCOD)); new_node.put(ABRA_CODVC,user_tree_voc()->current().get(ABRL_CODVC)); new_node.put(ABRA_NCOMP,user_tree_voc()->current().get(ABRL_IDCOMP)); user_tree()->insert_node(new_node,prec_id,father_id,ID_NULLO,IMMESSO); return TRUE; } return FALSE; } bool TAnalisi_bil::remove_node(const TNumeric_id &id) { user_tree()->goto_node(id); TToken_string key_rel; key_rel.add(user_tree()->current().get(ABRA_TIPOCOD)); key_rel.add(user_tree()->current().get(ABRA_CODVC)); key_rel.add(user_tree()->current().get(ABRA_NCOMP)); if (user_tree_voc()->goto_node(key_rel)) { // Questa delete_node vorrebbe un TNumeric_id; ma siccome non // ho nulla del genere, e sono già sul nodo da rimuovere, // gli passo ID_NULLO e rimuovo il nodo user_tree_voc()->delete_node(ID_NULLO); user_tree()->delete_node(id); user_tree_voc()->write_cache(); //solo debug user_tree()->write_cache(); //solo debug return TRUE; } error_box("Sto rimuovendo un nodo di relana che non dovrebbe esserci"); return FALSE; } int TAnalisi_bil::commit_body() const { TAlbero_AB::commit_body(); TRectype rec(_inter_tree_relvoci->current().num()); TRectype rec_car(_record_caradd->current_rec().num()); TRectype rec_col(_record_colldich->current_rec().num()); TLocalisamfile file_caradd(rec_car.num()); TLocalisamfile file_colldich(rec_col.num()); TLocalisamfile filebody(rec.num()); TString status; /**********************************************************/ /* PARTE RISERVATA A RELVOCI */ /**********************************************************/ //Estraggo un nodo per volta basandomi sull'assoc_array degli status while (_inter_tree_relvoci->dirty_nodes() > 0) { TString id = ID_NULLO_STR; int prova = _inter_tree_relvoci->dirty_nodes(); //solo debug rec= _inter_tree_relvoci->extract_dirtynode(id, status); //Ho un rectype e uno status node if (status == NODO_AGGIUNTO) { if (filebody.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 !!! 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 } /**********************************************************/ /* PARTE RISERVATA A CARADD */ /**********************************************************/ //Estraggo un nodo per volta basandomi sull'assoc_array degli status di caradd while (_record_caradd->dirty_nodes() > 0) { TString id = ID_NULLO_STR; rec_car= _record_caradd->extract_dirtynode(id, status); //Ho un rectype e uno status node if ((status == NODO_AGGIUNTO) || (status == NODO_MODIFICATO)) { if (file_caradd.curr().empty()) { //Se il cursor di relana è posizionato in modo errato: //lo sistemo sull'ultimo record del file file_caradd.last(); } int err = file_caradd.write(rec_car); if (err != NOERR) return err; if (status == NODO_MODIFICATO) { int err = file_caradd.rewrite(rec_car); if (err != NOERR) return err; } if (status == NODO_RIMOSSO) { file_caradd.read(rec_car); //Devo leggere sul file per posizionare il cursore: stesso motivo di prima int err = file_caradd.remove(rec_car); if (err != NOERR) return err; } // In tutti gli altri casi il nodo non viene modificato } } /**********************************************************/ /* PARTE RISERVATA A COLLDICH */ /**********************************************************/ //Estraggo un nodo per volta basandomi sull'assoc_array degli status di colldich while (_record_colldich->dirty_nodes() > 0) { TString id = ID_NULLO_STR; rec_col= _record_colldich->extract_dirtynode(id, status); //Ho un rectype e uno status node if ((status == NODO_AGGIUNTO) || (status == NODO_MODIFICATO)) { if (file_colldich.curr().empty()) { //Se il cursor di relana è posizionato in modo errato: //lo sistemo sull'ultimo record del file file_colldich.last(); } int err = file_colldich.write(rec_col); if (err != NOERR) return err; if (status == NODO_MODIFICATO) { int err = file_colldich.rewrite(rec_col); if (err != NOERR) return err; } if (status == NODO_RIMOSSO) { file_colldich.read(rec_col); //Devo leggere sul file per posizionare il cursore: stesso motivo di prima int err = file_colldich.remove(rec_col); if (err != NOERR) return err; } // In tutti gli altri casi il nodo non viene modificato } } return NOERR; } void TAnalisi_bil::modify_node(TToken_string &codice) { //Posiziono il nodo sul record selezionato TNumeric_id temp_id = codice.get(0); user_tree()->goto_node(temp_id); //Aggiorno il contenuto del record user_tree()->current().put(ABRA_TIPOCOD,codice.get(1)); user_tree()->current().put(ABRA_CODVC,codice.get(2)); user_tree()->current().put(ABRA_NCOMP,codice.get(3)); user_tree()->current().put(ABRA_VOCEINCID,codice.get(4)); user_tree()->current().put(ABRA_DESCRIZ,codice.get(5)); user_tree()->current().put(ABRA_USACARADD,codice.get(6)); user_tree()->current().put(ABRA_IDCARADD,codice.get(8)); user_tree()->current().put(ABRA_IDCOLDICH,codice.get(9)); // Rintraccio il record corrispondente di relvoci e apporto le modifiche TToken_string key_rel; key_rel.add(codice.get(1)); key_rel.add(codice.get(2)); key_rel.add(codice.get(3)); user_tree_voc()->goto_node(key_rel); user_tree_voc()->current().put(ABRL_CODCOMP,codice.get(7)); // Salvo le modifiche di relana user_tree()->current_cache().put(user_tree()->current()); user_tree()->set_status_node(temp_id.string(),NODO_MODIFICATO); user_tree()->write_cache(); //Solo debug // Salvo le modifiche di relvoci user_tree_voc()->current_cache().put(user_tree_voc()->current()); user_tree_voc()->set_status_node(key_rel,NODO_MODIFICATO); user_tree_voc()->write_cache(); //Solo debug } /***********************************************************/ /***********************************************************/ TRecord::TRecord(int filenum) { // debug mods: _currrec = new TRectype(filenum); if (filenum==LF_COLLDICH) { _r= new TIsamtempfile(filenum,"/com/colldich",FALSE,FALSE); } if (filenum == LF_CARADD) { _r= new TIsamtempfile(filenum,"/com/caradd",FALSE,FALSE); } _local_cache_record = new TRWrecord_cache(_r); } TRecord::~TRecord() { delete _currrec; delete _local_cache_record; delete _r; _currrec = NULL; _local_cache_record = NULL; _r = NULL; } TRectype TRecord::extract_dirtynode(TString &id, TString& status) { TRectype rec(current_rec()); THash_object *row_status; _status_node_record.restart(); row_status = _status_node_record.get_hashobj(); status = (TString&)row_status->obj(); id = (TString)row_status->key(); rec = current_cache_record().get(id); _status_node_record.remove(id); return rec; //Ritorno un Trectype e passo lo stauts node } void TRecord::set_status_node(const TString &id, const char *status) { TString key = id; const TString *prec_status = (const TString *)_status_node_record.objptr(key); if (!prec_status) { //Elemento ancora non presente nell'array _status_node_record.add(key,TString (status)); } else { //Elemento già inserito if (stricmp(status,NODO_AGGIUNTO)==0 && (*prec_status != NODO_AGGIUNTO)) _status_node_record.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_record.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 //message_box("Sto rimuovendo un nodo appena modificato e non ancora registrato"); } } } TRecord_caradd::TRecord_caradd() : TRecord(LF_CARADD) { } TRecord_colldich::TRecord_colldich() : TRecord(LF_COLLDICH) { }