|
|
| version 1.16, 2001/11/23 12:56:37 | version 1.41, 2007/08/20 10:02:51 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser: @b hashfile parser class. | Parser: @b hashfile parser class. |
| Copyright (c) 2001 ArtLebedev Group (http://www.artlebedev.com) | Copyright (c) 2001-2005 ArtLebedev Group (http://www.artlebedev.com) |
| Author: Alexander Petrosyan <paf@design.ru> (http://paf.design.ru) | Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru) |
| $Id$ | |
| */ | */ |
| #include "pa_config_includes.h" | static const char * const IDENT="$Id$"; |
| #include "classes.h" | #include "classes.h" |
| #ifdef DB2 | |
| #include "pa_request.h" | #include "pa_request.h" |
| #include "pa_vmethod_frame.h" | |
| #include "pa_vhashfile.h" | #include "pa_vhashfile.h" |
| #include "pa_vhash.h" | #include "pa_vhash.h" |
| // defines | |
| #define HASH_CLASS_NAME "hashfile" | |
| // class | // class |
| class MHashfile : public Methoded { | class MHashfile : public Methoded { |
| public: // VStateless_class | public: // VStateless_class |
| Value *create_new_value(Pool& pool) { return new(pool) VHashfile(pool); } | Value *create_new_value(Pool& apool, HashStringValue&) { return new VHashfile(apool); } |
| public: | public: |
| MHashfile(Pool& pool); | MHashfile(); |
| public: // Methoded | public: // Methoded |
| bool used_directly() { return true; } | bool used_directly() { return true; } |
| }; | }; |
| // methods | // global variable |
| static void _open(Request& r, const String& method_name, MethodParams *params) { | DECLARE_CLASS_VAR(hashfile, new MHashfile, 0); |
| Pool& pool=r.pool(); | |
| VHashfile& self=*static_cast<VHashfile *>(r.self); | |
| self.assign( | |
| params->as_string(0, "DB_HOME must be string"), | |
| params->as_string(1, "filename must be string") | |
| ); | |
| } | |
| static void _transaction(Request& r, const String& method_name, MethodParams *params) { | // externs |
| Pool& pool=r.pool(); | |
| VHashfile& self=*static_cast<VHashfile *>(r.self); | extern String cycle_data_name; |
| // body code | |
| Value& body_code=params->as_junction(0, "body must be code"); | |
| // table | // defines for statics |
| DB_Table_ptr table_ptr=self.get_table_ptr(&method_name); | |
| // transaction | #define OPEN_DATA_NAME "HASHFILE-OPEN-DATA" |
| DB_Transaction transaction(pool, *table_ptr, self.current_transaction); | |
| // execute body | // methods |
| try { | |
| r.write_assign_lang(r.process(body_code)); | typedef Hash<const String::Body, bool> HashStringBool; |
| } catch(...) { // process/commit problem | |
| transaction.mark_to_rollback(); | static void _open(Request& r, MethodParams& params) { |
| HashStringBool* file_list=static_cast<HashStringBool*>(r.classes_conf.get(OPEN_DATA_NAME)); | |
| /*re*/throw; | if(!file_list) { |
| file_list=new HashStringBool(); | |
| r.classes_conf.put(OPEN_DATA_NAME, file_list); | |
| } | } |
| const String& file_spec=r.absolute(params.as_string(0, FILE_NAME_MUST_BE_STRING)); | |
| if(file_list->get(file_spec)) | |
| throw Exception(PARSER_RUNTIME, | |
| 0, | |
| "this hashfile is already opened, use existing variable"); | |
| file_list->put(file_spec, true); | |
| VHashfile& self=GET_SELF(r, VHashfile); | |
| self.open(file_spec); | |
| } | } |
| static void _hash(Request& r, const String& method_name, MethodParams *params) { | static void _hash(Request& r, MethodParams&) { |
| Pool& pool=r.pool(); | VHashfile& self=GET_SELF(r, VHashfile); |
| VHashfile& self=*static_cast<VHashfile *>(r.self); | |
| // write out result | // write out result |
| VHash& result=*new(pool) VHash(pool, *self.get_hash(&method_name)); | VHash& result=*new VHash(*self.get_hash()); |
| result.set_name(method_name); | |
| r.write_no_lang(result); | r.write_no_lang(result); |
| } | } |
| static void _cache(Request& r, const String& method_name, MethodParams *params) { | static void _delete(Request& r, MethodParams& params) { |
| Pool& pool=r.pool(); | VHashfile& self=GET_SELF(r, VHashfile); |
| VHashfile& self=*static_cast<VHashfile *>(r.self); | |
| // key, expires, body code | if(!params.count()) { |
| const String &key=params->as_string(0, "key must be string"); | // ^hashfile.delete[] asked to delete hashfile itself |
| time_t lifespan=(time_t)params->as_double(1, "lifespan must be number", r); | self.delete_files(); |
| Value& body_code=params->as_junction(2, "body must be code"); | return; |
| // transaction | |
| DB_Transaction transaction(pool, *self.get_table_ptr(&method_name), self.current_transaction); | |
| // execute body | |
| try { | |
| if(lifespan) { // 'lifespan' specified? try cached copy... | |
| if(String *cached_body=transaction.get(key, lifespan)) { // have cached copy? | |
| r.write_assign_lang(*cached_body); | |
| // happy with it | |
| return; | |
| } | |
| } else // 'lifespan'=0, forget cached copy | |
| transaction.remove(key); | |
| // save | |
| Autosave_marked_to_cancel_cache saved(self); | |
| // process | |
| Value& processed_body=r.process(body_code); | |
| r.write_assign_lang(processed_body); | |
| // put it to cache if 'lifespan' specified & never called ^delete[] | |
| if(lifespan && !self.marked_to_cancel_cache()) | |
| transaction.put(key, processed_body.as_string(), lifespan); | |
| } catch(...) { // process/commit problem | |
| transaction.mark_to_rollback(); | |
| /*re*/throw; | |
| } | } |
| // key | |
| const String &key=params.as_string(0, "key must be string"); | |
| // remove | |
| self.remove(key); | |
| } | |
| static void _clear(Request& r, MethodParams&) { | |
| VHashfile& self=GET_SELF(r, VHashfile); | |
| self.delete_files(); | |
| } | |
| #ifndef DOXYGEN | |
| struct Foreach_info { | |
| Request* r; | |
| const String* key_var_name; | |
| const String* value_var_name; | |
| Value* body_code; | |
| Value* delim_maybe_code; | |
| Value* var_context; | |
| VString* vkey; | |
| VString* vvalue; | |
| bool need_delim; | |
| }; | |
| #endif | |
| static bool one_foreach_cycle(const String::Body key, const String& value, void* ainfo) { | |
| Foreach_info& info=*static_cast<Foreach_info*>(ainfo); | |
| info.vkey->set_string(*new String(key, String::L_TAINTED)); | |
| info.vvalue->set_string(value); | |
| info.var_context->put_element(*info.var_context, *info.key_var_name, info.vkey, false); | |
| info.var_context->put_element(*info.var_context, *info.value_var_name, info.vvalue, false); | |
| StringOrValue sv_processed=info.r->process(*info.body_code); | |
| Request::Skip lskip=info.r->get_skip(); info.r->set_skip(Request::SKIP_NOTHING); | |
| const String* s_processed=sv_processed.get_string(); | |
| if(info.delim_maybe_code && s_processed && s_processed->length()) { // delimiter set and we have body | |
| if(info.need_delim) // need delim & iteration produced string? | |
| info.r->write_pass_lang(info.r->process(*info.delim_maybe_code)); | |
| info.need_delim=true; | |
| } | |
| info.r->write_pass_lang(sv_processed); | |
| return lskip==Request::SKIP_BREAK; | |
| } | } |
| static void _foreach(Request& r, MethodParams& params) { | |
| Temp_hash_value<const String::Body, void*> | |
| cycle_data_setter(r.classes_conf, cycle_data_name, /*any not null flag*/&r); | |
| static void _delete(Request& r, const String& method_name, MethodParams *params) { | VHashfile& self=GET_SELF(r, VHashfile); |
| Pool& pool=r.pool(); | |
| VHashfile& self=*static_cast<VHashfile *>(r.self); | Foreach_info info; |
| if(params->size()==0) | info.r=&r; |
| self.mark_to_cancel_cache(); | info.key_var_name=¶ms.as_string(0, "key-var name must be string"); |
| else { | info.value_var_name=¶ms.as_string(1, "value-var name must be string"); |
| // key | info.body_code=¶ms.as_junction(2, "body must be code"); |
| const String &key=params->as_string(0, "key must be string"); | info.delim_maybe_code=params.count()>3?params.get(3):0; |
| // remove | |
| self.get_table_ptr(&method_name)->remove(self.current_transaction, key); | info.var_context=info.body_code->get_junction()->wcontext; |
| } | info.vkey=new VString; |
| info.vvalue=new VString; | |
| info.need_delim=false; | |
| self.for_each(one_foreach_cycle, &info); | |
| } | } |
| static void _clear(Request& r, const String& method_name, MethodParams *) { | static bool one_cleanup_cycle(const String::Body, const String&, void*) { |
| Pool& pool=r.pool(); | return false; |
| VHashfile& self=*static_cast<VHashfile *>(r.self); | } |
| DB_Cursor cursor( | static void _cleanup(Request& r, MethodParams&) { |
| *self.get_table_ptr(&method_name), | VHashfile& self=GET_SELF(r, VHashfile); |
| self.current_transaction, &method_name); | |
| while(true) { | |
| if(!cursor.move(DB_NEXT)) | |
| break; | |
| cursor.remove(0/*flags*/); | self.for_each(one_cleanup_cycle, 0); |
| } | |
| } | } |
| static void _foreach(Request& r, const String& method_name, MethodParams *params) { | static void _release(Request& r, MethodParams&) { |
| Pool& pool=r.pool(); | VHashfile& self=GET_SELF(r, VHashfile); |
| VHashfile& self=*static_cast<VHashfile *>(r.self); | self.close(); |
| const String& key_var_name=params->as_string(0, "key-var name must be string"); | |
| const String& value_var_name=params->as_string(1, "value-var name must be string"); | |
| Value& body_code=params->as_junction(2, "body must be code"); | |
| Value *delim_maybe_code=params->size()>3?¶ms->get(3):0; | |
| bool need_delim=false; | |
| Value& var_context=*body_code.get_junction()->wcontext; | |
| VString& vkey=*new(pool) VString(pool); | |
| VString& vvalue=*new(pool) VString(pool); | |
| DB_Cursor cursor( | |
| *self.get_table_ptr(&method_name), | |
| self.current_transaction, &method_name); | |
| while(true) { | |
| String *key; | |
| String *data; | |
| if(!cursor.get(pool, key, data, DB_NEXT)) | |
| break; | |
| if(!key) | |
| continue; // expired | |
| vkey.set_string(*key); | |
| vvalue.set_string(*data); | |
| var_context.put_element(key_var_name, &vkey); | |
| var_context.put_element(value_var_name, &vvalue); | |
| Value& processed_body=r.process(body_code); | |
| if(delim_maybe_code) { // delimiter set? | |
| const String *string=processed_body.get_string(); | |
| if(need_delim && string && string->size()) // need delim & iteration produced string? | |
| r.write_pass_lang(r.process(*delim_maybe_code)); | |
| need_delim=true; | |
| } | |
| r.write_pass_lang(processed_body); | |
| } | |
| } | } |
| // constructor | // constructor |
| MHashfile::MHashfile(Pool& apool) : Methoded(apool) { | MHashfile::MHashfile(): Methoded("hashfile") { |
| set_name(*NEW String(pool(), HASH_CLASS_NAME)); | // ^hashfile::open[filename] |
| add_native_method("open", Method::CT_DYNAMIC, _open, 1, 1); | |
| // ^hashfile::open[db_home;filename] | // ^hashfile.hash[] |
| add_native_method("open", Method::CT_DYNAMIC, _open, 2, 2); | |
| // ^transaction{code} | |
| add_native_method("transaction", Method::CT_DYNAMIC, _transaction, 1, 1); | |
| // ^hash[] | |
| add_native_method("hash", Method::CT_DYNAMIC, _hash, 0, 0); | add_native_method("hash", Method::CT_DYNAMIC, _hash, 0, 0); |
| // ^cache[key](seconds){code} | |
| add_native_method("cache", Method::CT_DYNAMIC, _cache, 3, 3); | |
| // ^hashfile.delete[key] | // ^hashfile.delete[key] |
| add_native_method("delete", Method::CT_DYNAMIC, _delete, 0, 1); | add_native_method("delete", Method::CT_DYNAMIC, _delete, 0, 1); |
| // ^hashfile.clear[] | // ^hashfile.clear[] -- for backward compatibility. use .delete[] instead. |
| add_native_method("clear", Method::CT_DYNAMIC, _clear, 0, 0); | add_native_method("clear", Method::CT_DYNAMIC, _clear, 0, 0); |
| // ^hashfile.release[] | |
| add_native_method("release", Method::CT_DYNAMIC, _release, 0, 0); | |
| // ^hashfile.cleanup[] | |
| add_native_method("cleanup", Method::CT_DYNAMIC, _cleanup, 0, 0); | |
| add_native_method("defecate", Method::CT_DYNAMIC, _cleanup, 0, 0); | |
| // ^hashfile.foreach[key;value]{code}[delim] | // ^hashfile.foreach[key;value]{code}[delim] |
| add_native_method("foreach", Method::CT_DYNAMIC, _foreach, 2+1, 2+1+1); | add_native_method("foreach", Method::CT_DYNAMIC, _foreach, 2+1, 2+1+1); |
| } | } |
| // global variable | |
| Methoded *hashfile_base_class; | |
| #endif | |
| // creator | |
| Methoded *MHashfile_create(Pool& pool) { | |
| return | |
| #ifdef DB2 | |
| hashfile_base_class=new(pool) MHashfile(pool) | |
| #else | |
| 0 | |
| #endif | |
| ; | |
| } |