Patch level : 12.0 no-patch

Files correlati     : fp
Commento            : Continuo sviluppo personalizzazioni FP
- Creata classe ad-hoc (TFP_expression) per il controllo delle espressioni così da non avere la classe TDoc_fp piena di roba
- Aggiunte funzioni comuni e lettura delle condizioni
- Inserita gestione delle variabili tramite TVariant (con allocazione furba) per effettuare delle comparazioni migliori
This commit is contained in:
Mattia Tollari 2019-06-12 17:29:35 +02:00
parent 751102232e
commit 2582b6c7b4
4 changed files with 343 additions and 169 deletions

View File

@ -175,7 +175,7 @@ BEGIN
DISPLAY "Tipo riga@8" CODTAB
DISPLAY "Descrizione@50" S0
OUTPUT S_ADG_TIPORIGA CODTAB
HELP "Condizione che se è vera abilita la riga"
HELP "La personalizzazione viene abilitata solo su queste righe"
END
STRING S_ADG_COND 200 50

View File

@ -269,8 +269,6 @@ protected:
bool export_paf0100f();
bool export_paf3200f();
// Record clifo
//void set_rec_clifo(char tipocf, long codcf);
@ -287,14 +285,6 @@ public:
int force_commit();
void set_cache_insert(const bool v) { _cache_insert = v; }
// Test
static const TString& parse_expression(const TString& expr, TRiga_documento& rdoc);
static TString& parse_read(const TString& str, TRiga_documento& rdoc);
static TString& do_read(const TString& tabella, const TString& campo, TRiga_documento& rdoc);
static TString& parse_search(const TString& str, TRiga_documento& rdoc);
TDoc_fp();
~TDoc_fp();
};
@ -490,4 +480,29 @@ public:
for (int __r = bodyof##__radg.first_row(); bodyof##__radg.exist(__r) && (__radg=&(TRectype&)bodyof##__radg.row(__r))!=NULL; __r=bodyof##__radg.succ_row(__r))
class TFP_expression : public TObject
{
private:
enum TFP_operator { error, eq, neq, gt, ls, gteq, lseq };
// Etrattori
static void extract_info(const TString& expr, TString& tabella, TString& campo, TToken_string* search);
static bool calc_table(const TString& tabella, int& file);
static void split_condition(const TString& cond, TString& cond_sx, TString& cond_dx, TFP_operator& symb);
static TVariant& get_value(const TRectype& rec, const TString& campo);
static TVariant& parse_var(const TString& str);
// Calcolatori
static TVariant& parse_read(const TString& str, TRiga_documento& rdoc);
static TVariant& do_read(const TString& tabella, const TString& campo, TRiga_documento& rdoc);
static TVariant& parse_search(const TString& str, TRiga_documento& rdoc);
public:
static const TVariant parse_expression(const TString& expr, TRiga_documento& rdoc);
static bool check_condition(const TString& cond, TRiga_documento& rdoc);
TFP_expression() = default;
virtual ~TFP_expression() = default;
};
#endif // __FPLIB_H

View File

@ -1408,164 +1408,6 @@ bool TDoc_fp::export_paf3200f()
return true;
}
/*
* Sintassi:
* READ(TABELLA, CAMPO) -> legge dalle tabelle predefinite
* SEARCH(TABELLA, CAMPO, RICERCA) -> legge da una tabella qualsiasi
* SEARCH e READ sono diverse sotto richiesta di Ilaria per avere una sintassi più chiara
* resto: TExpr_documento()
*/
const TString& TDoc_fp::parse_expression(const TString& expr, TRiga_documento& rdoc)
{
const TToken_string exprs(expr, '+');
TString& result = get_tmp_string().cut(0);
TExpr_documento expr_documento(_mixexpr, &rdoc.doc(), &rdoc);
FOR_EACH_STR_TOKEN(exprs, str)
{
// Come prima cosa trimmo la vita
str.trim();
if(str.starts_with("READ(", true))
{
result << parse_read(str, rdoc);
} else if(str.starts_with("SEARCH(", true))
{
result << parse_search(str, rdoc);
} else
{
// Controllo non sia una ricerca a DB con il punto
if(expr.contains("->"))
{
// Se contiene una freccia non posso fare un TToken_string easy
// Non esistono campi con -> vero?
str.replace("->", ".");
}
// Questo diventa true se sono passato sopra o effettivamente ha un punto dall'inizio
if(expr.contains('.'))
{
TToken_string simple_field(expr, '.');
result << do_read(simple_field.get(0), simple_field.get(), rdoc);
}
// Se inizia come una stringa o non contiene parentesi -> no expr
else if (str.starts_with("\"") || !(str.contains("(") && str.contains(")")))
{
str.replace("\"", "");
result << str;
}
else
{
expr_documento.set(str, _mixexpr);
result << expr_documento.as_string();
}
}
}
return result;
}
void extract_info(const TString& expr, TString& tabella, TString& campo, TToken_string* search)
{
// Prendo la stringa pulita della parte sinistra
TString clean_expr = expr.mid(expr.find('(') + 1);
// Tolgo eventuali spazi ai lati
clean_expr.trim();
// Tolgo la virgola finale
clean_expr.rtrim(1);
TToken_string info(clean_expr, ',');
// Trimmare sempre come se non sapessi fare altro nella vita
tabella.cut(0) << info.remove(0); tabella.trim();
campo.cut(0) << info.remove(0); campo.trim();
if(search != nullptr)
{
// Prendo il resto
search->cut(0) << info;
search->trim();
search->restart();
}
}
bool calc_table(const TString& tabella, int& file)
{
file = atoi(tabella);
if (file == 0)
file = table2logic(tabella);
return is_multi_table(file);
}
TString& TDoc_fp::parse_read(const TString& str, TRiga_documento& rdoc)
{
TString tabella, campo;
extract_info(str, tabella, campo, nullptr);
return do_read(tabella, campo, rdoc);
}
TString& TDoc_fp::do_read(const TString& tabella, const TString& campo, TRiga_documento& rdoc)
{
TString& result = get_tmp_string().cut(0);
int file;
bool multi_table = calc_table(tabella, file);
switch (file)
{
case LF_DOC:
result = rdoc.doc().get(campo);
break;
case LF_RIGHEDOC:
result = rdoc.get(campo);
break;
case LF_CLIFO:
result = rdoc.doc().clifor().get(campo);
break;
case LF_CFVEN:
result = rdoc.doc().clifor().vendite().get(campo);
break;
case LF_LETINT:
result = rdoc.doc().clifor().lettera().get(campo);
break;
case LF_ANAMAG:
result = rdoc.articolo().get(campo);
break;
default:
// Skip
break;
}
return result;
}
TString& TDoc_fp::parse_search(const TString& str, TRiga_documento& rdoc)
{
TString& result = get_tmp_string();
TString tabella, campo;
TToken_string input_search, search;
int file;
extract_info(str, tabella, campo, &input_search);
const bool multi_table = calc_table(tabella, file);
// Parso ogni singolo token della ricerca
FOR_EACH_TOKEN(input_search, tok)
{
search.add(parse_expression(tok, rdoc));
}
if(multi_table)
{
result.cut(0) << cache().get(tabella, search, campo);
} else
{
result.cut(0) << cache().get(file, search, campo);
}
return result;
}
bool TDoc_fp::doc_to_paf(TDocumentoEsteso& doc)
{
if (!initialize(doc))

View File

@ -37,6 +37,7 @@ void TFP_custom::autoload(const TSheet_field& sf, const int file) const
{
TRectype& rec_row = rcaus.row(-1, true);
rec_row.put(FPCCAUS_VALORE, row->get(0));
rec_row.put(FPCCAUS_COND, row->get());
}
}
break;
@ -49,6 +50,7 @@ void TFP_custom::autoload(const TSheet_field& sf, const int file) const
TRectype& rec_row = rart.row(-1, true);
rec_row.put(FPCART_TIPO, row->get(0));
rec_row.put(FPCART_VALORE, row->get());
rec_row.put(FPCART_COND, row->get());
}
}
break;
@ -63,6 +65,9 @@ void TFP_custom::autoload(const TSheet_field& sf, const int file) const
rec_row.put(FPCADG_RTESTO, row->get());
rec_row.put(FPCADG_RNUMERO, row->get());
rec_row.put(FPCADG_RDATA, row->get());
rec_row.put(FPCADG_TIPORIGA, row->get());
rec_row.put(FPCADG_SPLIT, row->get());
rec_row.put(FPCADG_COND, row->get());
}
}
break;
@ -89,3 +94,315 @@ TFP_custom::TFP_custom(const char* codcust) : TFP_custom()
init();
load(codcust);
}
/*
* Sintassi:
* READ(TABELLA, CAMPO) -> legge dalle tabelle predefinite
* SEARCH(TABELLA, CAMPO, RICERCA) -> legge da una tabella qualsiasi
* SEARCH e READ sono diverse sotto richiesta di Ilaria per avere una sintassi più chiara
* resto: TExpr_documento()
*/
const TVariant TFP_expression::parse_expression(const TString& expr, TRiga_documento& rdoc)
{
const TToken_string exprs(expr, '+');
TVariant result;
TExpr_documento expr_documento(_mixexpr, &rdoc.doc(), &rdoc);
FOR_EACH_STR_TOKEN(exprs, str)
{
// Come prima cosa trimmo la vita
str.trim();
if (str.starts_with("READ(", true))
{
result.add(parse_read(str, rdoc));
}
else if (str.starts_with("SEARCH(", true))
{
result.add(parse_search(str, rdoc));
}
else
{
// Controllo non sia una ricerca a DB con il punto
if (expr.contains("->"))
{
// Se contiene una freccia non posso fare un TToken_string easy
// Non esistono campi con -> vero?
str.replace("->", ".");
}
// Questo diventa true se sono passato sopra o effettivamente ha un punto dall'inizio
if (expr.contains('.'))
{
TToken_string simple_field(expr, '.');
result.add(do_read(simple_field.get(0), simple_field.get(), rdoc));
}
// Se inizia come una stringa o non contiene parentesi -> no expr
else if (str.starts_with("\"") || !(str.contains("(") && str.contains(")")))
{
str.replace("\"", "");
// Cerchiamo di capire che è
result.add(parse_var(str));
}
else
{
expr_documento.set(str, _mixexpr);
result.add(expr_documento.as_string());
}
}
}
return result;
}
bool TFP_expression::check_condition(const TString& cond, TRiga_documento& rdoc)
{
TString cond_sx, cond_dx;
TFP_operator symb;
split_condition(cond, cond_sx, cond_dx, symb);
const TVariant value_sx = parse_expression(cond_sx, rdoc);
const TVariant value_dx = parse_expression(cond_dx, rdoc);
switch(symb)
{
case eq:
return value_sx == value_dx;
case neq:
return value_sx != value_dx;
case gt:
return value_sx > value_dx;
case ls:
return value_sx < value_dx;
case gteq:
return value_sx >= value_dx;
case lseq:
return value_sx <= value_dx;
default:
return false;
}
}
void TFP_expression::extract_info(const TString& expr, TString& tabella, TString& campo, TToken_string* search)
{
// Prendo la stringa pulita della parte sinistra
TString clean_expr = expr.mid(expr.find('(') + 1);
// Tolgo eventuali spazi ai lati
clean_expr.trim();
// Tolgo la virgola finale
clean_expr.rtrim(1);
TToken_string info(clean_expr, ',');
// Trimmare sempre come se non sapessi fare altro nella vita
tabella.cut(0) << info.remove(0); tabella.trim();
campo.cut(0) << info.remove(0); campo.trim();
if (search != nullptr)
{
// Prendo il resto
search->cut(0) << info;
search->trim();
search->restart();
}
}
bool TFP_expression::calc_table(const TString& tabella, int& file)
{
file = atoi(tabella);
if (file == 0)
file = table2logic(tabella);
return is_multi_table(file);
}
void TFP_expression::split_condition(const TString& cond, TString& cond_sx, TString& cond_dx, TFP_operator& symb)
{
// Cerchiamo di capire che condizione ho
TString4 symbol;
if(cond.contains(" == "))
{
symb = eq;
symbol = " == ";
}
else if(cond.contains(" != "))
{
symb = neq;
symbol = " != ";
}
else if (cond.contains(" >= "))
{
symb = gteq;
symbol = " >= ";
}
else if (cond.contains(" <= "))
{
symb = lseq;
symbol = " <= ";
}
else if (cond.contains(" > "))
{
symb = gt;
symbol = " > ";
}
else if (cond.contains(" < "))
{
symb = ls;
symbol = " < ";
}
if(symb != error)
{
cond_sx = cond.left(cond.find(symbol));
cond_dx = cond.right(cond.len() - (cond.find(symbol) + symbol.len()));
}
else
{
// Considero come se è true o no il campo in generale
symb = gt;
cond_sx = cond;
cond_dx.cut(0);
}
}
// Questa funzione potrebbe diventare standard per TRectype
TVariant& TFP_expression::get_value(const TRectype& rec, const TString& campo)
{
TVariant& ret = get_tmp_var();
switch(rec.type(campo))
{
case _intfld:
ret.set(rec.get_int(campo));
break;
case _longfld:
ret.set(rec.get_long(campo));
break;
case _realfld:
ret.set(rec.get_real(campo));
break;
case _datefld:
ret.set(rec.get_date(campo));
break;
case _wordfld:
ret.set(rec.get_word(campo));
break;
case _charfld:
ret.set(rec.get_char(campo));
break;
case _boolfld:
ret.set(rec.get_bool(campo));
break;
case _nullfld:
case _alfafld:
case _intzerofld:
case _longzerofld:
case _memofld:
ret.set(rec.get(campo));
break;
}
return ret;
}
TVariant& TFP_expression::parse_var(const TString& str)
{
TVariant& ret = get_tmp_var();
// Come prima cosa leggo la stringa e cerco di capire se ci sono numeri o no
bool is_real = false, is_string = false;
for(int i = 0; i < str.len() && !is_string; i++)
{
const char val = str[i];
if(val < '0' || val > '9')
{
if (val == '.' || val == ',')
is_real = true;
else
is_string = true;
}
}
// Una volta che ho capito che ho davanti tento il trick!
if (is_string)
{
// Tento la data
const TDate pop(str);
if (pop.ok())
ret.add(pop);
else
ret.add(str);
}
else if (is_real)
ret.add(real(str));
else
{
char* err;
const long long_val = strtol(str, &err, 10);
// Ho avuto un errore? Il controllo ha fallito!
if(err && *err)
{
ret.add(str);
}
else
{
ret.add(long_val);
}
}
return ret;
}
TVariant& TFP_expression::parse_read(const TString& str, TRiga_documento& rdoc)
{
TString tabella, campo;
extract_info(str, tabella, campo, nullptr);
return do_read(tabella, campo, rdoc);
}
TVariant& TFP_expression::do_read(const TString& tabella, const TString& campo, TRiga_documento& rdoc)
{
int file;
calc_table(tabella, file);
switch (file)
{
case LF_DOC:
return get_value(rdoc.doc(), campo);
case LF_RIGHEDOC:
return get_value(rdoc, campo);
case LF_CLIFO:
return get_value(rdoc.doc().clifor(), campo);
case LF_CFVEN:
return get_value(rdoc.doc().clifor().vendite(), campo);
case LF_LETINT:
return get_value(rdoc.doc().clifor().lettera(), campo);
case LF_ANAMAG:
return get_value(rdoc.articolo(), campo);
default:
static TVariant null_var(EMPTY_STRING);
return null_var;
}
}
TVariant& TFP_expression::parse_search(const TString& str, TRiga_documento& rdoc)
{
TString tabella, campo;
TToken_string input_search, search;
int file;
extract_info(str, tabella, campo, &input_search);
const bool multi_table = calc_table(tabella, file);
// Parso ogni singolo token della ricerca
FOR_EACH_TOKEN(input_search, tok)
{
search.add(parse_expression(tok, rdoc).as_string());
}
if (multi_table)
{
return get_value(cache().get(tabella, search), campo);
}
else
{
return get_value(cache().get(file, search), campo);
}
}