Annotation of parser3/src/classes/array.C, revision 1.9

1.1       moko        1: /** @file
                      2:        Parser: @b array parser class.
                      3: 
                      4:        Copyright (c) 2001-2023 Art. Lebedev Studio (http://www.artlebedev.com)
                      5:        Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru>
                      6: */
                      7: 
                      8: #include "classes.h"
                      9: #include "pa_vmethod_frame.h"
                     10: 
                     11: #include "pa_request.h"
                     12: #include "pa_charsets.h"
                     13: #include "pa_varray.h"
                     14: #include "pa_vvoid.h"
                     15: #include "pa_sql_connection.h"
                     16: #include "pa_vtable.h"
                     17: #include "pa_vbool.h"
                     18: #include "pa_vmethod_frame.h"
                     19: 
1.9     ! moko       20: volatile const char * IDENT_ARRAY_C="$Id: array.C,v 1.8 2024/09/22 13:56:09 moko Exp $";
1.1       moko       21: 
                     22: // class
                     23: 
                     24: class MArray: public Methoded {
                     25: public: // VStateless_class
                     26:        Value* create_new_value(Pool&) { return new VArray; }
                     27: 
                     28: public:
                     29:        MArray();
                     30: };
                     31: 
                     32: // global variable
                     33: 
                     34: DECLARE_CLASS_VAR(array, new MArray);
                     35: 
1.5       moko       36: const char* const PARAM_ARRAY_OR_HASH = "param must be array or hash";
1.7       moko       37: const char* const PARAM_INDEX = "index must be integer";
1.5       moko       38: 
1.1       moko       39: // methods
                     40: 
                     41: static void _create_or_add(Request& r, MethodParams& params) {
                     42:        if(params.count()) {
1.5       moko       43:                Value& vsrc=params.as_no_junction(0, PARAM_ARRAY_OR_HASH);
1.1       moko       44:                VArray& self=GET_SELF(r, VArray);
                     45:                ArrayValue& self_array=self.array();
                     46: 
1.3       moko       47:                if(VArray* src=dynamic_cast<VArray*>(&vsrc)) {
1.6       moko       48:                        if(src==&self)
                     49:                                throw Exception(PARSER_RUNTIME, 0, "source and destination are the same array");
1.5       moko       50:                        self_array.append(src->array());
1.1       moko       51:                } else {
                     52:                        HashStringValue* src_hash=vsrc.get_hash();
1.5       moko       53:                        if(!src_hash)
                     54:                                return;
                     55:                        for(HashStringValue::Iterator i(*src_hash); i; i.next()){
1.7       moko       56:                                self_array.put(VArray::index(i.key()), i.value());
1.5       moko       57:                        }
1.1       moko       58:                }
1.4       moko       59:                self.invalidate();
1.1       moko       60:        }
                     61: }
                     62: 
