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

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

E-mail: