#include "fplib.h" #include #include #include #include void TFP_custom::init() { add_file(LF_FPCCAUS, FPCCAUS_NRIGA); add_file(LF_FPCART, FPCART_NRIGA); add_file(LF_FPCADG, FPCADG_NRIGA); } int TFP_custom::write_rewrite(TBaseisamfile& f, bool re) const { return TMultiple_rectype::write_rewrite(f, re); } bool TFP_custom::load(const char* codcust) { // Metto il codice nel rectype principale ed effettuo una read put(FPCUST_CODICE, codcust); return _loaded = TMultiple_rectype::read() == NOERR; } void TFP_custom::autoload(const TSheet_field& sf, const int file) const { switch(file) { case LF_FPCCAUS: { TRecord_array& rcaus = body(file); rcaus.destroy_rows(); FOR_EACH_SHEET_ROW(sf, r, row) { TRectype& rec_row = rcaus.row(-1, true); rec_row.put(FPCCAUS_VALORE, row->get(0)); rec_row.put(FPCCAUS_COND, row->get()); } } break; case LF_FPCART: { TRecord_array& rart = body(file); rart.destroy_rows(); FOR_EACH_SHEET_ROW(sf, r, row) { 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; case LF_FPCADG: { TRecord_array& radg = body(file); radg.destroy_rows(); FOR_EACH_SHEET_ROW(sf, r, row) { TRectype& rec_row = radg.row(-1, true); rec_row.put(FPCADG_TIPODATO, row->get(0)); 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; default: fatal_box("File non riconosciuto %d", file); break; } } TFP_custom::TFP_custom() : TMultiple_rectype(LF_FPCUST), _codcust(""), _loaded(false) { init(); } TFP_custom::TFP_custom(const TRectype& rec) : TMultiple_rectype(rec) { _codcust = get(FPCUST_CODICE); _loaded = _codcust.full(); init(); } 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); } }