1.6       moko       63: static ArrayValue::Action_options get_action_options(Request& r, MethodParams& params, size_t options_index) {
                     64:        ArrayValue::Action_options result;
                     65:        if(params.count() <= options_index)
                     66:                return result;
                     67: 
                     68:        HashStringValue* options=params.as_hash(options_index);
                     69:        if(!options)
                     70:                return result;
                     71: 
                     72:        result.defined=true;
                     73:        int valid_options=0;
                     74: 
                     75:        if(Value* voffset=options->get(sql_offset_name)) {
                     76:                valid_options++;
1.8       moko       77:                int offset=r.process(*voffset).as_int();
                     78:                result.offset=offset < 0 ? 0 : offset;
1.6       moko       79:        }
                     80:        if(Value* vlimit=options->get(sql_limit_name)) {
                     81:                valid_options++;
1.8       moko       82:                int limit=r.process(*vlimit).as_int();
                     83:                result.limit=limit < 0 ? 0: limit;
1.6       moko       84:        }
                     85: 
                     86:        if(valid_options!=options->count())
                     87:                throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
                     88: 
                     89:        return result;
                     90: }
                     91: 
                     92: static void _join(Request& r, MethodParams& params) {
1.7       moko       93:        Value& vsrc=params.as_no_junction(0, PARAM_ARRAY_OR_HASH);
1.6       moko       94:        ArrayValue::Action_options o=get_action_options(r, params, 1);
                     95: 
                     96:        VArray& self=GET_SELF(r, VArray);
                     97:        ArrayValue& self_array=self.array();
                     98: 
                     99:        if(VArray* src=dynamic_cast<VArray*>(&vsrc)) {
                    100:                if(src==&self)
                    101:                        throw Exception(PARSER_RUNTIME, 0, "source and destination are the same array");
                    102: 
                    103:                if(o.defined){
                    104:                        for(ArrayValue::Iterator i(src->array()); i; i.next()){
                    105:                                if(i.value()){
                    106:                                        if(o.offset > 0){
                    107:                                                o.offset--;
                    108:                                                continue;
                    109:                                        }
                    110:                                        if(o.limit-- == 0)
                    111:                                                break;
                    112:                                        self_array+=i.value();
                    113:                                }
                    114:                        }
                    115:                } else {
                    116:                        for(ArrayValue::Iterator i(src->array()); i; i.next()){
                    117:                                if(i.value())
                    118:                                        self_array+=i.value();
                    119:                        }
                    120:                }
1.7       moko      121:        } else {
                    122:                HashStringValue* src_hash=vsrc.get_hash();
                    123:                if(!src_hash)
                    124:                        return;
                    125:                if(o.defined){
                    126:                        for(HashStringValue::Iterator i(*src_hash); i; i.next()){
                    127:                                if(o.offset > 0){
                    128:                                        o.offset--;
                    129:                                        continue;
                    130:                                }
                    131:                                if(o.limit-- == 0)
                    132:                                        break;
                    133:                                self_array+=i.value();
                    134:                        }
                    135:                } else {
                    136:                        for(HashStringValue::Iterator i(*src_hash); i; i.next()){
                    137:                                self_array+=i.value();
                    138:                        }
                    139:                }
                    140:        }
                    141:        self.invalidate();
1.6       moko      142: }
                    143: 
1.1       moko      144: static void _sql(Request& r, MethodParams& params) {}
                    145: 
                    146: 
1.9     ! moko      147: static void mid(Request& r, ArrayValue::Action_options o) {
        !           148:        ArrayValue& array=GET_SELF(r, VArray).array();
        !           149:        if(o.limit>0){
        !           150:                VArray *result=new VArray;
        !           151:                ArrayValue& result_array=result->array();
        !           152:                for(ArrayValue::Iterator i(array); i; i.next()){
        !           153:                        if(i.value()){
        !           154:                                if(o.offset > 0){
        !           155:                                        o.offset--;
        !           156:                                        continue;
        !           157:                                }
        !           158:                                if(o.limit-- == 0)
        !           159:                                        break;
        !           160:                                result_array+=i.value();
        !           161:                        }
        !           162:                }
        !           163:                r.write(*result);
        !           164:        } else {
        !           165:                r.write(*new VArray);
        !           166:        }
        !           167: }
1.1       moko      168: 
1.9     ! moko      169: static void _left(Request& r, MethodParams& params) {
        !           170:        int sn=params.as_int(0, "n must be int", r);
        !           171:        mid(r, ArrayValue::Action_options(0, sn < 0 ? 0 : sn));
        !           172: }
        !           173: 
        !           174: static void _right(Request& r, MethodParams& params) {
        !           175:        int sn=params.as_int(0, "n must be int", r);
        !           176: 
        !           177:        if(sn>0){
        !           178:                size_t used=GET_SELF(r, VArray).array().used();
        !           179:                if(sn<used){
        !           180:                        mid(r, ArrayValue::Action_options(used-sn, sn));
        !           181:                } else {
        !           182:                        mid(r, ArrayValue::Action_options());
        !           183:                }
        !           184:        } else {
        !           185:                mid(r, ArrayValue::Action_options(0, 0));
        !           186:        }
        !           187: }
        !           188: 
        !           189: static void _mid(Request& r, MethodParams& params) {
        !           190:        const String& string=GET_SELF(r, VString).string();
        !           191: 
        !           192:        int begin=params.as_int(0, "p must be int", r);
        !           193:        if(begin<0)
        !           194:                throw Exception(PARSER_RUNTIME, 0,  "p(%d) must be >=0", begin);
        !           195: 
        !           196:        size_t end;
        !           197:        size_t length=0;
1.1       moko      198: 
1.9     ! moko      199:        if(params.count()>1) {
        !           200:                int n=params.as_int(1, "n must be int", r);
        !           201:                if(n<0)
        !           202:                        throw Exception(PARSER_RUNTIME, 0, "n(%d) must be >=0", n);
        !           203:                mid(r, ArrayValue::Action_options(begin, n));
        !           204:        } else {
        !           205:                mid(r, ArrayValue::Action_options(begin));
        !           206:        }
        !           207: }
1.1       moko      208: 
                    209: static void _keys(Request& r, MethodParams& params) {
                    210:        const String* keys_column_name;
                    211:        if(params.count()>0)
                    212:                keys_column_name=&params.as_string(0, COLUMN_NAME_MUST_BE_STRING);
                    213:        else 
                    214:                keys_column_name=new String("key");
                    215: 
                    216:        Table::columns_type columns(new ArrayString(1));
                    217:        *columns+=keys_column_name;
                    218:        Table* table=new Table(columns);
                    219: 
                    220:        ArrayValue& array=GET_SELF(r, VArray).array();
                    221:        for(ArrayValue::Iterator i(array); i; i.next()){
                    222:                if(i.value()){
                    223:                        Table::element_type row(new ArrayString(1));
                    224:                        *row+=new String(i.key(), String::L_TAINTED);
                    225:                        *table+=row;
                    226:                }
                    227:        }
                    228: 
                    229:        r.write(*new VTable(table));
                    230: }
                    231: 
1.5       moko      232: static void _count(Request& r, MethodParams& params) {
                    233:        ArrayValue& array=GET_SELF(r, VArray).array();
                    234:        if(params.count()>0){
                    235:                const String& what=params.as_string(0, PARAMETER_MUST_BE_STRING);
                    236:                if(!what.is_empty()){
                    237:                        if(what != "all")
                    238:                                throw Exception(PARSER_RUNTIME, &what, "param must be empty or 'all'");
                    239:                        return r.write(*new VInt(array.count()));
                    240:                }
                    241:        }
                    242:        r.write(*new VInt(array.used()));
1.1       moko      243: }
                    244: 
1.2       moko      245: static void _append(Request& r, MethodParams& params) {
                    246:        VArray& self=GET_SELF(r, VArray);
                    247:        ArrayValue& array=self.array();
                    248: 
                    249:        int count=params.count();
                    250: 
                    251:        for(int i=0; i<count; i++){
                    252:                array+=&r.process(params[i]);
                    253:        }
1.4       moko      254:        self.invalidate();
1.2       moko      255: }
                    256: 
                    257: static void _insert(Request& r, MethodParams& params) {
                    258:        VArray& self=GET_SELF(r, VArray);
                    259:        ArrayValue& array=self.array();
                    260: 
                    261:        int count=params.count();
1.7       moko      262:        size_t index=VArray::index(params.as_int(0, PARAM_INDEX, r));
1.2       moko      263: 
                    264:        for(int i=1; i<count; i++){
1.8       moko      265:                array.insert(index++, &r.process(params[i]));
1.2       moko      266:        }
1.4       moko      267:        self.invalidate();
1.2       moko      268: }
                    269: 
1.1       moko      270: static void _delete(Request& r, MethodParams& params) {
1.7       moko      271:        VArray& self=GET_SELF(r, VArray);
1.1       moko      272:        if(params.count()>0)
1.7       moko      273:                self.array().clear(VArray::index(params.as_int(0, PARAM_INDEX, r)));
1.1       moko      274:        else
1.7       moko      275:                self.array().clear();
                    276:        self.invalidate();
                    277: }
                    278: 
                    279: static void _remove(Request& r, MethodParams& params) {
                    280:        VArray& self=GET_SELF(r, VArray);
                    281:        self.array().remove(VArray::index(params.as_int(0, PARAM_INDEX, r)));
                    282:        self.invalidate();
1.1       moko      283: }
                    284: 
                    285: static void _contains(Request& r, MethodParams& params) {
                    286:        VArray& self=GET_SELF(r, VArray);
1.7       moko      287:        bool result=self.contains(VArray::index(params.as_int(0, PARAM_INDEX, r)));
1.1       moko      288:        r.write(VBool::get(result));
                    289: }
                    290: 
1.5       moko      291: static void _for(Request& r, MethodParams& params) {
                    292:        InCycle temp(r);
                    293: 
1.7       moko      294:        const String* key_var_name=&params.as_string(0, "key-var name must be string");
                    295:        const String* value_var_name=&params.as_string(1, "value-var name must be string");
                    296:        Value* body_code=&params.as_junction(2, "body must be code");
                    297:        Value* delim_maybe_code=params.count()>3 ? &params[3] : 0;
1.5       moko      298:        Value& caller=*r.get_method_frame()->caller();
                    299: 
1.7       moko      300:        if(key_var_name->is_empty()) key_var_name=0;
1.5       moko      301:        if(value_var_name->is_empty()) value_var_name=0;
                    302: 
                    303:        ArrayValue& array=GET_SELF(r, VArray).array();
                    304: 
                    305:        if(delim_maybe_code){ // delimiter set
                    306:                bool need_delim=false;
                    307:                for(ArrayValue::Iterator i(array); i; i.next()){
1.7       moko      308:                        if(key_var_name){
                    309:                                VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
                    310:                                r.put_element(caller, *key_var_name, vkey);
                    311:                        }
                    312: 
1.5       moko      313:                        if(value_var_name)
                    314:                                r.put_element(caller, *value_var_name, i.value() ? i.value() : VVoid::get());
                    315: 
                    316:                        Value& sv_processed=r.process(*body_code);
                    317:                        TempSkip4Delimiter skip(r);
                    318: 
                    319:                        const String* s_processed=sv_processed.get_string();
                    320:                        if(s_processed && !s_processed->is_empty()) { // we have body
                    321:                                if(need_delim) // need delim & iteration produced string?
                    322:                                        r.write(r.process(*delim_maybe_code));
                    323:                                else
                    324:                                        need_delim=true;
                    325:                        }
                    326: 
                    327:                        r.write(sv_processed);
                    328: 
                    329:                        if(skip.check_break())
                    330:                                break;
                    331:                }
                    332:        } else {
                    333:                for(ArrayValue::Iterator i(array); i; i.next()){
1.7       moko      334:                        if(key_var_name){
                    335:                                VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
                    336:                                r.put_element(caller, *key_var_name, vkey);
                    337:                        }
                    338: 
1.5       moko      339:                        if(value_var_name)
                    340:                                r.put_element(caller, *value_var_name, i.value() ? i.value() : VVoid::get());
                    341: 
                    342:                        r.process_write(*body_code);
                    343: 
                    344:                        if(r.check_skip_break())
                    345:                                break;
                    346:                }
                    347:        }
                    348: }
                    349: 
