Annotation of parser3/src/classes/array.C, revision 1.1
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:
! 20: volatile const char * IDENT_ARRAY_C="$Id: array.C,v 1.157 2023/09/26 20:49:05 moko Exp $";
! 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:
! 36: // methods
! 37:
! 38: static void _create_or_add(Request& r, MethodParams& params) {
! 39: if(params.count()) {
! 40: Value& vsrc=params.as_no_junction(0, PARAM_MUST_BE_HASH);
! 41: VArray& self=GET_SELF(r, VArray);
! 42: ArrayValue& self_array=self.array();
! 43:
! 44: if(VArray* src=static_cast<VArray*>(vsrc.as(VARRAY_TYPE))) {
! 45: ArrayValue& src_array =src->array();
! 46: if(&src_array==&self_array) // same: doing nothing
! 47: return;
! 48: } else {
! 49: HashStringValue* src_hash=vsrc.get_hash();
! 50: if(src_hash)
! 51: for(HashStringValue::Iterator i(*src_hash); i; i.next())
! 52: self_array+=i.value();
! 53: }
! 54: self.clear_hash();
! 55: }
! 56: }
! 57:
! 58: static void _sql(Request& r, MethodParams& params) {}
! 59:
! 60: static void _sub(Request& r, MethodParams& params) {}
! 61:
! 62: static void _union(Request& r, MethodParams& params) {}
! 63:
! 64: static void _intersection(Request& r, MethodParams& params) {}
! 65:
! 66: static void _intersects(Request& r, MethodParams& params) {}
! 67:
! 68: static void _keys(Request& r, MethodParams& params) {
! 69: const String* keys_column_name;
! 70: if(params.count()>0)
! 71: keys_column_name=¶ms.as_string(0, COLUMN_NAME_MUST_BE_STRING);
! 72: else
! 73: keys_column_name=new String("key");
! 74:
! 75: Table::columns_type columns(new ArrayString(1));
! 76: *columns+=keys_column_name;
! 77: Table* table=new Table(columns);
! 78:
! 79: ArrayValue& array=GET_SELF(r, VArray).array();
! 80: for(ArrayValue::Iterator i(array); i; i.next()){
! 81: if(i.value()){
! 82: Table::element_type row(new ArrayString(1));
! 83: *row+=new String(i.key(), String::L_TAINTED);
! 84: *table+=row;
! 85: }
! 86: }
! 87:
! 88: r.write(*new VTable(table));
! 89: }
! 90:
! 91: static void _count(Request& r, MethodParams&) {
! 92: r.write(*new VInt(GET_SELF(r, VArray).count()));
! 93: }
! 94:
! 95: static void _delete(Request& r, MethodParams& params) {
! 96: if(params.count()>0)
! 97: GET_SELF(r, VArray).clear(VArray::index(params.as_int(0, "index must be integer", r)));
! 98: else
! 99: GET_SELF(r, VArray).clear();
! 100: }
! 101:
! 102: static void _contains(Request& r, MethodParams& params) {
! 103: VArray& self=GET_SELF(r, VArray);
! 104: bool result=self.contains(VArray::index(params.as_int(0, "index must be integer", r)));
! 105: r.write(VBool::get(result));
! 106: }
! 107:
! 108: static void _foreach(Request& r, MethodParams& params) {
! 109: InCycle temp(r);
! 110:
! 111: const String* key_var_name=¶ms.as_string(0, "key-var name must be string");
! 112: const String* value_var_name=¶ms.as_string(1, "value-var name must be string");
! 113: Value* body_code=¶ms.as_junction(2, "body must be code");
! 114: Value* delim_maybe_code=params.count()>3?¶ms[3]:0;
! 115: Value& caller=*r.get_method_frame()->caller();
! 116:
! 117: if(key_var_name->is_empty()) key_var_name=0;
! 118: if(value_var_name->is_empty()) value_var_name=0;
! 119:
! 120: ArrayValue& array=GET_SELF(r, VArray).array();
! 121:
! 122: if(delim_maybe_code){ // delimiter set
! 123: bool need_delim=false;
! 124: for(ArrayValue::Iterator i(array); i; i.next()){
! 125: if(i.value()){
! 126: if(key_var_name){
! 127: VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
! 128: r.put_element(caller, *key_var_name, vkey);
! 129: }
! 130:
! 131: if(value_var_name)
! 132: r.put_element(caller, *value_var_name, i.value());
! 133:
! 134: Value& sv_processed=r.process(*body_code);
! 135: TempSkip4Delimiter skip(r);
! 136:
! 137: const String* s_processed=sv_processed.get_string();
! 138: if(s_processed && !s_processed->is_empty()) { // we have body
! 139: if(need_delim) // need delim & iteration produced string?
! 140: r.write(r.process(*delim_maybe_code));
! 141: else
! 142: need_delim=true;
! 143: }
! 144:
! 145: r.write(sv_processed);
! 146:
! 147: if(skip.check_break())
! 148: break;
! 149: }
! 150: }
! 151: } else {
! 152: for(ArrayValue::Iterator i(array); i; i.next()){
! 153: if(i.value()){
! 154: if(key_var_name){
! 155: VString* vkey=new VString(*new String(i.key(), String::L_TAINTED));
! 156: r.put_element(caller, *key_var_name, vkey);
! 157: }
! 158:
! 159: if(value_var_name)
! 160: r.put_element(caller, *value_var_name, i.value());
! 161:
! 162: r.process_write(*body_code);
! 163:
! 164: if(r.check_skip_break())
! 165: break;
! 166: }
! 167: }
! 168: }
! 169: }
! 170:
! 171:
! 172: enum AtResultType {
! 173: AtResultTypeValue = 0,
! 174: AtResultTypeKey = 1,
! 175: AtResultTypeHash = 2
! 176: };
! 177:
! 178: inline Value& SingleElementHash(String::Body akey, Value* avalue) {
! 179: Value& result=*new VHash;
! 180: result.put_element(*new String(akey, String::L_TAINTED), avalue);
! 181: return result;
! 182: }
! 183:
! 184: #ifndef DOXYGEN
! 185: struct Array_seq_item : public PA_Allocated {
! 186: Value *array_data;
! 187: union {
! 188: const char *c_str;
! 189: double d;
! 190: } value;
! 191: };
! 192: #endif
! 193:
! 194: static int sort_cmp_string(const void *a, const void *b) {
! 195: return strcmp(
! 196: static_cast<const Array_seq_item *>(a)->value.c_str,
! 197: static_cast<const Array_seq_item *>(b)->value.c_str
! 198: );
! 199: }
! 200: static int sort_cmp_double(const void *a, const void *b) {
! 201: double va=static_cast<const Array_seq_item *>(a)->value.d;
! 202: double vb=static_cast<const Array_seq_item *>(b)->value.d;
! 203: if(va<vb)
! 204: return -1;
! 205: else if(va>vb)
! 206: return +1;
! 207: else
! 208: return 0;
! 209: }
! 210:
! 211: static void _sort(Request& r, MethodParams& params){
! 212: const String& key_var_name=params.as_string(0, "key-var name must be string");
! 213: const String& value_var_name=params.as_string(1, "value-var name must be string");
! 214: Value& key_maker=params.as_junction(2, "key-maker must be code");
! 215: bool reverse=params.count()>3 && params.as_no_junction(3, "order must not be code").as_string()=="desc"; // default=asc
! 216:
! 217: const String* key_var=key_var_name.is_empty()? 0 : &key_var_name;
! 218: const String* value_var=value_var_name.is_empty()? 0 : &value_var_name;
! 219: VMethodFrame* context=r.get_method_frame()->caller();
! 220:
! 221: VArray& self=GET_SELF(r, VArray);
! 222: ArrayValue& array=self.array();
! 223: int count=self.count(); // not array.count()
! 224:
! 225: Array_seq_item* seq=new Array_seq_item[count];
! 226: int pos=0;
! 227: bool key_values_are_strings=true;
! 228:
! 229: for(ArrayValue::Iterator i(array); i; i.next() ){
! 230: if(i.value()){
! 231: if(key_var)
! 232: r.put_element(*context, *key_var, new VString(*new String(i.key(), String::L_TAINTED)));
! 233: if(value_var)
! 234: r.put_element(*context, *value_var, i.value());
! 235:
! 236: Value& value=r.process(key_maker);
! 237: if(pos==0) // determining key values type by first one
! 238: key_values_are_strings=value.is_string();
! 239:
! 240: seq[pos].array_data=i.value();
! 241: if(key_values_are_strings)
! 242: seq[pos++].value.c_str=value.as_string().cstr();
! 243: else
! 244: seq[pos++].value.d=value.as_expr_result().as_double();
! 245: }
! 246: }
! 247:
! 248: // @todo: handle this elsewhere
! 249: if(r.charsets.source().NAME()=="KOI8-R" && key_values_are_strings)
! 250: for(pos=0; pos<count; pos++)
! 251: if(*seq[pos].value.c_str)
! 252: seq[pos].value.c_str=Charset::transcode(seq[pos].value.c_str, r.charsets.source(), pa_UTF8_charset).cstr();
! 253:
! 254: // sort keys
! 255: qsort(seq, count, sizeof(Array_seq_item), key_values_are_strings ? sort_cmp_string : sort_cmp_double);
! 256:
! 257: // reorder array as required in 'seq'
! 258: array.clear();
! 259: if(reverse)
! 260: for(pos=count-1; pos>=0; pos--)
! 261: array+=seq[pos].array_data;
! 262: else
! 263: for(pos=0; pos<count; pos++)
! 264: array+=seq[pos].array_data;
! 265:
! 266: delete[] seq;
! 267: }
! 268:
! 269: static void _at(Request& r, MethodParams& params) {
! 270: VArray& self=GET_SELF(r, VArray);
! 271: ArrayValue& array=self.array();
! 272: size_t count=self.count();
! 273:
! 274: int pos=0;
! 275:
! 276: AtResultType result_type=AtResultTypeValue;
! 277: if(params.count() > 1) {
! 278: const String& stype=params.as_string(1, "type must be string");
! 279: if(stype == "key")
! 280: result_type=AtResultTypeKey;
! 281: else if(stype == "hash")
! 282: result_type=AtResultTypeHash;
! 283: else if(stype != "value")
! 284: throw Exception(PARSER_RUNTIME, &stype, "type must be 'key', 'value' or 'hash'");
! 285: }
! 286:
! 287: Value& vwhence=params[0];
! 288: if(vwhence.is_string()) {
! 289: const String& swhence=*vwhence.get_string();
! 290: if(swhence == "last")
! 291: pos=count-1;
! 292: else if(swhence != "first")
! 293: throw Exception(PARSER_RUNTIME, &swhence, "whence must be 'first', 'last' or expression");
! 294: } else {
! 295: pos=r.process(vwhence).as_int();
! 296: if(pos < 0)
! 297: pos+=count;
! 298: }
! 299:
! 300: if(count && pos >= 0 && (size_t)pos < count){
! 301: switch(result_type) {
! 302: case AtResultTypeKey:
! 303: {
! 304: for(ArrayValue::Iterator i(array); i; i.next() ){
! 305: if(i.value() && !(pos--)){
! 306: r.write(*new VString(*new String(i.key(), String::L_TAINTED)));
! 307: break;
! 308: }
! 309: }
! 310: break;
! 311: }
! 312: case AtResultTypeValue:
! 313: {
! 314: for(ArrayValue::Iterator i(array); i; i.next() )
! 315: if(i.value() &&!(pos--)){
! 316: r.write(*i.value());
! 317: break;
! 318: }
! 319: break;
! 320: }
! 321: case AtResultTypeHash:
! 322: {
! 323: for(ArrayValue::Iterator i(array); i; i.next() )
! 324: if(i.value() &&!(pos--)){
! 325: r.write(SingleElementHash(i.key(), i.value()));
! 326: break;
! 327: }
! 328: break;
! 329: }
! 330: }
! 331: }
! 332: }
! 333:
! 334:
! 335: extern String table_reverse_name;
! 336:
! 337: static void _select(Request& r, MethodParams& params) {
! 338: InCycle temp(r);
! 339: const String* key_var_name=¶ms.as_string(0, "key-var name must be string");
! 340: const String* value_var_name=¶ms.as_string(1, "value-var name must be string");
! 341: Value& vcondition=params.as_expression(2, "condition must be number, bool or expression");
! 342:
! 343: if(key_var_name->is_empty()) key_var_name=0;
! 344: if(value_var_name->is_empty()) value_var_name=0;
! 345:
! 346: ArrayValue& source_array=GET_SELF(r, VArray).array();
! 347: Value& caller=*r.get_method_frame()->caller();
! 348:
! 349: int limit=source_array.count();
! 350: bool reverse=false;
! 351:
! 352: if(params.count()>3)
! 353: if(HashStringValue* options=params.as_hash(3)) {
! 354: int valid_options=0;
! 355: if(Value* vlimit=options->get(sql_limit_name)) {
! 356: valid_options++;
! 357: limit=r.process(*vlimit).as_int();
! 358: }
! 359: if(Value* vreverse=options->get(table_reverse_name)) {
! 360: valid_options++;
! 361: reverse=r.process(*vreverse).as_bool();
! 362: }
! 363: if(valid_options!=options->count())
! 364: throw Exception(PARSER_RUNTIME, 0, CALLED_WITH_INVALID_OPTION);
! 365: }
! 366:
! 367: VArray *result=new VArray;
! 368: ArrayValue& result_array=result->array();
! 369:
! 370: if(limit>0){
! 371:
! 372: if(reverse){
! 373: for(ArrayValue::ReverseIterator i(source_array); i; ){
! 374: Value *value=i.prev(); // here for correct i.key()
! 375: if(key_var_name)
! 376: r.put_element(caller, *key_var_name, new VString(*new String(i.key(), String::L_TAINTED)));
! 377: if(value_var_name)
! 378: r.put_element(caller, *value_var_name, value);
! 379:
! 380: bool condition=r.process(vcondition).as_bool();
! 381:
! 382: if(r.check_skip_break())
! 383: break;
! 384:
! 385: if(condition){
! 386: result_array+=value;
! 387: if(!--limit)
! 388: break;
! 389: }
! 390: }
! 391: } else {
! 392: for(ArrayValue::Iterator i(source_array); i; i.next() ){
! 393: Value *value=i.value();
! 394: if(value){
! 395: if(key_var_name)
! 396: r.put_element(caller, *key_var_name, new VString(*new String(i.key(), String::L_TAINTED)));
! 397: if(value_var_name)
! 398: r.put_element(caller, *value_var_name, value);
! 399:
! 400: bool condition=r.process(vcondition).as_bool();
! 401:
! 402: if(r.check_skip_break())
! 403: break;
! 404:
! 405: if(condition){
! 406: result_array+=value;
! 407: if(!--limit)
! 408: break;
! 409: }
! 410: }
! 411: }
! 412: }
! 413: }
! 414:
! 415: r.write(*result);
! 416: }
! 417:
! 418: static void _reverse(Request& r, MethodParams& params) {
! 419: VArray& self=GET_SELF(r, VArray);
! 420: ArrayValue& source_array=self.array();
! 421: size_t count=source_array.count();
! 422:
! 423: VArray& result=*new VArray(count);
! 424: ArrayValue& result_array=result.array();
! 425:
! 426: for(ArrayValue::Iterator i(source_array); i; ){
! 427: Value *v=i.next();
! 428: if(v){
! 429: result_array.fit(count-i.index() /* no -1 as .next() is called */, v);
! 430: }
! 431: }
! 432:
! 433: r.write(result);
! 434: }
! 435:
! 436:
! 437: static void _rename(Request& r, MethodParams& params) {}
! 438:
! 439: // constructor
! 440:
! 441: MArray::MArray(): Methoded(VARRAY_TYPE) {
! 442: // set_base(hash_class);
! 443:
! 444: // ^array::create[[copy_from]]
! 445: add_native_method("create", Method::CT_DYNAMIC, _create_or_add, 0, 1);
! 446: // ^array.add[add_from]
! 447: add_native_method("add", Method::CT_DYNAMIC, _create_or_add, 1, 1);
! 448:
! 449: // ^array.sub[sub_from]
! 450: add_native_method("sub", Method::CT_DYNAMIC, _sub, 1, 1);
! 451: // ^a.union[b] = array
! 452: add_native_method("union", Method::CT_DYNAMIC, _union, 1, 1);
! 453: // ^a.intersection[b][options array] = array
! 454: add_native_method("intersection", Method::CT_DYNAMIC, _intersection, 1, 2);
! 455: // ^a.intersects[b] = bool
! 456: add_native_method("intersects", Method::CT_DYNAMIC, _intersects, 1, 1);
! 457:
! 458: // ^a.delete[key]
! 459: add_native_method("delete", Method::CT_DYNAMIC, _delete, 0, 1);
! 460:
! 461: // ^a.contains[key]
! 462: add_native_method("contains", Method::CT_DYNAMIC, _contains, 1, 1);
! 463: // backward
! 464: add_native_method("contain", Method::CT_DYNAMIC, _contains, 1, 1);
! 465:
! 466: // ^array::sql[query][options array]
! 467: add_native_method("sql", Method::CT_DYNAMIC, _sql, 1, 2);
! 468:
! 469: // ^array._keys[[column name]]
! 470: add_native_method("_keys", Method::CT_DYNAMIC, _keys, 0, 1);
! 471:
! 472: // ^array._count[]
! 473: add_native_method("_count", Method::CT_DYNAMIC, _count, 0, 0);
! 474:
! 475: // ^array.foreach[key;value]{code}[delim]
! 476: add_native_method("foreach", Method::CT_DYNAMIC, _foreach, 2+1, 2+1+1);
! 477:
! 478: // ^array.sort[key;value]{string-key-maker}[[asc|desc]]
! 479: // ^array.sort[key;value](numeric-key-maker)[[asc|desc]]
! 480: add_native_method("sort", Method::CT_DYNAMIC, _sort, 3, 4);
! 481:
! 482: // ^array.select[key;value](bool-condition)[options array]
! 483: add_native_method("select", Method::CT_DYNAMIC, _select, 3, 4);
! 484:
! 485: // ^array.reverse[]
! 486: add_native_method("reverse", Method::CT_DYNAMIC, _reverse, 0, 0);
! 487:
! 488: // ^array._at[first|last[;'key'|'value'|'array']]
! 489: // ^array._at([-+]offset)[['key'|'value'|'array']]
! 490: add_native_method("_at", Method::CT_DYNAMIC, _at, 1, 2);
! 491:
! 492: // ^array.rename[from;to]
! 493: // ^array.rename[ $.from[to] ... ]
! 494: add_native_method("rename", Method::CT_DYNAMIC, _rename, 1, 2);
! 495:
! 496: #ifdef FEATURE_GET_ELEMENT4CALL
! 497: // aliases without "_"
! 498: add_native_method("keys", Method::CT_DYNAMIC, _keys, 0, 1);
! 499: add_native_method("count", Method::CT_DYNAMIC, _count, 0, 0);
! 500: add_native_method("at", Method::CT_DYNAMIC, _at, 1, 2);
! 501: #endif
! 502:
! 503: }
E-mail: