|
|
| version 1.110, 2001/09/06 06:11:13 | version 1.128, 2001/10/23 14:43:44 |
|---|---|
| Line 2 | Line 2 |
| Parser: @b table parser class. | Parser: @b table parser class. |
| Copyright (c) 2001 ArtLebedev Group (http://www.artlebedev.com) | Copyright (c) 2001 ArtLebedev Group (http://www.artlebedev.com) |
| Author: Alexander Petrosyan <paf@design.ru> (http://design.ru/paf) | Author: Alexander Petrosyan <paf@design.ru> (http://design.ru/paf) |
| $Id$ | |
| */ | */ |
| static const char *RCSId="$Id$"; | |
| #include "classes.h" | #include "classes.h" |
| #include "pa_config_includes.h" | #include "pa_config_includes.h" |
| Line 65 static void _set(Request& r, const Strin | Line 65 static void _set(Request& r, const Strin |
| while(i.has_next()) { | while(i.has_next()) { |
| Array& row=*new(pool) Array(pool); | Array& row=*new(pool) Array(pool); |
| const String& string=*i.next_string(); | const String& string=*i.next_string(); |
| // remove empty lines | // remove comment lines |
| if(!string.size()) | if(!string.size()) |
| continue; | continue; |
| Line 80 static void _set(Request& r, const Strin | Line 80 static void _set(Request& r, const Strin |
| static void _load(Request& r, const String& method_name, MethodParams *params) { | static void _load(Request& r, const String& method_name, MethodParams *params) { |
| Pool& pool=r.pool(); | Pool& pool=r.pool(); |
| // filename is last parameter | // filename is last parameter |
| Value& vfilename=params->as_no_junction(params->size()-1, | Value& vfile_name=params->as_no_junction(params->size()-1, |
| "file name must not be code"); | "file name must not be code"); |
| // loading text | // loading text |
| char *data=file_read_text(pool, r.absolute(vfilename.as_string())); | char *data=file_read_text(pool, r.absolute(vfile_name.as_string())); |
| // parse columns | // parse columns |
| Array *columns; | Array *columns; |
| Line 110 static void _load(Request& r, const Stri | Line 110 static void _load(Request& r, const Stri |
| Table& table=*new(pool) Table(pool, &method_name, columns); | Table& table=*new(pool) Table(pool, &method_name, columns); |
| char *row_chars; | char *row_chars; |
| while(row_chars=getrow(&data)) { | while(row_chars=getrow(&data)) { |
| if(!*row_chars) // remove empty lines | // remove empty&comment lines |
| if(!*row_chars || *row_chars == '#') | |
| continue; | continue; |
| Array *row=new(pool) Array(pool); | Array *row=new(pool) Array(pool); |
| while(char *cell_chars=lsplit(&row_chars, '\t')) { | while(char *cell_chars=lsplit(&row_chars, '\t')) { |
| Line 128 static void _load(Request& r, const Stri | Line 129 static void _load(Request& r, const Stri |
| static_cast<VTable *>(r.self)->set_table(table); | static_cast<VTable *>(r.self)->set_table(table); |
| } | } |
| static Table *fill_month_days(Request& r, | |
| const String& method_name, MethodParams *params, bool rus){ | |
| Pool& pool=r.pool(); | |
| Table *result=new(pool) Table(pool, &method_name, 0/*&columns*/); | |
| int year=params->as_int(1, r); | |
| int month=max(1, min(params->as_int(2, r), 12)) -1; | |
| tm tmIn={0, 0, 0, 1, month, year-1900}; | |
| time_t t=mktime(&tmIn); | |
| if(t<0) | |
| PTHROW(0, 0, | |
| &method_name, | |
| "invalid date"); | |
| tm *tmOut=localtime(&t); | |
| int weekDay1=tmOut->tm_wday; | |
| if(rus) | |
| weekDay1=weekDay1?weekDay1-1:6; //sunday last | |
| int monthDays=getMonthDays(year, month); | |
| for(int _day=1-weekDay1; _day<=monthDays;) { | |
| Array& row=*new(pool) Array(pool, 7); | |
| for(int wday=0; wday<7; wday++, _day++) { | |
| String *cell=new(pool) String(pool); | |
| if(_day>=1 && _day<=monthDays) { | |
| char *buf=(char *)pool.malloc(2+1); | |
| cell->APPEND_CLEAN(buf, sprintf(buf, "%02d", _day), | |
| method_name.origin().file, method_name.origin().line); | |
| } | |
| row+=cell; | |
| } | |
| *result+=&row; | |
| } | |
| return result; | |
| } | |
| static Table *fill_week_days(Request& r, | |
| const String& method_name, MethodParams *params, bool rus){ | |
| Pool& pool=r.pool(); | |
| Array& columns=*new(pool) Array(pool, 4); | |
| columns+=new(pool) String(pool, "year"); | |
| columns+=new(pool) String(pool, "month"); | |
| columns+=new(pool) String(pool, "day"); | |
| columns+=new(pool) String(pool, "weekday"); | |
| Table *result=new(pool) Table(pool, &method_name, &columns); | |
| int year=params->as_int(1, r); | |
| int month=max(1, min(params->as_int(2, r), 12)) -1; | |
| int day=params->as_int(3, r); | |
| tm tmIn={0, 0, 18, day, month, year-1900}; | |
| time_t t=mktime(&tmIn); | |
| if(t<0) | |
| PTHROW(0, 0, | |
| &method_name, | |
| "invalid date"); | |
| tm *tmOut=localtime(&t); | |
| int baseWeekDay=tmOut->tm_wday; | |
| if(rus) | |
| baseWeekDay=baseWeekDay?baseWeekDay-1:6; //sunday last | |
| t-=baseWeekDay*SECS_PER_DAY; | |
| for(int curWeekDay=0; curWeekDay<7; curWeekDay++, t+=SECS_PER_DAY) { | |
| tm *tmOut=localtime(&t); | |
| Array& row=*new(pool) Array(pool, 4); | |
| #define WDFILL(size, value) { \ | |
| char *buf=(char *)pool.malloc(size+1); \ | |
| String *cell=new(pool) String(pool); \ | |
| cell->APPEND_CLEAN(buf, sprintf(buf, "%0"#size"d", value), \ | |
| method_name.origin().file, \ | |
| method_name.origin().line); \ | |
| row+=cell; \ | |
| } | |
| WDFILL(4, 1900+tmOut->tm_year); | |
| WDFILL(2, 1+tmOut->tm_mon); | |
| WDFILL(2, tmOut->tm_mday); | |
| WDFILL(2, tmOut->tm_wday); | |
| *result+=&row; | |
| } | |
| return result; | |
| } | |
| static void _calendar(Request& r, const String& method_name, MethodParams *params) { | |
| Pool& pool=r.pool(); | |
| const String& what=params->as_string(0, "format must be strig"); | |
| bool rus=false; | |
| if(what=="rus") | |
| rus=true; | |
| else if(what=="eng") | |
| rus=false; | |
| else | |
| PTHROW(0, 0, | |
| &what, | |
| "must be rus|eng"); | |
| Table *result=0; | |
| if(params->size()==1+2) | |
| result=fill_month_days(r, method_name, params, rus); | |
| else // 1+3 | |
| result=fill_week_days(r, method_name, params, rus); | |
| // replace any previous table value | |
| static_cast<VTable *>(r.self)->set_table(*result); | |
| } | |
| static void _save(Request& r, const String& method_name, MethodParams *params) { | static void _save(Request& r, const String& method_name, MethodParams *params) { |
| Pool& pool=r.pool(); | Pool& pool=r.pool(); |
| Value& vtable_name=params->as_no_junction(params->size()-1, | Value& vfile_name=params->as_no_junction(params->size()-1, |
| "file name must not be code"); | "file name must not be code"); |
| Table& table=static_cast<VTable *>(r.self)->table(); | Table& table=static_cast<VTable *>(r.self)->table(); |
| Line 286 static void _save(Request& r, const Stri | Line 173 static void _save(Request& r, const Stri |
| } | } |
| // write | // write |
| file_write(pool, r.absolute(vtable_name.as_string()), | file_write(pool, r.absolute(vfile_name.as_string()), |
| sdata.cstr(), sdata.size(), true); | sdata.cstr(), sdata.size(), true); |
| } | } |
| Line 320 static void _offset(Request& r, const St | Line 207 static void _offset(Request& r, const St |
| static void _menu(Request& r, const String& method_name, MethodParams *params) { | static void _menu(Request& r, const String& method_name, MethodParams *params) { |
| Value& body_code=params->as_junction(0, "body must be code"); | Value& body_code=params->as_junction(0, "body must be code"); |
| Value *delim_code=params->size()==2?¶ms->get(1):0; | Value *delim_maybe_code=params->size()>1?¶ms->get(1):0; |
| VTable& vtable=*static_cast<VTable *>(r.self); | VTable& vtable=*static_cast<VTable *>(r.self); |
| Table& table=vtable.table(); | Table& table=vtable.table(); |
| Line 331 static void _menu(Request& r, const Stri | Line 218 static void _menu(Request& r, const Stri |
| table.set_current(row); | table.set_current(row); |
| Value& processed_body=r.process(body_code); | Value& processed_body=r.process(body_code); |
| if(delim_code) { // delimiter set? | if(delim_maybe_code) { // delimiter set? |
| const String *string=processed_body.get_string(); | const String *string=processed_body.get_string(); |
| if(need_delim && string && string->size()) // need delim & iteration produced string? | if(need_delim && string && string->size()) // need delim & iteration produced string? |
| r.write_pass_lang(r.process(*delim_code)); | r.write_pass_lang(r.process(*delim_maybe_code)); |
| need_delim=true; | need_delim=true; |
| } | } |
| r.write_pass_lang(processed_body); | r.write_pass_lang(processed_body); |
| Line 342 static void _menu(Request& r, const Stri | Line 229 static void _menu(Request& r, const Stri |
| table.set_current(saved_current); | table.set_current(saved_current); |
| } | } |
| static void _empty(Request& r, const String& method_name, MethodParams *params) { | |
| Pool& pool=r.pool(); | |
| Table& table=static_cast<VTable *>(r.self)->table(); | |
| r.write_no_lang(*new(pool) VBool(pool, table.size()==0)); | |
| } | |
| #ifndef DOXYGEN | #ifndef DOXYGEN |
| struct Row_info { | struct Row_info { |
| Table *table; | Table *table; |
| Line 364 static void table_row_to_hash(Array::Ite | Line 244 static void table_row_to_hash(Array::Ite |
| if(ri.key_field<row.size()) { | if(ri.key_field<row.size()) { |
| VHash& result=*new(pool) VHash(pool); | VHash& result=*new(pool) VHash(pool); |
| Hash& hash=*result.get_hash(); | Hash& hash=*result.get_hash(0); |
| for(int i=0; i<ri.value_fields->size(); i++) { | for(int i=0; i<ri.value_fields->size(); i++) { |
| int value_field=ri.value_fields->get_int(i); | int value_field=ri.value_fields->get_int(i); |
| if(value_field<row.size()) | if(value_field<row.size()) |
| Line 378 static void table_row_to_hash(Array::Ite | Line 258 static void table_row_to_hash(Array::Ite |
| } | } |
| static void _hash(Request& r, const String& method_name, MethodParams *params) { | static void _hash(Request& r, const String& method_name, MethodParams *params) { |
| Pool& pool=r.pool(); | Pool& pool=r.pool(); |
| Table& table=static_cast<VTable *>(r.self)->table(); | Table& self_table=static_cast<VTable *>(r.self)->table(); |
| Value& result=*new(pool) VHash(pool); | Value& result=*new(pool) VHash(pool); |
| if(const Array *columns=table.columns()) | if(const Array *columns=self_table.columns()) |
| if(columns->size()>1) { | if(columns->size()>1) { |
| const String& key_field_name=params->as_no_junction(0, | const String& key_field_name=params->as_no_junction(0, |
| "key field name must not be code").as_string(); | "key field name must not be code").as_string(); |
| int key_field=table.column_name2index(key_field_name, true); | int key_field=self_table.column_name2index(key_field_name, true); |
| int value_fields_count=params->size()-1; | |
| bool value_fields_by_params=value_fields_count!=0; | Array value_fields(pool); |
| if(!value_fields_by_params) | if(params->size()>1) { |
| value_fields_count=columns->size()-1; // all columns except key | Value& value_fields_param=params->as_no_junction(1, "value field(s) must not be code"); |
| Array value_fields(pool, value_fields_count); | if(value_fields_param.is_string()) { |
| if(value_fields_by_params) { | value_fields+=self_table.column_name2index(value_fields_param.as_string(), true); |
| for(int i=1; i<params->size(); i++) { | } else if(Table *value_fields_table=value_fields_param.get_table()) { |
| const String& value_field_name=params->as_no_junction(i, | for(int i=0; i<value_fields_table->size(); i++) { |
| "value field name must not be code").as_string(); | const String& value_field_name= |
| value_fields+=table.column_name2index(value_field_name, true); | *static_cast<Array *>(value_fields_table->get(i))->get_string(0); |
| } | value_fields+=self_table.column_name2index(value_field_name, true); |
| } else { // by all columns except key | } |
| } else | |
| throw Exception(0, 0, | |
| &method_name, | |
| "value field(s) must be string or self_table" | |
| ); | |
| } else { // by all columns, including key | |
| for(int i=0; i<columns->size(); i++) | for(int i=0; i<columns->size(); i++) |
| if(i!=key_field) | value_fields+=i; |
| value_fields+=i; | |
| } | } |
| // integers: key_field & value_fields | // integers: key_field & value_fields |
| Row_info row_info={&table, key_field, &value_fields, result.get_hash()}; | Row_info row_info={&self_table, key_field, &value_fields, result.get_hash(0)}; |
| table.for_each(table_row_to_hash, &row_info); | self_table.for_each(table_row_to_hash, &row_info); |
| } | } |
| result.set_name(method_name); | result.set_name(method_name); |
| r.write_no_lang(result); | r.write_no_lang(result); |
| Line 440 static void _sort(Request& r, const Stri | Line 324 static void _sort(Request& r, const Stri |
| Pool& pool=r.pool(); | Pool& pool=r.pool(); |
| Value& key_maker=params->as_junction(0, "key-maker must be code"); | Value& key_maker=params->as_junction(0, "key-maker must be code"); |
| bool reverse=params->size()==2/*..[desc|asc|]*/? | bool reverse=params->size()>1/*..[desc|asc|]*/? |
| reverse=params->as_no_junction(1, "order must not be code").as_string()=="desc": | reverse=params->as_no_junction(1, "order must not be code").as_string()=="desc": |
| false; // default=asc | false; // default=asc |
| Line 477 static void _sort(Request& r, const Stri | Line 361 static void _sort(Request& r, const Stri |
| for(i=0; i<old_table.size(); i++) | for(i=0; i<old_table.size(); i++) |
| new_table+=seq[reverse?old_table.size()-1-i:i].row; | new_table+=seq[reverse?old_table.size()-1-i:i].row; |
| r.write_no_lang(*new(pool) VTable(pool, &new_table)); | // replace any previous table value |
| static_cast<VTable *>(r.self)->set_table(new_table); | |
| } | } |
| static void _locate(Request& r, const String& method_name, MethodParams *params) { | static void _locate(Request& r, const String& method_name, MethodParams *params) { |
| Line 486 static void _locate(Request& r, const St | Line 371 static void _locate(Request& r, const St |
| VTable& vtable=*static_cast<VTable *>(r.self); | VTable& vtable=*static_cast<VTable *>(r.self); |
| Table& table=vtable.table(); | Table& table=vtable.table(); |
| Value& result=*new(pool) VBool(pool, table.locate( | Value& result=*new(pool) VBool(pool, table.locate( |
| params->get(0).as_string(), | params->as_string(0, "column name must be string"), |
| params->get(1).as_string())); | params->as_string(1, "value must be string") |
| )); | |
| result.set_name(method_name); | result.set_name(method_name); |
| r.write_no_lang(result); | r.write_no_lang(result); |
| } | } |
| Line 504 static void _flip(Request& r, const Stri | Line 390 static void _flip(Request& r, const Stri |
| Array& new_row=*new(pool) Array(pool, old_table.size()); | Array& new_row=*new(pool) Array(pool, old_table.size()); |
| for(int i=0; i<old_table.size(); i++) { | for(int i=0; i<old_table.size(); i++) { |
| const Array& old_row=old_table.at(i); | const Array& old_row=old_table.at(i); |
| new_row+=column<old_row.size()?old_row.get(column):empty_string; | new_row+=column<old_row.size()?old_row.get(column):new(pool) String(pool); |
| } | } |
| new_table+=&new_row; | new_table+=&new_row; |
| } | } |
| Line 531 static void _join(Request& r, const Stri | Line 417 static void _join(Request& r, const Stri |
| Table *maybe_src=params->as_no_junction(0, "table ref must not be code").get_table(); | Table *maybe_src=params->as_no_junction(0, "table ref must not be code").get_table(); |
| if(!maybe_src) | if(!maybe_src) |
| PTHROW(0, 0, | throw Exception(0, 0, |
| &method_name, | &method_name, |
| "source is not a table"); | "source is not a table"); |
| Table& src=*maybe_src; | Table& src=*maybe_src; |
| Table& dest=static_cast<VTable *>(r.self)->table(); | Table& dest=static_cast<VTable *>(r.self)->table(); |
| if(&src == &dest) | if(&src == &dest) |
| PTHROW(0, 0, | throw Exception(0, 0, |
| &method_name, | &method_name, |
| "source and destination are same table"); | "source and destination are same table"); |
| Line 610 static void _sql(Request& r, const Strin | Line 496 static void _sql(Request& r, const Strin |
| Pool& pool=r.pool(); | Pool& pool=r.pool(); |
| if(!r.connection) | if(!r.connection) |
| PTHROW(0, 0, | throw Exception(0, 0, |
| &method_name, | &method_name, |
| "without connect"); | "without connect"); |
| Line 621 static void _sql(Request& r, const Strin | Line 507 static void _sql(Request& r, const Strin |
| if(params->size()>1) { | if(params->size()>1) { |
| Value& voptions=params->as_no_junction(1, "options must be hash, not code"); | Value& voptions=params->as_no_junction(1, "options must be hash, not code"); |
| if(voptions.is_defined()) | if(voptions.is_defined()) |
| if(Hash *options=voptions.get_hash()) { | if(Hash *options=voptions.get_hash(&method_name)) { |
| if(Value *vlimit=(Value *)options->get(*sql_limit_name)) | if(Value *vlimit=(Value *)options->get(*sql_limit_name)) |
| limit=(ulong)r.process(*vlimit).as_double(); | limit=(ulong)r.process(*vlimit).as_double(); |
| if(Value *voffset=(Value *)options->get(*sql_offset_name)) | if(Value *voffset=(Value *)options->get(*sql_offset_name)) |
| offset=(ulong)r.process(*voffset).as_double(); | offset=(ulong)r.process(*voffset).as_double(); |
| } else | } else |
| PTHROW(0, 0, | throw Exception(0, 0, |
| &method_name, | &method_name, |
| "options must be hash"); | "options must be hash"); |
| } | } |
| Line 638 static void _sql(Request& r, const Strin | Line 524 static void _sql(Request& r, const Strin |
| statement_string.cstr(String::UL_UNSPECIFIED, r.connection); | statement_string.cstr(String::UL_UNSPECIFIED, r.connection); |
| Table_sql_event_handlers handlers(pool, method_name, | Table_sql_event_handlers handlers(pool, method_name, |
| statement_string, statement_cstr); | statement_string, statement_cstr); |
| bool need_rethrow=false; Exception rethrow_me; | try { |
| PTRY { | |
| r.connection->query( | r.connection->query( |
| statement_cstr, offset, limit, | statement_cstr, offset, limit, |
| handlers); | handlers); |
| } catch(const Exception& e) { // query problem | |
| // more specific source [were url] | |
| throw Exception(e.type(), e.code(), | |
| &statement_string, | |
| "%s", e.comment()); | |
| } | } |
| PCATCH(e) { // query problem | |
| rethrow_me=e; need_rethrow=true; | |
| } | |
| PEND_CATCH | |
| if(need_rethrow) | |
| PTHROW(rethrow_me.type(), rethrow_me.code(), | |
| &statement_string, // setting more specific source [were url] | |
| rethrow_me.comment()); | |
| Table *result= | Table *result= |
| handlers.table?handlers.table: // query resulted in table? return it | handlers.table?handlers.table: // query resulted in table? return it |
| Line 696 MTable::MTable(Pool& apool) : Methoded(a | Line 578 MTable::MTable(Pool& apool) : Methoded(a |
| // ^table:load[nameless;file] | // ^table:load[nameless;file] |
| add_native_method("load", Method::CT_DYNAMIC, _load, 1, 2); | add_native_method("load", Method::CT_DYNAMIC, _load, 1, 2); |
| // ^table:calendar[month|montheng;year;month] | |
| // ^table:calendar[week|weekeng;year;month;day] | |
| add_native_method("calendar", Method::CT_DYNAMIC, _calendar, 3, 4); | |
| // ^table.save[file] | // ^table.save[file] |
| // ^table.save[nameless;file] | // ^table.save[nameless;file] |
| add_native_method("save", Method::CT_DYNAMIC, _save, 1, 2); | add_native_method("save", Method::CT_DYNAMIC, _save, 1, 2); |
| Line 718 MTable::MTable(Pool& apool) : Methoded(a | Line 596 MTable::MTable(Pool& apool) : Methoded(a |
| // ^table.menu{code}[delim] | // ^table.menu{code}[delim] |
| add_native_method("menu", Method::CT_DYNAMIC, _menu, 1, 2); | add_native_method("menu", Method::CT_DYNAMIC, _menu, 1, 2); |
| // ^table.empty[] | |
| add_native_method("empty", Method::CT_DYNAMIC, _empty, 0, 0); | |
| // ^table:hash[key field name] | // ^table:hash[key field name] |
| // ^table:hash[key field name][value field name;...] | // ^table:hash[key field name][value field name(s) string/table] |
| add_native_method("hash", Method::CT_DYNAMIC, _hash, 1, 1000); | add_native_method("hash", Method::CT_DYNAMIC, _hash, 1, 2); |
| // ^table.sort{string-key-maker} ^table.sort{string-key-maker}[desc|asc] | // ^table.sort{string-key-maker} ^table.sort{string-key-maker}[desc|asc] |
| // ^table.sort(numeric-key-maker) ^table.sort(numeric-key-maker)[desc|asc] | // ^table.sort(numeric-key-maker) ^table.sort(numeric-key-maker)[desc|asc] |