1.1       moko      350: static void _foreach(Request& r, MethodParams& params) {
                    351:        InCycle temp(r);
                    352: 
                    353:        const String* key_var_name=&params.as_string(0, "key-var name must be string");
                    354:        const String* value_var_name=&params.as_string(1, "value-var name must be string");
                    355:        Value* body_code=&params.as_junction(2, "body must be code");
1.7       moko      356:        Value* delim_maybe_code=params.count()>3 ? &params[3] : 0;
1.1       moko      357:        Value& caller=*r.get_method_frame()->caller();
                    358: 
                    359:        if(key_var_name->is_empty()) key_var_name=0;
                    360:        if(value_var_name->is_empty()) value_var_name=0;
                    361: 
                    362:        ArrayValue& array=GET_SELF(r, VArray).array();
                    363: 
                    364:        if(delim_maybe_code){ // delimiter set
                    365:                bool need_delim=false;
                    366:                for(ArrayValue::Iterator i(array); i; i.next()){
                    367:                        if(i.value()){
                    368:                                if(key_var_name){
                    369:                                        VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
                    370:                                        r.put_element(caller, *key_var_name, vkey);
                    371:                                }
                    372: 
                    373:                                if(value_var_name)
                    374:                                        r.put_element(caller, *value_var_name, i.value());
                    375: 
                    376:                                Value& sv_processed=r.process(*body_code);
                    377:                                TempSkip4Delimiter skip(r);
                    378: 
                    379:                                const String* s_processed=sv_processed.get_string();
                    380:                                if(s_processed && !s_processed->is_empty()) { // we have body
                    381:                                        if(need_delim) // need delim & iteration produced string?
                    382:                                                r.write(r.process(*delim_maybe_code));
                    383:                                        else
                    384:                                                need_delim=true;
                    385:                                }
                    386: 
                    387:                                r.write(sv_processed);
                    388: 
                    389:                                if(skip.check_break())
                    390:                                        break;
                    391:                        }
                    392:                }
                    393:        } else {
                    394:                for(ArrayValue::Iterator i(array); i; i.next()){
                    395:                        if(i.value()){
                    396:                                if(key_var_name){
                    397:                                        VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
                    398:                                        r.put_element(caller, *key_var_name, vkey);
                    399:                                }
                    400: 
                    401:                                if(value_var_name)
                    402:                                        r.put_element(caller, *value_var_name, i.value());
                    403: 
                    404:                                r.process_write(*body_code);
                    405: 
                    406:                                if(r.check_skip_break())
                    407:                                        break;
                    408:                        }
                    409:                }
                    410:        }
                    411: }
                    412: 
                    413: #ifndef DOXYGEN
                    414: struct Array_seq_item : public PA_Allocated {
                    415:        Value *array_data;
                    416:        union {
                    417:                const char *c_str;
                    418:                double d;
                    419:        } value;
                    420: };
                    421: #endif
                    422: 
                    423: static int sort_cmp_string(const void *a, const void *b) {
                    424:        return strcmp(
                    425:                static_cast<const Array_seq_item *>(a)->value.c_str,
                    426:                static_cast<const Array_seq_item *>(b)->value.c_str
                    427:        );
                    428: }
                    429: static int sort_cmp_double(const void *a, const void *b) {
                    430:        double va=static_cast<const Array_seq_item *>(a)->value.d;
                    431:        double vb=static_cast<const Array_seq_item *>(b)->value.d;
                    432:        if(va<vb)
                    433:                return -1;
                    434:        else if(va>vb)
                    435:                return +1;
                    436:        else 
                    437:                return 0;
                    438: }
                    439: 
                    440: static void _sort(Request& r, MethodParams& params){
                    441:        const String& key_var_name=params.as_string(0, "key-var name must be string");
                    442:        const String& value_var_name=params.as_string(1, "value-var name must be string");
                    443:        Value& key_maker=params.as_junction(2, "key-maker must be code");
                    444:        bool reverse=params.count()>3 && params.as_no_junction(3, "order must not be code").as_string()=="desc"; // default=asc
                    445: 
                    446:        const String* key_var=key_var_name.is_empty()? 0 : &key_var_name;
                    447:        const String* value_var=value_var_name.is_empty()? 0 : &value_var_name;
                    448:        VMethodFrame* context=r.get_method_frame()->caller();
                    449: 
                    450:        VArray& self=GET_SELF(r, VArray);
                    451:        ArrayValue& array=self.array();
1.4       moko      452:        int count=array.used(); // not array.count()
1.1       moko      453: 
                    454:        Array_seq_item* seq=new Array_seq_item[count];
                    455:        int pos=0;
                    456:        bool key_values_are_strings=true;
                    457: 
                    458:        for(ArrayValue::Iterator i(array); i; i.next() ){
                    459:                if(i.value()){
                    460:                        if(key_var)
                    461:                                r.put_element(*context, *key_var, new VString(*new String(i.key(), String::L_TAINTED)));
                    462:                        if(value_var)
                    463:                                r.put_element(*context, *value_var, i.value());
                    464: 
                    465:                        Value& value=r.process(key_maker);
                    466:                        if(pos==0) // determining key values type by first one
                    467:                                key_values_are_strings=value.is_string();
                    468: 
                    469:                        seq[pos].array_data=i.value();
                    470:                        if(key_values_are_strings)
                    471:                                seq[pos++].value.c_str=value.as_string().cstr();
                    472:                        else
                    473:                                seq[pos++].value.d=value.as_expr_result().as_double();
                    474:                }
                    475:        }
                    476: 
                    477:        // @todo: handle this elsewhere
                    478:        if(r.charsets.source().NAME()=="KOI8-R" && key_values_are_strings)
                    479:                for(pos=0; pos<count; pos++)
                    480:                        if(*seq[pos].value.c_str)
                    481:                                seq[pos].value.c_str=Charset::transcode(seq[pos].value.c_str, r.charsets.source(), pa_UTF8_charset).cstr();
                    482: 
                    483:        // sort keys
                    484:        qsort(seq, count, sizeof(Array_seq_item), key_values_are_strings ? sort_cmp_string : sort_cmp_double);
                    485: 
                    486:        // reorder array as required in 'seq'
                    487:        array.clear();
                    488:        if(reverse)
                    489:                for(pos=count-1; pos>=0; pos--)
                    490:                        array+=seq[pos].array_data;
                    491:        else
                    492:                for(pos=0; pos<count; pos++)
                    493:                        array+=seq[pos].array_data;
                    494: 
                    495:        delete[] seq;
                    496: }
                    497: 
1.5       moko      498: enum AtResultType {
                    499:        AtResultTypeValue = 0,
                    500:        AtResultTypeKey = 1,
                    501:        AtResultTypeHash = 2
                    502: };
                    503: 
                    504: inline Value& SingleElementHash(String::Body akey, Value* avalue) {
                    505:        Value& result=*new VHash;
                    506:        result.put_element(*new String(akey, String::L_TAINTED), avalue);
                    507:        return result;
                    508: }
                    509: 
1.1       moko      510: static void _at(Request& r, MethodParams& params) {
                    511:        VArray& self=GET_SELF(r, VArray);
                    512:        ArrayValue& array=self.array();
1.5       moko      513:        size_t count=array.used(); // not array.count()
1.1       moko      514: 
                    515:        int pos=0;
                    516: 
                    517:        AtResultType result_type=AtResultTypeValue;
                    518:        if(params.count() > 1) {
                    519:                const String& stype=params.as_string(1, "type must be string");
                    520:                if(stype == "key")
                    521:                        result_type=AtResultTypeKey;
                    522:                else if(stype == "hash")
                    523:                        result_type=AtResultTypeHash;
                    524:                else if(stype != "value")
                    525:                        throw Exception(PARSER_RUNTIME, &stype, "type must be 'key', 'value' or 'hash'");
                    526:        }
                    527: 
                    528:        Value& vwhence=params[0];
                    529:        if(vwhence.is_string()) {
                    530:                const String& swhence=*vwhence.get_string();
                    531:                if(swhence == "last")
                    532:                        pos=count-1;
                    533:                else if(swhence != "first")
                    534:                        throw Exception(PARSER_RUNTIME, &swhence, "whence must be 'first', 'last' or expression");
                    535:        } else {
                    536:                pos=r.process(vwhence).as_int();
                    537:                if(pos < 0)
                    538:                        pos+=count;
                    539:        }
                    540: 
                    541:        if(count && pos >= 0 && (size_t)pos < count){
                    542:                switch(result_type) {
                    543:                        case AtResultTypeKey:
                    544:                                {
                    545:                                        for(ArrayValue::Iterator i(array); i; i.next() ){
                    546:                                                if(i.value() && !(pos--)){
                    547:                                                        r.write(*new VString(*new String(i.key(), String::L_TAINTED)));
                    548:                                                        break;
                    549:                                                }
                    550:                                        }
                    551:                                        break;
                    552:                                }
                    553:                        case AtResultTypeValue:
                    554:                                {
                    555:                                        for(ArrayValue::Iterator i(array); i; i.next() )
                    556:                                                if(i.value() &&!(pos--)){
                    557:                                                        r.write(*i.value());
                    558:                                                        break;
                    559:                                                }
                    560:                                        break;
                    561:                                }
                    562:                        case AtResultTypeHash:
                    563:                                {
                    564:                                        for(ArrayValue::Iterator i(array); i; i.next() )
                    565:                                                if(i.value() &&!(pos--)){
                    566:                                                        r.write(SingleElementHash(i.key(), i.value()));
                    567:                                                        break;
                    568:                                                }
                    569:                                        break;
                    570:                                }
                    571:                }
                    572:        }
                    573: }
                    574: 
                    575: 
                    576: extern String table_reverse_name;
                    577: 
                    578: static void _select(Request& r, MethodParams& params) {
                    579:        InCycle temp(r);
                    580:        const String* key_var_name=&params.as_string(0, "key-var name must be string");
                    581:        const String* value_var_name=&params.as_string(1, "value-var name must be string");
                    582:        Value& vcondition=params.as_expression(2, "condition must be number, bool or expression");
                    583: 
                    584:        if(key_var_name->is_empty()) key_var_name=0;
                    585:        if(value_var_name->is_empty()) value_var_name=0;
                    586: 
                    587:        ArrayValue& source_array=GET_SELF(r, VArray).array();
                    588:        Value& caller=*r.get_method_frame()->caller();
                    589: 
                    590:        int limit=source_array.count();
                    591:        bool reverse=false;
                    592: 
                    593:        if(params.count()>3)
                    594:                if(HashStringValue* options=params.as_hash(3)) {
                    595:                        int valid_options=0;
                    596:                        if(Value* vlimit=options->get(sql_limit_name)) {
                    597:                                valid_options++;
                    598:                                limit=r.process(*vlimit).as_int();
                    599:                        }
                    600:                        if(Value* vreverse=options->get(table_reverse_name)) {
                    601:                                valid_options++;
                    602:                                reverse=r.process(*vreverse).as_bool();
                    603:                        }
                    604:                        if(valid_options!=options->count())
                    605:                                throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
                    606:                }
                    607: 
                    608:        VArray *result=new VArray;
                    609:        ArrayValue& result_array=result->array();
                    610: 
                    611:        if(limit>0){
                    612:                if(reverse){
                    613:                        for(ArrayValue::ReverseIterator i(source_array); i; ){
1.5       moko      614:                                if(Value *value=i.prev()){ // here for correct i.key()
                    615:                                        if(key_var_name)
                    616:                                                r.put_element(caller, *key_var_name, new VString(*new String(i.key(), String::L_TAINTED)));
                    617:                                        if(value_var_name)
                    618:                                                r.put_element(caller, *value_var_name, value);
1.1       moko      619: 
1.5       moko      620:                                        bool condition=r.process(vcondition).as_bool();
1.1       moko      621: 
1.5       moko      622:                                        if(r.check_skip_break())
                    623:                                                break;
1.1       moko      624: 
1.5       moko      625:                                        if(condition){
                    626:                                                result_array+=value;
                    627:                                                if(!--limit)
                    628:                                                        break;
                    629:                                        }
1.1       moko      630:                                }
                    631:                        }
                    632:                } else {
                    633:                        for(ArrayValue::Iterator i(source_array); i; i.next() ){
1.5       moko      634:                                if(Value *value=i.value()){
1.1       moko      635:                                        if(key_var_name)
                    636:                                                r.put_element(caller, *key_var_name, new VString(*new String(i.key(), String::L_TAINTED)));
                    637:                                        if(value_var_name)
                    638:                                                r.put_element(caller, *value_var_name, value);
                    639: 
                    640:                                        bool condition=r.process(vcondition).as_bool();
                    641: 
                    642:                                        if(r.check_skip_break())
                    643:                                                break;
                    644: 
                    645:                                        if(condition){
                    646:                                                result_array+=value;
                    647:                                                if(!--limit)
                    648:                                                        break;
                    649:                                        }
                    650:                                }
                    651:                        }
                    652:                }
                    653:        }
                    654: 
                    655:        r.write(*result);
                    656: }
                    657: 
                    658: static void _reverse(Request& r, MethodParams& params) {
1.5       moko      659:        ArrayValue& source_array=GET_SELF(r, VArray).array();
1.1       moko      660: 
1.5       moko      661:        VArray& result=*new VArray(source_array.count());
1.1       moko      662:        ArrayValue& result_array=result.array();
                    663: 
1.4       moko      664:        for(ArrayValue::ReverseIterator i(source_array); i; ){
                    665:                result_array+=i.prev();
1.1       moko      666:        }
                    667: 
                    668:        r.write(result);
                    669: }
                    670: 
                    671: 
                    672: // constructor
                    673: 
                    674: MArray::MArray(): Methoded(VARRAY_TYPE) {
                    675: 
                    676:        // ^array::create[[copy_from]]
                    677:        add_native_method("create", Method::CT_DYNAMIC, _create_or_add, 0, 1);
                    678:        // ^array.add[add_from]
                    679:        add_native_method("add", Method::CT_DYNAMIC, _create_or_add, 1, 1);
1.6       moko      680:        // ^array.join[join_from[;options]]
                    681:        add_native_method("join", Method::CT_DYNAMIC, _join, 1, 2);
1.1       moko      682: 
1.9     ! moko      683:        // ^array.left(n)
        !           684:        add_native_method("left", Method::CT_DYNAMIC, _left, 1, 1);
        !           685:        // ^array.right(n)
        !           686:        add_native_method("right", Method::CT_DYNAMIC, _right, 1, 1);
        !           687:        // ^array.mid(p)
        !           688:        // ^array.mid(p;n)
        !           689:        add_native_method("mid", Method::CT_DYNAMIC, _mid, 1, 2);
1.1       moko      690: 
1.8       moko      691:        // ^array::new[value;value]
                    692:        add_native_method("new", Method::CT_DYNAMIC, _append, 0, 10000);
1.2       moko      693:        // ^array.append[value;value]
                    694:        add_native_method("append", Method::CT_DYNAMIC, _append, 1, 10000);
                    695:        // ^array.insert[index;value...]
                    696:        add_native_method("insert", Method::CT_DYNAMIC, _insert, 2, 10000);
                    697: 
                    698:        // ^array.delete[index]
1.1       moko      699:        add_native_method("delete", Method::CT_DYNAMIC, _delete, 0, 1);
1.7       moko      700:        // ^array.remove[index]
                    701:        add_native_method("remove", Method::CT_DYNAMIC, _remove, 1, 1);
1.1       moko      702: 
1.2       moko      703:        // ^array.contains[index]
1.1       moko      704:        add_native_method("contains", Method::CT_DYNAMIC, _contains, 1, 1);
                    705: 
                    706:        // ^array::sql[query][options array]
                    707:        add_native_method("sql", Method::CT_DYNAMIC, _sql, 1, 2);
                    708: 
                    709:        // ^array._keys[[column name]]
                    710:        add_native_method("_keys", Method::CT_DYNAMIC, _keys, 0, 1);
                    711: 
1.5       moko      712:        // ^array._count[[all]]
                    713:        add_native_method("_count", Method::CT_DYNAMIC, _count, 0, 1);
1.1       moko      714: 
1.7       moko      715:        // ^array.for[index;value]{code}[delim]
                    716:        add_native_method("for", Method::CT_DYNAMIC, _for, 3, 3+1);
1.2       moko      717:        // ^array.foreach[index;value]{code}[delim]
1.7       moko      718:        add_native_method("foreach", Method::CT_DYNAMIC, _foreach, 3, 3+1);
1.1       moko      719: 
1.2       moko      720:        // ^array.sort[index;value]{string-key-maker}[[asc|desc]]
                    721:        // ^array.sort[index;value](numeric-key-maker)[[asc|desc]]
1.1       moko      722:        add_native_method("sort", Method::CT_DYNAMIC, _sort, 3, 4);
                    723: 
1.2       moko      724:        // ^array.select[index;value](bool-condition)[options hash]
1.1       moko      725:        add_native_method("select", Method::CT_DYNAMIC, _select, 3, 4);
                    726: 
                    727:        // ^array.reverse[]
                    728:        add_native_method("reverse", Method::CT_DYNAMIC, _reverse, 0, 0);
                    729: 
1.2       moko      730:        // ^array._at[first|last[;'key'|'value'|'hash']]
                    731:        // ^array._at([-+]offset)[['key'|'value'|'hash']]
1.1       moko      732:        add_native_method("_at", Method::CT_DYNAMIC, _at, 1, 2);
                    733: 
                    734: #ifdef FEATURE_GET_ELEMENT4CALL
                    735:        // aliases without "_"
                    736:        add_native_method("keys", Method::CT_DYNAMIC, _keys, 0, 1);
                    737:        add_native_method("count", Method::CT_DYNAMIC, _count, 0, 0);
                    738:        add_native_method("at", Method::CT_DYNAMIC, _at, 1, 2);
                    739: #endif
                    740: 
                    741: }

E-mail: