relation.cpp Tolti i Trecord_array
relation.h Lo stesso git-svn-id: svn://10.65.10.50/trunk@4001 c028cbd2-c16b-5b4b-a496-9718f37d4682
This commit is contained in:
parent
e32c43ce24
commit
ad81a2dd56
@ -1839,386 +1839,6 @@ int TFieldref::len(TRectype &rec) const
|
|||||||
return len - _from;
|
return len - _from;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
// TRecord_Array
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
TRecord_array::TRecord_array(const TRectype& r, const char* numfield, int first)
|
|
||||||
: _file(r.num()), _num(numfield), _offset(first - 1)
|
|
||||||
{
|
|
||||||
read(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
TRecord_array::TRecord_array(int logicnum, const char* numfield, int first)
|
|
||||||
: _file(logicnum), _num(numfield), _offset(first - 1)
|
|
||||||
{
|
|
||||||
set_key(new TRectype(logicnum));
|
|
||||||
}
|
|
||||||
|
|
||||||
TRecord_array::TRecord_array(const TRecord_array& a)
|
|
||||||
: TArray(a), _file(a._file), _offset(a._offset), _num(a._num)
|
|
||||||
{}
|
|
||||||
|
|
||||||
TRecord_array::~TRecord_array()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void TRecord_array::set_key(TRectype* r)
|
|
||||||
{
|
|
||||||
CHECK(r != NULL, "TRecord_array can't have a null key");
|
|
||||||
CHECK(r->num() == _file, "Bad key record");
|
|
||||||
|
|
||||||
add(r, 0); // Setta il nuovo campo chiave
|
|
||||||
if (rows() > 0)
|
|
||||||
{
|
|
||||||
const RecDes* recd = r->rec_des(); // Descrizione del record della testata
|
|
||||||
const KeyDes& kd = recd->Ky[0]; // Elenco dei campi della chiave 1
|
|
||||||
// Copia tutti i campi chiave, tranne l'ultimo, in tutti i records
|
|
||||||
for (int i = kd.NkFields-2; i >= 0; i--)
|
|
||||||
{
|
|
||||||
const int nf = kd.FieldSeq[i] % MaxFields;
|
|
||||||
const RecFieldDes& rf = recd->Fd[nf];
|
|
||||||
const TString& val = r->get(rf.Name);
|
|
||||||
renum_key(rf.Name, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const TRectype& TRecord_array::key() const
|
|
||||||
{
|
|
||||||
TRectype* r = (TRectype*)objptr(0);
|
|
||||||
CHECK(r, "TRecord_array lost its key");
|
|
||||||
return *r;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TRecord_array::exist(int n) const
|
|
||||||
{
|
|
||||||
const int i = n > 0 ? n - _offset : -1;
|
|
||||||
TObject* r = objptr(i);
|
|
||||||
return r != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @doc EXTERNAL
|
|
||||||
|
|
||||||
// @mfunc Ritorna la riga <p r>-esima; se tale riga non esiste e se <p create> assume il valore TRUE la riga viene creata
|
|
||||||
//
|
|
||||||
// @rdesc Ritorna un TRectype alla riga cercata o eventualemete il reference della nuova creata,
|
|
||||||
// NULL nel caso non esista e non venga creata.
|
|
||||||
TRectype& TRecord_array::row(
|
|
||||||
int n, // @parm Numero della riga da creare
|
|
||||||
bool create) // @parm Indica se creare una nuova riga se non esiste:
|
|
||||||
//
|
|
||||||
// @flag TRUE | Se la riga <p n> non esiste viene creata
|
|
||||||
// @flag FALSE | Se la riga <p n> non esiste non viene creata
|
|
||||||
|
|
||||||
// @comm Nel caso <p create> sia TRUE e venga richiesta una riga non esistente si crea un nuovo
|
|
||||||
// record copiando la chiave.
|
|
||||||
{
|
|
||||||
const int i = n >= 0 ? n - _offset : -1;
|
|
||||||
TRectype* r = (TRectype*)objptr(i);
|
|
||||||
if (r == NULL && create)
|
|
||||||
{
|
|
||||||
r = (TRectype*)key().dup(); // Crea nuovo record copiando la chiave
|
|
||||||
n = add(r, i) + _offset; // Riassegna n se era negativo!
|
|
||||||
char str[16]; sprintf(str, "%d", n);
|
|
||||||
r->renum_key(_num, str); // Aggiorna campo numero riga
|
|
||||||
}
|
|
||||||
CHECKD(r && n > 0, "Bad record number ", n);
|
|
||||||
return *r;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @doc EXTERNAL
|
|
||||||
|
|
||||||
// @mfunc Rinumera il campo chiave in seguito a reinsert
|
|
||||||
//
|
|
||||||
// @rdesc Ritorna se e' stato rinumerato il campo chiave
|
|
||||||
bool TRecord_array::renum_key(
|
|
||||||
const char* field, // @parm Campo della chiave a cui assegnare un nuovo valore
|
|
||||||
const TString& num) // @parm Nuovo valore da assegnare al campo
|
|
||||||
// @parm long | num | Nuovo valore da assegnare al campo
|
|
||||||
|
|
||||||
// @syntax bool renum_key(const char* field, const TString& num);
|
|
||||||
// @syntax bool renum_key(const char* field, long num);
|
|
||||||
|
|
||||||
{
|
|
||||||
CHECKS(_num != field, "You can't renumber field ", field);
|
|
||||||
const TString& curr = key().get(field);
|
|
||||||
if (curr == num)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
for (int i = last(); i >= 0; i--)
|
|
||||||
{
|
|
||||||
TRectype* r = (TRectype*)objptr(i);
|
|
||||||
if (r != NULL)
|
|
||||||
r->renum_key(field, num);
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TRecord_array::renum_key(const char* field, long num)
|
|
||||||
{
|
|
||||||
CHECKS(num > 0, "Null key value for field: ", field);
|
|
||||||
TString16 n; n << num;
|
|
||||||
return renum_key(field, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int TRecord_array::rec2row(const TRectype& r) const
|
|
||||||
{
|
|
||||||
CHECK(r.num() == _file, "Incompatible record");
|
|
||||||
const int n = atoi(r.get(_num)) - _offset; // Non e' detto che sia un int!
|
|
||||||
CHECKD(n >= 0 && n < 30000, "Bad line number in record ", n + _offset);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TRecord_array::insert_row(TRectype* r)
|
|
||||||
{
|
|
||||||
const int nr = rec2row(*r);
|
|
||||||
CHECK(nr > 0, "You cannot insert a new key");
|
|
||||||
const bool shift = exist(nr);
|
|
||||||
insert(r, nr);
|
|
||||||
if (shift)
|
|
||||||
{
|
|
||||||
for (int f = last(); f > nr; f = pred(f))
|
|
||||||
{
|
|
||||||
char n[16];
|
|
||||||
TRectype & rec = row(f, FALSE);
|
|
||||||
|
|
||||||
sprintf(n, "%ld", (long)_offset + f);
|
|
||||||
rec.renum_key(_num, n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TRecord_array::add_row(TRectype* r)
|
|
||||||
{
|
|
||||||
const int nr = rec2row(*r);
|
|
||||||
add(r, nr);
|
|
||||||
if (nr == 0 && rows() > 0) // Se ho cambiato il record campione
|
|
||||||
{ // e ci sono altre righe ...
|
|
||||||
for (int f = r->items()-1; f >= 0; f--)
|
|
||||||
{
|
|
||||||
const char* fn = r->fieldname(f);
|
|
||||||
const TString& v = r->get(fn);
|
|
||||||
if (v.not_empty()) renum_key(fn, v); // ... aggiorna tutte le righe in base
|
|
||||||
} // ai campi non vuoti del campione
|
|
||||||
}
|
|
||||||
return nr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// @doc EXTERNAL
|
|
||||||
|
|
||||||
// @mfunc Elimina le righe vuote
|
|
||||||
void TRecord_array::pack()
|
|
||||||
{
|
|
||||||
TArray::pack();
|
|
||||||
|
|
||||||
for (int i = size()-1; i > 0; i--)
|
|
||||||
{
|
|
||||||
TRectype* r = (TRectype*)objptr(i);
|
|
||||||
if (r != NULL)
|
|
||||||
{
|
|
||||||
TString16 n; n << i+_offset;
|
|
||||||
r->renum_key(_num, n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// @doc EXTERNAL
|
|
||||||
|
|
||||||
// @mfunc Cancella una riga
|
|
||||||
//
|
|
||||||
// @rdesc Ritorna se e' riuscito ad eliminare la riga
|
|
||||||
bool TRecord_array::destroy_row(
|
|
||||||
int r, // @parm Numero della riga da eliminare
|
|
||||||
bool pack) // @parm Indica se compattare gli elementi dell'array (default FALSE)
|
|
||||||
// @parm const TRectype& | r | Record da eliminare
|
|
||||||
|
|
||||||
// @syntax bool destroy_row(int n, bool pack);
|
|
||||||
// @syntax bool destroy_row(const TRectype& r, bool pack);
|
|
||||||
|
|
||||||
{
|
|
||||||
CHECKD(r > _offset, "Can't destroy row ", r);
|
|
||||||
const int index = r - _offset;
|
|
||||||
const bool ok = destroy(index, pack);
|
|
||||||
|
|
||||||
if (ok && pack)
|
|
||||||
{
|
|
||||||
for (int i = size()-1; i >= index; i--)
|
|
||||||
{
|
|
||||||
TRectype* r = (TRectype*)objptr(i);
|
|
||||||
if (r != NULL)
|
|
||||||
{
|
|
||||||
TString16 n; n << i+_offset;
|
|
||||||
r->renum_key(_num, n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TRecord_array::destroy_rows()
|
|
||||||
{
|
|
||||||
for (int i = last(); i > 0; i = pred(i))
|
|
||||||
destroy(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @doc EXTERNAL
|
|
||||||
|
|
||||||
// @mfunc Confronta i campi della chiave 1 scartando l'ultimo
|
|
||||||
//
|
|
||||||
// @rdesc Ritorna il risulato del confronto:
|
|
||||||
//
|
|
||||||
// @flag TRUE | Se i campi sono uguali
|
|
||||||
// @flag FALSE | Se i campi sono diversi
|
|
||||||
bool TRecord_array::good(
|
|
||||||
const TRectype& rec) const // @parm Record di cui confrontare i campi
|
|
||||||
{
|
|
||||||
const bool yes = key().same_key(rec, 1, 1);
|
|
||||||
return yes;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TRecord_array::read(TRectype* filter)
|
|
||||||
{
|
|
||||||
CHECKD(filter->num() == _file, "Bad key record ", filter->num());
|
|
||||||
CHECKS(filter->get(_num).empty(), "You can't specify in the filter the field ", (const char*)_num);
|
|
||||||
|
|
||||||
destroy();
|
|
||||||
int err = NOERR;
|
|
||||||
if (filter != NULL && !filter->empty())
|
|
||||||
{
|
|
||||||
TLocalisamfile f(_file);
|
|
||||||
|
|
||||||
set_key(filter);
|
|
||||||
TRectype* rec = (TRectype*)filter->dup();
|
|
||||||
err = rec->read(f, _isgteq);
|
|
||||||
for (int e = err; e == NOERR && good(*rec); e = rec->next(f))
|
|
||||||
{
|
|
||||||
add_row(rec);
|
|
||||||
rec = (TRectype*)(key().dup());
|
|
||||||
}
|
|
||||||
delete rec;
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TRecord_array::read(const TRectype& filter)
|
|
||||||
{
|
|
||||||
TRectype* f = (TRectype*)filter.dup();
|
|
||||||
return read(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
int TRecord_array::remove_from(int pos) const
|
|
||||||
{
|
|
||||||
int err = NOERR;
|
|
||||||
|
|
||||||
TRectype* rec = (TRectype*)key().dup();
|
|
||||||
CHECK(!rec->empty(), "Can't use empty key");
|
|
||||||
|
|
||||||
TLocalisamfile f(_file);
|
|
||||||
rec->put(_num, pos);
|
|
||||||
for (int e = rec->read(f, _isgteq); e == NOERR && good(*rec); e = rec->next(f))
|
|
||||||
{
|
|
||||||
const int found = rec->get_int(_num);
|
|
||||||
if (found >= pos)
|
|
||||||
{
|
|
||||||
err = rec->remove(f);
|
|
||||||
if (err != NOERR)
|
|
||||||
break;
|
|
||||||
pos = found+1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
delete rec;
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int TRecord_array::write(bool re) const
|
|
||||||
{
|
|
||||||
const int EOR = 32000; // End of records on file
|
|
||||||
int last_on_file = 0; // Last record found on file
|
|
||||||
int err = NOERR;
|
|
||||||
|
|
||||||
TLocalisamfile f(_file);
|
|
||||||
const int u = last();
|
|
||||||
for (int i = 1; i <= u; i++)
|
|
||||||
{
|
|
||||||
const TRectype* r = (TRectype*)objptr(i);
|
|
||||||
|
|
||||||
if (r != NULL)
|
|
||||||
{
|
|
||||||
if (re)
|
|
||||||
{
|
|
||||||
err = r->rewrite(f);
|
|
||||||
if (err == _iskeynotfound || err == _iseof || err == _isemptyfile)
|
|
||||||
err = r->write(f);
|
|
||||||
if (err != NOERR)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
err = r->write(f);
|
|
||||||
if (err == _isreinsert)
|
|
||||||
{
|
|
||||||
err = r->rewrite(f);
|
|
||||||
re = TRUE;
|
|
||||||
}
|
|
||||||
if (err != NOERR)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const int pos = i+_offset;
|
|
||||||
|
|
||||||
// Se pos < last_on_file sicuramente la read fallira' per cui la riga
|
|
||||||
// non puo' esistere su file: non esistendo nemmeno in memoria non devo
|
|
||||||
// fare assolutamente nulla!
|
|
||||||
if (pos >= last_on_file) // Puo' esistere su file?
|
|
||||||
{
|
|
||||||
TRectype* rec = (TRectype*)key().dup();
|
|
||||||
CHECK(!rec->empty(), "TRecord_array has an empty key");
|
|
||||||
rec->put(_num, pos);
|
|
||||||
err = rec->read(f, _isgteq);
|
|
||||||
if (err == NOERR && good(*rec)) // Cerca una riga >= pos sul file
|
|
||||||
{
|
|
||||||
last_on_file = atoi(rec->get(_num));
|
|
||||||
if (last_on_file == pos) // La riga c'era ma ora non piu'
|
|
||||||
{
|
|
||||||
err = (rec->remove(f)); // Cancello il record indesiderato
|
|
||||||
if (err != NOERR)
|
|
||||||
{
|
|
||||||
delete rec; // Orrore! Un errore di cancellazione!
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
last_on_file = EOR; // Sul file non ci sono piu' righe da cancellare
|
|
||||||
delete rec;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cancella eventuali residui successivi
|
|
||||||
if (err == NOERR && last_on_file != EOR)
|
|
||||||
remove_from(i + _offset);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int TRecord_array::remove() const
|
|
||||||
{
|
|
||||||
return remove_from(1 + _offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
// TRelation_description
|
// TRelation_description
|
||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
|
@ -209,124 +209,6 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
// TRecord_Array
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// @doc EXTERNAL
|
|
||||||
|
|
||||||
// @class TRecord_array | Classe per la gestione di un array di record
|
|
||||||
//
|
|
||||||
// @base public | TArray
|
|
||||||
class TRecord_array : private TArray
|
|
||||||
|
|
||||||
// @author:(INTERNAL) Guido
|
|
||||||
|
|
||||||
// @access:(INTERNAL) Private Member
|
|
||||||
{
|
|
||||||
// @cmember:(INTERNAL) Numero logico del file principale
|
|
||||||
int _file;
|
|
||||||
// @cmember:(INTERNAL) Offset iniziale del record array
|
|
||||||
int _offset;
|
|
||||||
// @cmember:(INTERNAL) Nome del campo col numero di riga
|
|
||||||
TString16 _num;
|
|
||||||
|
|
||||||
// @access Protected Member
|
|
||||||
protected:
|
|
||||||
// @cmember Ritorna il numero riga del record <p r>
|
|
||||||
int rec2row(const TRectype& r) const;
|
|
||||||
// @cmember Ritorna il nome del campo con numero di riga
|
|
||||||
const TString& num_field() const
|
|
||||||
{ return _num; }
|
|
||||||
// @cmember Elimina il record di posizione <p i> (ritorna il risultato dell'operazione)
|
|
||||||
int remove_from(int i) const;
|
|
||||||
// @cmember Confronta i campi della chiave 1 scartando l'ultimo
|
|
||||||
bool good(const TRectype& rec) const;
|
|
||||||
|
|
||||||
// @access Public Member
|
|
||||||
public:
|
|
||||||
// @cmember Ritorna il record che fa da chiave per tutti gli altri
|
|
||||||
const TRectype& key() const;
|
|
||||||
// @cmember Ritorna il numero di righe presenti
|
|
||||||
int rows() const
|
|
||||||
{ return items()-1; }
|
|
||||||
|
|
||||||
// @cmember Ritorna la riga successiva non vuota a partire da <p r>
|
|
||||||
int succ_row(int r) const
|
|
||||||
{ return succ(r - _offset) + _offset; }
|
|
||||||
// @cmember Ritorna la riga precedente non vuota a partire da <p r>
|
|
||||||
int pred_row(int r) const
|
|
||||||
{ return pred(r - _offset) + _offset; };
|
|
||||||
// @cmember Ritorna l'ultma riga non vuota
|
|
||||||
int last_row() const
|
|
||||||
{ return last() + _offset; }
|
|
||||||
// @cmember Ritorna la prima riga non vuota
|
|
||||||
int first_row() const
|
|
||||||
{ return succ_row(0); }
|
|
||||||
|
|
||||||
// @cmember Ritorna il record <p r>-esimo
|
|
||||||
const TRectype& row(int r) const
|
|
||||||
{ CHECKD(r > _offset, "Bad record number ", r); return (const TRectype&)operator[](r - _offset); }
|
|
||||||
|
|
||||||
// @cmember Controlla se esiste la riga <p r> (TRUE se esiste)
|
|
||||||
bool exist(int r) const;
|
|
||||||
// @cmember Ritorna la riga <p r>-esima; se tale riga non esiste e se <p create> assume il valore TRUE la riga viene creata
|
|
||||||
TRectype& row(int r, bool create);
|
|
||||||
|
|
||||||
// @cmember Inserisce una riga alla posizione indicata nel record, sposta gli altri elementi se la posizione era gia' occupata
|
|
||||||
virtual int insert_row(TRectype* r);
|
|
||||||
// @cmember Inserisce una riga alla posizione indicata nel record, sposta gli altri elementi se la posizione era gia' occupata
|
|
||||||
int insert_row(const TRectype& r)
|
|
||||||
{ return insert_row((TRectype*)r.dup()); }
|
|
||||||
|
|
||||||
// @cmember Aggiunge/sostituisce una riga alla posizione indicata nel record
|
|
||||||
virtual int add_row(TRectype* r);
|
|
||||||
// @cmember Aggiunge/sostituisce una riga alla posizione indicata nel record
|
|
||||||
int add_row(const TRectype& r)
|
|
||||||
{ return add_row((TRectype*)r.dup()); }
|
|
||||||
// @cmember Compatta le righe piene
|
|
||||||
virtual void pack() ;
|
|
||||||
// @cmember Cancella una riga identificata da <p n>
|
|
||||||
virtual bool destroy_row(int n, bool pack = FALSE);
|
|
||||||
// @cmember Cancella una riga identificata da <p r>
|
|
||||||
virtual bool destroy_row(const TRectype& r, bool pack = FALSE)
|
|
||||||
{ return destroy_row(rec2row(r), pack); }
|
|
||||||
// @cmember Cancella tutte le righe
|
|
||||||
void destroy_rows();
|
|
||||||
|
|
||||||
// @cmember Ritorna il numero logico del file principale
|
|
||||||
int logic_num() const
|
|
||||||
{ return _file; }
|
|
||||||
// @cmember Cambia l'intera chiave (solo se vuoto)
|
|
||||||
void set_key(TRectype* r);
|
|
||||||
// @cmember Rinumera il campo chiave in seguito a reinsert
|
|
||||||
bool renum_key(const char* field, const TString& num);
|
|
||||||
// @cmember Rinumera il campo chiave in seguito a reinsert
|
|
||||||
bool renum_key(const char* field, long num);
|
|
||||||
|
|
||||||
// @cmember Legge tutto l'array dal file
|
|
||||||
virtual int read(const TRectype& r);
|
|
||||||
// @cmember Legge tutto l'array dal file
|
|
||||||
virtual int read(TRectype* r);
|
|
||||||
// @cmember Aggiorna il file (se <p re> == TRUE allora viene aggiornato il record se esisteva)
|
|
||||||
virtual int write(bool re = FALSE) const;
|
|
||||||
// @cmember Aggiorna il record sul file
|
|
||||||
virtual int rewrite() const
|
|
||||||
{ return write(TRUE); }
|
|
||||||
// @cmember Cancella tutti i record dal file
|
|
||||||
virtual int remove() const;
|
|
||||||
|
|
||||||
// @cmember Costruttore
|
|
||||||
TRecord_array(const TRectype& r, const char* numfield, int first = 1);
|
|
||||||
// @cmember Costruttore
|
|
||||||
TRecord_array(int logicnum, const char* numfield, int first = 1);
|
|
||||||
// @cmember Costruttore
|
|
||||||
TRecord_array(const TRecord_array& a);
|
|
||||||
// @cmember Distruttore
|
|
||||||
virtual ~TRecord_array();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class TExpression;
|
class TExpression;
|
||||||
|
|
||||||
// @doc EXTERNAL
|
// @doc EXTERNAL
|
||||||
|
Loading…
x
Reference in New Issue
Block a user