1 /* 2 * Hunt - A redis client library for D programming language. 3 * 4 * Copyright (C) 2018-2019 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.redis.BuilderFactory; 13 14 import hunt.redis.Builder; 15 import hunt.redis.GeoCoordinate; 16 import hunt.redis.GeoRadiusResponse; 17 import hunt.redis.Module; 18 import hunt.redis.StreamEntry; 19 import hunt.redis.StreamEntryID; 20 import hunt.redis.StreamPendingEntry; 21 import hunt.redis.Tuple; 22 23 import hunt.collection; 24 import hunt.Boolean; 25 import hunt.Byte; 26 import hunt.Double; 27 import hunt.Exceptions; 28 import hunt.Integer; 29 import hunt.logging.ConsoleLogger; 30 import hunt.Long; 31 import hunt.String; 32 import hunt.redis.util.SafeEncoder; 33 34 import std.conv; 35 import std.concurrency : initOnce; 36 import std.range; 37 38 39 /** 40 * TODO: 41 */ 42 class BuilderFactory { 43 44 static Builder!(Double) DOUBLE() { 45 __gshared Builder!(Double) inst; 46 47 return initOnce!inst(new class Builder!(Double) { 48 override 49 Double build(Object data) { 50 string str = STRING.build(data); 51 if (str is null) return null; 52 try { 53 return Double.valueOf(str.to!double); 54 } catch (NumberFormatException e) { 55 if (str == "inf" || str == "+inf") return new Double(Double.POSITIVE_INFINITY); 56 if (str == "-inf") return new Double(Double.NEGATIVE_INFINITY); 57 throw e; 58 } 59 } 60 61 override 62 string toString() { 63 return "double"; 64 } 65 }); 66 } 67 68 static Builder!(Boolean) BOOLEAN() { 69 __gshared Builder!(Boolean) inst; 70 71 return initOnce!inst(new class Builder!(Boolean) { 72 override 73 Boolean build(Object data) { 74 return Boolean.valueOf((cast(Long) data) == 1); 75 } 76 77 override 78 string toString() { 79 return "bool"; 80 } 81 }); 82 } 83 84 static Builder!(const(ubyte)[]) BYTE_ARRAY() { 85 __gshared Builder!(const(ubyte)[]) inst; 86 87 return initOnce!inst(new class Builder!(const(ubyte)[]) { 88 override 89 const(ubyte)[] build(Object data) { 90 if(data is null) 91 return null; 92 93 warning(typeid(data)); 94 Bytes bytesObj = cast(Bytes)data; 95 return cast(const(ubyte)[]) bytesObj.value(); // deleted == 1 96 } 97 98 override string toString() { 99 return "const(ubyte)[]"; 100 } 101 }); 102 } 103 104 static Builder!(Long) LONG() { 105 __gshared Builder!(Long) inst; 106 107 return initOnce!inst(new class Builder!(Long) { 108 override 109 Long build(Object data) { 110 return cast(Long) data; 111 } 112 113 override 114 string toString() { 115 return "long"; 116 } 117 }); 118 } 119 120 static Builder!(string) STRING() { 121 __gshared Builder!(string) inst; 122 123 return initOnce!inst(new class Builder!(string) { 124 override 125 string build(Object data) { 126 if(data is null) { 127 version(HUNT_DEBUG) warning("data is null"); 128 return null; 129 } 130 131 Bytes bytesObject = cast(Bytes)data; 132 if(bytesObject !is null) { 133 byte[] bytes = bytesObject.value(); 134 version(HUNT_REDIS_DEBUG) tracef("value: %s, %(0x%02X, %)", cast(string)bytes, bytes); 135 return cast(string)bytes; 136 } else { 137 version(HUNT_DEBUG) warningf("value: %s, type: %s", data.toString(), typeid(data)); 138 return data.toString(); 139 } 140 } 141 142 override 143 string toString() { 144 return "string"; 145 } 146 }); 147 } 148 149 static Builder!(List!(string)) STRING_LIST() { 150 __gshared Builder!(List!(string)) inst; 151 152 return initOnce!inst(new class Builder!(List!(string)) { 153 override List!(string) build(Object data) { 154 if (data is null) { 155 return null; 156 } 157 version(HUNT_REDIS_DEBUG) warning(typeid(data)); 158 List!(Object) l = cast(List!(Object)) data; 159 ArrayList!(string) result = new ArrayList!(string)(l.size()); 160 foreach(Object barray ; l) { 161 Bytes bytes = cast(Bytes)barray; 162 if (bytes is null) { 163 result.add(cast(string)null); 164 } else { 165 result.add(SafeEncoder.encode(cast(ubyte[])bytes.value())); 166 } 167 } 168 return result; 169 } 170 171 override string toString() { 172 return "List!(string)"; 173 } 174 175 }); 176 } 177 178 static Builder!(Map!(string, string)) STRING_MAP() { 179 __gshared Builder!(Map!(string, string)) inst; 180 181 return initOnce!inst(new class Builder!(Map!(string, string)) { 182 override 183 Map!(string, string) build(Object data) { 184 // warning(typeid(data)); 185 List!(const(ubyte)[]) flatHash = cast(List!(const(ubyte)[])) data; 186 Map!(string, string) hash = new HashMap!(string, string)(flatHash.size()/2, 1); 187 InputRange!(const(ubyte)[]) iterator = flatHash.iterator(); 188 while (!iterator.empty()) { 189 const(ubyte)[] first = iterator.front(); iterator.popFront(); 190 const(ubyte)[] second = iterator.front(); iterator.popFront(); 191 hash.put(SafeEncoder.encode(first), SafeEncoder.encode(second)); 192 } 193 194 return hash; 195 } 196 197 override 198 string toString() { 199 return "Map!(string, string)"; 200 } 201 }); 202 } 203 204 static Builder!(Map!(string, string)) PUBSUB_NUMSUB_MAP() { 205 __gshared Builder!(Map!(string, string)) inst; 206 207 return initOnce!inst(new class Builder!(Map!(string, string)) { 208 209 override 210 Map!(string, string) build(Object data) { 211 // warning(typeid(data)); 212 List!(Object) flatHash = cast(List!(Object)) data; 213 Map!(string, string) hash = new HashMap!(string, string)(flatHash.size()/2, 1); 214 215 InputRange!(Object) iterator = flatHash.iterator(); 216 while (!iterator.empty()) { 217 218 Object firstObj = iterator.front; 219 iterator.popFront(); 220 221 Object secondObj = iterator.front; 222 iterator.popFront(); 223 224 String stringObj = cast(String)firstObj; 225 Long longObj = cast(Long)secondObj; 226 227 hash.put(cast(string)SafeEncoder.encode(stringObj.value), 228 to!string(longObj.value)); 229 } 230 231 return hash; 232 } 233 234 override 235 string toString() { 236 return "PUBSUB_NUMSUB_MAP!(string, string)"; 237 } 238 }); 239 } 240 241 static Builder!(Set!(string)) STRING_SET() { 242 __gshared Builder!(Set!(string)) inst; 243 244 return initOnce!inst(new class Builder!(Set!(string)) { 245 246 override 247 Set!(string) build(Object data) { 248 if (data is null) { 249 return null; 250 } 251 warning(typeid(data)); 252 implementationMissing(); 253 254 // List!(const(ubyte)[]) l = cast(List!(const(ubyte)[])) data; 255 // Set!(string) result = new HashSet!(string)(l.size(), 1); 256 // foreach(const(ubyte)[] barray ; l) { 257 // if (barray is null) { 258 // result.add(null); 259 // } else { 260 // result.add(SafeEncoder.encode(barray)); 261 // } 262 // } 263 // return result; 264 return null; 265 } 266 267 override 268 string toString() { 269 return "Set!(string)"; 270 } 271 }); 272 } 273 274 static Builder!(List!(const(ubyte)[])) BYTE_ARRAY_LIST() { 275 __gshared Builder!(List!(const(ubyte)[])) inst; 276 return initOnce!inst(new class Builder!(List!(const(ubyte)[])) { 277 override 278 279 List!(const(ubyte)[]) build(Object data) { 280 if (data is null) { 281 return null; 282 } 283 284 List!Object lst = cast(List!Object)data; 285 if(lst is null) { 286 version(HUNT_DEBUG) warning("lst is null"); 287 return null; 288 } else { 289 ArrayList!(const(ubyte)[]) result = new ArrayList!(const(ubyte)[])(lst.size()); 290 foreach(Object obj; lst) { 291 Bytes bytes = cast(Bytes)obj; 292 if(bytes is null) { 293 result.add(null); 294 } else { 295 result.add(cast(ubyte[])bytes.value); 296 } 297 } 298 return result; 299 } 300 } 301 302 override 303 string toString() { 304 return "List!(const(ubyte)[])"; 305 } 306 }); 307 } 308 309 static Builder!(Set!(const(ubyte)[])) BYTE_ARRAY_ZSET() { 310 __gshared Builder!(Set!(const(ubyte)[])) inst; 311 312 return initOnce!inst(new class Builder!(Set!(const(ubyte)[])) { 313 314 override 315 Set!(const(ubyte)[]) build(Object data) { 316 if (data is null) { 317 return null; 318 } 319 warning(typeid(data)); 320 implementationMissing(); 321 return null; 322 323 // List!(const(ubyte)[]) l = cast(List!(const(ubyte)[])) data; 324 // Set!(const(ubyte)[]) result = new LinkedHashSet!(const(ubyte)[])(l); 325 // foreach(const(ubyte)[] barray ; l) { 326 // if (barray is null) { 327 // result.add(null); 328 // } else { 329 // result.add(barray); 330 // } 331 // } 332 // return result; 333 } 334 335 override 336 string toString() { 337 return "ZSet!(const(ubyte)[])"; 338 } 339 }); 340 } 341 342 static Builder!(Map!(const(ubyte)[], const(ubyte)[])) BYTE_ARRAY_MAP() { 343 __gshared Builder!(Map!(const(ubyte)[], const(ubyte)[])) inst; 344 345 return initOnce!inst(new class Builder!(Map!(const(ubyte)[], const(ubyte)[])) { 346 347 override 348 Map!(const(ubyte)[], const(ubyte)[]) build(Object data) { 349 if (data is null) { 350 return null; 351 } 352 warning(typeid(data)); 353 implementationMissing(); 354 return null; 355 356 // Map!(const(ubyte)[], const(ubyte)[]) build(Object data) { 357 // List!(const(ubyte)[]) flatHash = (List!(const(ubyte)[])) data; 358 // Map!(const(ubyte)[], const(ubyte)[]) hash = new RedisByteHashMap(); 359 // Iterator!(const(ubyte)[]) iterator = flatHash.iterator(); 360 // while (iterator.hasNext()) { 361 // hash.put(iterator.next(), iterator.next()); 362 // } 363 364 // return hash; 365 } 366 367 override 368 string toString() { 369 return "Map!(const(ubyte)[], const(ubyte)[])"; 370 } 371 }); 372 } 373 374 375 static Builder!(Set!(string)) STRING_ZSET() { 376 __gshared Builder!(Set!(string)) inst; 377 378 return initOnce!inst(new class Builder!(Set!(string)) { 379 380 override 381 Set!(string) build(Object data) { 382 if (data is null) { 383 return null; 384 } 385 warning(typeid(data)); 386 implementationMissing(); 387 return null; 388 389 // List!(const(ubyte)[]) l = (List!(const(ubyte)[])) data; 390 // Set!(string) result = new LinkedHashSet!(string)(l.size(), 1); 391 // foreach(const(ubyte)[] barray ; l) { 392 // if (barray is null) { 393 // result.add(null); 394 // } else { 395 // result.add(SafeEncoder.encode(barray)); 396 // } 397 // } 398 // return result; 399 } 400 401 override 402 string toString() { 403 return "ZSet!(string)"; 404 } 405 }); 406 } 407 408 409 static Builder!(Set!(Tuple)) TUPLE_ZSET() { 410 __gshared Builder!(Set!(Tuple)) inst; 411 412 return initOnce!inst(new class Builder!(Set!(Tuple)) { 413 414 override 415 Set!(Tuple) build(Object data) { 416 if (data is null) { 417 return null; 418 } 419 warning(typeid(data)); 420 implementationMissing(); 421 return null; 422 423 // List!(const(ubyte)[]) l = (List!(const(ubyte)[])) data; 424 // Set!(Tuple) result = new LinkedHashSet!(Tuple)(l.size()/2, 1); 425 // Iterator!(const(ubyte)[]) iterator = l.iterator(); 426 // while (iterator.hasNext()) { 427 // result.add(new Tuple(iterator.next(), DOUBLE.build(iterator.next()))); 428 // } 429 // return result; 430 } 431 432 override 433 string toString() { 434 return "ZSet!(Tuple)"; 435 } 436 }); 437 } 438 439 static Builder!(Object) EVAL_RESULT() { 440 __gshared Builder!(Object) inst; 441 442 return initOnce!inst(new class Builder!(Object) { 443 444 override 445 Object build(Object data) { 446 if (data is null) { 447 return null; 448 } 449 warning(typeid(data)); 450 implementationMissing(); 451 return null; 452 453 // if (result instanceof const(ubyte)[]) return SafeEncoder.encode((const(ubyte)[]) result); 454 455 // if (result instanceof List<?>) { 456 // List<?> list = (List<?>) result; 457 // List!(Object) listResult = new ArrayList!(Object)(list.size()); 458 // foreach(Object bin ; list) { 459 // listResult.add(evalResult(bin)); 460 // } 461 462 // return listResult; 463 // } 464 465 // return result; 466 } 467 468 469 override 470 string toString() { 471 return "Eval!(Object)"; 472 } 473 }); 474 } 475 476 477 static Builder!(Object) EVAL_BINARY_RESULT() { 478 __gshared Builder!(Object) inst; 479 480 return initOnce!inst(new class Builder!(Object) { 481 482 override 483 Object build(Object data) { 484 if (data is null) { 485 return null; 486 } 487 warning(typeid(data)); 488 implementationMissing(); 489 return null; 490 491 // if (result instanceof List<?>) { 492 // List<?> list = (List<?>) result; 493 // List!(Object) listResult = new ArrayList!(Object)(list.size()); 494 // foreach(Object bin ; list) { 495 // listResult.add(evalResult(bin)); 496 // } 497 498 // return listResult; 499 // } 500 501 // return result; 502 } 503 504 505 override 506 string toString() { 507 return "Eval!(Object)"; 508 } 509 }); 510 } 511 512 static Builder!(List!(GeoCoordinate)) GEO_COORDINATE_LIST() { 513 __gshared Builder!(List!(GeoCoordinate)) inst; 514 return initOnce!inst(new class Builder!(List!(GeoCoordinate)) { 515 override List!(GeoCoordinate) build(Object data) { 516 if (data is null) { 517 return null; 518 } 519 return interpretGeoposResult(cast(List!(Object)) data); 520 } 521 522 override string toString() { 523 return "List!(GeoCoordinate)"; 524 } 525 }); 526 } 527 528 private static List!(GeoCoordinate) interpretGeoposResult(List!(Object) responses) { 529 List!(GeoCoordinate) responseCoordinate = new ArrayList!(GeoCoordinate)(responses.size()); 530 foreach(Object response ; responses) { 531 if (response is null) { 532 responseCoordinate.add(null); 533 } else { 534 List!(Object) respList = cast(List!(Object)) response; 535 Double first = DOUBLE.build(respList.get(0)); 536 Double second = DOUBLE.build(respList.get(1)); 537 assert(first !is null && second !is null); 538 GeoCoordinate coord = new GeoCoordinate(first.value(), second.value()); 539 responseCoordinate.add(coord); 540 } 541 } 542 return responseCoordinate; 543 } 544 545 static Builder!(List!(GeoRadiusResponse)) GEORADIUS_WITH_PARAMS_RESULT() { 546 __gshared Builder!(List!(GeoRadiusResponse)) inst; 547 548 return initOnce!inst(new class Builder!(List!(GeoRadiusResponse)) { 549 550 override List!(GeoRadiusResponse) build(Object data) { 551 if (data is null) { 552 return null; 553 } 554 555 List!(Object) objectList = cast(List!(Object)) data; 556 557 List!(GeoRadiusResponse) responses = new ArrayList!(GeoRadiusResponse)(objectList.size()); 558 if (objectList.isEmpty()) { 559 return responses; 560 } 561 562 List!Object tmp = cast(List!(Object)) objectList.get(0); 563 if (tmp !is null) { 564 // list of members with additional informations 565 GeoRadiusResponse resp; 566 foreach(Object obj ; objectList) { 567 List!(Object) informations = cast(List!(Object)) obj; 568 569 Object tempObj = informations.get(0); 570 Bytes tempData = cast(Bytes)tempObj; 571 if(tempData is null) { 572 warning(typeid(tempObj)); 573 } 574 resp = new GeoRadiusResponse(cast(string)tempData.value()); 575 576 int size = informations.size(); 577 for (int idx = 1; idx < size; idx++) { 578 Object infoObj = informations.get(idx); 579 List!(Object) coord = cast(List!(Object))infoObj; 580 if (coord !is null) { 581 // coordinate 582 resp.setCoordinate(new GeoCoordinate(DOUBLE.build(coord.get(0)).value(), 583 DOUBLE.build(coord.get(1)).value())); 584 } else { 585 // distance 586 resp.setDistance(DOUBLE.build(infoObj).value()); 587 } 588 } 589 590 responses.add(resp); 591 } 592 } else { 593 // list of members 594 foreach(Object obj ; objectList) { 595 Bytes tempObj = cast(Bytes)obj; 596 if(tempObj is null) { 597 warning(typeid(obj)); 598 } 599 responses.add(new GeoRadiusResponse(cast(string)tempObj.value())); 600 } 601 } 602 603 return responses; 604 } 605 606 override string toString() { 607 return "GeoRadiusWithParamsResult"; 608 } 609 }); 610 } 611 612 static Builder!(List!(Module)) MODULE_LIST() { 613 __gshared Builder!(List!(Module)) inst; 614 615 return initOnce!inst(new class Builder!(List!(Module)) { 616 617 override 618 List!(Module) build(Object data) { 619 if (data is null) { 620 return null; 621 } 622 623 List!(List!(Object)) objectList = cast(List!(List!(Object))) data; 624 625 List!(Module) responses = new ArrayList!(Module)(objectList.size()); 626 if (objectList.isEmpty()) { 627 return responses; 628 } 629 630 foreach(List!(Object) moduleResp; objectList) { 631 Object tempObj1 = moduleResp.get(1); 632 Object tempObj3 = moduleResp.get(3); 633 634 635 String strObj = cast(String)tempObj1; 636 if(strObj is null) { 637 warning(typeid(tempObj1)); 638 implementationMissing(); 639 } 640 641 Long longObj = cast(Long)tempObj3; 642 if(longObj is null) { 643 warning(typeid(tempObj3)); 644 implementationMissing(); 645 } 646 647 Module m = new Module(cast(string)SafeEncoder.encode(strObj.value), 648 longObj.intValue()); 649 responses.add(m); 650 } 651 652 return responses; 653 } 654 655 override string toString() { 656 return "List!(Module)"; 657 } 658 }); 659 } 660 661 static Builder!(List!(Long)) LONG_LIST() { 662 __gshared Builder!(List!(Long)) inst; 663 664 return initOnce!inst(new class Builder!(List!(Long)) { 665 override List!(Long) build(Object data) { 666 if (data is null) { 667 return null; 668 } 669 670 version(HUNT_REDIS_DEBUG) warning(typeid(data)); 671 return cast(List!(Long)) data; 672 } 673 674 override string toString() { 675 return "List!(Long)"; 676 } 677 678 }); 679 } 680 681 static Builder!(StreamEntryID) STREAM_ENTRY_ID() { 682 __gshared Builder!(StreamEntryID) inst; 683 684 return initOnce!inst(new class Builder!(StreamEntryID) { 685 override StreamEntryID build(Object data) { 686 if (data is null) { 687 return null; 688 } 689 690 String strObj = cast(String)data; 691 if(strObj is null) { 692 warning(typeid(data)); 693 implementationMissing(); 694 } 695 696 string id = cast(string)SafeEncoder.encode(strObj.value); 697 return new StreamEntryID(id); 698 } 699 700 override string toString() { 701 return "StreamEntryID"; 702 } 703 704 }); 705 } 706 707 static Builder!(List!(StreamEntry)) STREAM_ENTRY_LIST() { 708 709 __gshared Builder!(List!(StreamEntry)) inst; 710 711 return initOnce!inst(new class Builder!(List!(StreamEntry)) { 712 713 override List!(StreamEntry) build(Object data) { 714 715 List!(Object) objectList = cast(ArrayList!(Object)) data; 716 if(objectList is null) { 717 warningf("mismatched type: %s", typeid(data)); 718 return new ArrayList!(StreamEntry)(); 719 } 720 721 List!(StreamEntry) responses = new ArrayList!(StreamEntry)(objectList.size()/2); 722 if (objectList.isEmpty()) { 723 return responses; 724 } 725 726 foreach(Object obj0; objectList) { 727 ArrayList!(Object) res = cast(ArrayList!(Object))obj0; 728 729 assert(res !is null); 730 version(HUNT_REDIS_DEBUG_MORE) { 731 warning(typeid(res.get(0))); 732 } 733 734 Object obj = res.get(0); 735 Bytes bytes = cast(Bytes)obj; 736 if(bytes is null) { 737 warningf("wrong type: %s", typeid(obj)); 738 continue; 739 } 740 741 string entryIdString = SafeEncoder.encode(cast(ubyte[])bytes.value()); 742 StreamEntryID entryID = new StreamEntryID(entryIdString); 743 744 obj = res.get(1); 745 version(HUNT_REDIS_DEBUG_MORE) { 746 warningf("%s", typeid(obj)); 747 } 748 749 List!(Object) hash = cast(List!(Object))obj; 750 if(hash is null) { 751 warningf("wrong type: %s", typeid(obj)); 752 continue; 753 } 754 755 Map!(string, string) map = new HashMap!(string, string)(hash.size()/2); 756 757 for(int i=0; i<hash.size(); i=i+2) { 758 bytes = cast(Bytes)hash[i]; 759 string first = SafeEncoder.encode(cast(ubyte[])bytes.value()); 760 761 bytes = cast(Bytes)hash[i+1]; 762 string second = SafeEncoder.encode(cast(ubyte[])bytes.value()); 763 764 version(HUNT_REDIS_DEBUG_MORE) { 765 warningf("first: %s, second: %s", first, second); 766 } 767 map.put(first, second); 768 } 769 770 responses.add(new StreamEntry(entryID, map)); 771 } 772 773 return responses; 774 } 775 776 override string toString() { 777 return "List!(StreamEntry)"; 778 } 779 780 }); 781 } 782 783 static Builder!(List!(StreamPendingEntry)) STREAM_PENDING_ENTRY_LIST() { 784 __gshared Builder!(List!(StreamPendingEntry)) inst; 785 786 return initOnce!inst(new class Builder!(List!(StreamPendingEntry)) { 787 override List!(StreamPendingEntry) build(Object data) { 788 789 List!(Object) streamsEntries = cast(List!(Object))data; 790 if(streamsEntries is null) { 791 warningf("mismatched type: %s", typeid(data)); 792 return new ArrayList!(StreamPendingEntry)(); 793 } 794 795 List!(StreamPendingEntry) result = new ArrayList!(StreamPendingEntry)(streamsEntries.size()); 796 797 foreach(Object streamObj ; streamsEntries) { 798 List!(Object) stream = cast(List!(Object))streamObj; 799 800 // 801 Object obj = stream.get(0); 802 Bytes bytes = cast(Bytes)obj; 803 if(bytes is null) { 804 warningf("wrong type: %s", typeid(obj)); 805 continue; 806 } 807 string id = SafeEncoder.encode(cast(ubyte[])bytes.value()); 808 809 // 810 obj = stream.get(1); 811 bytes = cast(Bytes)obj; 812 if(bytes is null) { 813 warningf("wrong type: %s", typeid(obj)); 814 continue; 815 } 816 string consumerName = SafeEncoder.encode(cast(ubyte[])bytes.value()); 817 818 819 long idleTime = BuilderFactory.LONG.build(stream.get(2)).value(); 820 long deliveredTimes = BuilderFactory.LONG.build(stream.get(3)).value(); 821 822 result.add(new StreamPendingEntry(new StreamEntryID(id), consumerName, idleTime, deliveredTimes)); 823 } 824 825 return result; 826 } 827 828 override string toString() { 829 return "List!(StreamPendingEntry)"; 830 } 831 832 }); 833 } 834 835 static Builder!(Object) OBJECT() { 836 __gshared Builder!(Object) inst; 837 return initOnce!inst(new class Builder!(Object) { 838 override Object build(Object data) { 839 return data; 840 } 841 842 override string toString() { 843 return "Object"; 844 } 845 }); 846 } 847 848 private this() { 849 throw new InstantiationError( "Must not instantiate this class" ); 850 } 851 852 }